Merge "Update feature flag bugs" into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index c231b30..70cda8b 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -71,6 +71,8 @@
     ":android.appwidget.flags-aconfig-java{.generated_srcjars}",
     ":android.webkit.flags-aconfig-java{.generated_srcjars}",
     ":android.provider.flags-aconfig-java{.generated_srcjars}",
+    ":android.chre.flags-aconfig-java{.generated_srcjars}",
+    ":android.speech.flags-aconfig-java{.generated_srcjars}",
 ]
 
 filegroup {
@@ -180,6 +182,12 @@
     ],
 }
 
+cc_aconfig_library {
+    name: "android_location_flags_c_lib",
+    vendor_available: true,
+    aconfig_declarations: "android.location.flags-aconfig",
+}
+
 java_aconfig_library {
     name: "android.location.flags-aconfig-java",
     aconfig_declarations: "android.location.flags-aconfig",
@@ -249,6 +257,13 @@
     defaults: ["framework-minus-apex-aconfig-java-defaults"],
 }
 
+java_aconfig_library {
+    name: "android.app.usage.flags-aconfig-java-host",
+    aconfig_declarations: "android.app.usage.flags-aconfig",
+    host_supported: true,
+    defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
 // OS
 aconfig_declarations {
     name: "android.os.flags-aconfig",
@@ -892,3 +907,23 @@
     aconfig_declarations: "android.provider.flags-aconfig",
     defaults: ["framework-minus-apex-aconfig-java-defaults"],
 }
+
+// ContextHub
+java_aconfig_library {
+    name: "android.chre.flags-aconfig-java",
+    aconfig_declarations: "chre_flags",
+    defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
+// Speech
+aconfig_declarations {
+    name: "android.speech.flags-aconfig",
+    package: "android.speech.flags",
+    srcs: ["core/java/android/speech/flags/*.aconfig"],
+}
+
+java_aconfig_library {
+    name: "android.speech.flags-aconfig-java",
+    aconfig_declarations: "android.speech.flags-aconfig",
+    defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
diff --git a/Android.bp b/Android.bp
index bb93048..c84de51 100644
--- a/Android.bp
+++ b/Android.bp
@@ -132,6 +132,7 @@
         ":libcamera_client_aidl",
         ":libcamera_client_framework_aidl",
         ":libupdate_engine_aidl",
+        ":libupdate_engine_stable-V2-java-source",
         ":logd_aidl",
         ":resourcemanager_aidl",
         ":storaged_aidl",
@@ -214,9 +215,11 @@
         "apex_aidl_interface-java",
         "packagemanager_aidl-java",
         "framework-protos",
+        "libtombstone_proto_java",
         "updatable-driver-protos",
         "ota_metadata_proto_java",
         "android.hidl.base-V1.0-java",
+        "android.hidl.manager-V1.2-java",
         "android.hardware.cas-V1-java", // AIDL
         "android.hardware.cas-V1.0-java",
         "android.hardware.cas-V1.1-java",
@@ -502,6 +505,9 @@
             "-Xep:AndroidFrameworkUid:ERROR",
         ],
     },
+    lint: {
+        baseline_filename: "lint-baseline.xml",
+    },
 }
 
 java_library {
@@ -516,6 +522,7 @@
     },
     lint: {
         enabled: false,
+
     },
 }
 
@@ -634,6 +641,10 @@
         "//frameworks/base/services/net",
         "//packages/modules/Wifi/framework",
     ],
+    lint: {
+        baseline_filename: "lint-baseline.xml",
+    },
+    apex_available: ["com.android.wifi"],
 }
 
 filegroup {
diff --git a/ProtoLibraries.bp b/ProtoLibraries.bp
index e7adf20..d03bbd2 100644
--- a/ProtoLibraries.bp
+++ b/ProtoLibraries.bp
@@ -34,7 +34,6 @@
         ":ipconnectivity-proto-src",
         ":libstats_atom_enum_protos",
         ":libstats_atom_message_protos",
-        ":libtombstone_proto-src",
         "core/proto/**/*.proto",
         "libs/incident/**/*.proto",
     ],
diff --git a/STABILITY_OWNERS b/STABILITY_OWNERS
new file mode 100644
index 0000000..a7ecb4d
--- /dev/null
+++ b/STABILITY_OWNERS
@@ -0,0 +1,2 @@
+gaillard@google.com
+
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 117faa2..ecfd86c 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -132,18 +132,20 @@
     },
     {
       "name": "vts_treble_vintf_vendor_test"
+    },
+    {
+      "name": "CtsStrictJavaPackagesTestCases"
     }
   ],
   "postsubmit-ravenwood": [
-    // TODO(ravenwood) promote it to presubmit
-    // TODO: Enable it once the infra knows how to run it.
-//    {
-//      "name": "CtsUtilTestCasesRavenwood",
-//      "file_patterns": [
-//        "*Ravenwood*",
-//        "*ravenwood*"
-//      ]
-//    }
+    {
+      "name": "CtsUtilTestCasesRavenwood",
+      "host": true,
+      "file_patterns": [
+        "*Ravenwood*",
+        "*ravenwood*"
+      ]
+    }
   ],
   "postsubmit-managedprofile-stress": [
     {
diff --git a/WEAR_OWNERS b/WEAR_OWNERS
index 4f3bc27..4127f99 100644
--- a/WEAR_OWNERS
+++ b/WEAR_OWNERS
@@ -4,3 +4,9 @@
 adsule@google.com
 andriyn@google.com
 yfz@google.com
+con@google.com
+leetodd@google.com
+sadrul@google.com
+rwmyers@google.com
+nalmalki@google.com
+shijianli@google.com
diff --git a/apct-tests/perftests/core/src/android/database/SQLiteDatabasePerfTest.java b/apct-tests/perftests/core/src/android/database/SQLiteDatabasePerfTest.java
index b7460cd..fa4387b 100644
--- a/apct-tests/perftests/core/src/android/database/SQLiteDatabasePerfTest.java
+++ b/apct-tests/perftests/core/src/android/database/SQLiteDatabasePerfTest.java
@@ -433,6 +433,17 @@
         performMultithreadedReadWriteTest();
     }
 
+    /**
+     * This test measures a multi-threaded read-write environment where there are 2 readers and
+     * 1 writer in the database using WAL journal mode and NORMAL syncMode.
+     */
+    @Test
+    public void testMultithreadedReadWriteWithWalNormal() {
+        recreateTestDatabase(SQLiteDatabase.JOURNAL_MODE_WAL, SQLiteDatabase.SYNC_MODE_NORMAL);
+        insertT1TestDataSet();
+        performMultithreadedReadWriteTest();
+    }
+
     private void doReadLoop(int totalIterations) {
         Random rnd = new Random(0);
         int currentIteration = 0;
@@ -472,7 +483,6 @@
     }
 
     private void doUpdateLoop(int totalIterations) {
-        SQLiteDatabase db = mContext.openOrCreateDatabase(DB_NAME, Context.MODE_PRIVATE, null);
         Random rnd = new Random(0);
         int i = 0;
         ContentValues cv = new ContentValues();
@@ -485,24 +495,12 @@
             cv.put("COL_B", "UpdatedValue");
             cv.put("COL_C", i);
             argArray[0] = String.valueOf(id);
-            db.update("T1", cv, "_ID=?", argArray);
+            mDatabase.update("T1", cv, "_ID=?", argArray);
             i++;
             android.os.Trace.endSection();
         }
     }
 
-    /**
-     * This test measures a multi-threaded read-write environment where there are 2 readers and
-     * 1 writer in the database using WAL journal mode and NORMAL syncMode.
-     */
-    @Test
-    public void testMultithreadedReadWriteWithWalNormal() {
-        recreateTestDatabase(SQLiteDatabase.JOURNAL_MODE_WAL, SQLiteDatabase.SYNC_MODE_NORMAL);
-        insertT1TestDataSet();
-
-        performMultithreadedReadWriteTest();
-    }
-
     private void performMultithreadedReadWriteTest() {
         int totalBGIterations = 10000;
         // Writer - Fixed iterations to avoid consuming cycles from mainloop benchmark iterations
@@ -555,4 +553,3 @@
         createOrOpenTestDatabase(journalMode, syncMode);
     }
 }
-
diff --git a/apct-tests/perftests/core/src/android/graphics/perftests/CanvasPerfTest.java b/apct-tests/perftests/core/src/android/graphics/perftests/CanvasPerfTest.java
index e5a06c9..3c361d7 100644
--- a/apct-tests/perftests/core/src/android/graphics/perftests/CanvasPerfTest.java
+++ b/apct-tests/perftests/core/src/android/graphics/perftests/CanvasPerfTest.java
@@ -16,12 +16,14 @@
 
 package android.graphics.perftests;
 
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
 import android.content.Context;
 import android.graphics.Bitmap;
 import android.graphics.Bitmap.Config;
 import android.graphics.Color;
+import android.graphics.ColorSpace;
 import android.graphics.ImageDecoder;
 import android.graphics.Paint;
 import android.graphics.RecordingCanvas;
@@ -104,15 +106,36 @@
     }
 
     @Test
-    public void testCreateScaledBitmap() throws IOException {
+    public void testCreateScaledSrgbBitmap() throws IOException {
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         final Context context = InstrumentationRegistry.getContext();
         Bitmap source = ImageDecoder.decodeBitmap(
                 ImageDecoder.createSource(context.getResources(), R.drawable.fountain_night),
                 (decoder, info, source1) -> {
                     decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
+                    decoder.setTargetColorSpace(ColorSpace.get(ColorSpace.Named.SRGB));
                 });
         source.setGainmap(null);
+        assertEquals(source.getColorSpace().getId(), ColorSpace.Named.SRGB.ordinal());
+
+        while (state.keepRunning()) {
+            Bitmap.createScaledBitmap(source, source.getWidth() / 2, source.getHeight() / 2, true)
+                    .recycle();
+        }
+    }
+
+    @Test
+    public void testCreateScaledP3Bitmap() throws IOException {
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final Context context = InstrumentationRegistry.getContext();
+        Bitmap source = ImageDecoder.decodeBitmap(
+                ImageDecoder.createSource(context.getResources(), R.drawable.fountain_night),
+                (decoder, info, source1) -> {
+                    decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
+                    decoder.setTargetColorSpace(ColorSpace.get(ColorSpace.Named.DISPLAY_P3));
+                });
+        source.setGainmap(null);
+        assertEquals(source.getColorSpace().getId(), ColorSpace.Named.DISPLAY_P3.ordinal());
 
         while (state.keepRunning()) {
             Bitmap.createScaledBitmap(source, source.getWidth() / 2, source.getHeight() / 2, true)
diff --git a/apct-tests/perftests/packagemanager/src/android/os/PackageParsingPerfTest.kt b/apct-tests/perftests/packagemanager/src/android/os/PackageParsingPerfTest.kt
index 4352c8a..ea10690 100644
--- a/apct-tests/perftests/packagemanager/src/android/os/PackageParsingPerfTest.kt
+++ b/apct-tests/perftests/packagemanager/src/android/os/PackageParsingPerfTest.kt
@@ -24,11 +24,11 @@
 import android.content.res.TypedArray
 import android.perftests.utils.BenchmarkState
 import android.perftests.utils.PerfStatusReporter
-import android.util.ArraySet
 import androidx.test.filters.LargeTest
 import com.android.internal.pm.parsing.pkg.PackageImpl
 import com.android.internal.pm.pkg.parsing.ParsingPackageUtils
 import com.android.internal.util.ConcurrentUtils
+import com.android.server.SystemConfig
 import java.io.File
 import java.io.FileOutputStream
 import java.util.concurrent.ArrayBlockingQueue
@@ -217,8 +217,10 @@
                     isCoreApp,
                     this,
                 )
-                override fun getHiddenApiWhitelistedApps() = ArraySet<String>()
-                override fun getInstallConstraintsAllowlist() = ArraySet<String>()
+                override fun getHiddenApiWhitelistedApps() =
+                        SystemConfig.getInstance().hiddenApiWhitelistedApps
+                override fun getInstallConstraintsAllowlist() =
+                        SystemConfig.getInstance().installConstraintsAllowlist
             })
 
         override fun parseImpl(file: File) =
diff --git a/apct-tests/perftests/permission/Android.bp b/apct-tests/perftests/permission/Android.bp
new file mode 100644
index 0000000..b80a6af
--- /dev/null
+++ b/apct-tests/perftests/permission/Android.bp
@@ -0,0 +1,87 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+    // 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: "PermissionServicePerfTests",
+
+    srcs: [
+        "src/**/*.java",
+        "src/**/*.kt",
+    ],
+
+    static_libs: [
+        "platform-compat-test-rules",
+        "androidx.appcompat_appcompat",
+        "androidx.test.rules",
+        "androidx.test.ext.junit",
+        "androidx.annotation_annotation",
+        "apct-perftests-utils",
+        "androidx.benchmark_benchmark-common",
+        "androidx.benchmark_benchmark-junit4",
+        "collector-device-lib-platform",
+        "cts-install-lib-java",
+    ],
+
+    libs: ["android.test.base"],
+
+    platform_apis: true,
+
+    test_suites: ["device-tests"],
+
+    data: [
+        ":UsePermissionApp0",
+        ":UsePermissionApp1",
+        ":UsePermissionApp2",
+        ":UsePermissionApp3",
+        ":UsePermissionApp4",
+        ":UsePermissionApp5",
+        ":UsePermissionApp6",
+        ":UsePermissionApp7",
+        ":UsePermissionApp8",
+        ":UsePermissionApp9",
+        ":UsePermissionApp10",
+        ":UsePermissionApp11",
+        ":UsePermissionApp12",
+        ":UsePermissionApp13",
+        ":UsePermissionApp14",
+        ":UsePermissionApp15",
+        ":UsePermissionApp16",
+        ":UsePermissionApp17",
+        ":UsePermissionApp18",
+        ":UsePermissionApp19",
+        ":UsePermissionApp20",
+        ":UsePermissionApp21",
+        ":UsePermissionApp22",
+        ":UsePermissionApp23",
+        ":UsePermissionApp24",
+        ":UsePermissionApp25",
+        ":UsePermissionApp26",
+        ":UsePermissionApp27",
+        ":UsePermissionApp28",
+        ":UsePermissionApp29",
+        ":perfetto_artifacts",
+    ],
+
+    certificate: "platform",
+
+}
diff --git a/apct-tests/perftests/permission/AndroidManifest.xml b/apct-tests/perftests/permission/AndroidManifest.xml
new file mode 100644
index 0000000..fa29ad0
--- /dev/null
+++ b/apct-tests/perftests/permission/AndroidManifest.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2024 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.perftests.permission">
+
+    <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/>
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+        <activity android:name="android.perftests.utils.PerfTestActivity"
+            android:exported="true">
+          <intent-filter>
+            <action android:name="android.perftests.permission.PERFTEST" />
+          </intent-filter>
+        </activity>
+    </application>
+
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+        android:targetPackage="android.perftests.permission"/>
+
+</manifest>
diff --git a/apct-tests/perftests/permission/AndroidTest.xml b/apct-tests/perftests/permission/AndroidTest.xml
new file mode 100644
index 0000000..07558de
--- /dev/null
+++ b/apct-tests/perftests/permission/AndroidTest.xml
@@ -0,0 +1,180 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<configuration description="Runs PermissionServicePerfTests metric instrumentation.">
+    <option name="test-suite-tag" value="apct"/>
+    <option name="test-suite-tag" value="apct-metric-instrumentation"/>
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true"/>
+        <option name="test-file-name" value="PermissionServicePerfTests.apk"/>
+    </target_preparer>
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true"/>
+        <option name="force-queryable" value="false"/>
+        <option name="test-file-name" value="UsePermissionApp0.apk"/>
+        <option name="test-file-name" value="UsePermissionApp1.apk"/>
+        <option name="test-file-name" value="UsePermissionApp2.apk"/>
+        <option name="test-file-name" value="UsePermissionApp3.apk"/>
+        <option name="test-file-name" value="UsePermissionApp4.apk"/>
+        <option name="test-file-name" value="UsePermissionApp5.apk"/>
+        <option name="test-file-name" value="UsePermissionApp6.apk"/>
+        <option name="test-file-name" value="UsePermissionApp7.apk"/>
+        <option name="test-file-name" value="UsePermissionApp8.apk"/>
+        <option name="test-file-name" value="UsePermissionApp9.apk"/>
+        <option name="test-file-name" value="UsePermissionApp10.apk"/>
+        <option name="test-file-name" value="UsePermissionApp11.apk"/>
+        <option name="test-file-name" value="UsePermissionApp12.apk"/>
+        <option name="test-file-name" value="UsePermissionApp13.apk"/>
+        <option name="test-file-name" value="UsePermissionApp14.apk"/>
+        <option name="test-file-name" value="UsePermissionApp15.apk"/>
+        <option name="test-file-name" value="UsePermissionApp16.apk"/>
+        <option name="test-file-name" value="UsePermissionApp17.apk"/>
+        <option name="test-file-name" value="UsePermissionApp18.apk"/>
+        <option name="test-file-name" value="UsePermissionApp19.apk"/>
+        <option name="test-file-name" value="UsePermissionApp20.apk"/>
+        <option name="test-file-name" value="UsePermissionApp21.apk"/>
+        <option name="test-file-name" value="UsePermissionApp22.apk"/>
+        <option name="test-file-name" value="UsePermissionApp23.apk"/>
+        <option name="test-file-name" value="UsePermissionApp24.apk"/>
+        <option name="test-file-name" value="UsePermissionApp25.apk"/>
+        <option name="test-file-name" value="UsePermissionApp26.apk"/>
+        <option name="test-file-name" value="UsePermissionApp27.apk"/>
+        <option name="test-file-name" value="UsePermissionApp28.apk"/>
+        <option name="test-file-name" value="UsePermissionApp29.apk"/>
+    </target_preparer>
+
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+        <option name="package" value="android.perftests.permission"/>
+        <option name="hidden-api-checks" value="false"/>
+    </test>
+
+    <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
+        <option name="directory-keys" value="/data/local/PermissionServicePerfTests"/>
+        <option name="collect-on-run-ended-only" value="true"/>
+    </metrics_collector>
+
+    <!-- Needed for pushing the trace config file -->
+    <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+        <option name="push-file" key="trace_config_detailed.textproto"
+                value="/data/misc/perfetto-traces/trace_config.textproto"/>
+        <!--Install the content provider automatically when we push some file in sdcard folder.-->
+        <!--Needed to avoid the installation during the test suite.-->
+        <option name="push-file" key="trace_config_detailed.textproto"
+                value="/sdcard/sample.textproto"/>
+        <option name="push-file" key="UsePermissionApp0.apk"
+                value="/data/local/tmp/perftests/UsePermissionApp0.apk" />
+        <option name="push-file" key="UsePermissionApp1.apk"
+                value="/data/local/tmp/perftests/UsePermissionApp1.apk" />
+        <option name="push-file" key="UsePermissionApp2.apk"
+                value="/data/local/tmp/perftests/UsePermissionApp2.apk" />
+        <option name="push-file" key="UsePermissionApp3.apk"
+                value="/data/local/tmp/perftests/UsePermissionApp3.apk" />
+        <option name="push-file" key="UsePermissionApp4.apk"
+                value="/data/local/tmp/perftests/UsePermissionApp4.apk" />
+        <option name="push-file" key="UsePermissionApp5.apk"
+                value="/data/local/tmp/perftests/UsePermissionApp5.apk" />
+        <option name="push-file" key="UsePermissionApp6.apk"
+                value="/data/local/tmp/perftests/UsePermissionApp6.apk" />
+        <option name="push-file" key="UsePermissionApp7.apk"
+                value="/data/local/tmp/perftests/UsePermissionApp7.apk" />
+        <option name="push-file" key="UsePermissionApp8.apk"
+                value="/data/local/tmp/perftests/UsePermissionApp8.apk" />
+        <option name="push-file" key="UsePermissionApp9.apk"
+                value="/data/local/tmp/perftests/UsePermissionApp9.apk" />
+        <option name="push-file" key="UsePermissionApp10.apk"
+                value="/data/local/tmp/perftests/UsePermissionApp10.apk" />
+        <option name="push-file" key="UsePermissionApp11.apk"
+                value="/data/local/tmp/perftests/UsePermissionApp11.apk" />
+        <option name="push-file" key="UsePermissionApp12.apk"
+                value="/data/local/tmp/perftests/UsePermissionApp12.apk" />
+        <option name="push-file" key="UsePermissionApp13.apk"
+                value="/data/local/tmp/perftests/UsePermissionApp13.apk" />
+        <option name="push-file" key="UsePermissionApp14.apk"
+                value="/data/local/tmp/perftests/UsePermissionApp14.apk" />
+        <option name="push-file" key="UsePermissionApp15.apk"
+                value="/data/local/tmp/perftests/UsePermissionApp15.apk" />
+        <option name="push-file" key="UsePermissionApp16.apk"
+                value="/data/local/tmp/perftests/UsePermissionApp16.apk" />
+        <option name="push-file" key="UsePermissionApp17.apk"
+                value="/data/local/tmp/perftests/UsePermissionApp17.apk" />
+        <option name="push-file" key="UsePermissionApp18.apk"
+                value="/data/local/tmp/perftests/UsePermissionApp18.apk" />
+        <option name="push-file" key="UsePermissionApp19.apk"
+                value="/data/local/tmp/perftests/UsePermissionApp19.apk" />
+        <option name="push-file" key="UsePermissionApp20.apk"
+                value="/data/local/tmp/perftests/UsePermissionApp20.apk" />
+        <option name="push-file" key="UsePermissionApp21.apk"
+                value="/data/local/tmp/perftests/UsePermissionApp21.apk" />
+        <option name="push-file" key="UsePermissionApp22.apk"
+                value="/data/local/tmp/perftests/UsePermissionApp22.apk" />
+        <option name="push-file" key="UsePermissionApp23.apk"
+                value="/data/local/tmp/perftests/UsePermissionApp23.apk" />
+        <option name="push-file" key="UsePermissionApp24.apk"
+                value="/data/local/tmp/perftests/UsePermissionApp24.apk" />
+        <option name="push-file" key="UsePermissionApp25.apk"
+                value="/data/local/tmp/perftests/UsePermissionApp25.apk" />
+        <option name="push-file" key="UsePermissionApp26.apk"
+                value="/data/local/tmp/perftests/UsePermissionApp26.apk" />
+        <option name="push-file" key="UsePermissionApp27.apk"
+                value="/data/local/tmp/perftests/UsePermissionApp27.apk" />
+        <option name="push-file" key="UsePermissionApp28.apk"
+                value="/data/local/tmp/perftests/UsePermissionApp28.apk" />
+        <option name="push-file" key="UsePermissionApp29.apk"
+                value="/data/local/tmp/perftests/UsePermissionApp29.apk" />
+    </target_preparer>
+
+    <!-- Needed for pulling the collected trace config on to the host -->
+    <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
+        <option name="pull-pattern-keys" value="perfetto_file_path"/>
+    </metrics_collector>
+
+    <!-- Needed for storing the perfetto trace files in the sdcard/test_results -->
+    <option name="isolated-storage" value="false"/>
+
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+        <option name="package" value="android.perftests.permission"/>
+        <option name="hidden-api-checks" value="false"/>
+
+        <!-- Listener related args for collecting the traces and waiting for the device to
+             stabilize. -->
+        <option name="device-listeners"
+                value="android.device.collectors.ProcLoadListener,android.device.collectors.PerfettoListener"/>
+        <!-- Guarantee that user defined RunListeners will be running before any of the default
+             listeners defined in this runner. -->
+        <option name="instrumentation-arg" key="newRunListenerMode" value="true"/>
+
+        <!-- ProcLoadListener related arguments -->
+        <!-- Wait for device last minute threshold to reach 3 with 2 minute timeout before starting
+             the test run -->
+        <option name="instrumentation-arg" key="procload-collector:per_run" value="true"/>
+        <option name="instrumentation-arg" key="proc-loadavg-threshold" value="3"/>
+        <option name="instrumentation-arg" key="proc-loadavg-timeout" value="120000"/>
+        <option name="instrumentation-arg" key="proc-loadavg-interval" value="10000"/>
+
+        <!-- PerfettoListener related arguments -->
+        <option name="instrumentation-arg" key="perfetto_config_text_proto" value="true"/>
+        <option name="instrumentation-arg" key="perfetto_config_file"
+                value="trace_config.textproto"/>
+
+        <!--
+         PackageInstallerBenchmark will break for 5 minutes time out so it changes to 10 minutes
+          -->
+        <option name="test-timeout" value="600000" />
+    </test>
+
+
+</configuration>
diff --git a/apct-tests/perftests/permission/apps/usepermissionapp/Android.bp b/apct-tests/perftests/permission/apps/usepermissionapp/Android.bp
new file mode 100644
index 0000000..1ad20b6
--- /dev/null
+++ b/apct-tests/perftests/permission/apps/usepermissionapp/Android.bp
@@ -0,0 +1,232 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test_helper_app {
+    name: "UsePermissionApp0",
+    aaptflags: [
+        "--rename-manifest-package android.perftests.appenumeration0",
+    ],
+}
+
+android_test_helper_app {
+    name: "UsePermissionApp1",
+    aaptflags: [
+        "--rename-manifest-package android.perftests.appenumeration1",
+    ],
+}
+
+android_test_helper_app {
+    name: "UsePermissionApp2",
+    aaptflags: [
+        "--rename-manifest-package android.perftests.appenumeration2",
+    ],
+}
+
+android_test_helper_app {
+    name: "UsePermissionApp3",
+    aaptflags: [
+        "--rename-manifest-package android.perftests.appenumeration3",
+    ],
+}
+
+android_test_helper_app {
+    name: "UsePermissionApp4",
+    aaptflags: [
+        "--rename-manifest-package android.perftests.appenumeration4",
+    ],
+}
+
+android_test_helper_app {
+    name: "UsePermissionApp5",
+    aaptflags: [
+        "--rename-manifest-package android.perftests.appenumeration5",
+    ],
+}
+
+android_test_helper_app {
+    name: "UsePermissionApp6",
+    aaptflags: [
+        "--rename-manifest-package android.perftests.appenumeration6",
+    ],
+}
+
+android_test_helper_app {
+    name: "UsePermissionApp7",
+    aaptflags: [
+        "--rename-manifest-package android.perftests.appenumeration7",
+    ],
+}
+
+android_test_helper_app {
+    name: "UsePermissionApp8",
+    aaptflags: [
+        "--rename-manifest-package android.perftests.appenumeration8",
+    ],
+}
+
+android_test_helper_app {
+    name: "UsePermissionApp9",
+    aaptflags: [
+        "--rename-manifest-package android.perftests.appenumeration9",
+    ],
+}
+
+android_test_helper_app {
+    name: "UsePermissionApp10",
+    aaptflags: [
+        "--rename-manifest-package android.perftests.appenumeration10",
+    ],
+}
+
+android_test_helper_app {
+    name: "UsePermissionApp11",
+    aaptflags: [
+        "--rename-manifest-package android.perftests.appenumeration11",
+    ],
+}
+
+android_test_helper_app {
+    name: "UsePermissionApp12",
+    aaptflags: [
+        "--rename-manifest-package android.perftests.appenumeration12",
+    ],
+}
+
+android_test_helper_app {
+    name: "UsePermissionApp13",
+    aaptflags: [
+        "--rename-manifest-package android.perftests.appenumeration13",
+    ],
+}
+
+android_test_helper_app {
+    name: "UsePermissionApp14",
+    aaptflags: [
+        "--rename-manifest-package android.perftests.appenumeration14",
+    ],
+}
+
+android_test_helper_app {
+    name: "UsePermissionApp15",
+    aaptflags: [
+        "--rename-manifest-package android.perftests.appenumeration15",
+    ],
+}
+
+android_test_helper_app {
+    name: "UsePermissionApp16",
+    aaptflags: [
+        "--rename-manifest-package android.perftests.appenumeration16",
+    ],
+}
+
+android_test_helper_app {
+    name: "UsePermissionApp17",
+    aaptflags: [
+        "--rename-manifest-package android.perftests.appenumeration17",
+    ],
+}
+
+android_test_helper_app {
+    name: "UsePermissionApp18",
+    aaptflags: [
+        "--rename-manifest-package android.perftests.appenumeration18",
+    ],
+}
+
+android_test_helper_app {
+    name: "UsePermissionApp19",
+    aaptflags: [
+        "--rename-manifest-package android.perftests.appenumeration19",
+    ],
+}
+
+android_test_helper_app {
+    name: "UsePermissionApp20",
+    aaptflags: [
+        "--rename-manifest-package android.perftests.appenumeration20",
+    ],
+}
+
+android_test_helper_app {
+    name: "UsePermissionApp21",
+    aaptflags: [
+        "--rename-manifest-package android.perftests.appenumeration21",
+    ],
+}
+
+android_test_helper_app {
+    name: "UsePermissionApp22",
+    aaptflags: [
+        "--rename-manifest-package android.perftests.appenumeration22",
+    ],
+}
+
+android_test_helper_app {
+    name: "UsePermissionApp23",
+    aaptflags: [
+        "--rename-manifest-package android.perftests.appenumeration23",
+    ],
+}
+
+android_test_helper_app {
+    name: "UsePermissionApp24",
+    aaptflags: [
+        "--rename-manifest-package android.perftests.appenumeration24",
+    ],
+}
+
+android_test_helper_app {
+    name: "UsePermissionApp25",
+    aaptflags: [
+        "--rename-manifest-package android.perftests.appenumeration25",
+    ],
+}
+
+android_test_helper_app {
+    name: "UsePermissionApp26",
+    aaptflags: [
+        "--rename-manifest-package android.perftests.appenumeration26",
+    ],
+}
+
+android_test_helper_app {
+    name: "UsePermissionApp27",
+    aaptflags: [
+        "--rename-manifest-package android.perftests.appenumeration27",
+    ],
+}
+
+android_test_helper_app {
+    name: "UsePermissionApp28",
+    aaptflags: [
+        "--rename-manifest-package android.perftests.appenumeration28",
+    ],
+}
+
+android_test_helper_app {
+    name: "UsePermissionApp29",
+    aaptflags: [
+        "--rename-manifest-package android.perftests.appenumeration29",
+    ],
+}
diff --git a/apct-tests/perftests/permission/apps/usepermissionapp/AndroidManifest.xml b/apct-tests/perftests/permission/apps/usepermissionapp/AndroidManifest.xml
new file mode 100644
index 0000000..3bccefd
--- /dev/null
+++ b/apct-tests/perftests/permission/apps/usepermissionapp/AndroidManifest.xml
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2024 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="android.perftests.appenumeration">
+
+    <uses-permission android:name="android.permission.RECORD_AUDIO"/>
+    <uses-permission android:name="android.permission.CAMERA"/>
+    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.ACCESS_MEDIA_LOCATION" />
+    <uses-permission android:name="android.permission.RECORD_BACKGROUND_AUDIO" />
+    <uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" />
+    <uses-permission android:name="android.permission.BACKGROUND_CAMERA" />
+    <uses-permission android:name="android.permission.HIGH_SAMPLING_RATE_SENSORS" />
+    <uses-permission android:name="android.permission.BODY_SENSORS" />
+    <uses-permission android:name="android.permission.USE_BIOMETRIC" />
+    <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
+    <uses-permission android:name="android.permission.SET_WALLPAPER" />
+    <uses-permission android:name="android.permission.SET_WALLPAPER_HINTS" />
+
+    <queries>
+        <package android:name="android.perftests.appenumeration0" />
+        <package android:name="android.perftests.appenumeration1" />
+        <package android:name="android.perftests.appenumeration2" />
+        <package android:name="android.perftests.appenumeration3" />
+        <package android:name="android.perftests.appenumeration4" />
+        <package android:name="android.perftests.appenumeration5" />
+        <package android:name="android.perftests.appenumeration6" />
+        <package android:name="android.perftests.appenumeration7" />
+        <package android:name="android.perftests.appenumeration8" />
+        <package android:name="android.perftests.appenumeration9" />
+        <package android:name="android.perftests.appenumeration10" />
+        <package android:name="android.perftests.appenumeration11" />
+        <package android:name="android.perftests.appenumeration12" />
+        <package android:name="android.perftests.appenumeration13" />
+        <package android:name="android.perftests.appenumeration14" />
+        <package android:name="android.perftests.appenumeration15" />
+        <package android:name="android.perftests.appenumeration16" />
+        <package android:name="android.perftests.appenumeration17" />
+        <package android:name="android.perftests.appenumeration18" />
+        <package android:name="android.perftests.appenumeration19" />
+        <package android:name="android.perftests.appenumeration20" />
+        <package android:name="android.perftests.appenumeration21" />
+        <package android:name="android.perftests.appenumeration22" />
+        <package android:name="android.perftests.appenumeration23" />
+        <package android:name="android.perftests.appenumeration24" />
+        <package android:name="android.perftests.appenumeration25" />
+        <package android:name="android.perftests.appenumeration26" />
+        <package android:name="android.perftests.appenumeration27" />
+        <package android:name="android.perftests.appenumeration28" />
+        <package android:name="android.perftests.appenumeration29" />
+    </queries>
+
+    <application android:hasCode="false" >
+        <activity android:name="android.perftests.utils.PerfTestActivity"
+            android:exported="true">
+            <intent-filter>
+                <action android:name="android.perftests.permission.PERFTEST" />
+            </intent-filter>
+        </activity>
+    </application>
+
+</manifest>
diff --git a/apct-tests/perftests/permission/src/android/perftests/permission/PermissionServicePerfTest.kt b/apct-tests/perftests/permission/src/android/perftests/permission/PermissionServicePerfTest.kt
new file mode 100644
index 0000000..13e67e3
--- /dev/null
+++ b/apct-tests/perftests/permission/src/android/perftests/permission/PermissionServicePerfTest.kt
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.perftests.permission
+
+import android.Manifest
+import android.os.ParcelFileDescriptor
+import android.os.Trace
+import android.perftests.utils.PerfManualStatusReporter
+import android.perftests.utils.TraceMarkParser
+import android.util.Log
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.compatibility.common.util.AdoptShellPermissionsRule
+import com.android.compatibility.common.util.SystemUtil.eventually
+import com.android.compatibility.common.util.SystemUtil.runShellCommand
+import com.google.common.truth.Truth.assertThat
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import java.io.BufferedReader
+import java.io.IOException
+import java.io.InputStreamReader
+import java.util.concurrent.TimeUnit
+import java.util.function.BiConsumer
+
+@RunWith(AndroidJUnit4::class)
+class PermissionServicePerfTest {
+    @get:Rule val mPerfManualStatusReporter = PerfManualStatusReporter()
+    @get:Rule val mAdoptShellPermissionsRule = AdoptShellPermissionsRule(
+        InstrumentationRegistry.getInstrumentation().getUiAutomation(),
+        Manifest.permission.INSTALL_PACKAGES,
+        Manifest.permission.DELETE_PACKAGES
+    )
+    val mUiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation()
+
+    @Test
+    fun testInstallPackages() {
+        mUiAutomation.executeShellCommand(COMMAND_TRACE_START)
+        eventually { assertThat(Trace.isTagEnabled(TRACE_TAG)).isTrue() }
+        val benchmarkState = mPerfManualStatusReporter.benchmarkState
+        val durations = ArrayList<Long>()
+
+        while (benchmarkState.keepRunning(durations)) {
+            uninstallAllTestApps()
+            installAllTestApps()
+
+            val parser = TraceMarkParser { line -> line.name.contains(PKG_INSTALL_TRACE_PREFIX) }
+            dumpResult(parser) { _, slices ->
+                slices.forEachIndexed { _, slice ->
+                    durations.add(TimeUnit.MICROSECONDS.toNanos(slice.durationInMicroseconds))
+                }
+            }
+        }
+
+        mUiAutomation.executeShellCommand(COMMAND_TRACE_END)
+    }
+
+    private fun installAllTestApps() {
+        for (i in 0..29) {
+            installTestApp(i)
+        }
+    }
+
+    private fun installTestApp(appId: Int) {
+        val apkPath = "$APK_DIR$APK_NAME$appId.apk"
+        runShellCommand("pm install -t $apkPath")
+    }
+
+    private fun uninstallAllTestApps() {
+        for (i in 0..29) {
+            uninstallTestApp(i)
+        }
+    }
+
+    private fun uninstallTestApp(appId: Int) {
+        val packageName = "$PKG_NAME$appId"
+        runShellCommand("pm uninstall $packageName")
+    }
+
+    private fun dumpResult(
+        parser: TraceMarkParser,
+        handler: BiConsumer<String, List<TraceMarkParser.TraceMarkSlice>>
+    ) {
+        parser.reset()
+        try {
+            val inputStream = ParcelFileDescriptor.AutoCloseInputStream(
+                mUiAutomation.executeShellCommand(COMMAND_TRACE_DUMP)
+            )
+            val reader = BufferedReader(InputStreamReader(inputStream))
+            var line = reader.readLine()
+            while (line != null) {
+                parser.visit(line)
+                line = reader.readLine()
+            }
+        } catch (e: IOException) {
+            Log.e(LOG_TAG, "IO error while reading trace dump file.")
+        }
+        parser.forAllSlices(handler)
+    }
+
+    companion object {
+        private val LOG_TAG = PermissionServicePerfTest::class.java.simpleName
+        private const val TRACE_TAG = Trace.TRACE_TAG_PACKAGE_MANAGER
+        private const val PKG_INSTALL_TRACE_PREFIX =
+            "TaggedTracingPermissionManagerServiceImpl#onPackageInstalled"
+        private const val COMMAND_TRACE_START = "atrace --async_start -b 16000 pm"
+        private const val COMMAND_TRACE_END = "atrace --async_stop"
+        private const val COMMAND_TRACE_DUMP = "atrace --async_dump"
+        private const val APK_DIR = "/data/local/tmp/perftests/"
+        private const val APK_NAME = "UsePermissionApp"
+        private const val PKG_NAME = "android.perftests.appenumeration"
+    }
+}
diff --git a/apex/jobscheduler/service/aconfig/job.aconfig b/apex/jobscheduler/service/aconfig/job.aconfig
index c65e506..de6f023 100644
--- a/apex/jobscheduler/service/aconfig/job.aconfig
+++ b/apex/jobscheduler/service/aconfig/job.aconfig
@@ -12,4 +12,11 @@
     namespace: "backstage_power"
     description: "Throw an exception if an unsupported app uses JobInfo.setBias"
     bug: "300477393"
-}
\ No newline at end of file
+}
+
+flag {
+    name: "batch_jobs_on_network_activation"
+    namespace: "backstage_power"
+    description: "Have JobScheduler attempt to delay the start of some connectivity jobs until the network is actually active"
+    bug: "318394184"
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
index 5a32a02..abf8008 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -115,6 +115,7 @@
 import android.os.ShellCallback;
 import android.os.ShellCommand;
 import android.os.SystemClock;
+import android.os.SystemProperties;
 import android.os.ThreadLocalWorkSource;
 import android.os.Trace;
 import android.os.UserHandle;
@@ -229,6 +230,9 @@
 
     private static final long TEMPORARY_QUOTA_DURATION = INTERVAL_DAY;
 
+    // System property read on some device configurations to initialize time properly.
+    private static final String TIMEOFFSET_PROPERTY = "persist.sys.time.offset";
+
     private final Intent mBackgroundIntent
             = new Intent().addFlags(Intent.FLAG_FROM_BACKGROUND);
 
@@ -2142,6 +2146,9 @@
             // "GMT" if the ID is unrecognized). The parameter ID is used here rather than
             // newZone.getId(). It will be rejected if it is invalid.
             timeZoneWasChanged = SystemTimeZone.setTimeZoneId(tzId, confidence, logInfo);
+
+            final int gmtOffset = newZone.getOffset(mInjector.getCurrentTimeMillis());
+            SystemProperties.set(TIMEOFFSET_PROPERTY, String.valueOf(gmtOffset));
         }
 
         // Clear the default time zone in the system server process. This forces the next call
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 d940e38..b0f378d 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -159,6 +159,7 @@
 import java.util.Map;
 import java.util.Objects;
 import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
 import java.util.function.Consumer;
 import java.util.function.Predicate;
 
@@ -513,6 +514,10 @@
                     if (name == null) {
                         continue;
                     }
+                    if (DEBUG) {
+                        Slog.d(TAG, "DeviceConfig " + name
+                                + " changed to " + properties.getString(name, null));
+                    }
                     switch (name) {
                         case Constants.KEY_ENABLE_API_QUOTAS:
                         case Constants.KEY_ENABLE_EXECUTION_SAFEGUARDS_UDC:
@@ -3507,7 +3512,10 @@
                 }
 
                 final boolean shouldForceBatchJob;
-                if (job.shouldTreatAsExpeditedJob() || job.shouldTreatAsUserInitiatedJob()) {
+                if (job.overrideState > JobStatus.OVERRIDE_NONE) {
+                    // The job should run for some test. Don't force batch it.
+                    shouldForceBatchJob = false;
+                } else if (job.shouldTreatAsExpeditedJob() || job.shouldTreatAsUserInitiatedJob()) {
                     // Never batch expedited or user-initiated jobs, even for RESTRICTED apps.
                     shouldForceBatchJob = false;
                 } else if (job.getEffectiveStandbyBucket() == RESTRICTED_INDEX) {
@@ -4960,6 +4968,8 @@
         Slog.d(TAG, "executeRunCommand(): " + pkgName + "/" + namespace + "/" + userId
                 + " " + jobId + " s=" + satisfied + " f=" + force);
 
+        final CountDownLatch delayLatch = new CountDownLatch(1);
+        final JobStatus js;
         try {
             final int uid = AppGlobals.getPackageManager().getPackageUid(pkgName, 0,
                     userId != UserHandle.USER_ALL ? userId : UserHandle.USER_SYSTEM);
@@ -4968,7 +4978,7 @@
             }
 
             synchronized (mLock) {
-                final JobStatus js = mJobs.getJobByUidAndJobId(uid, namespace, jobId);
+                js = mJobs.getJobByUidAndJobId(uid, namespace, jobId);
                 if (js == null) {
                     return JobSchedulerShellCommand.CMD_ERR_NO_JOB;
                 }
@@ -4979,23 +4989,71 @@
                 // Re-evaluate constraints after the override is set in case one of the overridden
                 // constraints was preventing another constraint from thinking it needed to update.
                 for (int c = mControllers.size() - 1; c >= 0; --c) {
-                    mControllers.get(c).reevaluateStateLocked(uid);
+                    mControllers.get(c).evaluateStateLocked(js);
                 }
 
                 if (!js.isConstraintsSatisfied()) {
-                    js.overrideState = JobStatus.OVERRIDE_NONE;
-                    return JobSchedulerShellCommand.CMD_ERR_CONSTRAINTS;
+                    if (js.hasConnectivityConstraint()
+                            && !js.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY)
+                            && js.wouldBeReadyWithConstraint(JobStatus.CONSTRAINT_CONNECTIVITY)) {
+                        // Because of how asynchronous the connectivity signals are, JobScheduler
+                        // may not get the connectivity satisfaction signal immediately. In this
+                        // case, wait a few seconds to see if it comes in before saying the
+                        // connectivity constraint isn't satisfied.
+                        mHandler.postDelayed(
+                                checkConstraintRunnableForTesting(
+                                        mHandler, js, delayLatch, 5, 1000),
+                                1000);
+                    } else {
+                        // There's no asynchronous signal to wait for. We can immediately say the
+                        // job's constraints aren't satisfied and return.
+                        js.overrideState = JobStatus.OVERRIDE_NONE;
+                        return JobSchedulerShellCommand.CMD_ERR_CONSTRAINTS;
+                    }
+                } else {
+                    delayLatch.countDown();
                 }
-
-                queueReadyJobsForExecutionLocked();
-                maybeRunPendingJobsLocked();
             }
         } catch (RemoteException e) {
             // can't happen
+            return 0;
+        }
+
+        // Choose to block the return until we're sure about the state of the connectivity job
+        // so that tests can expect a reliable state after calling the run command.
+        try {
+            delayLatch.await(7L, TimeUnit.SECONDS);
+        } catch (InterruptedException e) {
+            Slog.e(TAG, "Couldn't wait for asynchronous constraint change", e);
+        }
+
+        synchronized (mLock) {
+            if (!js.isConstraintsSatisfied()) {
+                js.overrideState = JobStatus.OVERRIDE_NONE;
+                return JobSchedulerShellCommand.CMD_ERR_CONSTRAINTS;
+            }
+
+            queueReadyJobsForExecutionLocked();
+            maybeRunPendingJobsLocked();
         }
         return 0;
     }
 
+    private static Runnable checkConstraintRunnableForTesting(@NonNull final Handler handler,
+            @NonNull final JobStatus js, @NonNull final CountDownLatch latch,
+            final int remainingAttempts, final long delayMs) {
+        return () -> {
+            if (remainingAttempts <= 0 || js.isConstraintsSatisfied()) {
+                latch.countDown();
+                return;
+            }
+            handler.postDelayed(
+                    checkConstraintRunnableForTesting(
+                            handler, js, latch, remainingAttempts - 1, delayMs),
+                    delayMs);
+        };
+    }
+
     // Shell command infrastructure: immediately timeout currently executing jobs
     int executeStopCommand(PrintWriter pw, String pkgName, int userId,
             @Nullable String namespace, boolean hasJobId, int jobId,
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 bdc2246..d39863c 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
@@ -2192,7 +2192,7 @@
      * @return Whether or not this job would be ready to run if it had the specified constraint
      * granted, based on its requirements.
      */
-    boolean wouldBeReadyWithConstraint(int constraint) {
+    public boolean wouldBeReadyWithConstraint(int constraint) {
         return readinessStatusWithConstraint(constraint, true);
     }
 
diff --git a/api/Android.bp b/api/Android.bp
index 363197a..1686943 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -444,3 +444,12 @@
         targets: ["droid"],
     },
 }
+
+phony_rule {
+    name: "checkapi",
+    phony_deps: [
+        "frameworks-base-api-current-compat",
+        "frameworks-base-api-system-current-compat",
+        "frameworks-base-api-module-lib-current-compat",
+    ],
+}
diff --git a/api/Android.mk b/api/Android.mk
deleted file mode 100644
index ce5f995..0000000
--- a/api/Android.mk
+++ /dev/null
@@ -1,2 +0,0 @@
-.PHONY: checkapi
-checkapi: frameworks-base-api-current-compat frameworks-base-api-system-current-compat frameworks-base-api-module-lib-current-compat
diff --git a/api/ApiDocs.bp b/api/ApiDocs.bp
index bcfb68f..6b8c02f 100644
--- a/api/ApiDocs.bp
+++ b/api/ApiDocs.bp
@@ -183,6 +183,7 @@
         "-federationapi AndroidX $(location :current-androidx-api)",
         // doclava contains checks for a few issues that are have been migrated to metalava.
         // disable them in doclava, to avoid mistriggering or double triggering.
+        "-hide 101", // TODO: turn Lint 101 back into an error again
         "-hide 111", // HIDDEN_SUPERCLASS
         "-hide 113", // DEPRECATION_MISMATCH
         "-hide 125", // REQUIRES_PERMISSION
diff --git a/api/StubLibraries.bp b/api/StubLibraries.bp
index 28b2d4b..ef1fa60 100644
--- a/api/StubLibraries.bp
+++ b/api/StubLibraries.bp
@@ -900,10 +900,19 @@
     ],
     api_levels_sdk_type: "system",
     extensions_info_file: ":sdk-extensions-info",
+    dists: [
+        // Make the api-versions.xml file for the system API available in the
+        // sdk build target.
+        {
+            targets: ["sdk"],
+            dest: "api-versions_system.xml",
+            tag: ".api_versions.xml",
+        },
+    ],
 }
 
 // This module can be built with:
-// m out/soong/.intermediates/frameworks/base/api_versions_module_lib/android_common/metalava/api-versions.xml
+// m out/soong/.intermediates/frameworks/base/api/api_versions_module_lib/android_common/metalava/api-versions.xml
 droidstubs {
     name: "api_versions_module_lib",
     srcs: [":android_module_stubs_current_with_test_libs{.jar}"],
diff --git a/api/api.go b/api/api.go
index 2668999..43713aa 100644
--- a/api/api.go
+++ b/api/api.go
@@ -64,6 +64,7 @@
 
 type CombinedApis struct {
 	android.ModuleBase
+	android.DefaultableModuleBase
 
 	properties CombinedApisProperties
 }
@@ -74,6 +75,7 @@
 
 func registerBuildComponents(ctx android.RegistrationContext) {
 	ctx.RegisterModuleType("combined_apis", combinedApisModuleFactory)
+	ctx.RegisterModuleType("combined_apis_defaults", CombinedApisModuleDefaultsFactory)
 }
 
 var PrepareForCombinedApisTest = android.FixtureRegisterWithContext(registerBuildComponents)
@@ -409,6 +411,7 @@
 	module := &CombinedApis{}
 	module.AddProperties(&module.properties)
 	android.InitAndroidModule(module)
+	android.InitDefaultableModule(module)
 	android.AddLoadHook(module, func(ctx android.LoadHookContext) { module.createInternalModules(ctx) })
 	return module
 }
@@ -445,3 +448,16 @@
 	}
 	return s2
 }
+
+// Defaults
+type CombinedApisModuleDefaults struct {
+	android.ModuleBase
+	android.DefaultsModuleBase
+}
+
+func CombinedApisModuleDefaultsFactory() android.Module {
+	module := &CombinedApisModuleDefaults{}
+	module.AddProperties(&CombinedApisProperties{})
+	android.InitDefaultsModule(module)
+	return module
+}
diff --git a/cmds/uinput/README.md b/cmds/uinput/README.md
index e7361fe..b6e4e0d 100644
--- a/cmds/uinput/README.md
+++ b/cmds/uinput/README.md
@@ -7,11 +7,23 @@
   or app (such as the CTS tests via [`UinputDevice`][UinputDevice]).
 * `uinput <filename>` reads commands from a file instead of standard input.
 
+There are also two supported input formats, described in the sections below. The tool will
+automatically detect which format is being used.
+
 [UinputDevice]: https://cs.android.com/android/platform/superproject/main/+/main:cts/libs/input/src/com/android/cts/input/UinputDevice.java
 
-## Command format
+## evemu recording format (recommended)
 
-Input commands should be in JSON format, though the parser is in [lenient mode] to allow comments,
+`uinput` supports the evemu format, as used by the [FreeDesktop project's evemu suite][FreeDesktop].
+This is a simple text-based format compatible with recording and replay tools on other platforms.
+However, it only supports playback of events from one device from a single recording. Recordings can
+be made using the `evemu-record` command on Android or other Linux-based OSes.
+
+[FreeDesktop]: https://gitlab.freedesktop.org/libevdev/evemu
+
+## JSON-like format
+
+The other supported format is JSON-based, though the parser is in [lenient mode] to allow comments,
 and integers can be specified in hexadecimal (e.g. `0xABCD`). The input file (or standard input) can
 contain multiple commands, which will be executed in sequence. Simply add multiple JSON objects to
 the file, one after the other without separators:
@@ -34,9 +46,9 @@
 [lenient mode]: https://developer.android.com/reference/android/util/JsonReader#setLenient(boolean)
 [cts-example-jsons]: https://cs.android.com/android/platform/superproject/main/+/main:cts/tests/tests/hardware/res/raw/
 
-## Command reference
+### Command reference
 
-### `register`
+#### `register`
 
 Register a new uinput device
 
@@ -122,7 +134,7 @@
 
 [struct input_absinfo]: https://cs.android.com/android/platform/superproject/main/+/main:bionic/libc/kernel/uapi/linux/input.h?q=%22struct%20input_absinfo%22
 
-#### Waiting for registration
+##### Waiting for registration
 
 After the command is sent, there will be a delay before the device is set up by the Android input
 stack, and `uinput` does not wait for that process to finish. Any commands sent to the device during
@@ -135,12 +147,12 @@
 
 [onInputDeviceAdded]: https://developer.android.com/reference/android/hardware/input/InputManager.InputDeviceListener.html
 
-#### Unregistering the device
+##### Unregistering the device
 
 As soon as EOF is reached (either in interactive mode, or in file mode), the device that was created
 will be unregistered. There is no explicit command for unregistering a device.
 
-### `delay`
+#### `delay`
 
 Add a delay to command processing
 
@@ -160,7 +172,7 @@
 }
 ```
 
-### `inject`
+#### `inject`
 
 Send an array of uinput event packets to the uinput device
 
@@ -190,7 +202,7 @@
 }
 ```
 
-### `sync`
+#### `sync`
 
 A command used to get a response once the command is processed. When several `inject` and `delay`
 commands are used in a row, the `sync` command can be used to track the progress of the command
diff --git a/core/api/current.txt b/core/api/current.txt
index 43ff0c9..ac7e6cc 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -49,6 +49,7 @@
     field public static final String BIND_SCREENING_SERVICE = "android.permission.BIND_SCREENING_SERVICE";
     field public static final String BIND_TELECOM_CONNECTION_SERVICE = "android.permission.BIND_TELECOM_CONNECTION_SERVICE";
     field public static final String BIND_TEXT_SERVICE = "android.permission.BIND_TEXT_SERVICE";
+    field @FlaggedApi("android.media.tv.flags.enable_ad_service_fw") public static final String BIND_TV_AD_SERVICE = "android.permission.BIND_TV_AD_SERVICE";
     field public static final String BIND_TV_INPUT = "android.permission.BIND_TV_INPUT";
     field public static final String BIND_TV_INTERACTIVE_APP = "android.permission.BIND_TV_INTERACTIVE_APP";
     field public static final String BIND_VISUAL_VOICEMAIL_SERVICE = "android.permission.BIND_VISUAL_VOICEMAIL_SERVICE";
@@ -102,6 +103,7 @@
     field public static final String FOREGROUND_SERVICE_HEALTH = "android.permission.FOREGROUND_SERVICE_HEALTH";
     field public static final String FOREGROUND_SERVICE_LOCATION = "android.permission.FOREGROUND_SERVICE_LOCATION";
     field public static final String FOREGROUND_SERVICE_MEDIA_PLAYBACK = "android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK";
+    field @FlaggedApi("android.content.pm.introduce_media_processing_type") public static final String FOREGROUND_SERVICE_MEDIA_PROCESSING = "android.permission.FOREGROUND_SERVICE_MEDIA_PROCESSING";
     field public static final String FOREGROUND_SERVICE_MEDIA_PROJECTION = "android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION";
     field public static final String FOREGROUND_SERVICE_MICROPHONE = "android.permission.FOREGROUND_SERVICE_MICROPHONE";
     field public static final String FOREGROUND_SERVICE_PHONE_CALL = "android.permission.FOREGROUND_SERVICE_PHONE_CALL";
@@ -1600,6 +1602,7 @@
     field public static final int switchTextOff = 16843628; // 0x101036c
     field public static final int switchTextOn = 16843627; // 0x101036b
     field public static final int syncable = 16842777; // 0x1010019
+    field @FlaggedApi("android.multiuser.enable_system_user_only_for_services_and_providers") public static final int systemUserOnly;
     field public static final int tabStripEnabled = 16843453; // 0x10102bd
     field public static final int tabStripLeft = 16843451; // 0x10102bb
     field public static final int tabStripRight = 16843452; // 0x10102bc
@@ -5315,6 +5318,7 @@
     ctor @Deprecated public AutomaticZenRule(String, android.content.ComponentName, android.net.Uri, int, boolean);
     ctor public AutomaticZenRule(@NonNull String, @Nullable android.content.ComponentName, @Nullable android.content.ComponentName, @NonNull android.net.Uri, @Nullable android.service.notification.ZenPolicy, int, boolean);
     ctor public AutomaticZenRule(android.os.Parcel);
+    method @FlaggedApi("android.app.modes_api") public boolean canUpdate();
     method public int describeContents();
     method public android.net.Uri getConditionId();
     method @Nullable public android.content.ComponentName getConfigurationActivity();
@@ -12378,6 +12382,7 @@
   public class PackageInfo implements android.os.Parcelable {
     ctor public PackageInfo();
     method public int describeContents();
+    method @FlaggedApi("android.content.pm.provide_info_of_apk_in_apex") @Nullable public String getApexPackageName();
     method @FlaggedApi("android.content.pm.archiving") public long getArchiveTimeMillis();
     method public long getLongVersionCode();
     method public void setLongVersionCode(long);
@@ -12815,7 +12820,7 @@
     method public boolean isPackageSuspended();
     method @CheckResult public abstract boolean isPermissionRevokedByPolicy(@NonNull String, @NonNull String);
     method public abstract boolean isSafeMode();
-    method @FlaggedApi("android.content.pm.get_package_info") @WorkerThread public <T> T parseAndroidManifest(@NonNull String, @NonNull java.util.function.Function<android.content.res.XmlResourceParser,T>) throws java.io.IOException;
+    method @FlaggedApi("android.content.pm.get_package_info") @WorkerThread public <T> T parseAndroidManifest(@NonNull java.io.File, @NonNull java.util.function.Function<android.content.res.XmlResourceParser,T>) throws java.io.IOException;
     method @NonNull public java.util.List<android.content.pm.PackageManager.Property> queryActivityProperty(@NonNull String);
     method @NonNull public java.util.List<android.content.pm.PackageManager.Property> queryApplicationProperty(@NonNull String);
     method @NonNull public abstract java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceivers(@NonNull android.content.Intent, int);
@@ -12932,6 +12937,7 @@
     field public static final String FEATURE_MIDI = "android.software.midi";
     field public static final String FEATURE_NFC = "android.hardware.nfc";
     field public static final String FEATURE_NFC_BEAM = "android.sofware.nfc.beam";
+    field @FlaggedApi("android.nfc.enable_nfc_charging") public static final String FEATURE_NFC_CHARGING = "android.hardware.nfc.charging";
     field public static final String FEATURE_NFC_HOST_CARD_EMULATION = "android.hardware.nfc.hce";
     field public static final String FEATURE_NFC_HOST_CARD_EMULATION_NFCF = "android.hardware.nfc.hcef";
     field public static final String FEATURE_NFC_OFF_HOST_CARD_EMULATION_ESE = "android.hardware.nfc.ese";
@@ -13284,6 +13290,7 @@
     field @RequiresPermission(allOf={android.Manifest.permission.FOREGROUND_SERVICE_LOCATION}, anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}, conditional=true) public static final int FOREGROUND_SERVICE_TYPE_LOCATION = 8; // 0x8
     field public static final int FOREGROUND_SERVICE_TYPE_MANIFEST = -1; // 0xffffffff
     field @RequiresPermission(value=android.Manifest.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK, conditional=true) public static final int FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK = 2; // 0x2
+    field @FlaggedApi("android.content.pm.introduce_media_processing_type") @RequiresPermission(android.Manifest.permission.FOREGROUND_SERVICE_MEDIA_PROCESSING) public static final int FOREGROUND_SERVICE_TYPE_MEDIA_PROCESSING = 8192; // 0x2000
     field @RequiresPermission(value=android.Manifest.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION, conditional=true) public static final int FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION = 32; // 0x20
     field @RequiresPermission(allOf={android.Manifest.permission.FOREGROUND_SERVICE_MICROPHONE}, anyOf={android.Manifest.permission.CAPTURE_AUDIO_OUTPUT, android.Manifest.permission.RECORD_AUDIO}, conditional=true) public static final int FOREGROUND_SERVICE_TYPE_MICROPHONE = 128; // 0x80
     field @Deprecated public static final int FOREGROUND_SERVICE_TYPE_NONE = 0; // 0x0
@@ -18612,6 +18619,7 @@
   }
 
   public final class SyncFence implements java.lang.AutoCloseable android.os.Parcelable {
+    ctor @FlaggedApi("com.android.window.flags.sdk_desired_present_time") public SyncFence(@NonNull android.hardware.SyncFence);
     method public boolean await(@NonNull java.time.Duration);
     method public boolean awaitForever();
     method public void close();
@@ -19727,7 +19735,7 @@
   @FlaggedApi("com.android.internal.camera.flags.concert_mode") public final class LensIntrinsicsSample {
     ctor @FlaggedApi("com.android.internal.camera.flags.concert_mode") public LensIntrinsicsSample(long, @NonNull float[]);
     method @FlaggedApi("com.android.internal.camera.flags.concert_mode") @NonNull public float[] getLensIntrinsics();
-    method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public long getTimestamp();
+    method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public long getTimestampNanos();
   }
 
   public final class LensShadingMap {
@@ -22099,7 +22107,7 @@
   }
 
   @FlaggedApi("android.media.audio.loudness_configurator_api") public class LoudnessCodecConfigurator {
-    method @FlaggedApi("android.media.audio.loudness_configurator_api") public void addMediaCodec(@NonNull android.media.MediaCodec);
+    method @FlaggedApi("android.media.audio.loudness_configurator_api") public boolean addMediaCodec(@NonNull android.media.MediaCodec);
     method @FlaggedApi("android.media.audio.loudness_configurator_api") @NonNull public static android.media.LoudnessCodecConfigurator create();
     method @FlaggedApi("android.media.audio.loudness_configurator_api") @NonNull public static android.media.LoudnessCodecConfigurator create(@NonNull java.util.concurrent.Executor, @NonNull android.media.LoudnessCodecConfigurator.OnLoudnessCodecUpdateListener);
     method @FlaggedApi("android.media.audio.loudness_configurator_api") @NonNull public android.os.Bundle getLoudnessCodecParams(@NonNull android.media.AudioTrack, @NonNull android.media.MediaCodec);
@@ -26288,6 +26296,7 @@
     field public static final int STATE_FAST_FORWARDING = 4; // 0x4
     field public static final int STATE_NONE = 0; // 0x0
     field public static final int STATE_PAUSED = 2; // 0x2
+    field @FlaggedApi("com.android.media.flags.enable_notifying_activity_manager_with_media_session_status_change") public static final int STATE_PLAYBACK_SUPPRESSED = 12; // 0xc
     field public static final int STATE_PLAYING = 3; // 0x3
     field public static final int STATE_REWINDING = 5; // 0x5
     field public static final int STATE_SKIPPING_TO_NEXT = 10; // 0xa
@@ -28813,6 +28822,7 @@
     method public void enableReaderMode(android.app.Activity, android.nfc.NfcAdapter.ReaderCallback, int, android.os.Bundle);
     method public static android.nfc.NfcAdapter getDefaultAdapter(android.content.Context);
     method @Nullable public android.nfc.NfcAntennaInfo getNfcAntennaInfo();
+    method @FlaggedApi("android.nfc.enable_nfc_charging") @Nullable public android.nfc.WlcLDeviceInfo getWlcLDeviceInfo();
     method public boolean ignore(android.nfc.Tag, int, android.nfc.NfcAdapter.OnTagRemovedListener, android.os.Handler);
     method public boolean isEnabled();
     method @FlaggedApi("android.nfc.nfc_observe_mode") public boolean isObserveModeSupported();
@@ -28820,6 +28830,9 @@
     method @FlaggedApi("android.nfc.enable_nfc_reader_option") public boolean isReaderOptionSupported();
     method public boolean isSecureNfcEnabled();
     method public boolean isSecureNfcSupported();
+    method @FlaggedApi("android.nfc.enable_nfc_charging") public boolean isWlcEnabled();
+    method @FlaggedApi("android.nfc.enable_nfc_set_discovery_tech") public void resetDiscoveryTechnology(@NonNull android.app.Activity);
+    method @FlaggedApi("android.nfc.enable_nfc_set_discovery_tech") public void setDiscoveryTechnology(@NonNull android.app.Activity, int, int);
     field public static final String ACTION_ADAPTER_STATE_CHANGED = "android.nfc.action.ADAPTER_STATE_CHANGED";
     field public static final String ACTION_NDEF_DISCOVERED = "android.nfc.action.NDEF_DISCOVERED";
     field @RequiresPermission(android.Manifest.permission.NFC_PREFERRED_PAYMENT_INFO) public static final String ACTION_PREFERRED_PAYMENT_CHANGED = "android.nfc.action.PREFERRED_PAYMENT_CHANGED";
@@ -28835,6 +28848,13 @@
     field public static final String EXTRA_READER_PRESENCE_CHECK_DELAY = "presence";
     field public static final String EXTRA_SECURE_ELEMENT_NAME = "android.nfc.extra.SECURE_ELEMENT_NAME";
     field public static final String EXTRA_TAG = "android.nfc.extra.TAG";
+    field @FlaggedApi("android.nfc.enable_nfc_set_discovery_tech") public static final int FLAG_LISTEN_DISABLE = 0; // 0x0
+    field @FlaggedApi("android.nfc.enable_nfc_set_discovery_tech") public static final int FLAG_LISTEN_KEEP = -1; // 0xffffffff
+    field @FlaggedApi("android.nfc.enable_nfc_set_discovery_tech") public static final int FLAG_LISTEN_NFC_PASSIVE_A = 1; // 0x1
+    field @FlaggedApi("android.nfc.enable_nfc_set_discovery_tech") public static final int FLAG_LISTEN_NFC_PASSIVE_B = 2; // 0x2
+    field @FlaggedApi("android.nfc.enable_nfc_set_discovery_tech") public static final int FLAG_LISTEN_NFC_PASSIVE_F = 4; // 0x4
+    field @FlaggedApi("android.nfc.enable_nfc_set_discovery_tech") public static final int FLAG_READER_DISABLE = 0; // 0x0
+    field @FlaggedApi("android.nfc.enable_nfc_set_discovery_tech") public static final int FLAG_READER_KEEP = -1; // 0xffffffff
     field public static final int FLAG_READER_NFC_A = 1; // 0x1
     field public static final int FLAG_READER_NFC_B = 2; // 0x2
     field public static final int FLAG_READER_NFC_BARCODE = 16; // 0x10
@@ -28905,6 +28925,20 @@
     ctor public TagLostException(String);
   }
 
+  @FlaggedApi("android.nfc.enable_nfc_charging") public final class WlcLDeviceInfo implements android.os.Parcelable {
+    ctor public WlcLDeviceInfo(double, double, double, int);
+    method public int describeContents();
+    method public double getBatteryLevel();
+    method public double getProductId();
+    method public int getState();
+    method public double getTemperature();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field public static final int CONNECTED_CHARGING = 2; // 0x2
+    field public static final int CONNECTED_DISCHARGING = 3; // 0x3
+    field @NonNull public static final android.os.Parcelable.Creator<android.nfc.WlcLDeviceInfo> CREATOR;
+    field public static final int DISCONNECTED = 1; // 0x1
+  }
+
 }
 
 package android.nfc.cardemulation {
@@ -28927,7 +28961,7 @@
     method public boolean supportsAidPrefixRegistration();
     method @NonNull @RequiresPermission(android.Manifest.permission.NFC) public boolean unsetOffHostForService(@NonNull android.content.ComponentName);
     method public boolean unsetPreferredService(android.app.Activity);
-    field public static final String ACTION_CHANGE_DEFAULT = "android.nfc.cardemulation.action.ACTION_CHANGE_DEFAULT";
+    field @Deprecated public static final String ACTION_CHANGE_DEFAULT = "android.nfc.cardemulation.action.ACTION_CHANGE_DEFAULT";
     field public static final String CATEGORY_OTHER = "other";
     field public static final String CATEGORY_PAYMENT = "payment";
     field public static final String EXTRA_CATEGORY = "category";
@@ -33368,15 +33402,17 @@
 
   @FlaggedApi("com.android.server.power.optimization.power_monitor_api") public final class PowerMonitorReadings {
     method @FlaggedApi("com.android.server.power.optimization.power_monitor_api") public long getConsumedEnergy(@NonNull android.os.PowerMonitor);
-    method @FlaggedApi("com.android.server.power.optimization.power_monitor_api") public long getTimestamp(@NonNull android.os.PowerMonitor);
+    method @FlaggedApi("com.android.server.power.optimization.power_monitor_api") public long getTimestampMillis(@NonNull android.os.PowerMonitor);
     field @FlaggedApi("com.android.server.power.optimization.power_monitor_api") public static final int ENERGY_UNAVAILABLE = -1; // 0xffffffff
   }
 
   public class Process {
     ctor public Process();
+    method public static final int getAppUidForSdkSandboxUid(int);
     method public static final long getElapsedCpuTime();
     method public static final int[] getExclusiveCores();
     method public static final int getGidForName(String);
+    method @FlaggedApi("com.android.sdksandbox.flags.sdk_sandbox_uid_to_app_uid_api") public static final int getSdkSandboxUidForAppUid(int);
     method public static long getStartElapsedRealtime();
     method public static long getStartRequestedElapsedRealtime();
     method public static long getStartRequestedUptimeMillis();
@@ -33388,6 +33424,7 @@
     method public static final boolean isIsolated();
     method public static final boolean isIsolatedUid(int);
     method public static final boolean isSdkSandbox();
+    method public static final boolean isSdkSandboxUid(int);
     method public static final void killProcess(int);
     method public static final int myPid();
     method @NonNull public static String myProcessName();
@@ -33877,7 +33914,6 @@
 
   @FlaggedApi("android.os.adpf_gpu_report_actual_work_duration") public final class WorkDuration implements android.os.Parcelable {
     ctor public WorkDuration();
-    ctor public WorkDuration(long, long, long, long);
     method public int describeContents();
     method public long getActualCpuDurationNanos();
     method public long getActualGpuDurationNanos();
@@ -33960,8 +33996,8 @@
   }
 
   public class SystemHealthManager {
-    method @FlaggedApi("com.android.server.power.optimization.power_monitor_api") public void getPowerMonitorReadings(@NonNull java.util.List<android.os.PowerMonitor>, @Nullable android.os.Handler, @NonNull java.util.function.Consumer<android.os.PowerMonitorReadings>, @NonNull java.util.function.Consumer<java.lang.RuntimeException>);
-    method @FlaggedApi("com.android.server.power.optimization.power_monitor_api") public void getSupportedPowerMonitors(@Nullable android.os.Handler, @NonNull java.util.function.Consumer<java.util.List<android.os.PowerMonitor>>);
+    method @FlaggedApi("com.android.server.power.optimization.power_monitor_api") public void getPowerMonitorReadings(@NonNull java.util.List<android.os.PowerMonitor>, @Nullable java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.os.PowerMonitorReadings,java.lang.RuntimeException>);
+    method @FlaggedApi("com.android.server.power.optimization.power_monitor_api") public void getSupportedPowerMonitors(@Nullable java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.util.List<android.os.PowerMonitor>>);
     method public android.os.health.HealthStats takeMyUidSnapshot();
     method public android.os.health.HealthStats takeUidSnapshot(int);
     method public android.os.health.HealthStats[] takeUidSnapshots(int[]);
@@ -41516,6 +41552,8 @@
     field public static final String EXTRA_LANGUAGE_MODEL = "android.speech.extra.LANGUAGE_MODEL";
     field public static final String EXTRA_LANGUAGE_PREFERENCE = "android.speech.extra.LANGUAGE_PREFERENCE";
     field public static final String EXTRA_LANGUAGE_SWITCH_ALLOWED_LANGUAGES = "android.speech.extra.LANGUAGE_SWITCH_ALLOWED_LANGUAGES";
+    field @FlaggedApi("android.speech.flags.multilang_extra_launch") public static final String EXTRA_LANGUAGE_SWITCH_INITIAL_ACTIVE_DURATION_TIME_MILLIS = "android.speech.extra.LANGUAGE_SWITCH_INITIAL_ACTIVE_DURATION_TIME_MILLIS";
+    field @FlaggedApi("android.speech.flags.multilang_extra_launch") public static final String EXTRA_LANGUAGE_SWITCH_MAX_SWITCHES = "android.speech.extra.LANGUAGE_SWITCH_MAX_SWITCHES";
     field public static final String EXTRA_MASK_OFFENSIVE_WORDS = "android.speech.extra.MASK_OFFENSIVE_WORDS";
     field public static final String EXTRA_MAX_RESULTS = "android.speech.extra.MAX_RESULTS";
     field public static final String EXTRA_ONLY_RETURN_LANGUAGE_PREFERENCE = "android.speech.extra.ONLY_RETURN_LANGUAGE_PREFERENCE";
@@ -45307,7 +45345,7 @@
     method public void addOnSubscriptionsChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.SubscriptionManager.OnSubscriptionsChangedListener);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void addSubscriptionsIntoGroup(@NonNull java.util.List<java.lang.Integer>, @NonNull android.os.ParcelUuid);
     method public boolean canManageSubscription(android.telephony.SubscriptionInfo);
-    method @FlaggedApi("com.android.internal.telephony.flags.work_profile_api_split") @NonNull public android.telephony.SubscriptionManager createForAllUserProfiles();
+    method @FlaggedApi("com.android.internal.telephony.flags.enforce_subscription_user_filter") @NonNull public android.telephony.SubscriptionManager createForAllUserProfiles();
     method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public android.os.ParcelUuid createSubscriptionGroup(@NonNull java.util.List<java.lang.Integer>);
     method @Deprecated public static android.telephony.SubscriptionManager from(android.content.Context);
     method public java.util.List<android.telephony.SubscriptionInfo> getAccessibleSubscriptionInfoList();
@@ -45682,6 +45720,7 @@
     field public static final String ACTION_MULTI_SIM_CONFIG_CHANGED = "android.telephony.action.MULTI_SIM_CONFIG_CHANGED";
     field public static final String ACTION_NETWORK_COUNTRY_CHANGED = "android.telephony.action.NETWORK_COUNTRY_CHANGED";
     field @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final String ACTION_PHONE_STATE_CHANGED = "android.intent.action.PHONE_STATE";
+    field @FlaggedApi("com.android.internal.telephony.flags.reset_mobile_network_settings") public static final String ACTION_RESET_MOBILE_NETWORK_SETTINGS = "android.telephony.action.RESET_MOBILE_NETWORK_SETTINGS";
     field public static final String ACTION_RESPOND_VIA_MESSAGE = "android.intent.action.RESPOND_VIA_MESSAGE";
     field public static final String ACTION_SECRET_CODE = "android.telephony.action.SECRET_CODE";
     field public static final String ACTION_SHOW_VOICEMAIL_NOTIFICATION = "android.telephony.action.SHOW_VOICEMAIL_NOTIFICATION";
@@ -47258,6 +47297,7 @@
     method public int getLineForOffset(int);
     method public int getLineForVertical(int);
     method public float getLineLeft(int);
+    method @FlaggedApi("com.android.text.flags.inter_character_justification") @IntRange(from=0) public int getLineLetterSpacingUnitCount(@IntRange(from=0) int, boolean);
     method public float getLineMax(int);
     method public float getLineRight(int);
     method @FlaggedApi("com.android.text.flags.use_bounds_for_width") public final float getLineSpacingAmount();
@@ -51764,6 +51804,7 @@
   public static class SurfaceControl.Transaction implements java.io.Closeable android.os.Parcelable {
     ctor public SurfaceControl.Transaction();
     method @NonNull public android.view.SurfaceControl.Transaction addTransactionCommittedListener(@NonNull java.util.concurrent.Executor, @NonNull android.view.SurfaceControl.TransactionCommittedListener);
+    method @FlaggedApi("com.android.window.flags.sdk_desired_present_time") @NonNull public android.view.SurfaceControl.Transaction addTransactionCompletedListener(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.view.SurfaceControl.TransactionStats>);
     method public void apply();
     method @NonNull public android.view.SurfaceControl.Transaction clearFrameRate(@NonNull android.view.SurfaceControl);
     method @NonNull public android.view.SurfaceControl.Transaction clearTrustedPresentationCallback(@NonNull android.view.SurfaceControl);
@@ -51780,9 +51821,11 @@
     method @NonNull public android.view.SurfaceControl.Transaction setCrop(@NonNull android.view.SurfaceControl, @Nullable android.graphics.Rect);
     method @NonNull public android.view.SurfaceControl.Transaction setDamageRegion(@NonNull android.view.SurfaceControl, @Nullable android.graphics.Region);
     method @NonNull public android.view.SurfaceControl.Transaction setDataSpace(@NonNull android.view.SurfaceControl, int);
+    method @FlaggedApi("com.android.window.flags.sdk_desired_present_time") @NonNull public android.view.SurfaceControl.Transaction setDesiredPresentTime(long);
     method @NonNull public android.view.SurfaceControl.Transaction setExtendedRangeBrightness(@NonNull android.view.SurfaceControl, float, float);
     method @NonNull public android.view.SurfaceControl.Transaction setFrameRate(@NonNull android.view.SurfaceControl, @FloatRange(from=0.0) float, int);
     method @NonNull public android.view.SurfaceControl.Transaction setFrameRate(@NonNull android.view.SurfaceControl, @FloatRange(from=0.0) float, int, int);
+    method @FlaggedApi("com.android.window.flags.sdk_desired_present_time") @NonNull public android.view.SurfaceControl.Transaction setFrameTimeline(long);
     method @Deprecated @NonNull public android.view.SurfaceControl.Transaction setGeometry(@NonNull android.view.SurfaceControl, @Nullable android.graphics.Rect, @Nullable android.graphics.Rect, int);
     method @NonNull public android.view.SurfaceControl.Transaction setLayer(@NonNull android.view.SurfaceControl, @IntRange(from=java.lang.Integer.MIN_VALUE, to=java.lang.Integer.MAX_VALUE) int);
     method @NonNull public android.view.SurfaceControl.Transaction setOpaque(@NonNull android.view.SurfaceControl, boolean);
@@ -51798,6 +51841,11 @@
     method public void onTransactionCommitted();
   }
 
+  @FlaggedApi("com.android.window.flags.sdk_desired_present_time") public static final class SurfaceControl.TransactionStats {
+    method @FlaggedApi("com.android.window.flags.sdk_desired_present_time") public long getLatchTime();
+    method @FlaggedApi("com.android.window.flags.sdk_desired_present_time") @NonNull public android.hardware.SyncFence getPresentFence();
+  }
+
   public static final class SurfaceControl.TrustedPresentationThresholds {
     ctor public SurfaceControl.TrustedPresentationThresholds(@FloatRange(from=0.0f, fromInclusive=false, to=1.0f) float, @FloatRange(from=0.0f, fromInclusive=false, to=1.0f) float, @IntRange(from=1) int);
   }
@@ -53936,11 +53984,14 @@
     field public static final String PROPERTY_CAMERA_COMPAT_ALLOW_REFRESH = "android.window.PROPERTY_CAMERA_COMPAT_ALLOW_REFRESH";
     field public static final String PROPERTY_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE = "android.window.PROPERTY_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE";
     field public static final String PROPERTY_COMPAT_ALLOW_DISPLAY_ORIENTATION_OVERRIDE = "android.window.PROPERTY_COMPAT_ALLOW_DISPLAY_ORIENTATION_OVERRIDE";
+    field @FlaggedApi("com.android.window.flags.app_compat_properties_api") public static final String PROPERTY_COMPAT_ALLOW_IGNORING_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED = "android.window.PROPERTY_COMPAT_ALLOW_IGNORING_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED";
     field @FlaggedApi("com.android.window.flags.app_compat_properties_api") public static final String PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE = "android.window.PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE";
     field public static final String PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE = "android.window.PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE";
+    field @FlaggedApi("com.android.window.flags.app_compat_properties_api") public static final String PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES = "android.window.PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES";
     field public static final String PROPERTY_COMPAT_ALLOW_SANDBOXING_VIEW_BOUNDS_APIS = "android.window.PROPERTY_COMPAT_ALLOW_SANDBOXING_VIEW_BOUNDS_APIS";
     field public static final String PROPERTY_COMPAT_ENABLE_FAKE_FOCUS = "android.window.PROPERTY_COMPAT_ENABLE_FAKE_FOCUS";
     field public static final String PROPERTY_COMPAT_IGNORE_REQUESTED_ORIENTATION = "android.window.PROPERTY_COMPAT_IGNORE_REQUESTED_ORIENTATION";
+    field @FlaggedApi("com.android.window.flags.supports_multi_instance_system_ui") public static final String PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI = "android.window.PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI";
   }
 
   public static class WindowManager.BadTokenException extends java.lang.RuntimeException {
@@ -56452,6 +56503,7 @@
     field public static final String TYPE_EMAIL = "email";
     field public static final String TYPE_FLIGHT_NUMBER = "flight";
     field public static final String TYPE_OTHER = "other";
+    field @FlaggedApi("android.service.notification.redact_sensitive_notifications_from_untrusted_listeners") public static final String TYPE_OTP_CODE = "otp_code";
     field public static final String TYPE_PHONE = "phone";
     field public static final String TYPE_UNKNOWN = "";
     field public static final String TYPE_URL = "url";
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index de330de..c1b9f64 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -395,8 +395,6 @@
   }
 
   public class Process {
-    method public static final int getAppUidForSdkSandboxUid(int);
-    method public static final boolean isSdkSandboxUid(int);
     method public static final int toSdkSandboxUid(int);
     field public static final int NFC_UID = 1027; // 0x403
     field public static final int VPN_UID = 1016; // 0x3f8
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 51e61e6..a532cdb 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -95,7 +95,7 @@
     field public static final String BYPASS_ROLE_QUALIFICATION = "android.permission.BYPASS_ROLE_QUALIFICATION";
     field public static final String CALL_AUDIO_INTERCEPTION = "android.permission.CALL_AUDIO_INTERCEPTION";
     field public static final String CAMERA_DISABLE_TRANSMIT_LED = "android.permission.CAMERA_DISABLE_TRANSMIT_LED";
-    field public static final String CAMERA_HEADLESS_SYSTEM_USER = "android.permission.CAMERA_HEADLESS_SYSTEM_USER";
+    field @FlaggedApi("com.android.internal.camera.flags.camera_hsum_permission") public static final String CAMERA_HEADLESS_SYSTEM_USER = "android.permission.CAMERA_HEADLESS_SYSTEM_USER";
     field public static final String CAMERA_OPEN_CLOSE_LISTENER = "android.permission.CAMERA_OPEN_CLOSE_LISTENER";
     field public static final String CAPTURE_AUDIO_HOTWORD = "android.permission.CAPTURE_AUDIO_HOTWORD";
     field public static final String CAPTURE_CONSENTLESS_BUGREPORT_ON_USERDEBUG_BUILD = "android.permission.CAPTURE_CONSENTLESS_BUGREPORT_ON_USERDEBUG_BUILD";
@@ -473,8 +473,9 @@
     field public static final int config_defaultCallScreening = 17039398; // 0x1040026
     field public static final int config_defaultDialer = 17039395; // 0x1040023
     field public static final int config_defaultNotes = 17039429; // 0x1040045
-    field public static final int config_defaultRetailDemo;
+    field @FlaggedApi("android.permission.flags.retail_demo_role_enabled") public static final int config_defaultRetailDemo;
     field public static final int config_defaultSms = 17039396; // 0x1040024
+    field @FlaggedApi("android.permission.flags.wallet_role_enabled") public static final int config_defaultWallet;
     field public static final int config_devicePolicyManagement = 17039421; // 0x104003d
     field public static final int config_feedbackIntentExtraKey = 17039391; // 0x104001f
     field public static final int config_feedbackIntentNameKey = 17039392; // 0x1040020
@@ -1595,12 +1596,14 @@
     method public int getDensityLevel();
     method @NonNull public java.time.Instant getEndTime();
     method public int getEventType();
+    method @FlaggedApi("android.app.ambient_heart_rate") @IntRange(from=0xffffffff) public int getRatePerMinute();
     method @NonNull public java.time.Instant getStartTime();
     method @NonNull public android.os.PersistableBundle getVendorData();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.app.ambientcontext.AmbientContextEvent> CREATOR;
     field public static final int EVENT_BACK_DOUBLE_TAP = 3; // 0x3
     field public static final int EVENT_COUGH = 1; // 0x1
+    field @FlaggedApi("android.app.ambient_heart_rate") public static final int EVENT_HEART_RATE = 4; // 0x4
     field public static final int EVENT_SNORE = 2; // 0x2
     field public static final int EVENT_UNKNOWN = 0; // 0x0
     field public static final int EVENT_VENDOR_WEARABLE_START = 100000; // 0x186a0
@@ -1620,6 +1623,7 @@
     method @NonNull public android.app.ambientcontext.AmbientContextEvent.Builder setDensityLevel(int);
     method @NonNull public android.app.ambientcontext.AmbientContextEvent.Builder setEndTime(@NonNull java.time.Instant);
     method @NonNull public android.app.ambientcontext.AmbientContextEvent.Builder setEventType(int);
+    method @FlaggedApi("android.app.ambient_heart_rate") @NonNull public android.app.ambientcontext.AmbientContextEvent.Builder setRatePerMinute(@IntRange(from=0xffffffff) int);
     method @NonNull public android.app.ambientcontext.AmbientContextEvent.Builder setStartTime(@NonNull java.time.Instant);
     method @NonNull public android.app.ambientcontext.AmbientContextEvent.Builder setVendorData(@NonNull android.os.PersistableBundle);
   }
@@ -3357,7 +3361,7 @@
 
   @FlaggedApi("android.companion.virtual.flags.virtual_camera") public final class VirtualCameraConfig implements android.os.Parcelable {
     method public int describeContents();
-    method @StringRes public int getDisplayNameStringRes();
+    method @NonNull public String getName();
     method @NonNull public java.util.Set<android.companion.virtual.camera.VirtualCameraStreamConfig> getStreamConfigs();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.companion.virtual.camera.VirtualCameraConfig> CREATOR;
@@ -3367,7 +3371,7 @@
     ctor public VirtualCameraConfig.Builder();
     method @NonNull public android.companion.virtual.camera.VirtualCameraConfig.Builder addStreamConfig(int, int, int);
     method @NonNull public android.companion.virtual.camera.VirtualCameraConfig build();
-    method @NonNull public android.companion.virtual.camera.VirtualCameraConfig.Builder setDisplayNameStringRes(@StringRes int);
+    method @NonNull public android.companion.virtual.camera.VirtualCameraConfig.Builder setName(@NonNull String);
     method @NonNull public android.companion.virtual.camera.VirtualCameraConfig.Builder setVirtualCameraCallback(@NonNull java.util.concurrent.Executor, @NonNull android.companion.virtual.camera.VirtualCameraCallback);
   }
 
@@ -4141,7 +4145,7 @@
     field public static final int PROTECTION_FLAG_MODULE = 4194304; // 0x400000
     field public static final int PROTECTION_FLAG_OEM = 16384; // 0x4000
     field public static final int PROTECTION_FLAG_RECENTS = 33554432; // 0x2000000
-    field @Deprecated public static final int PROTECTION_FLAG_RETAIL_DEMO = 16777216; // 0x1000000
+    field public static final int PROTECTION_FLAG_RETAIL_DEMO = 16777216; // 0x1000000
     field public static final int PROTECTION_FLAG_ROLE = 67108864; // 0x4000000
     field public static final int PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER = 65536; // 0x10000
     field public static final int PROTECTION_FLAG_VENDOR_PRIVILEGED = 32768; // 0x8000
@@ -5747,7 +5751,7 @@
     method public void addOnCompleteListener(@NonNull java.util.concurrent.Executor, @NonNull android.hardware.radio.ProgramList.OnCompleteListener);
     method public void addOnCompleteListener(@NonNull android.hardware.radio.ProgramList.OnCompleteListener);
     method public void close();
-    method @Deprecated @Nullable public android.hardware.radio.RadioManager.ProgramInfo get(@NonNull android.hardware.radio.ProgramSelector.Identifier);
+    method @Nullable public android.hardware.radio.RadioManager.ProgramInfo get(@NonNull android.hardware.radio.ProgramSelector.Identifier);
     method @FlaggedApi("android.hardware.radio.hd_radio_improved") @NonNull public java.util.List<android.hardware.radio.RadioManager.ProgramInfo> getProgramInfos(@NonNull android.hardware.radio.ProgramSelector.Identifier);
     method public void registerListCallback(@NonNull java.util.concurrent.Executor, @NonNull android.hardware.radio.ProgramList.ListCallback);
     method public void registerListCallback(@NonNull android.hardware.radio.ProgramList.ListCallback);
@@ -5799,7 +5803,7 @@
     field @Deprecated public static final int IDENTIFIER_TYPE_DAB_SIDECC = 5; // 0x5
     field @Deprecated public static final int IDENTIFIER_TYPE_DAB_SID_EXT = 5; // 0x5
     field public static final int IDENTIFIER_TYPE_DRMO_FREQUENCY = 10; // 0xa
-    field @Deprecated public static final int IDENTIFIER_TYPE_DRMO_MODULATION = 11; // 0xb
+    field public static final int IDENTIFIER_TYPE_DRMO_MODULATION = 11; // 0xb
     field public static final int IDENTIFIER_TYPE_DRMO_SERVICE_ID = 9; // 0x9
     field public static final int IDENTIFIER_TYPE_HD_STATION_ID_EXT = 3; // 0x3
     field @FlaggedApi("android.hardware.radio.hd_radio_improved") public static final int IDENTIFIER_TYPE_HD_STATION_LOCATION = 15; // 0xf
@@ -5807,8 +5811,8 @@
     field @Deprecated public static final int IDENTIFIER_TYPE_HD_SUBCHANNEL = 4; // 0x4
     field public static final int IDENTIFIER_TYPE_INVALID = 0; // 0x0
     field public static final int IDENTIFIER_TYPE_RDS_PI = 2; // 0x2
-    field @Deprecated public static final int IDENTIFIER_TYPE_SXM_CHANNEL = 13; // 0xd
-    field @Deprecated public static final int IDENTIFIER_TYPE_SXM_SERVICE_ID = 12; // 0xc
+    field public static final int IDENTIFIER_TYPE_SXM_CHANNEL = 13; // 0xd
+    field public static final int IDENTIFIER_TYPE_SXM_SERVICE_ID = 12; // 0xc
     field public static final int IDENTIFIER_TYPE_VENDOR_END = 1999; // 0x7cf
     field @Deprecated public static final int IDENTIFIER_TYPE_VENDOR_PRIMARY_END = 1999; // 0x7cf
     field @Deprecated public static final int IDENTIFIER_TYPE_VENDOR_PRIMARY_START = 1000; // 0x3e8
@@ -5861,7 +5865,7 @@
     field public static final int CONFIG_DAB_DAB_SOFT_LINKING = 8; // 0x8
     field public static final int CONFIG_DAB_FM_LINKING = 7; // 0x7
     field public static final int CONFIG_DAB_FM_SOFT_LINKING = 9; // 0x9
-    field @Deprecated public static final int CONFIG_FORCE_ANALOG = 2; // 0x2
+    field public static final int CONFIG_FORCE_ANALOG = 2; // 0x2
     field @FlaggedApi("android.hardware.radio.hd_radio_improved") public static final int CONFIG_FORCE_ANALOG_AM = 11; // 0xb
     field @FlaggedApi("android.hardware.radio.hd_radio_improved") public static final int CONFIG_FORCE_ANALOG_FM = 10; // 0xa
     field public static final int CONFIG_FORCE_DIGITAL = 3; // 0x3
@@ -9884,17 +9888,20 @@
     method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enable();
     method @FlaggedApi("android.nfc.enable_nfc_reader_option") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enableReaderOption(boolean);
     method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enableSecureNfc(boolean);
+    method @FlaggedApi("android.nfc.enable_nfc_charging") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enableWlc(boolean);
     method @FlaggedApi("android.nfc.enable_nfc_mainline") public int getAdapterState();
     method @NonNull @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public java.util.Map<java.lang.String,java.lang.Boolean> getTagIntentAppPreferenceForUser(int);
     method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public boolean isControllerAlwaysOn();
     method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public boolean isControllerAlwaysOnSupported();
     method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean isTagIntentAppPreferenceSupported();
     method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public void registerControllerAlwaysOnListener(@NonNull java.util.concurrent.Executor, @NonNull android.nfc.NfcAdapter.ControllerAlwaysOnListener);
+    method @FlaggedApi("android.nfc.enable_nfc_charging") public void registerWlcStateListener(@NonNull java.util.concurrent.Executor, @NonNull android.nfc.NfcAdapter.WlcStateListener);
     method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean removeNfcUnlockHandler(android.nfc.NfcAdapter.NfcUnlockHandler);
     method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public boolean setControllerAlwaysOn(boolean);
     method @FlaggedApi("android.nfc.enable_nfc_mainline") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setReaderMode(boolean);
     method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public int setTagIntentAppPreferenceForUser(int, @NonNull String, boolean);
     method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public void unregisterControllerAlwaysOnListener(@NonNull android.nfc.NfcAdapter.ControllerAlwaysOnListener);
+    method @FlaggedApi("android.nfc.enable_nfc_charging") public void unregisterWlcStateListener(@NonNull android.nfc.NfcAdapter.WlcStateListener);
     field @FlaggedApi("android.nfc.enable_nfc_mainline") public static final String ACTION_REQUIRE_UNLOCK_FOR_NFC = "android.nfc.action.REQUIRE_UNLOCK_FOR_NFC";
     field public static final int TAG_INTENT_APP_PREF_RESULT_PACKAGE_NOT_FOUND = -1; // 0xffffffff
     field public static final int TAG_INTENT_APP_PREF_RESULT_SUCCESS = 0; // 0x0
@@ -9909,6 +9916,10 @@
     method public boolean onUnlockAttempted(android.nfc.Tag);
   }
 
+  @FlaggedApi("android.nfc.enable_nfc_charging") public static interface NfcAdapter.WlcStateListener {
+    method public void onWlcStateChanged(@NonNull android.nfc.WlcLDeviceInfo);
+  }
+
 }
 
 package android.nfc.cardemulation {
@@ -9960,6 +9971,7 @@
   }
 
   public final class CardEmulation {
+    method @FlaggedApi("android.permission.flags.wallet_role_enabled") @Nullable @RequiresPermission(android.Manifest.permission.NFC_PREFERRED_PAYMENT_INFO) public android.nfc.cardemulation.ApduServiceInfo getPreferredPaymentService();
     method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public java.util.List<android.nfc.cardemulation.ApduServiceInfo> getServices(@NonNull String, int);
   }
 
@@ -10564,7 +10576,7 @@
     method @RequiresPermission(anyOf={android.Manifest.permission.RECOVERY, android.Manifest.permission.REBOOT}) public static int rebootAndApply(@NonNull android.content.Context, @NonNull String, boolean) throws java.io.IOException;
     method @RequiresPermission(allOf={android.Manifest.permission.RECOVERY, android.Manifest.permission.REBOOT}) public static void rebootWipeAb(android.content.Context, java.io.File, String) throws java.io.IOException;
     method @RequiresPermission(android.Manifest.permission.RECOVERY) public static void scheduleUpdateOnBoot(android.content.Context, java.io.File) throws java.io.IOException;
-    method public static boolean verifyPackageCompatibility(java.io.File) throws java.io.IOException;
+    method @Deprecated public static boolean verifyPackageCompatibility(java.io.File) throws java.io.IOException;
     field public static final int RESUME_ON_REBOOT_REBOOT_ERROR_INVALID_PACKAGE_NAME = 2000; // 0x7d0
     field public static final int RESUME_ON_REBOOT_REBOOT_ERROR_LSKF_NOT_CAPTURED = 3000; // 0xbb8
     field public static final int RESUME_ON_REBOOT_REBOOT_ERROR_PROVIDER_PREPARATION_FAILURE = 5000; // 0x1388
@@ -14672,6 +14684,7 @@
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isLteCdmaEvdoGsmWcdmaEnabled();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isMobileDataPolicyEnabled(int);
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isNrDualConnectivityEnabled();
+    method @FlaggedApi("com.android.internal.telephony.flags.enable_modem_cipher_transparency") @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isNullCipherNotificationsEnabled();
     method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isOffhook();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isOpportunisticNetworkEnabled();
     method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isPotentialEmergencyNumber(@NonNull String);
@@ -14711,6 +14724,7 @@
     method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataEnabled(int, boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataRoamingEnabled(boolean);
     method @FlaggedApi("com.android.internal.telephony.flags.enable_identifier_disclosure_transparency") @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setEnableCellularIdentifierDisclosureNotifications(boolean);
+    method @FlaggedApi("com.android.internal.telephony.flags.enable_modem_cipher_transparency") @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setEnableNullCipherNotifications(boolean);
     method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public android.telephony.PinResult setIccLockEnabled(boolean, @NonNull String);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setMobileDataPolicyEnabled(int, boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setMultiSimCarrierRestriction(boolean);
@@ -15118,7 +15132,7 @@
     method public final void notifyDataCallListChanged(java.util.List<android.telephony.data.DataCallResponse>);
     method public final void notifyDataProfileUnthrottled(@NonNull android.telephony.data.DataProfile);
     method public void requestDataCallList(@NonNull android.telephony.data.DataServiceCallback);
-    method @FlaggedApi("com.android.internal.telephony.flags.network_validation") public void requestValidation(int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
+    method @FlaggedApi("com.android.internal.telephony.flags.network_validation") public void requestNetworkValidation(int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
     method public void setDataProfile(@NonNull java.util.List<android.telephony.data.DataProfile>, boolean, @NonNull android.telephony.data.DataServiceCallback);
     method public void setInitialAttachApn(@NonNull android.telephony.data.DataProfile, boolean, @NonNull android.telephony.data.DataServiceCallback);
     method public void setupDataCall(int, @NonNull android.telephony.data.DataProfile, boolean, boolean, int, @Nullable android.net.LinkProperties, @NonNull android.telephony.data.DataServiceCallback);
@@ -16325,7 +16339,7 @@
 
   public interface RegistrationManager {
     field public static final int SUGGESTED_ACTION_NONE = 0; // 0x0
-    field @FlaggedApi("com.android.internal.telephony.flags.add_rat_related_suggested_action_to_ims_registration") public static final int SUGGESTED_ACTION_TRIGGER_CLEAR_RAT_BLOCK = 4; // 0x4
+    field @FlaggedApi("com.android.internal.telephony.flags.add_rat_related_suggested_action_to_ims_registration") public static final int SUGGESTED_ACTION_TRIGGER_CLEAR_RAT_BLOCKS = 4; // 0x4
     field public static final int SUGGESTED_ACTION_TRIGGER_PLMN_BLOCK = 1; // 0x1
     field public static final int SUGGESTED_ACTION_TRIGGER_PLMN_BLOCK_WITH_TIMEOUT = 2; // 0x2
     field @FlaggedApi("com.android.internal.telephony.flags.add_rat_related_suggested_action_to_ims_registration") public static final int SUGGESTED_ACTION_TRIGGER_RAT_BLOCK = 3; // 0x3
@@ -17023,8 +17037,8 @@
 
   @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public final class PointingInfo implements android.os.Parcelable {
     method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public int describeContents();
-    method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public float getSatelliteAzimuthDegrees();
-    method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public float getSatelliteElevationDegrees();
+    method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @FloatRange(from=0xffffff4c, to=180) public float getSatelliteAzimuthDegrees();
+    method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @FloatRange(from=0xffffffa6, to=90) public float getSatelliteElevationDegrees();
     method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public void writeToParcel(@NonNull android.os.Parcel, int);
     field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @NonNull public static final android.os.Parcelable.Creator<android.telephony.satellite.PointingInfo> CREATOR;
   }
@@ -17063,7 +17077,7 @@
     method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void registerForNtnSignalStrengthChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.NtnSignalStrengthCallback) throws android.telephony.satellite.SatelliteManager.SatelliteException;
     method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForSatelliteCapabilitiesChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteCapabilitiesCallback);
     method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForSatelliteDatagram(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteDatagramCallback);
-    method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForSatelliteModemStateChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteStateCallback);
+    method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForSatelliteModemStateChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteModemStateCallback);
     method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForSatelliteProvisionStateChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteProvisionStateCallback);
     method @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void removeSatelliteAttachRestrictionForCarrier(int, int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
     method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestIsDemoModeEnabled(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>);
@@ -17084,7 +17098,7 @@
     method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForNtnSignalStrengthChanged(@NonNull android.telephony.satellite.NtnSignalStrengthCallback);
     method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForSatelliteCapabilitiesChanged(@NonNull android.telephony.satellite.SatelliteCapabilitiesCallback);
     method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForSatelliteDatagram(@NonNull android.telephony.satellite.SatelliteDatagramCallback);
-    method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForSatelliteModemStateChanged(@NonNull android.telephony.satellite.SatelliteStateCallback);
+    method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForSatelliteModemStateChanged(@NonNull android.telephony.satellite.SatelliteModemStateCallback);
     method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForSatelliteProvisionStateChanged(@NonNull android.telephony.satellite.SatelliteProvisionStateCallback);
     field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int DATAGRAM_TYPE_LOCATION_SHARING = 2; // 0x2
     field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int DATAGRAM_TYPE_SOS_MESSAGE = 1; // 0x1
@@ -17154,12 +17168,12 @@
     method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public int getErrorCode();
   }
 
-  @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public interface SatelliteProvisionStateCallback {
-    method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public void onSatelliteProvisionStateChanged(boolean);
+  @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public interface SatelliteModemStateCallback {
+    method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public void onSatelliteModemStateChanged(int);
   }
 
-  @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public interface SatelliteStateCallback {
-    method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public void onSatelliteModemStateChanged(int);
+  @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public interface SatelliteProvisionStateCallback {
+    method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public void onSatelliteProvisionStateChanged(boolean);
   }
 
   @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public interface SatelliteTransmissionUpdateCallback {
diff --git a/core/api/system-lint-baseline.txt b/core/api/system-lint-baseline.txt
index b2a28b2..0505af4 100644
--- a/core/api/system-lint-baseline.txt
+++ b/core/api/system-lint-baseline.txt
@@ -2359,8 +2359,8 @@
     New API must be flagged with @FlaggedApi: method android.telephony.satellite.SatelliteManager.provisionSatelliteService(String,byte[],android.os.CancellationSignal,java.util.concurrent.Executor,java.util.function.Consumer<java.lang.Integer>)
 UnflaggedApi: android.telephony.satellite.SatelliteManager#registerForSatelliteDatagram(java.util.concurrent.Executor, android.telephony.satellite.SatelliteDatagramCallback):
     New API must be flagged with @FlaggedApi: method android.telephony.satellite.SatelliteManager.registerForSatelliteDatagram(java.util.concurrent.Executor,android.telephony.satellite.SatelliteDatagramCallback)
-UnflaggedApi: android.telephony.satellite.SatelliteManager#registerForSatelliteModemStateChanged(java.util.concurrent.Executor, android.telephony.satellite.SatelliteStateCallback):
-    New API must be flagged with @FlaggedApi: method android.telephony.satellite.SatelliteManager.registerForSatelliteModemStateChanged(java.util.concurrent.Executor,android.telephony.satellite.SatelliteStateCallback)
+UnflaggedApi: android.telephony.satellite.SatelliteManager#registerForSatelliteModemStateChanged(java.util.concurrent.Executor, android.telephony.satellite.SatelliteModemStateCallback):
+    New API must be flagged with @FlaggedApi: method android.telephony.satellite.SatelliteManager.registerForSatelliteModemStateChanged(java.util.concurrent.Executor,android.telephony.satellite.SatelliteModemStateCallback)
 UnflaggedApi: android.telephony.satellite.SatelliteManager#registerForSatelliteProvisionStateChanged(java.util.concurrent.Executor, android.telephony.satellite.SatelliteProvisionStateCallback):
     New API must be flagged with @FlaggedApi: method android.telephony.satellite.SatelliteManager.registerForSatelliteProvisionStateChanged(java.util.concurrent.Executor,android.telephony.satellite.SatelliteProvisionStateCallback)
 UnflaggedApi: android.telephony.satellite.SatelliteManager#requestIsDemoModeEnabled(java.util.concurrent.Executor, android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>):
@@ -2389,8 +2389,8 @@
     New API must be flagged with @FlaggedApi: method android.telephony.satellite.SatelliteManager.stopSatelliteTransmissionUpdates(android.telephony.satellite.SatelliteTransmissionUpdateCallback,java.util.concurrent.Executor,java.util.function.Consumer<java.lang.Integer>)
 UnflaggedApi: android.telephony.satellite.SatelliteManager#unregisterForSatelliteDatagram(android.telephony.satellite.SatelliteDatagramCallback):
     New API must be flagged with @FlaggedApi: method android.telephony.satellite.SatelliteManager.unregisterForSatelliteDatagram(android.telephony.satellite.SatelliteDatagramCallback)
-UnflaggedApi: android.telephony.satellite.SatelliteManager#unregisterForSatelliteModemStateChanged(android.telephony.satellite.SatelliteStateCallback):
-    New API must be flagged with @FlaggedApi: method android.telephony.satellite.SatelliteManager.unregisterForSatelliteModemStateChanged(android.telephony.satellite.SatelliteStateCallback)
+UnflaggedApi: android.telephony.satellite.SatelliteManager#unregisterForSatelliteModemStateChanged(android.telephony.satellite.SatelliteModemStateCallback):
+    New API must be flagged with @FlaggedApi: method android.telephony.satellite.SatelliteManager.unregisterForSatelliteModemStateChanged(android.telephony.satellite.SatelliteModemStateCallback)
 UnflaggedApi: android.telephony.satellite.SatelliteManager#unregisterForSatelliteProvisionStateChanged(android.telephony.satellite.SatelliteProvisionStateCallback):
     New API must be flagged with @FlaggedApi: method android.telephony.satellite.SatelliteManager.unregisterForSatelliteProvisionStateChanged(android.telephony.satellite.SatelliteProvisionStateCallback)
 UnflaggedApi: android.telephony.satellite.SatelliteManager.SatelliteException:
@@ -2403,10 +2403,10 @@
     New API must be flagged with @FlaggedApi: class android.telephony.satellite.SatelliteProvisionStateCallback
 UnflaggedApi: android.telephony.satellite.SatelliteProvisionStateCallback#onSatelliteProvisionStateChanged(boolean):
     New API must be flagged with @FlaggedApi: method android.telephony.satellite.SatelliteProvisionStateCallback.onSatelliteProvisionStateChanged(boolean)
-UnflaggedApi: android.telephony.satellite.SatelliteStateCallback:
-    New API must be flagged with @FlaggedApi: class android.telephony.satellite.SatelliteStateCallback
-UnflaggedApi: android.telephony.satellite.SatelliteStateCallback#onSatelliteModemStateChanged(int):
-    New API must be flagged with @FlaggedApi: method android.telephony.satellite.SatelliteStateCallback.onSatelliteModemStateChanged(int)
+UnflaggedApi: android.telephony.satellite.SatelliteModemStateCallback:
+    New API must be flagged with @FlaggedApi: class android.telephony.satellite.SatelliteModemStateCallback
+UnflaggedApi: android.telephony.satellite.SatelliteModemStateCallback#onSatelliteModemStateChanged(int):
+    New API must be flagged with @FlaggedApi: method android.telephony.satellite.SatelliteModemStateCallback.onSatelliteModemStateChanged(int)
 UnflaggedApi: android.telephony.satellite.SatelliteTransmissionUpdateCallback:
     New API must be flagged with @FlaggedApi: class android.telephony.satellite.SatelliteTransmissionUpdateCallback
 UnflaggedApi: android.telephony.satellite.SatelliteTransmissionUpdateCallback#onReceiveDatagramStateChanged(int, int, int):
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 812ba6d..bbe03a3 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -284,6 +284,16 @@
     method public default void onOpActiveChanged(@NonNull String, int, @NonNull String, @Nullable String, boolean, int, int);
   }
 
+  public final class AutomaticZenRule implements android.os.Parcelable {
+    method @FlaggedApi("android.app.modes_api") public int getUserModifiedFields();
+    field @FlaggedApi("android.app.modes_api") public static final int FIELD_INTERRUPTION_FILTER = 2; // 0x2
+    field @FlaggedApi("android.app.modes_api") public static final int FIELD_NAME = 1; // 0x1
+  }
+
+  @FlaggedApi("android.app.modes_api") public static final class AutomaticZenRule.Builder {
+    method @FlaggedApi("android.app.modes_api") @NonNull public android.app.AutomaticZenRule.Builder setUserModifiedFields(int);
+  }
+
   public class BroadcastOptions extends android.app.ComponentOptions {
     ctor public BroadcastOptions();
     ctor public BroadcastOptions(@NonNull android.os.Bundle);
@@ -1010,6 +1020,7 @@
     field public static final long OVERRIDE_CAMERA_COMPAT_DISABLE_REFRESH = 264304459L; // 0xfc0f74bL
     field public static final long OVERRIDE_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE = 264301586L; // 0xfc0ec12L
     field public static final long OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS = 263259275L; // 0xfb1048bL
+    field @FlaggedApi("com.android.window.flags.app_compat_properties_api") public static final long OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED = 273509367L; // 0x104d6bf7L
     field public static final long OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION = 254631730L; // 0xf2d5f32L
     field public static final long OVERRIDE_LANDSCAPE_ORIENTATION_TO_REVERSE_LANDSCAPE = 266124927L; // 0xfdcbe7fL
     field public static final long OVERRIDE_MIN_ASPECT_RATIO = 174042980L; // 0xa5faf64L
@@ -1871,6 +1882,7 @@
     method public void setRampingRingerEnabled(boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED) public void setRs2Value(float);
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setTestDeviceConnectionState(@NonNull android.media.AudioDeviceAttributes, boolean);
+    method @FlaggedApi("android.media.audio.focus_exclusive_with_recording") @RequiresPermission(android.Manifest.permission.QUERY_AUDIO_STATE) public boolean shouldNotificationSoundPlay(@NonNull android.media.AudioAttributes);
   }
 
   public static final class AudioRecord.MetricsConstants {
@@ -2332,6 +2344,9 @@
     field public static final int CPU_LOAD_RESET = 2; // 0x2
     field public static final int CPU_LOAD_RESUME = 3; // 0x3
     field public static final int CPU_LOAD_UP = 0; // 0x0
+    field @FlaggedApi("android.os.adpf_gpu_report_actual_work_duration") public static final int GPU_LOAD_DOWN = 6; // 0x6
+    field @FlaggedApi("android.os.adpf_gpu_report_actual_work_duration") public static final int GPU_LOAD_RESET = 7; // 0x7
+    field @FlaggedApi("android.os.adpf_gpu_report_actual_work_duration") public static final int GPU_LOAD_UP = 5; // 0x5
   }
 
   public final class PowerManager {
@@ -2343,9 +2358,7 @@
   }
 
   public class Process {
-    method public static final int getAppUidForSdkSandboxUid(int);
     method public static final int getThreadScheduler(int) throws java.lang.IllegalArgumentException;
-    method public static final boolean isSdkSandboxUid(int);
     method public static final int toSdkSandboxUid(int);
     field public static final int FIRST_APP_ZYGOTE_ISOLATED_UID = 90000; // 0x15f90
     field public static final int FIRST_ISOLATED_UID = 99000; // 0x182b8
@@ -3008,6 +3021,49 @@
     method @Deprecated public boolean isBound();
   }
 
+  @FlaggedApi("android.app.modes_api") public final class ZenDeviceEffects implements android.os.Parcelable {
+    method public int getUserModifiedFields();
+    field public static final int FIELD_DIM_WALLPAPER = 4; // 0x4
+    field public static final int FIELD_DISABLE_AUTO_BRIGHTNESS = 16; // 0x10
+    field public static final int FIELD_DISABLE_TAP_TO_WAKE = 32; // 0x20
+    field public static final int FIELD_DISABLE_TILT_TO_WAKE = 64; // 0x40
+    field public static final int FIELD_DISABLE_TOUCH = 128; // 0x80
+    field public static final int FIELD_GRAYSCALE = 1; // 0x1
+    field public static final int FIELD_MAXIMIZE_DOZE = 512; // 0x200
+    field public static final int FIELD_MINIMIZE_RADIO_USAGE = 256; // 0x100
+    field public static final int FIELD_NIGHT_MODE = 8; // 0x8
+    field public static final int FIELD_SUPPRESS_AMBIENT_DISPLAY = 2; // 0x2
+  }
+
+  @FlaggedApi("android.app.modes_api") public static final class ZenDeviceEffects.Builder {
+    method @NonNull public android.service.notification.ZenDeviceEffects.Builder setUserModifiedFields(int);
+  }
+
+  public final class ZenPolicy implements android.os.Parcelable {
+    method @FlaggedApi("android.app.modes_api") public int getUserModifiedFields();
+    field @FlaggedApi("android.app.modes_api") public static final int FIELD_ALLOW_CHANNELS = 8; // 0x8
+    field @FlaggedApi("android.app.modes_api") public static final int FIELD_CALLS = 2; // 0x2
+    field @FlaggedApi("android.app.modes_api") public static final int FIELD_CONVERSATIONS = 4; // 0x4
+    field @FlaggedApi("android.app.modes_api") public static final int FIELD_MESSAGES = 1; // 0x1
+    field @FlaggedApi("android.app.modes_api") public static final int FIELD_PRIORITY_CATEGORY_ALARMS = 128; // 0x80
+    field @FlaggedApi("android.app.modes_api") public static final int FIELD_PRIORITY_CATEGORY_EVENTS = 32; // 0x20
+    field @FlaggedApi("android.app.modes_api") public static final int FIELD_PRIORITY_CATEGORY_MEDIA = 256; // 0x100
+    field @FlaggedApi("android.app.modes_api") public static final int FIELD_PRIORITY_CATEGORY_REPEAT_CALLERS = 64; // 0x40
+    field @FlaggedApi("android.app.modes_api") public static final int FIELD_PRIORITY_CATEGORY_SYSTEM = 512; // 0x200
+    field @FlaggedApi("android.app.modes_api") public static final int FIELD_VISUAL_EFFECT_AMBIENT = 32768; // 0x8000
+    field @FlaggedApi("android.app.modes_api") public static final int FIELD_VISUAL_EFFECT_BADGE = 16384; // 0x4000
+    field @FlaggedApi("android.app.modes_api") public static final int FIELD_VISUAL_EFFECT_FULL_SCREEN_INTENT = 1024; // 0x400
+    field @FlaggedApi("android.app.modes_api") public static final int FIELD_VISUAL_EFFECT_LIGHTS = 2048; // 0x800
+    field @FlaggedApi("android.app.modes_api") public static final int FIELD_VISUAL_EFFECT_NOTIFICATION_LIST = 65536; // 0x10000
+    field @FlaggedApi("android.app.modes_api") public static final int FIELD_VISUAL_EFFECT_PEEK = 4096; // 0x1000
+    field @FlaggedApi("android.app.modes_api") public static final int FIELD_VISUAL_EFFECT_STATUS_BAR = 8192; // 0x2000
+  }
+
+  public static final class ZenPolicy.Builder {
+    ctor public ZenPolicy.Builder(@Nullable android.service.notification.ZenPolicy);
+    method @FlaggedApi("android.app.modes_api") @NonNull public android.service.notification.ZenPolicy.Builder setUserModifiedFields(int);
+  }
+
 }
 
 package android.service.quickaccesswallet {
@@ -3679,6 +3735,7 @@
 
   public class AnimationUtils {
     method @FlaggedApi("android.view.flags.expected_presentation_time_read_only") public static void lockAnimationClock(long, long);
+    method public static void lockAnimationClock(long);
     method public static void unlockAnimationClock();
   }
 
diff --git a/core/api/test-lint-baseline.txt b/core/api/test-lint-baseline.txt
index bf26bd0..5e904ef9 100644
--- a/core/api/test-lint-baseline.txt
+++ b/core/api/test-lint-baseline.txt
@@ -535,6 +535,10 @@
     Missing nullability on parameter `foreground` in method `isDefaultFocusHighlightNeeded`
 
 
+OptionalBuilderConstructorArgument: android.service.notification.ZenPolicy.Builder#Builder(android.service.notification.ZenPolicy) parameter #0:
+    Builder constructor arguments must be mandatory (i.e. not @Nullable): parameter policy in android.service.notification.ZenPolicy.Builder(android.service.notification.ZenPolicy policy)
+
+
 ProtectedMember: android.app.AppDetailsActivity#onCreate(android.os.Bundle):
     Protected methods not allowed; must be public: method android.app.AppDetailsActivity.onCreate(android.os.Bundle)}
 ProtectedMember: android.view.ViewGroup#resetResolvedDrawables():
@@ -2143,6 +2147,8 @@
     New API must be flagged with @FlaggedApi: field android.service.notification.NotificationRankingUpdate.PARCELABLE_WRITE_RETURN_VALUE
 UnflaggedApi: android.service.notification.NotificationRankingUpdate#isFdNotNullAndClosed():
     New API must be flagged with @FlaggedApi: method android.service.notification.NotificationRankingUpdate.isFdNotNullAndClosed()
+UnflaggedApi: android.service.notification.ZenPolicy.Builder#Builder(android.service.notification.ZenPolicy):
+    New API must be flagged with @FlaggedApi: constructor android.service.notification.ZenPolicy.Builder(android.service.notification.ZenPolicy)
 UnflaggedApi: android.telephony.TelephonyManager#HAL_SERVICE_SATELLITE:
     New API must be flagged with @FlaggedApi: field android.telephony.TelephonyManager.HAL_SERVICE_SATELLITE
 UnflaggedApi: android.telephony.ims.feature.MmTelFeature.MmTelCapabilities:
diff --git a/core/java/android/accounts/AbstractAccountAuthenticator.java b/core/java/android/accounts/AbstractAccountAuthenticator.java
index 45515dd..c1c5c0e 100644
--- a/core/java/android/accounts/AbstractAccountAuthenticator.java
+++ b/core/java/android/accounts/AbstractAccountAuthenticator.java
@@ -142,10 +142,7 @@
     private static final String KEY_ACCOUNT =
             "android.accounts.AbstractAccountAuthenticator.KEY_ACCOUNT";
 
-    private final Context mContext;
-
     public AbstractAccountAuthenticator(Context context) {
-        mContext = context;
     }
 
     private class Transport extends IAccountAuthenticator.Stub {
diff --git a/core/java/android/animation/AnimatorSet.java b/core/java/android/animation/AnimatorSet.java
index b4a6955..845a346 100644
--- a/core/java/android/animation/AnimatorSet.java
+++ b/core/java/android/animation/AnimatorSet.java
@@ -1311,8 +1311,9 @@
         if (!node.mEnded) {
             float durationScale = ValueAnimator.getDurationScale();
             durationScale = durationScale == 0  ? 1 : durationScale;
-            node.mEnded = node.mAnimation.pulseAnimationFrame(
-                    (long) (animPlayTime * durationScale));
+            if (node.mAnimation.pulseAnimationFrame((long) (animPlayTime * durationScale))) {
+                node.mEnded = true;
+            }
         }
     }
 
diff --git a/core/java/android/animation/ObjectAnimator.java b/core/java/android/animation/ObjectAnimator.java
index 1e1f155..5840f02 100644
--- a/core/java/android/animation/ObjectAnimator.java
+++ b/core/java/android/animation/ObjectAnimator.java
@@ -25,8 +25,6 @@
 import android.util.Property;
 import android.view.animation.AccelerateDecelerateInterpolator;
 
-import java.lang.ref.WeakReference;
-
 /**
  * This subclass of {@link ValueAnimator} provides support for animating properties on target objects.
  * The constructors of this class take parameters to define the target object that will be animated
@@ -73,11 +71,7 @@
 
     private static final boolean DBG = false;
 
-    /**
-     * A weak reference to the target object on which the property exists, set
-     * in the constructor. We'll cancel the animation if this goes away.
-     */
-    private WeakReference<Object> mTarget;
+    private Object mTarget;
 
     private String mPropertyName;
 
@@ -919,7 +913,7 @@
      */
     @Nullable
     public Object getTarget() {
-        return mTarget == null ? null : mTarget.get();
+        return mTarget;
     }
 
     @Override
@@ -929,7 +923,7 @@
             if (isStarted()) {
                 cancel();
             }
-            mTarget = target == null ? null : new WeakReference<Object>(target);
+            mTarget = target;
             // New target should cause re-initialization prior to starting
             mInitialized = false;
         }
@@ -977,13 +971,6 @@
     @Override
     void animateValue(float fraction) {
         final Object target = getTarget();
-        if (mTarget != null && target == null) {
-            // We lost the target reference, cancel and clean up. Note: we allow null target if the
-            /// target has never been set.
-            cancel();
-            return;
-        }
-
         super.animateValue(fraction);
         int numValues = mValues.length;
         for (int i = 0; i < numValues; ++i) {
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 5674a10..2103055 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -24,7 +24,9 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.app.WindowConfiguration.inMultiWindowMode;
 import static android.os.Process.myUid;
+
 import static com.android.sdksandbox.flags.Flags.sandboxActivitySdkBasedContext;
+
 import static java.lang.Character.MIN_VALUE;
 
 import android.annotation.AnimRes;
@@ -45,6 +47,7 @@
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
 import android.annotation.UiContext;
+import android.app.ActivityOptions.SceneTransitionInfo;
 import android.app.VoiceInteractor.Request;
 import android.app.admin.DevicePolicyManager;
 import android.app.assist.AssistContent;
@@ -930,8 +933,8 @@
     @UnsupportedAppUsage
     final FragmentController mFragments = FragmentController.createController(new HostCallbacks());
 
-    /** The options for scene transition. */
-    ActivityOptions mPendingOptions;
+    /** The scene transition info. */
+    SceneTransitionInfo mSceneTransitionInfo;
 
     /** Whether this activity was launched from a bubble. **/
     boolean mLaunchedFromBubble;
@@ -3061,7 +3064,7 @@
     }
 
     /**
-     * Request to put the freeform activity into fullscreen. The requester has to be the top-most
+     * Request to put the activity into fullscreen. The requester must be pinned or the top-most
      * activity of the focused display which can be verified using
      * {@link #onTopResumedActivityChanged(boolean)}. The request should also be a response to a
      * user input. When getting fullscreen and receiving corresponding
@@ -5796,10 +5799,9 @@
 
     private Bundle transferSpringboardActivityOptions(@Nullable Bundle options) {
         if (options == null && (mWindow != null && !mWindow.isActive())) {
-            final ActivityOptions activityOptions = getActivityOptions();
-            if (activityOptions != null &&
-                    activityOptions.getAnimationType() == ActivityOptions.ANIM_SCENE_TRANSITION) {
-                return activityOptions.toBundle();
+            final SceneTransitionInfo info = getSceneTransitionInfo();
+            if (info != null) {
+                return ActivityOptions.makeBasic().setSceneTransitionInfo(info).toBundle();
             }
         }
         return options;
@@ -8068,8 +8070,10 @@
      *
      * @param callback the method to call when all visible activities behind this one have been
      * drawn and it is safe to make this activity translucent again.
-     * @param options activity options delivered to the activity below this one. The options
-     * are retrieved using {@link #getActivityOptions}.
+     * @param options activity options that created from
+     *             {@link ActivityOptions#makeSceneTransitionAnimation} which will be converted to
+     *             {@link SceneTransitionInfo} and delivered to the activity below this one. The
+     *              options are retrieved using {@link #getSceneTransitionInfo}.
      * @return <code>true</code> if Window was opaque and will become translucent or
      * <code>false</code> if window was translucent and no change needed to be made.
      *
@@ -8105,27 +8109,27 @@
     }
 
     /** @hide */
-    public void onNewActivityOptions(ActivityOptions options) {
-        mActivityTransitionState.setEnterActivityOptions(this, options);
+    public void onNewSceneTransitionInfo(ActivityOptions.SceneTransitionInfo info) {
+        mActivityTransitionState.setEnterSceneTransitionInfo(this, info);
         if (!mStopped) {
             mActivityTransitionState.enterReady(this);
         }
     }
 
     /**
-     * Takes the ActivityOptions passed in from the launching activity or passed back
+     * Takes the {@link SceneTransitionInfo} passed in from the launching activity or passed back
      * from an activity launched by this activity in its call to {@link
      * #convertToTranslucent(TranslucentConversionListener, ActivityOptions)}
      *
-     * @return The ActivityOptions passed to {@link #convertToTranslucent}.
+     * @return The {@link SceneTransitionInfo} which based on the ActivityOptions that originally
+     *         passed to {@link #convertToTranslucent}.
      * @hide
      */
-    @UnsupportedAppUsage
-    ActivityOptions getActivityOptions() {
-        final ActivityOptions options = mPendingOptions;
-        // The option only applies once.
-        mPendingOptions = null;
-        return options;
+    SceneTransitionInfo getSceneTransitionInfo() {
+        final SceneTransitionInfo sceneTransitionInfo = mSceneTransitionInfo;
+        // The info only applies once.
+        mSceneTransitionInfo = null;
+        return sceneTransitionInfo;
     }
 
     /**
@@ -8769,7 +8773,7 @@
         mVisibleFromClient = !mWindow.getWindowStyle().getBoolean(
                 com.android.internal.R.styleable.Window_windowNoDisplay, false);
         mFragments.dispatchActivityCreated();
-        mActivityTransitionState.setEnterActivityOptions(this, getActivityOptions());
+        mActivityTransitionState.setEnterSceneTransitionInfo(this, getSceneTransitionInfo());
         dispatchActivityPostCreated(icicle);
         Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
     }
@@ -8787,7 +8791,7 @@
                     + mComponent.getClassName());
         }
         dispatchActivityPreStarted();
-        mActivityTransitionState.setEnterActivityOptions(this, getActivityOptions());
+        mActivityTransitionState.setEnterSceneTransitionInfo(this, getSceneTransitionInfo());
         mFragments.noteStateNotSaved();
         mCalled = false;
         mFragments.execPendingActions();
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 37692d3..232fc92 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -1164,6 +1164,30 @@
      */
     public abstract void logFgsApiEnd(int apiType, int uid, int pid);
 
+    /**
+     * Checks whether an app will be able to start a foreground service or not.
+     *
+     * @param pid The process id belonging to the app to be checked.
+     * @param uid The UID of the app to be checked.
+     * @param packageName The package name of the app to be checked.
+     * @return whether the app will be able to start a foreground service or not.
+     */
+    public abstract boolean canStartForegroundService(
+            int pid, int uid, @NonNull String packageName);
+
+    /**
+     * Returns {@code true} if a foreground service started by an uid is allowed to have
+     * while-in-use permissions.
+     *
+     * @param pid The process id belonging to the app to be checked.
+     * @param uid The UID of the app to be checked.
+     * @param packageName The package name of the app to be checked.
+     * @return whether the foreground service is allowed to have while-in-use permissions.
+     * @hide
+     */
+    public abstract boolean canAllowWhileInUsePermissionInFgs(
+            int pid, int uid, @NonNull String packageName);
+
      /**
      * Temporarily allow foreground service started by an uid to have while-in-use permission
      * for durationMs.
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/UninstallActionListener.java b/core/java/android/app/ActivityOptions.aidl
similarity index 64%
copy from packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/UninstallActionListener.java
copy to core/java/android/app/ActivityOptions.aidl
index b8a9355..bd5cd88 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/UninstallActionListener.java
+++ b/core/java/android/app/ActivityOptions.aidl
@@ -1,11 +1,11 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
+/**
+ * Copyright (c) 2024, The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
- *      https://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,
@@ -14,11 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.packageinstaller.v2.ui;
+package android.app;
 
-public interface UninstallActionListener {
-
-    void onPositiveResponse(boolean keepData);
-
-    void onNegativeResponse();
-}
+/** @hide */
+parcelable ActivityOptions.SceneTransitionInfo;
\ No newline at end of file
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index 9c279c3..8af7ed1 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -357,22 +357,7 @@
     private static final String KEY_APPLY_NO_USER_ACTION_FLAG_FOR_SHORTCUT =
             "android:activity.applyNoUserActionFlagForShortcut";
 
-    /**
-     * For Activity transitions, the calling Activity's TransitionListener used to
-     * notify the called Activity when the shared element and the exit transitions
-     * complete.
-     */
-    private static final String KEY_TRANSITION_COMPLETE_LISTENER
-            = "android:activity.transitionCompleteListener";
-
-    private static final String KEY_TRANSITION_IS_RETURNING
-            = "android:activity.transitionIsReturning";
-    private static final String KEY_TRANSITION_SHARED_ELEMENTS
-            = "android:activity.sharedElementNames";
-    private static final String KEY_RESULT_DATA = "android:activity.resultData";
-    private static final String KEY_RESULT_CODE = "android:activity.resultCode";
-    private static final String KEY_EXIT_COORDINATOR_INDEX
-            = "android:activity.exitCoordinatorIndex";
+    private static final String KEY_SCENE_TRANSITION_INFO = "android:activity.sceneTransitionInfo";
 
     /** See {@link SourceInfo}. */
     private static final String KEY_SOURCE_INFO = "android.activity.sourceInfo";
@@ -472,12 +457,7 @@
     private int mHeight;
     private IRemoteCallback mAnimationStartedListener;
     private IRemoteCallback mAnimationFinishedListener;
-    private ResultReceiver mTransitionReceiver;
-    private boolean mIsReturning;
-    private ArrayList<String> mSharedElementNames;
-    private Intent mResultData;
-    private int mResultCode;
-    private int mExitCoordinatorIndex;
+    private SceneTransitionInfo mSceneTransitionInfo;
     private PendingIntent mUsageTimeReport;
     private int mLaunchDisplayId = INVALID_DISPLAY;
     private int mCallerDisplayId = INVALID_DISPLAY;
@@ -1006,8 +986,11 @@
         ExitTransitionCoordinator exit = makeSceneTransitionAnimation(
                 new ActivityExitTransitionCallbacks(activity), activity.mExitTransitionListener,
                 activity.getWindow(), opts, sharedElements);
-        opts.mExitCoordinatorIndex =
-                activity.mActivityTransitionState.addExitTransitionCoordinator(exit);
+        final SceneTransitionInfo info = opts.getSceneTransitionInfo();
+        if (info != null) {
+            info.setExitCoordinatorKey(
+                    activity.mActivityTransitionState.addExitTransitionCoordinator(exit));
+        }
         return opts;
     }
 
@@ -1029,13 +1012,16 @@
         ActivityOptions opts = new ActivityOptions();
         ExitTransitionCoordinator exit = makeSceneTransitionAnimation(
                 exitCallbacks, callback, window, opts, sharedElements);
-        opts.mExitCoordinatorIndex = -1;
+        final SceneTransitionInfo info = opts.getSceneTransitionInfo();
+        if (info != null) {
+            info.setExitCoordinatorKey(-1);
+        }
         return Pair.create(opts, exit);
     }
 
     /**
-     * This method should be called when the
-     * {@link #startSharedElementAnimation(Window, ExitTransitionCallbacks, Pair[])}
+     * This method should be called when the {@link #startSharedElementAnimation(Window,
+     * ExitTransitionCallbacks, SharedElementCallback, Pair[])}
      * animation must be stopped and the Views reset. This can happen if there was an error
      * from startActivity or a springboard activity and the animation should stop and reset.
      *
@@ -1088,9 +1074,11 @@
 
         ExitTransitionCoordinator exit = new ExitTransitionCoordinator(exitCallbacks, window,
                 callback, names, names, views, false);
-        opts.mTransitionReceiver = exit;
-        opts.mSharedElementNames = names;
-        opts.mIsReturning = false;
+        final SceneTransitionInfo info = new SceneTransitionInfo();
+        info.setResultReceiver(exit);
+        info.setSharedElementNames(names);
+        info.setReturning(false);
+        opts.setSceneTransitionInfo(info);
         return exit;
     }
 
@@ -1111,17 +1099,20 @@
             int resultCode, Intent resultData) {
         ActivityOptions opts = new ActivityOptions();
         opts.mAnimationType = ANIM_SCENE_TRANSITION;
-        opts.mSharedElementNames = sharedElementNames;
-        opts.mTransitionReceiver = exitCoordinator;
-        opts.mIsReturning = true;
-        opts.mResultCode = resultCode;
-        opts.mResultData = resultData;
+        final SceneTransitionInfo info = new SceneTransitionInfo();
+        info.setSharedElementNames(sharedElementNames);
+        info.setResultReceiver(exitCoordinator);
+        info.setReturning(true);
+        info.setResultCode(resultCode);
+        info.setResultData(resultData);
         if (activity == null) {
-            opts.mExitCoordinatorIndex = -1;
+            info.setExitCoordinatorKey(-1);
         } else {
-            opts.mExitCoordinatorIndex =
-                    activity.mActivityTransitionState.addExitTransitionCoordinator(exitCoordinator);
+            info.setExitCoordinatorKey(
+                    activity.mActivityTransitionState.addExitTransitionCoordinator(
+                            exitCoordinator));
         }
+        opts.setSceneTransitionInfo(info);
         return opts;
     }
 
@@ -1269,12 +1260,8 @@
                 break;
 
             case ANIM_SCENE_TRANSITION:
-                mTransitionReceiver = opts.getParcelable(KEY_TRANSITION_COMPLETE_LISTENER, android.os.ResultReceiver.class);
-                mIsReturning = opts.getBoolean(KEY_TRANSITION_IS_RETURNING, false);
-                mSharedElementNames = opts.getStringArrayList(KEY_TRANSITION_SHARED_ELEMENTS);
-                mResultData = opts.getParcelable(KEY_RESULT_DATA, android.content.Intent.class);
-                mResultCode = opts.getInt(KEY_RESULT_CODE);
-                mExitCoordinatorIndex = opts.getInt(KEY_EXIT_COORDINATOR_INDEX);
+                mSceneTransitionInfo = opts.getParcelable(KEY_SCENE_TRANSITION_INFO,
+                        SceneTransitionInfo.class);
                 break;
         }
         mLockTaskMode = opts.getBoolean(KEY_LOCK_TASK_MODE, false);
@@ -1437,9 +1424,6 @@
     }
 
     /** @hide */
-    public int getExitCoordinatorKey() { return mExitCoordinatorIndex; }
-
-    /** @hide */
     public void abort() {
         if (mAnimationStartedListener != null) {
             try {
@@ -1450,35 +1434,17 @@
     }
 
     /** @hide */
-    public boolean isReturning() {
-        return mIsReturning;
-    }
-
-    /**
-     * Returns whether or not the ActivityOptions was created with
-     * {@link #startSharedElementAnimation(Window, Pair[])}.
-     *
-     * @hide
-     */
-    boolean isCrossTask() {
-        return mExitCoordinatorIndex < 0;
+    public ActivityOptions setSceneTransitionInfo(SceneTransitionInfo info) {
+        mSceneTransitionInfo = info;
+        return this;
     }
 
     /** @hide */
-    public ArrayList<String> getSharedElementNames() {
-        return mSharedElementNames;
+    public SceneTransitionInfo getSceneTransitionInfo() {
+        return mSceneTransitionInfo;
     }
 
     /** @hide */
-    public ResultReceiver getResultReceiver() { return mTransitionReceiver; }
-
-    /** @hide */
-    public int getResultCode() { return mResultCode; }
-
-    /** @hide */
-    public Intent getResultData() { return mResultData; }
-
-    /** @hide */
     public PendingIntent getUsageTimeReport() {
         return mUsageTimeReport;
     }
@@ -2102,12 +2068,7 @@
             mPackageName = otherOptions.mPackageName;
         }
         mUsageTimeReport = otherOptions.mUsageTimeReport;
-        mTransitionReceiver = null;
-        mSharedElementNames = null;
-        mIsReturning = false;
-        mResultData = null;
-        mResultCode = 0;
-        mExitCoordinatorIndex = 0;
+        mSceneTransitionInfo = null;
         mAnimationType = otherOptions.mAnimationType;
         switch (otherOptions.mAnimationType) {
             case ANIM_CUSTOM:
@@ -2157,14 +2118,9 @@
                 mAnimationStartedListener = otherOptions.mAnimationStartedListener;
                 break;
             case ANIM_SCENE_TRANSITION:
-                mTransitionReceiver = otherOptions.mTransitionReceiver;
-                mSharedElementNames = otherOptions.mSharedElementNames;
-                mIsReturning = otherOptions.mIsReturning;
+                mSceneTransitionInfo = otherOptions.mSceneTransitionInfo;
                 mThumbnail = null;
                 mAnimationStartedListener = null;
-                mResultData = otherOptions.mResultData;
-                mResultCode = otherOptions.mResultCode;
-                mExitCoordinatorIndex = otherOptions.mExitCoordinatorIndex;
                 break;
         }
         mLockTaskMode = otherOptions.mLockTaskMode;
@@ -2240,14 +2196,9 @@
                         != null ? mAnimationStartedListener.asBinder() : null);
                 break;
             case ANIM_SCENE_TRANSITION:
-                if (mTransitionReceiver != null) {
-                    b.putParcelable(KEY_TRANSITION_COMPLETE_LISTENER, mTransitionReceiver);
+                if (mSceneTransitionInfo != null) {
+                    b.putParcelable(KEY_SCENE_TRANSITION_INFO, mSceneTransitionInfo);
                 }
-                b.putBoolean(KEY_TRANSITION_IS_RETURNING, mIsReturning);
-                b.putStringArrayList(KEY_TRANSITION_SHARED_ELEMENTS, mSharedElementNames);
-                b.putParcelable(KEY_RESULT_DATA, mResultData);
-                b.putInt(KEY_RESULT_CODE, mResultCode);
-                b.putInt(KEY_EXIT_COORDINATOR_INDEX, mExitCoordinatorIndex);
                 break;
         }
         if (mLockTaskMode) {
@@ -2607,4 +2558,124 @@
             }
         };
     }
+
+    /**
+     * This class contains necessary information for Activity Scene Transition.
+     *
+     * @hide
+     */
+    public static class SceneTransitionInfo implements Parcelable {
+        private boolean mIsReturning;
+        private int mResultCode;
+        @Nullable
+        private Intent mResultData;
+        @Nullable
+        private ArrayList<String> mSharedElementNames;
+        @Nullable
+        private ResultReceiver mResultReceiver;
+        private int mExitCoordinatorIndex;
+
+        public SceneTransitionInfo() {
+        }
+
+        SceneTransitionInfo(Parcel in) {
+            mIsReturning = in.readBoolean();
+            mResultCode = in.readInt();
+            mResultData = in.readTypedObject(Intent.CREATOR);
+            mSharedElementNames = in.createStringArrayList();
+            mResultReceiver = in.readTypedObject(ResultReceiver.CREATOR);
+            mExitCoordinatorIndex = in.readInt();
+        }
+
+        public static final Creator<SceneTransitionInfo> CREATOR = new Creator<>() {
+            @Override
+            public SceneTransitionInfo createFromParcel(Parcel in) {
+                return new SceneTransitionInfo(in);
+            }
+
+            @Override
+            public SceneTransitionInfo[] newArray(int size) {
+                return new SceneTransitionInfo[size];
+            }
+        };
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeBoolean(mIsReturning);
+            dest.writeInt(mResultCode);
+            dest.writeTypedObject(mResultData, flags);
+            dest.writeStringList(mSharedElementNames);
+            dest.writeTypedObject(mResultReceiver, flags);
+            dest.writeInt(mExitCoordinatorIndex);
+        }
+
+        public void setReturning(boolean isReturning) {
+            mIsReturning = isReturning;
+        }
+
+        public boolean isReturning() {
+            return mIsReturning;
+        }
+
+        public void setResultCode(int resultCode) {
+            mResultCode = resultCode;
+        }
+
+        public int getResultCode() {
+            return mResultCode;
+        }
+
+        public void setResultData(Intent resultData) {
+            mResultData = resultData;
+        }
+
+        @Nullable
+        public Intent getResultData() {
+            return mResultData;
+        }
+
+        public void setSharedElementNames(ArrayList<String> sharedElementNames) {
+            mSharedElementNames = sharedElementNames;
+        }
+
+        @Nullable
+        public ArrayList<String> getSharedElementNames() {
+            return mSharedElementNames;
+        }
+
+        public void setResultReceiver(ResultReceiver resultReceiver) {
+            mResultReceiver = resultReceiver;
+        }
+
+        @Nullable
+        public ResultReceiver getResultReceiver() {
+            return mResultReceiver;
+        }
+
+        public void setExitCoordinatorKey(int exitCoordinatorKey) {
+            mExitCoordinatorIndex = exitCoordinatorKey;
+        }
+
+        public int getExitCoordinatorKey() {
+            return mExitCoordinatorIndex;
+        }
+
+        boolean isCrossTask() {
+            return mExitCoordinatorIndex < 0;
+        }
+
+        @Override
+        public String toString() {
+            return "SceneTransitionInfo, mIsReturning=" + mIsReturning
+                    + ", mResultCode=" + mResultCode + ", mResultData=" + mResultData
+                    + ", mSharedElementNames=" + mSharedElementNames
+                    + ", mTransitionReceiver=" + mResultReceiver
+                    + ", mExitCoordinatorIndex=" + mExitCoordinatorIndex;
+        }
+    }
 }
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 6ddb36a..949e2ba 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -43,6 +43,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.ActivityOptions.SceneTransitionInfo;
 import android.app.RemoteServiceException.BadForegroundServiceNotificationException;
 import android.app.RemoteServiceException.BadUserInitiatedJobNotificationException;
 import android.app.RemoteServiceException.CannotPostForegroundServiceNotificationException;
@@ -208,9 +209,11 @@
 import android.view.translation.TranslationSpec;
 import android.view.translation.UiTranslationSpec;
 import android.webkit.WebView;
+import android.window.ITaskFragmentOrganizer;
 import android.window.SizeConfigurationBuckets;
 import android.window.SplashScreen;
 import android.window.SplashScreenView;
+import android.window.TaskFragmentTransaction;
 import android.window.WindowContextInfo;
 import android.window.WindowProviderService;
 import android.window.WindowTokenClientController;
@@ -407,7 +410,7 @@
 
     private int mLastSessionId;
     // Holds the value of the last reported device ID value from the server for the top activity.
-    int mLastReportedDeviceId;
+    int mLastReportedDeviceId = Context.DEVICE_ID_DEFAULT;
     final ArrayMap<IBinder, CreateServiceData> mServicesData = new ArrayMap<>();
     @UnsupportedAppUsage
     final ArrayMap<IBinder, Service> mServices = new ArrayMap<>();
@@ -630,8 +633,8 @@
         @UnsupportedAppUsage
         boolean mPreserveWindow;
 
-        /** The options for scene transition. */
-        ActivityOptions mActivityOptions;
+        /** The scene transition info. */
+        SceneTransitionInfo mSceneTransitionInfo;
 
         /** Whether this activiy was launched from a bubble. */
         boolean mLaunchedFromBubble;
@@ -658,7 +661,7 @@
                 ActivityInfo info, Configuration overrideConfig,
                 String referrer, IVoiceInteractor voiceInteractor, Bundle state,
                 PersistableBundle persistentState, List<ResultInfo> pendingResults,
-                List<ReferrerIntent> pendingNewIntents, ActivityOptions activityOptions,
+                List<ReferrerIntent> pendingNewIntents, SceneTransitionInfo sceneTransitionInfo,
                 boolean isForward, ProfilerInfo profilerInfo, ClientTransactionHandler client,
                 IBinder assistToken, IBinder shareableActivityToken, boolean launchedFromBubble,
                 IBinder taskFragmentToken) {
@@ -678,7 +681,7 @@
             this.profilerInfo = profilerInfo;
             this.overrideConfig = overrideConfig;
             this.packageInfo = client.getPackageInfoNoCheck(activityInfo.applicationInfo);
-            mActivityOptions = activityOptions;
+            mSceneTransitionInfo = sceneTransitionInfo;
             mLaunchedFromBubble = launchedFromBubble;
             mTaskFragmentToken = taskFragmentToken;
             init();
@@ -1958,9 +1961,9 @@
             sendMessage(H.TRANSLUCENT_CONVERSION_COMPLETE, token, drawComplete ? 1 : 0);
         }
 
-        public void scheduleOnNewActivityOptions(IBinder token, Bundle options) {
-            sendMessage(H.ON_NEW_ACTIVITY_OPTIONS,
-                    new Pair<IBinder, ActivityOptions>(token, ActivityOptions.fromBundle(options)));
+        public void scheduleOnNewSceneTransitionInfo(IBinder token, SceneTransitionInfo info) {
+            sendMessage(H.ON_NEW_SCENE_TRANSITION_INFO,
+                    new Pair<IBinder, SceneTransitionInfo>(token, info));
         }
 
         public void setProcessState(int state) {
@@ -2047,6 +2050,14 @@
         }
 
         @Override
+        public void scheduleTaskFragmentTransaction(@NonNull ITaskFragmentOrganizer organizer,
+                @NonNull TaskFragmentTransaction transaction) throws RemoteException {
+            // TODO(b/260873529): ITaskFragmentOrganizer can be cleanup to be a IBinder token
+            // after flag removal.
+            organizer.onTransactionReady(transaction);
+        }
+
+        @Override
         public void requestDirectActions(@NonNull IBinder activityToken,
                 @NonNull IVoiceInteractor interactor, @Nullable RemoteCallback cancellationCallback,
                 @NonNull RemoteCallback callback) {
@@ -2248,7 +2259,7 @@
         public static final int TRANSLUCENT_CONVERSION_COMPLETE = 144;
         @UnsupportedAppUsage
         public static final int INSTALL_PROVIDER        = 145;
-        public static final int ON_NEW_ACTIVITY_OPTIONS = 146;
+        public static final int ON_NEW_SCENE_TRANSITION_INFO = 146;
         @UnsupportedAppUsage
         public static final int ENTER_ANIMATION_COMPLETE = 149;
         public static final int START_BINDER_TRACKING = 150;
@@ -2304,7 +2315,7 @@
                     case REQUEST_ASSIST_CONTEXT_EXTRAS: return "REQUEST_ASSIST_CONTEXT_EXTRAS";
                     case TRANSLUCENT_CONVERSION_COMPLETE: return "TRANSLUCENT_CONVERSION_COMPLETE";
                     case INSTALL_PROVIDER: return "INSTALL_PROVIDER";
-                    case ON_NEW_ACTIVITY_OPTIONS: return "ON_NEW_ACTIVITY_OPTIONS";
+                    case ON_NEW_SCENE_TRANSITION_INFO: return "ON_NEW_SCENE_TRANSITION_INFO";
                     case ENTER_ANIMATION_COMPLETE: return "ENTER_ANIMATION_COMPLETE";
                     case LOCAL_VOICE_INTERACTION_STARTED: return "LOCAL_VOICE_INTERACTION_STARTED";
                     case ATTACH_AGENT: return "ATTACH_AGENT";
@@ -2510,9 +2521,10 @@
                         Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                     }
                     break;
-                case ON_NEW_ACTIVITY_OPTIONS:
-                    Pair<IBinder, ActivityOptions> pair = (Pair<IBinder, ActivityOptions>) msg.obj;
-                    onNewActivityOptions(pair.first, pair.second);
+                case ON_NEW_SCENE_TRANSITION_INFO:
+                    Pair<IBinder, SceneTransitionInfo> pair =
+                            (Pair<IBinder, SceneTransitionInfo>) msg.obj;
+                    onNewSceneTransitionInfo(pair.first, pair.second);
                     break;
                 case ENTER_ANIMATION_COMPLETE:
                     handleEnterAnimationComplete((IBinder) msg.obj);
@@ -3911,9 +3923,9 @@
                     activity.setTheme(theme);
                 }
 
-                if (r.mActivityOptions != null) {
-                    activity.mPendingOptions = r.mActivityOptions;
-                    r.mActivityOptions = null;
+                if (r.mSceneTransitionInfo != null) {
+                    activity.mSceneTransitionInfo = r.mSceneTransitionInfo;
+                    r.mSceneTransitionInfo = null;
                 }
                 activity.mLaunchedFromBubble = r.mLaunchedFromBubble;
                 activity.mCalled = false;
@@ -3952,7 +3964,7 @@
 
     @Override
     public void handleStartActivity(ActivityClientRecord r,
-            PendingTransactionActions pendingActions, ActivityOptions activityOptions) {
+            PendingTransactionActions pendingActions, SceneTransitionInfo sceneTransitionInfo) {
         final Activity activity = r.activity;
         if (!r.stopped) {
             throw new IllegalStateException("Can't start activity that is not stopped.");
@@ -3963,8 +3975,8 @@
         }
 
         unscheduleGcIdler();
-        if (activityOptions != null) {
-            activity.mPendingOptions = activityOptions;
+        if (sceneTransitionInfo != null) {
+            activity.mSceneTransitionInfo = sceneTransitionInfo;
         }
 
         // Start
@@ -4339,10 +4351,10 @@
         }
     }
 
-    public void onNewActivityOptions(IBinder token, ActivityOptions options) {
+    public void onNewSceneTransitionInfo(IBinder token, SceneTransitionInfo info) {
         ActivityClientRecord r = mActivities.get(token);
         if (r != null) {
-            r.activity.onNewActivityOptions(options);
+            r.activity.onNewSceneTransitionInfo(info);
         }
     }
 
@@ -4856,10 +4868,13 @@
             service.attach(context, this, data.info.name, data.token, app,
                     ActivityManager.getService());
             if (!service.isUiContext()) { // WindowProviderService is a UI Context.
-                VirtualDeviceManager vdm = context.getSystemService(VirtualDeviceManager.class);
-                if (mLastReportedDeviceId == Context.DEVICE_ID_DEFAULT
-                        || vdm.isValidVirtualDeviceId(mLastReportedDeviceId)) {
+                if (mLastReportedDeviceId == Context.DEVICE_ID_DEFAULT) {
                     service.updateDeviceId(mLastReportedDeviceId);
+                } else {
+                    VirtualDeviceManager vdm = context.getSystemService(VirtualDeviceManager.class);
+                    if (vdm != null && vdm.isValidVirtualDeviceId(mLastReportedDeviceId)) {
+                        service.updateDeviceId(mLastReportedDeviceId);
+                    }
                 }
             }
             service.onCreate();
diff --git a/core/java/android/app/ActivityTransitionState.java b/core/java/android/app/ActivityTransitionState.java
index 6f4bb45..d947a9b 100644
--- a/core/java/android/app/ActivityTransitionState.java
+++ b/core/java/android/app/ActivityTransitionState.java
@@ -15,6 +15,7 @@
  */
 package android.app;
 
+import android.app.ActivityOptions.SceneTransitionInfo;
 import android.content.Intent;
 import android.os.Bundle;
 import android.os.ResultReceiver;
@@ -81,9 +82,9 @@
     private EnterTransitionCoordinator mEnterTransitionCoordinator;
 
     /**
-     * ActivityOptions used on entering this Activity.
+     * {@link SceneTransitionInfo} used on entering this Activity.
      */
-    private ActivityOptions mEnterActivityOptions;
+    private SceneTransitionInfo mEnterSceneTransitionInfo;
 
     /**
      * Has an exit transition been started? If so, we don't want to double-exit.
@@ -165,7 +166,7 @@
         }
     }
 
-    public void setEnterActivityOptions(Activity activity, ActivityOptions options) {
+    public void setEnterSceneTransitionInfo(Activity activity, SceneTransitionInfo info) {
         final Window window = activity.getWindow();
         if (window == null) {
             return;
@@ -173,16 +174,15 @@
         // ensure Decor View has been created so that the window features are activated
         window.getDecorView();
         if (window.hasFeature(Window.FEATURE_ACTIVITY_TRANSITIONS)
-                && options != null && mEnterActivityOptions == null
-                && mEnterTransitionCoordinator == null
-                && options.getAnimationType() == ActivityOptions.ANIM_SCENE_TRANSITION) {
-            mEnterActivityOptions = options;
+                && info != null && mEnterSceneTransitionInfo == null
+                && mEnterTransitionCoordinator == null) {
+            mEnterSceneTransitionInfo = info;
             mIsEnterTriggered = false;
-            if (mEnterActivityOptions.isReturning()) {
+            if (mEnterSceneTransitionInfo.isReturning()) {
                 restoreExitedViews();
-                int result = mEnterActivityOptions.getResultCode();
+                int result = mEnterSceneTransitionInfo.getResultCode();
                 if (result != 0) {
-                    Intent intent = mEnterActivityOptions.getResultData();
+                    Intent intent = mEnterSceneTransitionInfo.getResultData();
                     if (intent != null) {
                         intent.setExtrasClassLoader(activity.getClassLoader());
                     }
@@ -193,25 +193,26 @@
     }
 
     public void enterReady(Activity activity) {
-        if (mEnterActivityOptions == null || mIsEnterTriggered) {
+        if (mEnterSceneTransitionInfo == null || mIsEnterTriggered) {
             return;
         }
         mIsEnterTriggered = true;
         mHasExited = false;
-        ArrayList<String> sharedElementNames = mEnterActivityOptions.getSharedElementNames();
-        ResultReceiver resultReceiver = mEnterActivityOptions.getResultReceiver();
-        final boolean isReturning = mEnterActivityOptions.isReturning();
+        final ArrayList<String> sharedElementNames =
+                mEnterSceneTransitionInfo.getSharedElementNames();
+        ResultReceiver resultReceiver = mEnterSceneTransitionInfo.getResultReceiver();
+        final boolean isReturning = mEnterSceneTransitionInfo.isReturning();
         if (isReturning) {
             restoreExitedViews();
             activity.getWindow().getDecorView().setVisibility(View.VISIBLE);
         }
         getPendingExitNames(); // Set mPendingExitNames before resetting mEnterTransitionCoordinator
         mEnterTransitionCoordinator = new EnterTransitionCoordinator(activity,
-                resultReceiver, sharedElementNames, mEnterActivityOptions.isReturning(),
-                mEnterActivityOptions.isCrossTask());
-        if (mEnterActivityOptions.isCrossTask()) {
-            mExitingFrom = new ArrayList<>(mEnterActivityOptions.getSharedElementNames());
-            mExitingTo = new ArrayList<>(mEnterActivityOptions.getSharedElementNames());
+                resultReceiver, sharedElementNames, mEnterSceneTransitionInfo.isReturning(),
+                mEnterSceneTransitionInfo.isCrossTask());
+        if (mEnterSceneTransitionInfo.isCrossTask() && sharedElementNames != null) {
+            mExitingFrom = new ArrayList<>(sharedElementNames);
+            mExitingTo = new ArrayList<>(sharedElementNames);
         }
 
         if (!mIsEnterPostponed) {
@@ -248,7 +249,7 @@
         mExitingFrom = null;
         mExitingTo = null;
         mExitingToView = null;
-        mEnterActivityOptions = null;
+        mEnterSceneTransitionInfo = null;
     }
 
     public void onStop(Activity activity) {
@@ -296,7 +297,7 @@
         mExitingToView = null;
         mCalledExitCoordinator = null;
         mEnterTransitionCoordinator = null;
-        mEnterActivityOptions = null;
+        mEnterSceneTransitionInfo = null;
         mExitTransitionCoordinators = null;
     }
 
@@ -386,9 +387,10 @@
                 mExitTransitionCoordinators == null) {
             return;
         }
-        ActivityOptions activityOptions = new ActivityOptions(options);
-        if (activityOptions.getAnimationType() == ActivityOptions.ANIM_SCENE_TRANSITION) {
-            int key = activityOptions.getExitCoordinatorKey();
+        final ActivityOptions activityOptions = new ActivityOptions(options);
+        final SceneTransitionInfo info = activityOptions.getSceneTransitionInfo();
+        if (info != null) {
+            int key = info.getExitCoordinatorKey();
             int index = mExitTransitionCoordinators.indexOfKey(key);
             if (index >= 0) {
                 mCalledExitCoordinator = mExitTransitionCoordinators.valueAt(index).get();
diff --git a/core/java/android/app/AppCompatTaskInfo.java b/core/java/android/app/AppCompatTaskInfo.java
index a998ff2..0bae5e6 100644
--- a/core/java/android/app/AppCompatTaskInfo.java
+++ b/core/java/android/app/AppCompatTaskInfo.java
@@ -97,6 +97,11 @@
     public boolean isUserFullscreenOverrideEnabled;
 
     /**
+     * Whether the system has forced the activity to be fullscreen
+     */
+    public boolean isSystemFullscreenOverrideEnabled;
+
+    /**
      * Hint about the letterbox state of the top activity.
      */
     public boolean topActivityBoundsLetterboxed;
@@ -202,7 +207,8 @@
                 && topActivityLetterboxHeight == that.topActivityLetterboxHeight
                 && topActivityLetterboxHorizontalPosition
                     == that.topActivityLetterboxHorizontalPosition
-                && isUserFullscreenOverrideEnabled == that.isUserFullscreenOverrideEnabled;
+                && isUserFullscreenOverrideEnabled == that.isUserFullscreenOverrideEnabled
+                && isSystemFullscreenOverrideEnabled == that.isSystemFullscreenOverrideEnabled;
     }
 
     /**
@@ -224,7 +230,8 @@
                 && topActivityLetterboxWidth == that.topActivityLetterboxWidth
                 && topActivityLetterboxHeight == that.topActivityLetterboxHeight
                 && cameraCompatControlState == that.cameraCompatControlState
-                && isUserFullscreenOverrideEnabled == that.isUserFullscreenOverrideEnabled;
+                && isUserFullscreenOverrideEnabled == that.isUserFullscreenOverrideEnabled
+                && isSystemFullscreenOverrideEnabled == that.isSystemFullscreenOverrideEnabled;
     }
 
     /**
@@ -243,6 +250,7 @@
         topActivityLetterboxWidth = source.readInt();
         topActivityLetterboxHeight = source.readInt();
         isUserFullscreenOverrideEnabled = source.readBoolean();
+        isSystemFullscreenOverrideEnabled = source.readBoolean();
     }
 
     /**
@@ -262,6 +270,7 @@
         dest.writeInt(topActivityLetterboxWidth);
         dest.writeInt(topActivityLetterboxHeight);
         dest.writeBoolean(isUserFullscreenOverrideEnabled);
+        dest.writeBoolean(isSystemFullscreenOverrideEnabled);
     }
 
     @Override
@@ -280,6 +289,7 @@
                 + " topActivityLetterboxWidth=" + topActivityLetterboxWidth
                 + " topActivityLetterboxHeight=" + topActivityLetterboxHeight
                 + " isUserFullscreenOverrideEnabled=" + isUserFullscreenOverrideEnabled
+                + " isSystemFullscreenOverrideEnabled=" + isSystemFullscreenOverrideEnabled
                 + " cameraCompatControlState="
                 + cameraCompatControlStateToString(cameraCompatControlState)
                 + "}";
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 4b24b1f..1db1caf 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -71,6 +71,7 @@
 import android.provider.DeviceConfig;
 import android.util.ArrayMap;
 import android.util.ArraySet;
+import android.util.Log;
 import android.util.LongSparseArray;
 import android.util.LongSparseLongArray;
 import android.util.Pools;
@@ -7705,6 +7706,14 @@
     @RequiresPermission(android.Manifest.permission.MANAGE_APP_OPS_MODES)
     public void setUidMode(int code, int uid, @Mode int mode) {
         try {
+            // TODO(b/302609140): Remove extra logging after this issue is diagnosed.
+            if (code == OP_BLUETOOTH_CONNECT) {
+                Log.i(DEBUG_LOGGING_TAG,
+                        "setUidMode called for OP_BLUETOOTH_CONNECT with mode: " + mode
+                                + " for uid: " + uid + " calling uid: " + Binder.getCallingUid()
+                                + " trace: "
+                                + Arrays.toString(Thread.currentThread().getStackTrace()));
+            }
             mService.setUidMode(code, uid, mode);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -7725,6 +7734,15 @@
     @RequiresPermission(android.Manifest.permission.MANAGE_APP_OPS_MODES)
     public void setUidMode(@NonNull String appOp, int uid, @Mode int mode) {
         try {
+            // TODO(b/302609140): Remove extra logging after this issue is diagnosed.
+            if (appOp.equals(OPSTR_BLUETOOTH_CONNECT)) {
+                Log.i(DEBUG_LOGGING_TAG,
+                        "setUidMode called for OPSTR_BLUETOOTH_CONNECT with mode: " + mode
+                                + " for uid: " + uid + " calling uid: " + Binder.getCallingUid()
+                                + " trace: "
+                                + Arrays.toString(Thread.currentThread().getStackTrace()));
+            }
+
             mService.setUidMode(AppOpsManager.strOpToOp(appOp), uid, mode);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -7765,6 +7783,14 @@
     @RequiresPermission(android.Manifest.permission.MANAGE_APP_OPS_MODES)
     public void setMode(int code, int uid, String packageName, @Mode int mode) {
         try {
+            // TODO(b/302609140): Remove extra logging after this issue is diagnosed.
+            if (code == OP_BLUETOOTH_CONNECT) {
+                Log.i(DEBUG_LOGGING_TAG,
+                        "setMode called for OPSTR_BLUETOOTH_CONNECT with mode: " + mode
+                                + " for uid: " + uid + " calling uid: " + Binder.getCallingUid()
+                                + " trace: "
+                                + Arrays.toString(Thread.currentThread().getStackTrace()));
+            }
             mService.setMode(code, uid, packageName, mode);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -7787,6 +7813,14 @@
     public void setMode(@NonNull String op, int uid, @Nullable String packageName,
             @Mode int mode) {
         try {
+            // TODO(b/302609140): Remove extra logging after this issue is diagnosed.
+            if (op.equals(OPSTR_BLUETOOTH_CONNECT)) {
+                Log.i(DEBUG_LOGGING_TAG,
+                        "setMode called for OPSTR_BLUETOOTH_CONNECT with mode: " + mode
+                                + " for uid: " + uid + " calling uid: " + Binder.getCallingUid()
+                                + " trace: "
+                                + Arrays.toString(Thread.currentThread().getStackTrace()));
+            }
             mService.setMode(strOpToOp(op), uid, packageName, mode);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 287d2bd..34c44f9 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -131,6 +131,7 @@
 
 import libcore.util.EmptyArray;
 
+import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
 import java.lang.ref.WeakReference;
@@ -4038,11 +4039,11 @@
     }
 
     @Override
-    public <T> T parseAndroidManifest(@NonNull String apkFilePath,
+    public <T> T parseAndroidManifest(@NonNull File apkFile,
             @NonNull Function<XmlResourceParser, T> parserFunction) throws IOException {
-        Objects.requireNonNull(apkFilePath, "apkFilePath cannot be null");
+        Objects.requireNonNull(apkFile, "apkFile cannot be null");
         Objects.requireNonNull(parserFunction, "parserFunction cannot be null");
-        try (XmlResourceParser xmlResourceParser = getAndroidManifestParser(apkFilePath)) {
+        try (XmlResourceParser xmlResourceParser = getAndroidManifestParser(apkFile)) {
             return parserFunction.apply(xmlResourceParser);
         } catch (IOException e) {
             Log.w(TAG, "Failed to get the android manifest parser", e);
@@ -4050,11 +4051,11 @@
         }
     }
 
-    private static XmlResourceParser getAndroidManifestParser(@NonNull String apkFilePath)
+    private static XmlResourceParser getAndroidManifestParser(@NonNull File apkFile)
             throws IOException {
         ApkAssets apkAssets = null;
         try {
-            apkAssets = ApkAssets.loadFromPath(apkFilePath);
+            apkAssets = ApkAssets.loadFromPath(apkFile.getAbsolutePath());
             return apkAssets.openXml(ApkLiteParseUtils.ANDROID_MANIFEST_FILENAME);
         } finally {
             if (apkAssets != null) {
diff --git a/core/java/android/app/AutomaticZenRule.java b/core/java/android/app/AutomaticZenRule.java
index 343348b..5b354fc 100644
--- a/core/java/android/app/AutomaticZenRule.java
+++ b/core/java/android/app/AutomaticZenRule.java
@@ -23,6 +23,7 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.TestApi;
 import android.app.NotificationManager.InterruptionFilter;
 import android.content.ComponentName;
 import android.net.Uri;
@@ -35,6 +36,7 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
 import java.util.Objects;
 
 /**
@@ -70,6 +72,8 @@
     public static final int TYPE_SCHEDULE_CALENDAR = 2;
     /**
      * The type for rules triggered by bedtime/sleeping, like time of day, or snore detection.
+     *
+     * <p>Only the 'Wellbeing' app may own rules of this type.
      */
     @FlaggedApi(Flags.FLAG_MODES_API)
     public static final int TYPE_BEDTIME = 3;
@@ -95,6 +99,8 @@
     /**
      * The type for rules created and managed by a device owner. These rules may not be fully
      * editable by the device user.
+     *
+     * <p>Only a 'Device Owner' app may own rules of this type.
      */
     @FlaggedApi(Flags.FLAG_MODES_API)
     public static final int TYPE_MANAGED = 7;
@@ -107,6 +113,30 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface Type {}
 
+    /** Used to track which rule variables have been modified by the user.
+     * Should be checked against the bitmask {@link #getUserModifiedFields()}.
+     * @hide
+     */
+    @IntDef(flag = true, prefix = { "FIELD_" }, value = {
+            FIELD_NAME,
+            FIELD_INTERRUPTION_FILTER,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ModifiableField {}
+
+    /**
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_MODES_API)
+    @TestApi
+    public static final int FIELD_NAME = 1 << 0;
+    /**
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_MODES_API)
+    @TestApi
+    public static final int FIELD_INTERRUPTION_FILTER = 1 << 1;
+
     private boolean enabled;
     private String name;
     private @InterruptionFilter int interruptionFilter;
@@ -116,12 +146,14 @@
     private long creationTime;
     private ZenPolicy mZenPolicy;
     private ZenDeviceEffects mDeviceEffects;
+    // TODO: b/310620812 - Remove this once FLAG_MODES_API is inlined.
     private boolean mModified = false;
     private String mPkg;
-    private int mType = TYPE_UNKNOWN;
+    private int mType = Flags.modesApi() ? TYPE_UNKNOWN : 0;
     private int mIconResId;
     private String mTriggerDescription;
     private boolean mAllowManualInvocation;
+    private @ModifiableField int mUserModifiedFields; // Bitwise representation
 
     /**
      * The maximum string length for any string contained in this automatic zen rule. This pertains
@@ -224,6 +256,7 @@
             mIconResId = source.readInt();
             mTriggerDescription = getTrimmedString(source.readString(), MAX_DESC_LENGTH);
             mType = source.readInt();
+            mUserModifiedFields = source.readInt();
         }
     }
 
@@ -274,6 +307,8 @@
      * Returns whether this rule's name has been modified by the user.
      * @hide
      */
+    // TODO: b/310620812 - Replace with mUserModifiedFields & FIELD_NAME once
+    //  FLAG_MODES_API is inlined.
     public boolean isModified() {
         return mModified;
     }
@@ -471,6 +506,32 @@
         return type;
     }
 
+    /**
+     * Gets the bitmask representing which fields are user modified. Bits are set using
+     * {@link ModifiableField}.
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_MODES_API)
+    @TestApi
+    public @ModifiableField int getUserModifiedFields() {
+        return mUserModifiedFields;
+    }
+
+    /**
+     * Returns {@code true} if the {@link AutomaticZenRule} can be updated.
+     * When this returns {@code false}, calls to
+     * {@link NotificationManager#updateAutomaticZenRule(String, AutomaticZenRule)}) with this rule
+     * will ignore changes to user-configurable fields.
+     */
+    @FlaggedApi(Flags.FLAG_MODES_API)
+    public boolean canUpdate() {
+        // The rule is considered updateable if its bitmask has no user modifications, and
+        // the bitmasks of the policy and device effects have no modification.
+        return mUserModifiedFields == 0
+                && (mZenPolicy == null || mZenPolicy.getUserModifiedFields() == 0)
+                && (mDeviceEffects == null || mDeviceEffects.getUserModifiedFields() == 0);
+    }
+
     @Override
     public int describeContents() {
         return 0;
@@ -499,6 +560,7 @@
             dest.writeInt(mIconResId);
             dest.writeString(mTriggerDescription);
             dest.writeInt(mType);
+            dest.writeInt(mUserModifiedFields);
         }
     }
 
@@ -520,12 +582,26 @@
                     .append(",allowManualInvocation=").append(mAllowManualInvocation)
                     .append(",iconResId=").append(mIconResId)
                     .append(",triggerDescription=").append(mTriggerDescription)
-                    .append(",type=").append(mType);
+                    .append(",type=").append(mType)
+                    .append(",userModifiedFields=")
+                    .append(modifiedFieldsToString(mUserModifiedFields));
         }
 
         return sb.append(']').toString();
     }
 
+    @FlaggedApi(Flags.FLAG_MODES_API)
+    private String modifiedFieldsToString(int bitmask) {
+        ArrayList<String> modified = new ArrayList<>();
+        if ((bitmask & FIELD_NAME) != 0) {
+            modified.add("FIELD_NAME");
+        }
+        if ((bitmask & FIELD_INTERRUPTION_FILTER) != 0) {
+            modified.add("FIELD_INTERRUPTION_FILTER");
+        }
+        return "{" + String.join(",", modified) + "}";
+    }
+
     @Override
     public boolean equals(@Nullable Object o) {
         if (!(o instanceof AutomaticZenRule)) return false;
@@ -547,7 +623,8 @@
                     && other.mAllowManualInvocation == mAllowManualInvocation
                     && other.mIconResId == mIconResId
                     && Objects.equals(other.mTriggerDescription, mTriggerDescription)
-                    && other.mType == mType;
+                    && other.mType == mType
+                    && other.mUserModifiedFields == mUserModifiedFields;
         }
         return finalEquals;
     }
@@ -557,7 +634,8 @@
         if (Flags.modesApi()) {
             return Objects.hash(enabled, name, interruptionFilter, conditionId, owner,
                     configurationActivity, mZenPolicy, mDeviceEffects, mModified, creationTime,
-                    mPkg, mAllowManualInvocation, mIconResId, mTriggerDescription, mType);
+                    mPkg, mAllowManualInvocation, mIconResId, mTriggerDescription, mType,
+                    mUserModifiedFields);
         }
         return Objects.hash(enabled, name, interruptionFilter, conditionId, owner,
                 configurationActivity, mZenPolicy, mModified, creationTime, mPkg);
@@ -626,6 +704,7 @@
         private boolean mAllowManualInvocation;
         private long mCreationTime;
         private String mPkg;
+        private @ModifiableField int mUserModifiedFields;
 
         public Builder(@NonNull AutomaticZenRule rule) {
             mName = rule.getName();
@@ -642,6 +721,7 @@
             mAllowManualInvocation = rule.isManualInvocationAllowed();
             mCreationTime = rule.getCreationTime();
             mPkg = rule.getPackageName();
+            mUserModifiedFields = rule.mUserModifiedFields;
         }
 
         public Builder(@NonNull String name, @NonNull Uri conditionId) {
@@ -768,6 +848,19 @@
             return this;
         }
 
+        /**
+         * Sets the bitmask representing which fields have been user-modified.
+         * This method should not be used outside of tests. The value of userModifiedFields
+         * should be set based on what values are changed when a rule is populated or updated..
+         * @hide
+         */
+        @FlaggedApi(Flags.FLAG_MODES_API)
+        @TestApi
+        public @NonNull Builder setUserModifiedFields(@ModifiableField int userModifiedFields) {
+            mUserModifiedFields = userModifiedFields;
+            return this;
+        }
+
         public @NonNull AutomaticZenRule build() {
             AutomaticZenRule rule = new AutomaticZenRule(mName, mOwner, mConfigurationActivity,
                     mConditionId, mPolicy, mInterruptionFilter, mEnabled);
@@ -778,6 +871,7 @@
             rule.mIconResId = mIconResId;
             rule.mAllowManualInvocation = mAllowManualInvocation;
             rule.setPackageName(mPkg);
+            rule.mUserModifiedFields = mUserModifiedFields;
 
             return rule;
         }
diff --git a/core/java/android/app/ClientTransactionHandler.java b/core/java/android/app/ClientTransactionHandler.java
index 25075e9..b300674 100644
--- a/core/java/android/app/ClientTransactionHandler.java
+++ b/core/java/android/app/ClientTransactionHandler.java
@@ -17,6 +17,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.ActivityOptions.SceneTransitionInfo;
 import android.app.ActivityThread.ActivityClientRecord;
 import android.app.servertransaction.ClientTransaction;
 import android.app.servertransaction.DestroyActivityItem;
@@ -207,7 +208,7 @@
 
     /** Perform activity start. */
     public abstract void handleStartActivity(@NonNull ActivityClientRecord r,
-            PendingTransactionActions pendingActions, ActivityOptions activityOptions);
+            PendingTransactionActions pendingActions, SceneTransitionInfo sceneTransitionInfo);
 
     /** Get package info. */
     public abstract LoadedApk getPackageInfoNoCheck(ApplicationInfo ai);
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 014ddd41..edeec77 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -3481,24 +3481,13 @@
         }
         mResources = r;
 
-        // only do this if the user already has more than one preferred locale
-        if (android.content.res.Flags.defaultLocale()
-                && r.getConfiguration().getLocales().size() > 1) {
-            LocaleConfig lc;
-            if (getUserId() < 0) {
-                lc = LocaleConfig.fromContextIgnoringOverride(this);
-            } else {
-                // This is needed because the app might have locale config overrides that need to
-                // be read from disk in order for resources to correctly choose which values to
-                // load.
-                StrictMode.ThreadPolicy policy = StrictMode.allowThreadDiskReads();
-                try {
-                    lc = new LocaleConfig(this);
-                } finally {
-                    StrictMode.setThreadPolicy(policy);
-                }
+        if (r != null) {
+            // only do this if the user already has more than one preferred locale
+            if (android.content.res.Flags.defaultLocale()
+                    && r.getConfiguration().getLocales().size() > 1) {
+                LocaleConfig lc = LocaleConfig.fromContextIgnoringOverride(this);
+                mResourcesManager.setLocaleConfig(lc);
             }
-            mResourcesManager.setLocaleConfig(lc);
         }
     }
 
diff --git a/core/java/android/app/ForegroundServiceTypePolicy.java b/core/java/android/app/ForegroundServiceTypePolicy.java
index ac9c497..d1e517b 100644
--- a/core/java/android/app/ForegroundServiceTypePolicy.java
+++ b/core/java/android/app/ForegroundServiceTypePolicy.java
@@ -30,6 +30,7 @@
 import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION;
 import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MANIFEST;
 import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK;
+import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROCESSING;
 import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION;
 import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MICROPHONE;
 import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_NONE;
@@ -577,6 +578,26 @@
     );
 
     /**
+     * The policy for the {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_MEDIA_PROCESSING}.
+     *
+     * @hide
+     */
+    public static final @NonNull ForegroundServiceTypePolicyInfo FGS_TYPE_POLICY_MEDIA_PROCESSING =
+            new ForegroundServiceTypePolicyInfo(
+                    FOREGROUND_SERVICE_TYPE_MEDIA_PROCESSING,
+                    ForegroundServiceTypePolicyInfo.INVALID_CHANGE_ID,
+                    ForegroundServiceTypePolicyInfo.INVALID_CHANGE_ID,
+                    new ForegroundServiceTypePermissions(new ForegroundServiceTypePermission[] {
+                            new RegularPermission(
+                                    Manifest.permission.FOREGROUND_SERVICE_MEDIA_PROCESSING)
+                    }, true),
+                    null /* anyOfPermissions */,
+                    null /* permissionEnforcementFlag */,
+                    true /* permissionEnforcementFlagDefaultValue */,
+                    false /* foregroundOnlyPermission */
+            );
+
+    /**
      * The policy for the {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_SPECIAL_USE}.
      *
      * @hide
@@ -1331,6 +1352,8 @@
                     FGS_TYPE_POLICY_SYSTEM_EXEMPTED);
             mForegroundServiceTypePolicies.put(FOREGROUND_SERVICE_TYPE_SHORT_SERVICE,
                     FGS_TYPE_POLICY_SHORT_SERVICE);
+            mForegroundServiceTypePolicies.put(FOREGROUND_SERVICE_TYPE_MEDIA_PROCESSING,
+                    FGS_TYPE_POLICY_MEDIA_PROCESSING);
             // TODO (b/271950506): revisit it in the next release.
             // Hide the file management type for now. If anyone uses it, will default to "none".
             mForegroundServiceTypePolicies.put(FOREGROUND_SERVICE_TYPE_SPECIAL_USE,
diff --git a/core/java/android/app/IApplicationThread.aidl b/core/java/android/app/IApplicationThread.aidl
index 5541e7a..59e0e99 100644
--- a/core/java/android/app/IApplicationThread.aidl
+++ b/core/java/android/app/IApplicationThread.aidl
@@ -16,6 +16,7 @@
 
 package android.app;
 
+import android.app.ActivityOptions.SceneTransitionInfo;
 import android.app.ContentProviderHolder;
 import android.app.IInstrumentationWatcher;
 import android.app.IUiAutomationConnection;
@@ -48,6 +49,8 @@
 import android.view.autofill.AutofillId;
 import android.view.translation.TranslationSpec;
 import android.view.translation.UiTranslationSpec;
+import android.window.ITaskFragmentOrganizer;
+import android.window.TaskFragmentTransaction;
 
 import com.android.internal.app.IVoiceInteractor;
 import com.android.internal.content.ReferrerIntent;
@@ -112,7 +115,7 @@
     void scheduleCreateBackupAgent(in ApplicationInfo app,
             int backupMode, int userId, int operationType);
     void scheduleDestroyBackupAgent(in ApplicationInfo app, int userId);
-    void scheduleOnNewActivityOptions(IBinder token, in Bundle options);
+    void scheduleOnNewSceneTransitionInfo(IBinder token, in SceneTransitionInfo info);
     void scheduleSuicide();
     void dispatchPackageBroadcast(int cmd, in String[] packages);
     void scheduleCrash(in String msg, int typeId, in Bundle extras);
@@ -157,6 +160,8 @@
     void scheduleApplicationInfoChanged(in ApplicationInfo ai);
     void setNetworkBlockSeq(long procStateSeq);
     void scheduleTransaction(in ClientTransaction transaction);
+    void scheduleTaskFragmentTransaction(in ITaskFragmentOrganizer organizer,
+            in TaskFragmentTransaction transaction);
     void requestDirectActions(IBinder activityToken, IVoiceInteractor intractor,
             in RemoteCallback cancellationCallback, in RemoteCallback callback);
     void performDirectAction(IBinder activityToken, String actionId,
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index b7db5f5..c3adbc3 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -222,7 +222,7 @@
     boolean removeAutomaticZenRule(String id, boolean fromUser);
     boolean removeAutomaticZenRules(String packageName, boolean fromUser);
     int getRuleInstanceCount(in ComponentName owner);
-    void setAutomaticZenRuleState(String id, in Condition condition, boolean fromUser);
+    void setAutomaticZenRuleState(String id, in Condition condition);
 
     byte[] getBackupPayload(int user);
     void applyRestore(in byte[] payload, int user);
diff --git a/core/java/android/app/LocalActivityManager.java b/core/java/android/app/LocalActivityManager.java
index 6b5f19a..1b19ecd 100644
--- a/core/java/android/app/LocalActivityManager.java
+++ b/core/java/android/app/LocalActivityManager.java
@@ -179,7 +179,7 @@
             }
 
             mActivityThread.handleStartActivity(clientRecord, pendingActions,
-                    null /* activityOptions */);
+                    null /* sceneTransitionInfo */);
             r.curState = STARTED;
             
             if (desiredState == RESUMED) {
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index f76a45b..0b6e24c 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -1391,20 +1391,9 @@
      * @param condition The new state of this rule
      */
     public void setAutomaticZenRuleState(@NonNull String id, @NonNull Condition condition) {
-        if (Flags.modesApi()) {
-            setAutomaticZenRuleState(id, condition,
-                    /* fromUser= */ condition.source == Condition.SOURCE_USER_ACTION);
-        } else {
-            setAutomaticZenRuleState(id, condition, /* fromUser= */ false);
-        }
-    }
-
-    /** @hide */
-    public void setAutomaticZenRuleState(@NonNull String id, @NonNull Condition condition,
-            boolean fromUser) {
         INotificationManager service = getService();
         try {
-            service.setAutomaticZenRuleState(id, condition, fromUser);
+            service.setAutomaticZenRuleState(id, condition);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java
index 385fd50..14195c4 100644
--- a/core/java/android/app/StatusBarManager.java
+++ b/core/java/android/app/StatusBarManager.java
@@ -241,6 +241,23 @@
     public static final int CAMERA_LAUNCH_SOURCE_QUICK_AFFORDANCE = 3;
 
     /**
+     * Broadcast action: sent to apps that hold the status bar permission when
+     * KeyguardManager#setPrivateNotificationsAllowed() is changed.
+     *
+     * Extras: #EXTRA_KM_PRIVATE_NOTIFS_ALLOWED
+     * @hide
+     */
+    public static final String ACTION_KEYGUARD_PRIVATE_NOTIFICATIONS_CHANGED
+            = "android.app.action.KEYGUARD_PRIVATE_NOTIFICATIONS_CHANGED";
+
+    /**
+     * Boolean, the latest value of KeyguardManager#getPrivateNotificationsAllowed()
+     * @hide
+     */
+    public static final String EXTRA_KM_PRIVATE_NOTIFS_ALLOWED
+            = "android.app.extra.KM_PRIVATE_NOTIFS_ALLOWED";
+
+    /**
      * Session flag for {@link #registerSessionListener} indicating the listener
      * is interested in sessions on the keygaurd.
      * Keyguard Session Boundaries:
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index be420de..62db90f 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -744,20 +744,21 @@
                         params, userId, /* getCropped = */ true);
                 Trace.endSection();
 
-                if (pfd != null) {
-                    try (InputStream is = new ParcelFileDescriptor.AutoCloseInputStream(pfd)) {
-                        ImageDecoder.Source src = ImageDecoder.createSource(is.readAllBytes());
-                        return ImageDecoder.decodeBitmap(src, ((decoder, info, source) -> {
-                            // Mutable and hardware config can't be set at the same time.
-                            decoder.setMutableRequired(!hardware);
-                            // Let's do color management
-                            if (cmProxy != null) {
-                                cmProxy.doColorManagement(decoder, info);
-                            }
-                        }));
-                    } catch (OutOfMemoryError | IOException e) {
-                        Log.w(TAG, "Can't decode file", e);
-                    }
+                if (pfd == null) {
+                    return null;
+                }
+                try (InputStream is = new ParcelFileDescriptor.AutoCloseInputStream(pfd)) {
+                    ImageDecoder.Source src = ImageDecoder.createSource(context.getResources(), is);
+                    return ImageDecoder.decodeBitmap(src, ((decoder, info, source) -> {
+                        // Mutable and hardware config can't be set at the same time.
+                        decoder.setMutableRequired(!hardware);
+                        // Let's do color management
+                        if (cmProxy != null) {
+                            cmProxy.doColorManagement(decoder, info);
+                        }
+                    }));
+                } catch (OutOfMemoryError | IOException e) {
+                    Log.w(TAG, "Can't decode file", e);
                 }
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 4a6349b1..5c42b0e 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -2598,8 +2598,8 @@
      * There can be at most one app that has this delegation.
      * If another app already had delegated certificate selection access,
      * it will lose the delegation when a new app is delegated.
-     * <p> The delegaetd app can also call {@link #grantKeyPairToApp} and
-     * {@link #revokeKeyPairFromApp} to directly grant KeyCain keys to other apps.
+     * <p> The delegated app can also call {@link #grantKeyPairToApp} and
+     * {@link #revokeKeyPairFromApp} to directly grant KeyChain keys to other apps.
      * <p> Can be granted by Device Owner or Profile Owner.
      */
     public static final String DELEGATION_CERT_SELECTION = "delegation-cert-selection";
diff --git a/core/java/android/app/ambient_context.aconfig b/core/java/android/app/ambient_context.aconfig
new file mode 100644
index 0000000..3f73da2
--- /dev/null
+++ b/core/java/android/app/ambient_context.aconfig
@@ -0,0 +1,8 @@
+package: "android.app"
+
+flag {
+  namespace: "biometrics_integration"
+  name: "ambient_heart_rate"
+  description: "Feature flag for adding heart rate api to ambient context."
+  bug: "318309481"
+}
diff --git a/core/java/android/app/ambientcontext/AmbientContextEvent.java b/core/java/android/app/ambientcontext/AmbientContextEvent.java
index b5c66ff..f94987e 100644
--- a/core/java/android/app/ambientcontext/AmbientContextEvent.java
+++ b/core/java/android/app/ambientcontext/AmbientContextEvent.java
@@ -16,7 +16,9 @@
 
 package android.app.ambientcontext;
 
+import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
 import android.os.Parcelable;
@@ -68,6 +70,14 @@
     public static final int EVENT_BACK_DOUBLE_TAP = 3;
 
     /**
+     * The integer indicating a heart rate measurement was done.
+     *
+     * @see #getRatePerMinute
+     */
+    @Event @FlaggedApi(android.app.Flags.FLAG_AMBIENT_HEART_RATE)
+    public static final int EVENT_HEART_RATE = 4;
+
+    /**
      * Integer indicating the start of wearable vendor defined events that can be detected.
      * These depend on the vendor implementation.
      */
@@ -79,12 +89,16 @@
      */
     public static final String KEY_VENDOR_WEARABLE_EVENT_NAME = "wearable_event_name";
 
+    /** Default value for the rate per minute data field. */
+    private static final int RATE_PER_MINUTE_UNKNOWN = -1;
+
     /** @hide */
     @IntDef(prefix = { "EVENT_" }, value = {
             EVENT_UNKNOWN,
             EVENT_COUGH,
             EVENT_SNORE,
             EVENT_BACK_DOUBLE_TAP,
+            EVENT_HEART_RATE,
             EVENT_VENDOR_WEARABLE_START,
     })
     @Retention(RetentionPolicy.SOURCE)
@@ -170,6 +184,16 @@
         return new PersistableBundle();
     }
 
+    /**
+     * Rate per minute of the event during the start to end time.
+     *
+     * @return the rate per minute, or {@link #RATE_PER_MINUTE_UNKNOWN} if the rate is unknown.
+     */
+    private final @IntRange(from = -1) int mRatePerMinute;
+    private static int defaultRatePerMinute() {
+        return RATE_PER_MINUTE_UNKNOWN;
+    }
+
 
 
     // Code below generated by codegen v1.0.23.
@@ -179,6 +203,8 @@
     //
     // To regenerate run:
     // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/app/ambientcontext/AmbientContextEvent.java
+    // then manually add @FlaggedApi(android.app.Flags.FLAG_AMBIENT_HEART_RATE) back to flagged
+    // APIs.
     //
     // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
     //   Settings > Editor > Code Style > Formatter Control
@@ -191,6 +217,7 @@
         EVENT_COUGH,
         EVENT_SNORE,
         EVENT_BACK_DOUBLE_TAP,
+        EVENT_HEART_RATE,
         EVENT_VENDOR_WEARABLE_START
     })
     @Retention(RetentionPolicy.SOURCE)
@@ -209,6 +236,8 @@
                     return "EVENT_SNORE";
             case EVENT_BACK_DOUBLE_TAP:
                     return "EVENT_BACK_DOUBLE_TAP";
+            case EVENT_HEART_RATE:
+                    return "EVENT_HEART_RATE";
             case EVENT_VENDOR_WEARABLE_START:
                     return "EVENT_VENDOR_WEARABLE_START";
             default: return Integer.toHexString(value);
@@ -255,7 +284,8 @@
             @NonNull Instant endTime,
             @LevelValue int confidenceLevel,
             @LevelValue int densityLevel,
-            @NonNull PersistableBundle vendorData) {
+            @NonNull PersistableBundle vendorData,
+            @IntRange(from = -1) int ratePerMinute) {
         this.mEventType = eventType;
         com.android.internal.util.AnnotationValidations.validate(
                 EventCode.class, null, mEventType);
@@ -274,6 +304,10 @@
         this.mVendorData = vendorData;
         com.android.internal.util.AnnotationValidations.validate(
                 NonNull.class, null, mVendorData);
+        this.mRatePerMinute = ratePerMinute;
+        com.android.internal.util.AnnotationValidations.validate(
+                IntRange.class, null, mRatePerMinute,
+                "from", -1);
 
         // onConstructed(); // You can define this method to get a callback
     }
@@ -330,6 +364,17 @@
         return mVendorData;
     }
 
+    /**
+     * Rate per minute of the event during the start to end time.
+     *
+     * @return the rate per minute, or {@link #RATE_PER_MINUTE_UNKNOWN} if the rate is unknown.
+     */
+    @DataClass.Generated.Member
+    @FlaggedApi(android.app.Flags.FLAG_AMBIENT_HEART_RATE)
+    public @IntRange(from = -1) int getRatePerMinute() {
+        return mRatePerMinute;
+    }
+
     @Override
     @DataClass.Generated.Member
     public String toString() {
@@ -342,7 +387,8 @@
                 "endTime = " + mEndTime + ", " +
                 "confidenceLevel = " + mConfidenceLevel + ", " +
                 "densityLevel = " + mDensityLevel + ", " +
-                "vendorData = " + mVendorData +
+                "vendorData = " + mVendorData + ", " +
+                "ratePerMinute = " + mRatePerMinute +
         " }";
     }
 
@@ -380,6 +426,7 @@
         dest.writeInt(mConfidenceLevel);
         dest.writeInt(mDensityLevel);
         dest.writeTypedObject(mVendorData, flags);
+        dest.writeInt(mRatePerMinute);
     }
 
     @Override
@@ -399,6 +446,7 @@
         int confidenceLevel = in.readInt();
         int densityLevel = in.readInt();
         PersistableBundle vendorData = (PersistableBundle) in.readTypedObject(PersistableBundle.CREATOR);
+        int ratePerMinute = in.readInt();
 
         this.mEventType = eventType;
         com.android.internal.util.AnnotationValidations.validate(
@@ -418,6 +466,10 @@
         this.mVendorData = vendorData;
         com.android.internal.util.AnnotationValidations.validate(
                 NonNull.class, null, mVendorData);
+        this.mRatePerMinute = ratePerMinute;
+        com.android.internal.util.AnnotationValidations.validate(
+                IntRange.class, null, mRatePerMinute,
+                "from", -1);
 
         // onConstructed(); // You can define this method to get a callback
     }
@@ -449,6 +501,7 @@
         private @LevelValue int mConfidenceLevel;
         private @LevelValue int mDensityLevel;
         private @NonNull PersistableBundle mVendorData;
+        private @IntRange(from = -1) int mRatePerMinute;
 
         private long mBuilderFieldsSet = 0L;
 
@@ -525,10 +578,22 @@
             return this;
         }
 
+        /**
+         * Rate per minute of the event during the start to end time.
+         */
+        @DataClass.Generated.Member
+        @FlaggedApi(android.app.Flags.FLAG_AMBIENT_HEART_RATE)
+        public @NonNull Builder setRatePerMinute(@IntRange(from = -1) int value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x40;
+            mRatePerMinute = value;
+            return this;
+        }
+
         /** Builds the instance. This builder should not be touched after calling this! */
         public @NonNull AmbientContextEvent build() {
             checkNotUsed();
-            mBuilderFieldsSet |= 0x40; // Mark builder used
+            mBuilderFieldsSet |= 0x80; // Mark builder used
 
             if ((mBuilderFieldsSet & 0x1) == 0) {
                 mEventType = defaultEventType();
@@ -548,18 +613,22 @@
             if ((mBuilderFieldsSet & 0x20) == 0) {
                 mVendorData = defaultVendorData();
             }
+            if ((mBuilderFieldsSet & 0x40) == 0) {
+                mRatePerMinute = defaultRatePerMinute();
+            }
             AmbientContextEvent o = new AmbientContextEvent(
                     mEventType,
                     mStartTime,
                     mEndTime,
                     mConfidenceLevel,
                     mDensityLevel,
-                    mVendorData);
+                    mVendorData,
+                    mRatePerMinute);
             return o;
         }
 
         private void checkNotUsed() {
-            if ((mBuilderFieldsSet & 0x40) != 0) {
+            if ((mBuilderFieldsSet & 0x80) != 0) {
                 throw new IllegalStateException(
                         "This Builder should not be reused. Use a new Builder instance instead");
             }
@@ -567,10 +636,10 @@
     }
 
     @DataClass.Generated(
-            time = 1671217108067L,
+            time = 1704895515931L,
             codegenVersion = "1.0.23",
             sourceFile = "frameworks/base/core/java/android/app/ambientcontext/AmbientContextEvent.java",
-            inputSignatures = "public static final  int EVENT_UNKNOWN\npublic static final  int EVENT_COUGH\npublic static final  int EVENT_SNORE\npublic static final  int EVENT_BACK_DOUBLE_TAP\npublic static final  int EVENT_VENDOR_WEARABLE_START\npublic static final  java.lang.String KEY_VENDOR_WEARABLE_EVENT_NAME\npublic static final  int LEVEL_UNKNOWN\npublic static final  int LEVEL_LOW\npublic static final  int LEVEL_MEDIUM_LOW\npublic static final  int LEVEL_MEDIUM\npublic static final  int LEVEL_MEDIUM_HIGH\npublic static final  int LEVEL_HIGH\nprivate final @android.app.ambientcontext.AmbientContextEvent.EventCode int mEventType\nprivate final @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInstant.class) @android.annotation.NonNull java.time.Instant mStartTime\nprivate final @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInstant.class) @android.annotation.NonNull java.time.Instant mEndTime\nprivate final @android.app.ambientcontext.AmbientContextEvent.LevelValue int mConfidenceLevel\nprivate final @android.app.ambientcontext.AmbientContextEvent.LevelValue int mDensityLevel\nprivate final @android.annotation.NonNull android.os.PersistableBundle mVendorData\nprivate static  int defaultEventType()\nprivate static @android.annotation.NonNull java.time.Instant defaultStartTime()\nprivate static @android.annotation.NonNull java.time.Instant defaultEndTime()\nprivate static  int defaultConfidenceLevel()\nprivate static  int defaultDensityLevel()\nprivate static  android.os.PersistableBundle defaultVendorData()\nclass AmbientContextEvent extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genConstructor=false, genHiddenConstDefs=true, genParcelable=true, genToString=true)")
+            inputSignatures = "public static final  int EVENT_UNKNOWN\npublic static final  int EVENT_COUGH\npublic static final  int EVENT_SNORE\npublic static final  int EVENT_BACK_DOUBLE_TAP\npublic static final @android.app.ambientcontext.AmbientContextEvent.Event @android.annotation.FlaggedApi int EVENT_HEART_RATE\npublic static final  int EVENT_VENDOR_WEARABLE_START\npublic static final  java.lang.String KEY_VENDOR_WEARABLE_EVENT_NAME\nprivate static final  int RATE_PER_MINUTE_UNKNOWN\npublic static final  int LEVEL_UNKNOWN\npublic static final  int LEVEL_LOW\npublic static final  int LEVEL_MEDIUM_LOW\npublic static final  int LEVEL_MEDIUM\npublic static final  int LEVEL_MEDIUM_HIGH\npublic static final  int LEVEL_HIGH\nprivate final @android.app.ambientcontext.AmbientContextEvent.EventCode int mEventType\nprivate final @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInstant.class) @android.annotation.NonNull java.time.Instant mStartTime\nprivate final @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInstant.class) @android.annotation.NonNull java.time.Instant mEndTime\nprivate final @android.app.ambientcontext.AmbientContextEvent.LevelValue int mConfidenceLevel\nprivate final @android.app.ambientcontext.AmbientContextEvent.LevelValue int mDensityLevel\nprivate final @android.annotation.NonNull android.os.PersistableBundle mVendorData\nprivate final @android.annotation.IntRange int mRatePerMinute\nprivate static  int defaultEventType()\nprivate static @android.annotation.NonNull java.time.Instant defaultStartTime()\nprivate static @android.annotation.NonNull java.time.Instant defaultEndTime()\nprivate static  int defaultConfidenceLevel()\nprivate static  int defaultDensityLevel()\nprivate static  android.os.PersistableBundle defaultVendorData()\nprivate static  int defaultRatePerMinute()\nclass AmbientContextEvent extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genConstructor=false, genHiddenConstDefs=true, genParcelable=true, genToString=true)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/app/backup/FullBackup.java b/core/java/android/app/backup/FullBackup.java
index 6371871..a41cb1f 100644
--- a/core/java/android/app/backup/FullBackup.java
+++ b/core/java/android/app/backup/FullBackup.java
@@ -239,7 +239,7 @@
                 Log.e(TAG, "Unable to create/open file " + outFile.getPath(), e);
             }
 
-            byte[] buffer = new byte[32 * 1024];
+            byte[] buffer = new byte[64 * 1024];
             final long origSize = size;
             FileInputStream in = new FileInputStream(data.getFileDescriptor());
             while (size > 0) {
diff --git a/core/java/android/app/notification.aconfig b/core/java/android/app/notification.aconfig
index fb0edb9..d11c6c5 100644
--- a/core/java/android/app/notification.aconfig
+++ b/core/java/android/app/notification.aconfig
@@ -36,3 +36,10 @@
   description: "Guards the security fix that ensures all URIs in intents and Person.java are valid"
   bug: "281044385"
 }
+
+flag {
+  name: "keyguard_private_notifications"
+  namespace: "systemui"
+  description: "Fixes the behavior of KeyguardManager#setPrivateNotificationsAllowed()"
+  bug: "309920145"
+}
diff --git a/core/java/android/app/servertransaction/ActivityRelaunchItem.java b/core/java/android/app/servertransaction/ActivityRelaunchItem.java
index 3ce094e..cbb0ae7 100644
--- a/core/java/android/app/servertransaction/ActivityRelaunchItem.java
+++ b/core/java/android/app/servertransaction/ActivityRelaunchItem.java
@@ -23,6 +23,7 @@
 import android.app.ActivityThread.ActivityClientRecord;
 import android.app.ClientTransactionHandler;
 import android.app.ResultInfo;
+import android.content.Context;
 import android.content.res.CompatibilityInfo;
 import android.os.IBinder;
 import android.os.Parcel;
@@ -85,6 +86,12 @@
         client.reportRelaunch(r);
     }
 
+    @Nullable
+    @Override
+    public Context getContextToUpdate(@NonNull ClientTransactionHandler client) {
+        return client.getActivity(getActivityToken());
+    }
+
     // ObjectPoolItem implementation
 
     private ActivityRelaunchItem() {}
diff --git a/core/java/android/app/servertransaction/ClientTransactionListenerController.java b/core/java/android/app/servertransaction/ClientTransactionListenerController.java
index 7418c06..9f97f6f 100644
--- a/core/java/android/app/servertransaction/ClientTransactionListenerController.java
+++ b/core/java/android/app/servertransaction/ClientTransactionListenerController.java
@@ -16,7 +16,7 @@
 
 package android.app.servertransaction;
 
-import static com.android.window.flags.Flags.syncWindowConfigUpdateFlag;
+import static com.android.window.flags.Flags.bundleClientTransactionFlag;
 
 import static java.util.Objects.requireNonNull;
 
@@ -67,7 +67,7 @@
      * window configuration.
      */
     public void onDisplayChanged(int displayId) {
-        if (!isSyncWindowConfigUpdateFlagEnabled()) {
+        if (!isBundleClientTransactionFlagEnabled()) {
             return;
         }
         if (ActivityThread.isSystem()) {
@@ -77,9 +77,9 @@
         mDisplayManager.handleDisplayChangeFromWindowManager(displayId);
     }
 
-    /** Whether {@link #syncWindowConfigUpdateFlag} feature flag is enabled. */
-    public boolean isSyncWindowConfigUpdateFlagEnabled() {
+    /** Whether {@link #bundleClientTransactionFlag} feature flag is enabled. */
+    public boolean isBundleClientTransactionFlagEnabled() {
         // Can't read flag from isolated process.
-        return !Process.isIsolated() && syncWindowConfigUpdateFlag();
+        return !Process.isIsolated() && bundleClientTransactionFlag();
     }
 }
diff --git a/core/java/android/app/servertransaction/LaunchActivityItem.java b/core/java/android/app/servertransaction/LaunchActivityItem.java
index d2ef65a..4d53701 100644
--- a/core/java/android/app/servertransaction/LaunchActivityItem.java
+++ b/core/java/android/app/servertransaction/LaunchActivityItem.java
@@ -23,13 +23,15 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityClient;
-import android.app.ActivityOptions;
+import android.app.ActivityOptions.SceneTransitionInfo;
+import android.app.ActivityThread;
 import android.app.ActivityThread.ActivityClientRecord;
 import android.app.ClientTransactionHandler;
 import android.app.IActivityClientController;
 import android.app.ProfilerInfo;
 import android.app.ResultInfo;
 import android.compat.annotation.UnsupportedAppUsage;
+import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
 import android.content.res.CompatibilityInfo;
@@ -71,7 +73,7 @@
     private PersistableBundle mPersistentState;
     private List<ResultInfo> mPendingResults;
     private List<ReferrerIntent> mPendingNewIntents;
-    private ActivityOptions mActivityOptions;
+    private SceneTransitionInfo mSceneTransitionInfo;
     private boolean mIsForward;
     private ProfilerInfo mProfilerInfo;
     private IBinder mAssistToken;
@@ -102,8 +104,8 @@
         Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
         ActivityClientRecord r = new ActivityClientRecord(mActivityToken, mIntent, mIdent, mInfo,
                 mOverrideConfig, mReferrer, mVoiceInteractor, mState, mPersistentState,
-                mPendingResults, mPendingNewIntents, mActivityOptions, mIsForward, mProfilerInfo,
-                client, mAssistToken, mShareableActivityToken, mLaunchedFromBubble,
+                mPendingResults, mPendingNewIntents, mSceneTransitionInfo, mIsForward,
+                mProfilerInfo, client, mAssistToken, mShareableActivityToken, mLaunchedFromBubble,
                 mTaskFragmentToken);
         client.handleLaunchActivity(r, pendingActions, mDeviceId, null /* customIntent */);
         Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
@@ -115,6 +117,13 @@
         client.countLaunchingActivities(-1);
     }
 
+    @Nullable
+    @Override
+    public Context getContextToUpdate(@NonNull ClientTransactionHandler client) {
+        // LaunchActivityItem may update the global config with #mCurConfig.
+        return ActivityThread.currentApplication();
+    }
+
     // ObjectPoolItem implementation
 
     private LaunchActivityItem() {}
@@ -127,7 +136,7 @@
             @Nullable IVoiceInteractor voiceInteractor, int procState, @Nullable Bundle state,
             @Nullable PersistableBundle persistentState, @Nullable List<ResultInfo> pendingResults,
             @Nullable List<ReferrerIntent> pendingNewIntents,
-            @Nullable ActivityOptions activityOptions,
+            @Nullable SceneTransitionInfo sceneTransitionInfo,
             boolean isForward, @Nullable ProfilerInfo profilerInfo, @NonNull IBinder assistToken,
             @Nullable IActivityClientController activityClientController,
             @NonNull IBinder shareableActivityToken, boolean launchedFromBubble,
@@ -143,7 +152,7 @@
                 persistentState != null ? new PersistableBundle(persistentState) : null,
                 pendingResults != null ? new ArrayList<>(pendingResults) : null,
                 pendingNewIntents != null ? new ArrayList<>(pendingNewIntents) : null,
-                activityOptions, isForward,
+                sceneTransitionInfo, isForward,
                 profilerInfo != null ? new ProfilerInfo(profilerInfo) : null,
                 assistToken, activityClientController, shareableActivityToken,
                 launchedFromBubble, taskFragmentToken);
@@ -184,7 +193,7 @@
         dest.writePersistableBundle(mPersistentState);
         dest.writeTypedList(mPendingResults, flags);
         dest.writeTypedList(mPendingNewIntents, flags);
-        dest.writeBundle(mActivityOptions != null ? mActivityOptions.toBundle() : null);
+        dest.writeTypedObject(mSceneTransitionInfo, flags);
         dest.writeBoolean(mIsForward);
         dest.writeTypedObject(mProfilerInfo, flags);
         dest.writeStrongBinder(mAssistToken);
@@ -204,7 +213,8 @@
                 in.readPersistableBundle(getClass().getClassLoader()),
                 in.createTypedArrayList(ResultInfo.CREATOR),
                 in.createTypedArrayList(ReferrerIntent.CREATOR),
-                ActivityOptions.fromBundle(in.readBundle()), in.readBoolean(),
+                in.readTypedObject(SceneTransitionInfo.CREATOR),
+                in.readBoolean(),
                 in.readTypedObject(ProfilerInfo.CREATOR),
                 in.readStrongBinder(),
                 IActivityClientController.Stub.asInterface(in.readStrongBinder()),
@@ -244,7 +254,7 @@
                 && areBundlesEqualRoughly(mPersistentState, other.mPersistentState)
                 && Objects.equals(mPendingResults, other.mPendingResults)
                 && Objects.equals(mPendingNewIntents, other.mPendingNewIntents)
-                && (mActivityOptions == null) == (other.mActivityOptions == null)
+                && (mSceneTransitionInfo == null) == (other.mSceneTransitionInfo == null)
                 && mIsForward == other.mIsForward
                 && Objects.equals(mProfilerInfo, other.mProfilerInfo)
                 && Objects.equals(mAssistToken, other.mAssistToken)
@@ -267,7 +277,7 @@
         result = 31 * result + getRoughBundleHashCode(mPersistentState);
         result = 31 * result + Objects.hashCode(mPendingResults);
         result = 31 * result + Objects.hashCode(mPendingNewIntents);
-        result = 31 * result + (mActivityOptions != null ? 1 : 0);
+        result = 31 * result + (mSceneTransitionInfo != null ? 1 : 0);
         result = 31 * result + (mIsForward ? 1 : 0);
         result = 31 * result + Objects.hashCode(mProfilerInfo);
         result = 31 * result + Objects.hashCode(mAssistToken);
@@ -316,7 +326,7 @@
                 + ",persistentState=" + mPersistentState
                 + ",pendingResults=" + mPendingResults
                 + ",pendingNewIntents=" + mPendingNewIntents
-                + ",options=" + mActivityOptions
+                + ",sceneTransitionInfo=" + mSceneTransitionInfo
                 + ",profilerInfo=" + mProfilerInfo
                 + ",assistToken=" + mAssistToken
                 + ",shareableActivityToken=" + mShareableActivityToken + "}";
@@ -331,7 +341,7 @@
             int procState, @Nullable Bundle state, @Nullable PersistableBundle persistentState,
             @Nullable List<ResultInfo> pendingResults,
             @Nullable List<ReferrerIntent> pendingNewIntents,
-            @Nullable ActivityOptions activityOptions, boolean isForward,
+            @Nullable SceneTransitionInfo sceneTransitionInfo, boolean isForward,
             @Nullable ProfilerInfo profilerInfo, @Nullable IBinder assistToken,
             @Nullable IActivityClientController activityClientController,
             @Nullable IBinder shareableActivityToken, boolean launchedFromBubble,
@@ -350,7 +360,7 @@
         instance.mPersistentState = persistentState;
         instance.mPendingResults = pendingResults;
         instance.mPendingNewIntents = pendingNewIntents;
-        instance.mActivityOptions = activityOptions;
+        instance.mSceneTransitionInfo = sceneTransitionInfo;
         instance.mIsForward = isForward;
         instance.mProfilerInfo = profilerInfo;
         instance.mAssistToken = assistToken;
diff --git a/core/java/android/app/servertransaction/MoveToDisplayItem.java b/core/java/android/app/servertransaction/MoveToDisplayItem.java
index 961da19..1353d16 100644
--- a/core/java/android/app/servertransaction/MoveToDisplayItem.java
+++ b/core/java/android/app/servertransaction/MoveToDisplayItem.java
@@ -22,6 +22,7 @@
 import android.annotation.Nullable;
 import android.app.ActivityThread.ActivityClientRecord;
 import android.app.ClientTransactionHandler;
+import android.content.Context;
 import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
 import android.os.IBinder;
@@ -55,6 +56,12 @@
         Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
     }
 
+    @Nullable
+    @Override
+    public Context getContextToUpdate(@NonNull ClientTransactionHandler client) {
+        return client.getActivity(getActivityToken());
+    }
+
     // ObjectPoolItem implementation
 
     private MoveToDisplayItem() {}
diff --git a/core/java/android/app/servertransaction/StartActivityItem.java b/core/java/android/app/servertransaction/StartActivityItem.java
index 8b98b21..a0f93ce 100644
--- a/core/java/android/app/servertransaction/StartActivityItem.java
+++ b/core/java/android/app/servertransaction/StartActivityItem.java
@@ -20,7 +20,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.app.ActivityOptions;
+import android.app.ActivityOptions.SceneTransitionInfo;
 import android.app.ActivityThread.ActivityClientRecord;
 import android.app.ClientTransactionHandler;
 import android.os.IBinder;
@@ -35,13 +35,13 @@
 
     private static final String TAG = "StartActivityItem";
 
-    private ActivityOptions mActivityOptions;
+    private SceneTransitionInfo mSceneTransitionInfo;
 
     @Override
     public void execute(@NonNull ClientTransactionHandler client, @NonNull ActivityClientRecord r,
             @NonNull PendingTransactionActions pendingActions) {
         Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "startActivityItem");
-        client.handleStartActivity(r, pendingActions, mActivityOptions);
+        client.handleStartActivity(r, pendingActions, mSceneTransitionInfo);
         Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
     }
 
@@ -57,13 +57,13 @@
     /** Obtain an instance initialized with provided params. */
     @NonNull
     public static StartActivityItem obtain(@NonNull IBinder activityToken,
-            @Nullable ActivityOptions activityOptions) {
+            @Nullable SceneTransitionInfo sceneTransitionInfo) {
         StartActivityItem instance = ObjectPool.obtain(StartActivityItem.class);
         if (instance == null) {
             instance = new StartActivityItem();
         }
         instance.setActivityToken(activityToken);
-        instance.mActivityOptions = activityOptions;
+        instance.mSceneTransitionInfo = sceneTransitionInfo;
 
         return instance;
     }
@@ -71,7 +71,7 @@
     @Override
     public void recycle() {
         super.recycle();
-        mActivityOptions = null;
+        mSceneTransitionInfo = null;
         ObjectPool.recycle(this);
     }
 
@@ -81,13 +81,13 @@
     @Override
     public void writeToParcel(@NonNull Parcel dest, int flags) {
         super.writeToParcel(dest, flags);
-        dest.writeBundle(mActivityOptions != null ? mActivityOptions.toBundle() : null);
+        dest.writeTypedObject(mSceneTransitionInfo, flags);
     }
 
     /** Read from Parcel. */
     private StartActivityItem(@NonNull Parcel in) {
         super(in);
-        mActivityOptions = ActivityOptions.fromBundle(in.readBundle());
+        mSceneTransitionInfo = in.readTypedObject(SceneTransitionInfo.CREATOR);
     }
 
     public static final @NonNull Creator<StartActivityItem> CREATOR = new Creator<>() {
@@ -109,21 +109,21 @@
             return false;
         }
         final StartActivityItem other = (StartActivityItem) o;
-        return (mActivityOptions == null) == (other.mActivityOptions == null);
+        return (mSceneTransitionInfo == null) == (other.mSceneTransitionInfo == null);
     }
 
     @Override
     public int hashCode() {
         int result = 17;
         result = 31 * result + super.hashCode();
-        result = 31 * result + (mActivityOptions != null ? 1 : 0);
+        result = 31 * result + (mSceneTransitionInfo != null ? 1 : 0);
         return result;
     }
 
     @Override
     public String toString() {
         return "StartActivityItem{" + super.toString()
-                + ",options=" + mActivityOptions + "}";
+                + ",sceneTransitionInfo=" + mSceneTransitionInfo + "}";
     }
 }
 
diff --git a/core/java/android/app/servertransaction/TransactionExecutor.java b/core/java/android/app/servertransaction/TransactionExecutor.java
index 9f5e0dc..ba94077 100644
--- a/core/java/android/app/servertransaction/TransactionExecutor.java
+++ b/core/java/android/app/servertransaction/TransactionExecutor.java
@@ -32,7 +32,7 @@
 import static android.app.servertransaction.TransactionExecutorHelper.tId;
 import static android.app.servertransaction.TransactionExecutorHelper.transactionToString;
 
-import static com.android.window.flags.Flags.syncWindowConfigUpdateFlag;
+import static com.android.window.flags.Flags.bundleClientTransactionFlag;
 
 import android.annotation.NonNull;
 import android.app.ActivityThread.ActivityClientRecord;
@@ -41,6 +41,8 @@
 import android.content.res.Configuration;
 import android.os.IBinder;
 import android.os.Process;
+import android.os.Trace;
+import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.IntArray;
 import android.util.Slog;
@@ -62,8 +64,11 @@
     private final PendingTransactionActions mPendingActions = new PendingTransactionActions();
     private final TransactionExecutorHelper mHelper = new TransactionExecutorHelper();
 
-    /** Keeps track of display ids whose Configuration got updated within a transaction. */
-    private final ArraySet<Integer> mConfigUpdatedDisplayIds = new ArraySet<>();
+    /**
+     * Keeps track of the Context whose Configuration got updated within a transaction, mapping to
+     * the config before the transaction.
+     */
+    private final ArrayMap<Context, Configuration> mContextToPreChangedConfigMap = new ArrayMap<>();
 
     /** Initialize an instance with transaction handler, that will execute all requested actions. */
     public TransactionExecutor(@NonNull ClientTransactionHandler clientTransactionHandler) {
@@ -83,24 +88,48 @@
             Slog.d(TAG, transactionToString(transaction, mTransactionHandler));
         }
 
-        if (transaction.getTransactionItems() != null) {
-            executeTransactionItems(transaction);
-        } else {
-            // TODO(b/260873529): cleanup after launch.
-            executeCallbacks(transaction);
-            executeLifecycleState(transaction);
+        Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "clientTransactionExecuted");
+        try {
+            if (transaction.getTransactionItems() != null) {
+                executeTransactionItems(transaction);
+            } else {
+                // TODO(b/260873529): cleanup after launch.
+                executeCallbacks(transaction);
+                executeLifecycleState(transaction);
+            }
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
         }
 
-        if (!mConfigUpdatedDisplayIds.isEmpty()) {
+        if (!mContextToPreChangedConfigMap.isEmpty()) {
             // Whether this transaction should trigger DisplayListener#onDisplayChanged.
-            final ClientTransactionListenerController controller =
-                    ClientTransactionListenerController.getInstance();
-            final int displayCount = mConfigUpdatedDisplayIds.size();
-            for (int i = 0; i < displayCount; i++) {
-                final int displayId = mConfigUpdatedDisplayIds.valueAt(i);
-                controller.onDisplayChanged(displayId);
+            try {
+                // Calculate display ids that have config changed.
+                final ArraySet<Integer> configUpdatedDisplayIds = new ArraySet<>();
+                final int contextCount = mContextToPreChangedConfigMap.size();
+                for (int i = 0; i < contextCount; i++) {
+                    final Context context = mContextToPreChangedConfigMap.keyAt(i);
+                    final Configuration preTransactionConfig =
+                            mContextToPreChangedConfigMap.valueAt(i);
+                    final Configuration postTransactionConfig = context.getResources()
+                            .getConfiguration();
+                    if (!areConfigurationsEqualForDisplay(
+                            postTransactionConfig, preTransactionConfig)) {
+                        configUpdatedDisplayIds.add(context.getDisplayId());
+                    }
+                }
+
+                // Dispatch the display changed callbacks.
+                final ClientTransactionListenerController controller =
+                        ClientTransactionListenerController.getInstance();
+                final int displayCount = configUpdatedDisplayIds.size();
+                for (int i = 0; i < displayCount; i++) {
+                    final int displayId = configUpdatedDisplayIds.valueAt(i);
+                    controller.onDisplayChanged(displayId);
+                }
+            } finally {
+                mContextToPreChangedConfigMap.clear();
             }
-            mConfigUpdatedDisplayIds.clear();
         }
 
         mPendingActions.clear();
@@ -182,26 +211,24 @@
             }
         }
 
-        // Can't read flag from isolated process.
-        final boolean isSyncWindowConfigUpdateFlagEnabled = !Process.isIsolated()
-                && syncWindowConfigUpdateFlag();
-        final Context configUpdatedContext = isSyncWindowConfigUpdateFlagEnabled
+        final boolean shouldTrackConfigUpdatedContext =
+                // No configuration change for local transaction.
+                !mTransactionHandler.isExecutingLocalTransaction()
+                        // Can't read flag from isolated process.
+                        && !Process.isIsolated()
+                        && bundleClientTransactionFlag();
+        final Context configUpdatedContext = shouldTrackConfigUpdatedContext
                 ? item.getContextToUpdate(mTransactionHandler)
                 : null;
-        final Configuration preExecutedConfig = configUpdatedContext != null
-                ? new Configuration(configUpdatedContext.getResources().getConfiguration())
-                : null;
+        if (configUpdatedContext != null
+                && !mContextToPreChangedConfigMap.containsKey(configUpdatedContext)) {
+            // Keep track of the first pre-executed config of each changed Context.
+            mContextToPreChangedConfigMap.put(configUpdatedContext,
+                    new Configuration(configUpdatedContext.getResources().getConfiguration()));
+        }
 
         item.execute(mTransactionHandler, mPendingActions);
 
-        if (configUpdatedContext != null) {
-            final Configuration postExecutedConfig = configUpdatedContext.getResources()
-                    .getConfiguration();
-            if (!areConfigurationsEqualForDisplay(postExecutedConfig, preExecutedConfig)) {
-                mConfigUpdatedDisplayIds.add(configUpdatedContext.getDisplayId());
-            }
-        }
-
         item.postExecute(mTransactionHandler, mPendingActions);
         if (r == null) {
             // Launch activity request will create an activity record.
@@ -297,7 +324,7 @@
                     break;
                 case ON_START:
                     mTransactionHandler.handleStartActivity(r, mPendingActions,
-                            null /* activityOptions */);
+                            null /* sceneTransitionInfo */);
                     break;
                 case ON_RESUME:
                     mTransactionHandler.handleResumeActivity(r, false /* finalStateRequest */,
diff --git a/core/java/android/app/servertransaction/WindowStateResizeItem.java b/core/java/android/app/servertransaction/WindowStateResizeItem.java
index 7d3eb87..193b03c 100644
--- a/core/java/android/app/servertransaction/WindowStateResizeItem.java
+++ b/core/java/android/app/servertransaction/WindowStateResizeItem.java
@@ -22,9 +22,12 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.ActivityThread;
 import android.app.ClientTransactionHandler;
+import android.content.Context;
 import android.os.Parcel;
 import android.os.RemoteException;
+import android.os.Trace;
 import android.util.MergedConfiguration;
 import android.view.IWindow;
 import android.view.InsetsState;
@@ -52,6 +55,11 @@
     @Override
     public void execute(@NonNull ClientTransactionHandler client,
             @NonNull PendingTransactionActions pendingActions) {
+        Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER,
+                mReportDraw ? "windowResizedReport" : "windowResized");
+        if (mWindow instanceof ResizeListener listener) {
+            listener.onExecutingWindowStateResizeItem();
+        }
         try {
             mWindow.resized(mFrames, mReportDraw, mConfiguration, mInsetsState, mForceLayout,
                     mAlwaysConsumeSystemBars, mDisplayId, mSyncSeqId, mDragResizing);
@@ -59,6 +67,14 @@
             // Should be a local call.
             throw new RuntimeException(e);
         }
+        Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
+    }
+
+    @Nullable
+    @Override
+    public Context getContextToUpdate(@NonNull ClientTransactionHandler client) {
+        // WindowStateResizeItem may update the global config with #mConfiguration.
+        return ActivityThread.currentApplication();
     }
 
     // ObjectPoolItem implementation
@@ -80,7 +96,7 @@
         instance.mFrames = new ClientWindowFrames(frames);
         instance.mReportDraw = reportDraw;
         instance.mConfiguration = new MergedConfiguration(configuration);
-        instance.mInsetsState = new InsetsState(insetsState);
+        instance.mInsetsState = new InsetsState(insetsState, true /* copySources */);
         instance.mForceLayout = forceLayout;
         instance.mAlwaysConsumeSystemBars = alwaysConsumeSystemBars;
         instance.mDisplayId = displayId;
@@ -190,4 +206,10 @@
                 + ", configuration=" + mConfiguration
                 + "}";
     }
+
+    /** The interface for IWindow to perform resize directly if possible. */
+    public interface ResizeListener {
+        /** Notifies that IWindow#resized is going to be called from WindowStateResizeItem. */
+        void onExecutingWindowStateResizeItem();
+    }
 }
diff --git a/core/java/android/app/wearable/OWNERS b/core/java/android/app/wearable/OWNERS
index 073e2d7..497eaf0 100644
--- a/core/java/android/app/wearable/OWNERS
+++ b/core/java/android/app/wearable/OWNERS
@@ -1,3 +1,5 @@
 charliewang@google.com
+hackz@google.com
 oni@google.com
+tomchan@google.com
 volnov@google.com
\ No newline at end of file
diff --git a/core/java/android/appwidget/flags.aconfig b/core/java/android/appwidget/flags.aconfig
index ec2e5fe..084cba3 100644
--- a/core/java/android/appwidget/flags.aconfig
+++ b/core/java/android/appwidget/flags.aconfig
@@ -20,3 +20,10 @@
   description: "Move state file IO to non-critical path"
   bug: "312949280"
 }
+
+flag {
+  name: "draw_data_parcel"
+  namespace: "app_widgets"
+  description: "Enable support for transporting draw instructions as data parcel"
+  bug: "286130467"
+}
diff --git a/core/java/android/companion/AssociationInfo.java b/core/java/android/companion/AssociationInfo.java
index 161fa79..843158c 100644
--- a/core/java/android/companion/AssociationInfo.java
+++ b/core/java/android/companion/AssociationInfo.java
@@ -71,6 +71,12 @@
      * @see CompanionDeviceManager#disassociate(int)
      */
     private final boolean mRevoked;
+    /**
+     * Indicates that the association is waiting for its corresponding companion app to be installed
+     * before it can be added to CDM. This is likely because it was restored onto the device from a
+     * backup.
+     */
+    private final boolean mPending;
     private final long mTimeApprovedMs;
     /**
      * A long value indicates the last time connected reported by selfManaged devices
@@ -88,7 +94,7 @@
             @Nullable String tag, @Nullable MacAddress macAddress,
             @Nullable CharSequence displayName, @Nullable String deviceProfile,
             @Nullable AssociatedDevice associatedDevice, boolean selfManaged,
-            boolean notifyOnDeviceNearby, boolean revoked, long timeApprovedMs,
+            boolean notifyOnDeviceNearby, boolean revoked, boolean pending, long timeApprovedMs,
             long lastTimeConnectedMs, int systemDataSyncFlags) {
         if (id <= 0) {
             throw new IllegalArgumentException("Association ID should be greater than 0");
@@ -109,6 +115,7 @@
         mSelfManaged = selfManaged;
         mNotifyOnDeviceNearby = notifyOnDeviceNearby;
         mRevoked = revoked;
+        mPending = pending;
         mTimeApprovedMs = timeApprovedMs;
         mLastTimeConnectedMs = lastTimeConnectedMs;
         mSystemDataSyncFlags = systemDataSyncFlags;
@@ -236,6 +243,15 @@
     }
 
     /**
+     * @return true if the association is waiting for its corresponding app to be installed
+     * before it can be added to CDM.
+     * @hide
+     */
+    public boolean isPending() {
+        return mPending;
+    }
+
+    /**
      * @return the last time self reported disconnected for selfManaged only.
      * @hide
      */
@@ -318,6 +334,7 @@
                 + ", mAssociatedDevice=" + mAssociatedDevice
                 + ", mNotifyOnDeviceNearby=" + mNotifyOnDeviceNearby
                 + ", mRevoked=" + mRevoked
+                + ", mPending=" + mPending
                 + ", mTimeApprovedMs=" + new Date(mTimeApprovedMs)
                 + ", mLastTimeConnectedMs=" + (
                     mLastTimeConnectedMs == Long.MAX_VALUE
@@ -336,6 +353,7 @@
                 && mSelfManaged == that.mSelfManaged
                 && mNotifyOnDeviceNearby == that.mNotifyOnDeviceNearby
                 && mRevoked == that.mRevoked
+                && mPending == that.mPending
                 && mTimeApprovedMs == that.mTimeApprovedMs
                 && mLastTimeConnectedMs == that.mLastTimeConnectedMs
                 && Objects.equals(mPackageName, that.mPackageName)
@@ -351,7 +369,7 @@
     public int hashCode() {
         return Objects.hash(mId, mUserId, mPackageName, mTag, mDeviceMacAddress, mDisplayName,
                 mDeviceProfile, mAssociatedDevice, mSelfManaged, mNotifyOnDeviceNearby, mRevoked,
-                mTimeApprovedMs, mLastTimeConnectedMs, mSystemDataSyncFlags);
+                mPending, mTimeApprovedMs, mLastTimeConnectedMs, mSystemDataSyncFlags);
     }
 
     @Override
@@ -372,6 +390,7 @@
         dest.writeBoolean(mSelfManaged);
         dest.writeBoolean(mNotifyOnDeviceNearby);
         dest.writeBoolean(mRevoked);
+        dest.writeBoolean(mPending);
         dest.writeLong(mTimeApprovedMs);
         dest.writeLong(mLastTimeConnectedMs);
         dest.writeInt(mSystemDataSyncFlags);
@@ -389,6 +408,7 @@
         mSelfManaged = in.readBoolean();
         mNotifyOnDeviceNearby = in.readBoolean();
         mRevoked = in.readBoolean();
+        mPending = in.readBoolean();
         mTimeApprovedMs = in.readLong();
         mLastTimeConnectedMs = in.readLong();
         mSystemDataSyncFlags = in.readInt();
@@ -427,6 +447,7 @@
         private boolean mSelfManaged;
         private boolean mNotifyOnDeviceNearby;
         private boolean mRevoked;
+        private boolean mPending;
         private long mTimeApprovedMs;
         private long mLastTimeConnectedMs;
         private int mSystemDataSyncFlags;
@@ -453,6 +474,31 @@
             mSelfManaged = info.mSelfManaged;
             mNotifyOnDeviceNearby = info.mNotifyOnDeviceNearby;
             mRevoked = info.mRevoked;
+            mPending = info.mPending;
+            mTimeApprovedMs = info.mTimeApprovedMs;
+            mLastTimeConnectedMs = info.mLastTimeConnectedMs;
+            mSystemDataSyncFlags = info.mSystemDataSyncFlags;
+        }
+
+        /**
+         * This builder is used specifically to create a new association to be restored to a device
+         * that is potentially using a different user ID from the backed-up device.
+         *
+         * @hide
+         */
+        public Builder(int id, int userId, @NonNull String packageName, AssociationInfo info) {
+            mId = id;
+            mUserId = userId;
+            mPackageName = packageName;
+            mTag = info.mTag;
+            mDeviceMacAddress = info.mDeviceMacAddress;
+            mDisplayName = info.mDisplayName;
+            mDeviceProfile = info.mDeviceProfile;
+            mAssociatedDevice = info.mAssociatedDevice;
+            mSelfManaged = info.mSelfManaged;
+            mNotifyOnDeviceNearby = info.mNotifyOnDeviceNearby;
+            mRevoked = info.mRevoked;
+            mPending = info.mPending;
             mTimeApprovedMs = info.mTimeApprovedMs;
             mLastTimeConnectedMs = info.mLastTimeConnectedMs;
             mSystemDataSyncFlags = info.mSystemDataSyncFlags;
@@ -526,6 +572,14 @@
         }
 
         /** @hide */
+        @NonNull
+        @SuppressLint("MissingGetterMatchingBuilder")
+        public Builder setPending(boolean pending) {
+            mPending = pending;
+            return this;
+        }
+
+        /** @hide */
         @TestApi
         @NonNull
         @SuppressLint("MissingGetterMatchingBuilder")
@@ -583,6 +637,7 @@
                     mSelfManaged,
                     mNotifyOnDeviceNearby,
                     mRevoked,
+                    mPending,
                     mTimeApprovedMs,
                     mLastTimeConnectedMs,
                     mSystemDataSyncFlags
diff --git a/core/java/android/companion/datatransfer/PermissionSyncRequest.java b/core/java/android/companion/datatransfer/PermissionSyncRequest.java
index 973fca30..34865f0 100644
--- a/core/java/android/companion/datatransfer/PermissionSyncRequest.java
+++ b/core/java/android/companion/datatransfer/PermissionSyncRequest.java
@@ -48,6 +48,15 @@
     }
 
     /** @hide */
+    @Override
+    public PermissionSyncRequest copyWithNewId(int associationId) {
+        PermissionSyncRequest newRequest = new PermissionSyncRequest(associationId);
+        newRequest.mUserId = this.mUserId;
+        newRequest.mUserConsented = this.mUserConsented;
+        return newRequest;
+    }
+
+    /** @hide */
     @NonNull
     public static final Creator<PermissionSyncRequest> CREATOR =
             new Creator<PermissionSyncRequest>() {
diff --git a/core/java/android/companion/datatransfer/SystemDataTransferRequest.java b/core/java/android/companion/datatransfer/SystemDataTransferRequest.java
index 38a553d..c3a2aa4 100644
--- a/core/java/android/companion/datatransfer/SystemDataTransferRequest.java
+++ b/core/java/android/companion/datatransfer/SystemDataTransferRequest.java
@@ -103,4 +103,15 @@
     public int describeContents() {
         return 0;
     }
+
+    /**
+     * Creates a copy of itself with new association ID.
+     *
+     * This method must be implemented to ensure that backup-and-restore can correctly re-map
+     * the restored requests to the restored associations that can potentially have different
+     * IDs than what was originally backed up.
+     *
+     * @hide
+     */
+    public abstract SystemDataTransferRequest copyWithNewId(int associationId);
 }
diff --git a/core/java/android/companion/virtual/camera/VirtualCameraConfig.java b/core/java/android/companion/virtual/camera/VirtualCameraConfig.java
index a939251..59fe9a1 100644
--- a/core/java/android/companion/virtual/camera/VirtualCameraConfig.java
+++ b/core/java/android/companion/virtual/camera/VirtualCameraConfig.java
@@ -20,11 +20,9 @@
 
 import android.annotation.FlaggedApi;
 import android.annotation.NonNull;
-import android.annotation.StringRes;
 import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.companion.virtual.flags.Flags;
-import android.content.res.Resources;
 import android.graphics.ImageFormat;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -45,16 +43,16 @@
 @FlaggedApi(Flags.FLAG_VIRTUAL_CAMERA)
 public final class VirtualCameraConfig implements Parcelable {
 
-    private final @StringRes int mNameStringRes;
+    private final String mName;
     private final Set<VirtualCameraStreamConfig> mStreamConfigurations;
     private final IVirtualCameraCallback mCallback;
 
     private VirtualCameraConfig(
-            int displayNameStringRes,
+            @NonNull String name,
             @NonNull Set<VirtualCameraStreamConfig> streamConfigurations,
             @NonNull Executor executor,
             @NonNull VirtualCameraCallback callback) {
-        mNameStringRes = displayNameStringRes;
+        mName = requireNonNull(name, "Missing name");
         mStreamConfigurations =
                 Set.copyOf(requireNonNull(streamConfigurations, "Missing stream configurations"));
         if (mStreamConfigurations.isEmpty()) {
@@ -68,7 +66,7 @@
     }
 
     private VirtualCameraConfig(@NonNull Parcel in) {
-        mNameStringRes = in.readInt();
+        mName = in.readString8();
         mCallback = IVirtualCameraCallback.Stub.asInterface(in.readStrongBinder());
         mStreamConfigurations =
                 Set.of(
@@ -84,18 +82,18 @@
 
     @Override
     public void writeToParcel(@NonNull Parcel dest, int flags) {
-        dest.writeInt(mNameStringRes);
+        dest.writeString8(mName);
         dest.writeStrongInterface(mCallback);
         dest.writeParcelableArray(
                 mStreamConfigurations.toArray(new VirtualCameraStreamConfig[0]), flags);
     }
 
     /**
-     * @return The display name of this VirtualCamera
+     * @return The name of this VirtualCamera
      */
-    @StringRes
-    public int getDisplayNameStringRes() {
-        return mNameStringRes;
+    @NonNull
+    public String getName() {
+        return mName;
     }
 
     /**
@@ -126,30 +124,22 @@
      * <li>At least one stream must be added with {@link #addStreamConfig(int, int, int)}.
      * <li>A callback must be set with {@link #setVirtualCameraCallback(Executor,
      *     VirtualCameraCallback)}
-     * <li>A user readable name can be set with {@link #setDisplayNameStringRes(int)}
+     * <li>A camera name must be set with {@link #setName(String)}
      */
     @FlaggedApi(Flags.FLAG_VIRTUAL_CAMERA)
     public static final class Builder {
 
-        private @StringRes int mDisplayNameStringRes = Resources.ID_NULL;
+        private String mName;
         private final ArraySet<VirtualCameraStreamConfig> mStreamConfigurations = new ArraySet<>();
         private Executor mCallbackExecutor;
         private VirtualCameraCallback mCallback;
 
         /**
-         * Set the visible name of this camera for the user.
-         *
-         * <p>Sets the resource to a string representing a user readable name for this virtual
-         * camera.
-         *
-         * @throws IllegalArgumentException if an invalid resource id is passed.
+         * Set the name of the virtual camera instance.
          */
         @NonNull
-        public Builder setDisplayNameStringRes(@StringRes int displayNameStringRes) {
-            if (displayNameStringRes <= 0) {
-                throw new IllegalArgumentException("Invalid resource passed for display name");
-            }
-            mDisplayNameStringRes = displayNameStringRes;
+        public Builder setName(@NonNull String name) {
+            mName = requireNonNull(name, "Display name cannot be null");
             return this;
         }
 
@@ -203,7 +193,7 @@
         @NonNull
         public VirtualCameraConfig build() {
             return new VirtualCameraConfig(
-                    mDisplayNameStringRes, mStreamConfigurations, mCallbackExecutor, mCallback);
+                    mName, mStreamConfigurations, mCallbackExecutor, mCallback);
         }
     }
 
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index c7a75ed..e9b94c9 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -41,6 +41,7 @@
 import android.database.Cursor;
 import android.database.MatrixCursor;
 import android.database.SQLException;
+import android.multiuser.Flags;
 import android.net.Uri;
 import android.os.AsyncTask;
 import android.os.Binder;
@@ -146,6 +147,7 @@
     private boolean mExported;
     private boolean mNoPerms;
     private boolean mSingleUser;
+    private boolean mSystemUserOnly;
     private SparseBooleanArray mUsersRedirectedToOwnerForMedia = new SparseBooleanArray();
 
     private ThreadLocal<AttributionSource> mCallingAttributionSource;
@@ -377,7 +379,9 @@
                             != PermissionChecker.PERMISSION_GRANTED
                             && getContext().checkUriPermission(userUri, Binder.getCallingPid(),
                             callingUid, Intent.FLAG_GRANT_READ_URI_PERMISSION)
-                            != PackageManager.PERMISSION_GRANTED) {
+                            != PackageManager.PERMISSION_GRANTED
+                            && !deniedAccessSystemUserOnlyProvider(callingUserId,
+                            mSystemUserOnly)) {
                         FrameworkStatsLog.write(GET_TYPE_ACCESSED_WITHOUT_PERMISSION,
                                 enumCheckUriPermission,
                                 callingUid, uri.getAuthority(), type);
@@ -865,6 +869,10 @@
     boolean checkUser(int pid, int uid, Context context) {
         final int callingUserId = UserHandle.getUserId(uid);
 
+        if (deniedAccessSystemUserOnlyProvider(callingUserId, mSystemUserOnly)) {
+            return false;
+        }
+
         if (callingUserId == context.getUserId() || mSingleUser) {
             return true;
         }
@@ -987,6 +995,9 @@
 
         // last chance, check against any uri grants
         final int callingUserId = UserHandle.getUserId(uid);
+        if (deniedAccessSystemUserOnlyProvider(callingUserId, mSystemUserOnly)) {
+            return PermissionChecker.PERMISSION_HARD_DENIED;
+        }
         final Uri userUri = (mSingleUser && !UserHandle.isSameUser(mMyUid, uid))
                 ? maybeAddUserId(uri, callingUserId) : uri;
         if (context.checkUriPermission(userUri, pid, uid, Intent.FLAG_GRANT_READ_URI_PERMISSION)
@@ -2623,6 +2634,7 @@
                 setPathPermissions(info.pathPermissions);
                 mExported = info.exported;
                 mSingleUser = (info.flags & ProviderInfo.FLAG_SINGLE_USER) != 0;
+                mSystemUserOnly = (info.flags & ProviderInfo.FLAG_SYSTEM_USER_ONLY) != 0;
                 setAuthorities(info.authority);
             }
             if (Build.IS_DEBUGGABLE) {
@@ -2756,6 +2768,11 @@
         String auth = uri.getAuthority();
         if (!mSingleUser) {
             int userId = getUserIdFromAuthority(auth, UserHandle.USER_CURRENT);
+            if (deniedAccessSystemUserOnlyProvider(mContext.getUserId(),
+                    mSystemUserOnly)) {
+                throw new SecurityException("Trying to query a SYSTEM user only content"
+                        + " provider from user:" + mContext.getUserId());
+            }
             if (userId != UserHandle.USER_CURRENT
                     && userId != mContext.getUserId()
                     // Since userId specified in content uri, the provider userId would be
@@ -2929,4 +2946,16 @@
             Trace.traceBegin(traceTag, methodName + subInfo);
         }
     }
+    /**
+     * Return true if access to content provider is denied because it's a SYSTEM user only
+     * provider and the calling user is not the SYSTEM user.
+     *
+     * @param callingUserId UserId of the caller accessing the content provider.
+     * @param systemUserOnly true when the content provider is only available for the SYSTEM user.
+     */
+    private static boolean deniedAccessSystemUserOnlyProvider(int callingUserId,
+            boolean systemUserOnly) {
+        return Flags.enableSystemUserOnlyForServicesAndProviders()
+                && (callingUserId != UserHandle.USER_SYSTEM && systemUserOnly);
+    }
 }
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index b75c64d..fa76e39 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3547,6 +3547,8 @@
      *
      * @param receiver The BroadcastReceiver to unregister.
      *
+     * @throws IllegalArgumentException if the {@code receiver} was not previously registered or
+     *                                  already unregistered.
      * @see #registerReceiver
      */
     public abstract void unregisterReceiver(BroadcastReceiver receiver);
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 183b9b0..d5eee63f 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -4207,7 +4207,9 @@
      * new state of quiet mode. This is only sent to registered receivers, not manifest receivers.
      *
      * <p>This broadcast is similar to {@link #ACTION_MANAGED_PROFILE_AVAILABLE} but functions as a
-     * generic broadcast for all profile users.
+     * generic broadcast for all users of type {@link android.os.UserManager#isProfile()}}. In
+     * case of a managed profile, both {@link #ACTION_MANAGED_PROFILE_AVAILABLE} and
+     * {@link #ACTION_PROFILE_AVAILABLE} broadcasts are sent.
      */
     @FlaggedApi(FLAG_ALLOW_PRIVATE_PROFILE)
     public static final String ACTION_PROFILE_AVAILABLE =
@@ -4221,7 +4223,9 @@
      * new state of quiet mode. This is only sent to registered receivers, not manifest receivers.
      *
      * <p>This broadcast is similar to {@link #ACTION_MANAGED_PROFILE_UNAVAILABLE} but functions as
-     * a generic broadcast for all profile users.
+     * a generic broadcast for all users of type {@link android.os.UserManager#isProfile()}}. In
+     * case of a managed profile, both {@link #ACTION_MANAGED_PROFILE_UNAVAILABLE} and
+     * {@link #ACTION_PROFILE_UNAVAILABLE} broadcasts are sent.
      */
     @FlaggedApi(FLAG_ALLOW_PRIVATE_PROFILE)
     public static final String ACTION_PROFILE_UNAVAILABLE =
@@ -6971,16 +6975,21 @@
     public static final int FLAG_DEBUG_LOG_RESOLUTION = 0x00000008;
     /**
      * If set, this intent will not match any components in packages that
-     * are currently stopped.  If this is not set, then the default behavior
-     * is to include such applications in the result.
+     * are currently
+     * {@linkplain android.content.pm.ApplicationInfo#FLAG_STOPPED stopped}.
+     * If this is not set, then the default behavior is to include such
+     * applications in the result.
      */
     public static final int FLAG_EXCLUDE_STOPPED_PACKAGES = 0x00000010;
     /**
      * If set, this intent will always match any components in packages that
-     * are currently stopped.  This is the default behavior when
+     * are currently
+     * {@linkplain android.content.pm.ApplicationInfo#FLAG_STOPPED stopped}.
+     * This is the default behavior when
      * {@link #FLAG_EXCLUDE_STOPPED_PACKAGES} is not set.  If both of these
      * flags are set, this one wins (it allows overriding of exclude for
-     * places where the framework may automatically set the exclude flag).
+     * places where the framework may automatically set the exclude flag,
+     * such as broadcasts).
      */
     public static final int FLAG_INCLUDE_STOPPED_PACKAGES = 0x00000020;
 
@@ -8090,7 +8099,7 @@
                         int end = data.indexOf('/', 14);
                         if (end < 0) {
                             // All we have is a package name.
-                            intent.mPackage = data.substring(14);
+                            intent.mPackage = Uri.decodeIfNeeded(data.substring(14));
                             if (!explicitAction) {
                                 intent.setAction(ACTION_MAIN);
                             }
@@ -8098,21 +8107,22 @@
                         } else {
                             // Target the Intent at the given package name always.
                             String authority = null;
-                            intent.mPackage = data.substring(14, end);
+                            intent.mPackage = Uri.decodeIfNeeded(data.substring(14, end));
                             int newEnd;
                             if ((end+1) < data.length()) {
                                 if ((newEnd=data.indexOf('/', end+1)) >= 0) {
                                     // Found a scheme, remember it.
-                                    scheme = data.substring(end+1, newEnd);
+                                    scheme = Uri.decodeIfNeeded(data.substring(end + 1, newEnd));
                                     end = newEnd;
                                     if (end < data.length() && (newEnd=data.indexOf('/', end+1)) >= 0) {
                                         // Found a authority, remember it.
-                                        authority = data.substring(end+1, newEnd);
+                                        authority = Uri.decodeIfNeeded(
+                                                data.substring(end + 1, newEnd));
                                         end = newEnd;
                                     }
                                 } else {
                                     // All we have is a scheme.
-                                    scheme = data.substring(end+1);
+                                    scheme = Uri.decodeIfNeeded(data.substring(end + 1));
                                 }
                             }
                             if (scheme == null) {
@@ -11753,27 +11763,33 @@
                         + this);
             }
             uri.append("android-app://");
-            uri.append(mPackage);
+            uri.append(Uri.encode(mPackage));
             String scheme = null;
             if (mData != null) {
-                scheme = mData.getScheme();
+                // All values here must be wrapped with Uri#encodeIfNotEncoded because it is
+                // possible to exploit the Uri API to return a raw unencoded value, which will
+                // not deserialize properly and may cause the resulting Intent to be transformed
+                // to a malicious value.
+                scheme = Uri.encodeIfNotEncoded(mData.getScheme(), null);
                 if (scheme != null) {
                     uri.append('/');
                     uri.append(scheme);
-                    String authority = mData.getEncodedAuthority();
+                    String authority = Uri.encodeIfNotEncoded(mData.getEncodedAuthority(), null);
                     if (authority != null) {
                         uri.append('/');
                         uri.append(authority);
-                        String path = mData.getEncodedPath();
+
+                        // Multiple path segments are allowed, don't encode the path / separator
+                        String path = Uri.encodeIfNotEncoded(mData.getEncodedPath(), "/");
                         if (path != null) {
                             uri.append(path);
                         }
-                        String queryParams = mData.getEncodedQuery();
+                        String queryParams = Uri.encodeIfNotEncoded(mData.getEncodedQuery(), null);
                         if (queryParams != null) {
                             uri.append('?');
                             uri.append(queryParams);
                         }
-                        String fragment = mData.getEncodedFragment();
+                        String fragment = Uri.encodeIfNotEncoded(mData.getEncodedFragment(), null);
                         if (fragment != null) {
                             uri.append('#');
                             uri.append(fragment);
diff --git a/core/java/android/content/OWNERS b/core/java/android/content/OWNERS
index 90c3d04..a37408b 100644
--- a/core/java/android/content/OWNERS
+++ b/core/java/android/content/OWNERS
@@ -4,6 +4,7 @@
 per-file *Content* = file:/services/core/java/com/android/server/am/OWNERS
 per-file *Sync* = file:/services/core/java/com/android/server/am/OWNERS
 per-file IntentFilter.java = file:/PACKAGE_MANAGER_OWNERS
+per-file UriRelativeFilter* = file:/PACKAGE_MANAGER_OWNERS
 per-file IntentFilter.java = file:/services/core/java/com/android/server/am/OWNERS
 per-file Intent.java = file:/INTENT_OWNERS
 per-file AutofillOptions* = file:/core/java/android/service/autofill/OWNERS
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 12da665..9fe8af5 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -16,13 +16,13 @@
 
 package android.content.pm;
 
+import android.annotation.FlaggedApi;
 import android.annotation.FloatRange;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.TestApi;
 import android.app.Activity;
-import android.app.compat.CompatChanges;
 import android.compat.annotation.ChangeId;
 import android.compat.annotation.Disabled;
 import android.compat.annotation.EnabledSince;
@@ -36,12 +36,12 @@
 import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.os.UserHandle;
 import android.util.ArraySet;
 import android.util.Printer;
 import android.window.OnBackInvokedCallback;
 
 import com.android.internal.util.Parcelling;
+import com.android.window.flags.Flags;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -1099,6 +1099,8 @@
     @ChangeId
     @Overridable
     @Disabled
+    @TestApi
+    @FlaggedApi(Flags.FLAG_APP_COMPAT_PROPERTIES_API)
     public static final long OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED =
             273509367L; // buganizer id
 
@@ -1786,8 +1788,7 @@
      * @hide
      */
     public boolean isChangeEnabled(long changeId) {
-        return CompatChanges.isChangeEnabled(changeId, applicationInfo.packageName,
-                UserHandle.getUserHandleForUid(applicationInfo.uid));
+        return applicationInfo.isChangeEnabled(changeId);
     }
 
     /** @hide */
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 3713380..a8dba51 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -26,6 +26,7 @@
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
+import android.app.compat.CompatChanges;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.pm.PackageManager.NameNotFoundException;
@@ -375,6 +376,19 @@
     /**
      * Value for {@link #flags}: true if this application's package is in
      * the stopped state.
+     *
+     * <p>Stopped is the initial state after an app is installed, before it is launched
+     * or otherwise directly interacted with by the user. The system tries not to
+     * start it unless initiated by a user interaction (typically launching its icon
+     * from the launcher, could also include user actions like adding it as an app widget,
+     * selecting it as a live wallpaper, selecting it as a keyboard, etc). Stopped
+     * applications will not receive implicit broadcasts unless the sender specifies
+     * {@link android.content.Intent#FLAG_INCLUDE_STOPPED_PACKAGES}.
+     *
+     * <p>Applications should avoid launching activities, binding to or starting services, or
+     * otherwise causing a stopped application to run unless initiated by the user.
+     *
+     * <p>An app can also return to the stopped state by a "force stop".
      */
     public static final int FLAG_STOPPED = 1<<21;
 
@@ -2632,6 +2646,17 @@
     }
 
     /**
+     * Checks if a changeId is enabled for the current user
+     * @param changeId The changeId to verify
+     * @return True of the changeId is enabled
+     * @hide
+     */
+    public boolean isChangeEnabled(long changeId) {
+        return CompatChanges.isChangeEnabled(changeId, packageName,
+                UserHandle.getUserHandleForUid(uid));
+    }
+
+    /**
      * @return whether the app has requested exemption from the foreground service restrictions.
      * This does not take any affect for now.
      * @hide
diff --git a/core/java/android/content/pm/ModuleInfo.java b/core/java/android/content/pm/ModuleInfo.java
index a7306a3..c6e93bb 100644
--- a/core/java/android/content/pm/ModuleInfo.java
+++ b/core/java/android/content/pm/ModuleInfo.java
@@ -16,10 +16,14 @@
 
 package android.content.pm;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
 import java.util.Objects;
 
 /**
@@ -46,6 +50,13 @@
     /** Whether or not this module is hidden from the user. */
     private boolean mHidden;
 
+    /**
+     * The list of the package names of all APK-in-APEX apps in the module, or
+     * null if there are none.
+     */
+    @Nullable
+    private List<String> mApkInApexPackageNames;
+
     // TODO: Decide whether we need an additional metadata bundle to support out of band
     // updates to ModuleInfo.
     //
@@ -61,6 +72,9 @@
         mPackageName = orig.mPackageName;
         mHidden = orig.mHidden;
         mApexModuleName = orig.mApexModuleName;
+        if (orig.mApkInApexPackageNames != null) {
+            mApkInApexPackageNames = List.copyOf(orig.mApkInApexPackageNames);
+        }
     }
 
     /** @hide Sets the public name of this module. */
@@ -107,6 +121,22 @@
         return mApexModuleName;
     }
 
+    /** @hide Set the list of the package names of all APK-in-APEX apps in this module. */
+    public ModuleInfo setApkInApexPackageNames(@NonNull Collection<String> apkInApexPackageNames) {
+        Objects.requireNonNull(apkInApexPackageNames);
+        mApkInApexPackageNames = List.copyOf(apkInApexPackageNames);
+        return this;
+    }
+
+    /** @hide Get the list of the package names of all APK-in-APEX apps in the module. */
+    @NonNull
+    public Collection<String> getApkInApexPackageNames() {
+        if (mApkInApexPackageNames == null) {
+            return Collections.emptyList();
+        }
+        return mApkInApexPackageNames;
+    }
+
     /** Returns a string representation of this object. */
     public String toString() {
         return "ModuleInfo{"
@@ -125,6 +155,7 @@
         hashCode = 31 * hashCode + Objects.hashCode(mName);
         hashCode = 31 * hashCode + Objects.hashCode(mPackageName);
         hashCode = 31 * hashCode + Objects.hashCode(mApexModuleName);
+        hashCode = 31 * hashCode + Objects.hashCode(mApkInApexPackageNames);
         hashCode = 31 * hashCode + Boolean.hashCode(mHidden);
         return hashCode;
     }
@@ -138,6 +169,7 @@
         return Objects.equals(mName, other.mName)
                 && Objects.equals(mPackageName, other.mPackageName)
                 && Objects.equals(mApexModuleName, other.mApexModuleName)
+                && Objects.equals(mApkInApexPackageNames, other.mApkInApexPackageNames)
                 && mHidden == other.mHidden;
     }
 
@@ -147,6 +179,8 @@
         dest.writeString(mPackageName);
         dest.writeBoolean(mHidden);
         dest.writeString(mApexModuleName);
+        // Parcel#writeStringList handles null case, we can use it directly
+        dest.writeStringList(mApkInApexPackageNames);
     }
 
     private ModuleInfo(Parcel source) {
@@ -154,6 +188,7 @@
         mPackageName = source.readString();
         mHidden = source.readBoolean();
         mApexModuleName = source.readString();
+        mApkInApexPackageNames = source.createStringArrayList();
     }
 
     public static final @android.annotation.NonNull Parcelable.Creator<ModuleInfo> CREATOR =
diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java
index 4f61613..c1c9928 100644
--- a/core/java/android/content/pm/PackageInfo.java
+++ b/core/java/android/content/pm/PackageInfo.java
@@ -499,6 +499,16 @@
      */
     public boolean isActiveApex;
 
+    /**
+     * If the package is an APEX package (i.e. the value of {@link #isApex}
+     * is true), this field is the package name of the APEX. If the package
+     * is one APK-in-APEX app, this field is the package name of the parent
+     * APEX that contains the app. If the package is not one of the above
+     * two cases, this field is {@code null}.
+     */
+    @Nullable
+    private String mApexPackageName;
+
     public PackageInfo() {
     }
 
@@ -535,6 +545,26 @@
         mArchiveTimeMillis = value;
     }
 
+    /**
+     * If the package is an APEX package (i.e. the value of {@link #isApex}
+     * is true), returns the package name of the APEX. If the package
+     * is one APK-in-APEX app, returns the package name of the parent
+     * APEX that contains the app. If the package is not one of the above
+     * two cases, returns {@code null}.
+     */
+    @Nullable
+    @FlaggedApi(android.content.pm.Flags.FLAG_PROVIDE_INFO_OF_APK_IN_APEX)
+    public String getApexPackageName() {
+        return mApexPackageName;
+    }
+
+    /**
+     * @hide
+     */
+    public void setApexPackageName(@Nullable String apexPackageName) {
+        mApexPackageName = apexPackageName;
+    }
+
     @Override
     public String toString() {
         return "PackageInfo{"
@@ -603,6 +633,12 @@
         dest.writeBoolean(isApex);
         dest.writeBoolean(isActiveApex);
         dest.writeLong(mArchiveTimeMillis);
+        if (mApexPackageName != null) {
+            dest.writeInt(1);
+            dest.writeString8(mApexPackageName);
+        } else {
+            dest.writeInt(0);
+        }
         dest.restoreAllowSquashing(prevAllowSquashing);
     }
 
@@ -669,5 +705,9 @@
         isApex = source.readBoolean();
         isActiveApex = source.readBoolean();
         mArchiveTimeMillis = source.readLong();
+        int hasApexPackageName = source.readInt();
+        if (hasApexPackageName != 0) {
+            mApexPackageName = source.readString8();
+        }
     }
 }
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 0e131b4..43322641 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -672,6 +672,13 @@
     public @interface UserActionReason {}
 
     /**
+     * The unarchival status is not set.
+     *
+     * @hide
+     */
+    public static final int UNARCHIVAL_STATUS_UNSET = -1;
+
+    /**
      * The unarchival is possible and will commence.
      *
      * <p> Note that this does not mean that the unarchival has completed. This status should be
@@ -736,6 +743,7 @@
      * @hide
      */
     @IntDef(value = {
+            UNARCHIVAL_STATUS_UNSET,
             UNARCHIVAL_OK,
             UNARCHIVAL_ERROR_USER_ACTION_NEEDED,
             UNARCHIVAL_ERROR_INSUFFICIENT_STORAGE,
@@ -2696,8 +2704,6 @@
         public int developmentInstallFlags = 0;
         /** {@hide} */
         public int unarchiveId = -1;
-        /** {@hide} */
-        public IntentSender unarchiveIntentSender;
 
         private final ArrayMap<String, Integer> mPermissionStates;
 
@@ -2750,7 +2756,6 @@
             applicationEnabledSettingPersistent = source.readBoolean();
             developmentInstallFlags = source.readInt();
             unarchiveId = source.readInt();
-            unarchiveIntentSender = source.readParcelable(null, IntentSender.class);
         }
 
         /** {@hide} */
@@ -2785,7 +2790,6 @@
             ret.applicationEnabledSettingPersistent = applicationEnabledSettingPersistent;
             ret.developmentInstallFlags = developmentInstallFlags;
             ret.unarchiveId = unarchiveId;
-            ret.unarchiveIntentSender = unarchiveIntentSender;
             return ret;
         }
 
@@ -3495,7 +3499,6 @@
                     applicationEnabledSettingPersistent);
             pw.printHexPair("developmentInstallFlags", developmentInstallFlags);
             pw.printPair("unarchiveId", unarchiveId);
-            pw.printPair("unarchiveIntentSender", unarchiveIntentSender);
             pw.println();
         }
 
@@ -3540,7 +3543,6 @@
             dest.writeBoolean(applicationEnabledSettingPersistent);
             dest.writeInt(developmentInstallFlags);
             dest.writeInt(unarchiveId);
-            dest.writeParcelable(unarchiveIntentSender, flags);
         }
 
         public static final Parcelable.Creator<SessionParams>
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index a863870..8e5e825 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -16,6 +16,8 @@
 
 package android.content.pm;
 
+import static com.android.internal.pm.pkg.parsing.ParsingPackageUtils.PARSE_COLLECT_CERTIFICATES;
+
 import android.Manifest;
 import android.annotation.CallbackExecutor;
 import android.annotation.CheckResult;
@@ -55,7 +57,6 @@
 import android.content.IntentSender;
 import android.content.pm.PackageInstaller.SessionParams;
 import android.content.pm.dex.ArtManager;
-import android.content.pm.pkg.FrameworkPackageUserState;
 import android.content.pm.verify.domain.DomainVerificationManager;
 import android.content.res.Configuration;
 import android.content.res.Resources;
@@ -91,6 +92,10 @@
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.pm.parsing.PackageInfoCommonUtils;
+import com.android.internal.pm.parsing.PackageParser2;
+import com.android.internal.pm.parsing.PackageParserException;
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.DataClass;
 
@@ -817,6 +822,8 @@
             GET_DISABLED_UNTIL_USED_COMPONENTS,
             GET_UNINSTALLED_PACKAGES,
             MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS,
+            MATCH_DIRECT_BOOT_AWARE,
+            MATCH_DIRECT_BOOT_UNAWARE,
             GET_ATTRIBUTIONS_LONG,
     })
     @Retention(RetentionPolicy.SOURCE)
@@ -2518,6 +2525,7 @@
             USER_MIN_ASPECT_RATIO_16_9,
             USER_MIN_ASPECT_RATIO_3_2,
             USER_MIN_ASPECT_RATIO_FULLSCREEN,
+            USER_MIN_ASPECT_RATIO_APP_DEFAULT,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface UserMinAspectRatio {}
@@ -2571,6 +2579,16 @@
      */
     public static final int USER_MIN_ASPECT_RATIO_FULLSCREEN = 6;
 
+    /**
+     * Aspect ratio override code: user sets to app's default aspect ratio.
+     * This resets both the user-forced aspect ratio, and the device manufacturer
+     * per-app override {@link ActivityInfo#OVERRIDE_ANY_ORIENTATION_TO_USER}.
+     * It is different from {@link #USER_MIN_ASPECT_RATIO_UNSET} as the latter may
+     * apply the device manufacturer per-app orientation override if any,
+     * @hide
+     */
+    public static final int USER_MIN_ASPECT_RATIO_APP_DEFAULT = 7;
+
     /** @hide */
     @IntDef(flag = true, prefix = { "DELETE_" }, value = {
             DELETE_KEEP_DATA,
@@ -3295,6 +3313,14 @@
 
     /**
      * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device supports NFC charging.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    @FlaggedApi(android.nfc.Flags.FLAG_ENABLE_NFC_CHARGING)
+    public static final String FEATURE_NFC_CHARGING = "android.hardware.nfc.charging";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
      * {@link #hasSystemFeature}: The Beam API is enabled on the device.
      */
     @SdkConstant(SdkConstantType.FEATURE)
@@ -3304,7 +3330,7 @@
      * Feature for {@link #getSystemAvailableFeatures} and
      * {@link #hasSystemFeature}: The device supports any
      * one of the {@link #FEATURE_NFC}, {@link #FEATURE_NFC_HOST_CARD_EMULATION},
-     * or {@link #FEATURE_NFC_HOST_CARD_EMULATION_NFCF} features.
+     * {@link #FEATURE_NFC_HOST_CARD_EMULATION_NFCF}, or {@link #FEATURE_NFC_CHARGING} features.
      *
      * @hide
      */
@@ -8609,28 +8635,56 @@
     @Nullable
     public PackageInfo getPackageArchiveInfo(@NonNull String archiveFilePath,
             @NonNull PackageInfoFlags flags) {
-        long flagsBits = flags.getValue();
-        final PackageParser parser = new PackageParser();
-        parser.setCallback(new PackageParser.CallbackImpl(this));
         final File apkFile = new File(archiveFilePath);
-        try {
-            if ((flagsBits & (MATCH_DIRECT_BOOT_UNAWARE | MATCH_DIRECT_BOOT_AWARE)) != 0) {
-                // Caller expressed an explicit opinion about what encryption
-                // aware/unaware components they want to see, so fall through and
-                // give them what they want
-            } else {
-                // Caller expressed no opinion, so match everything
-                flagsBits |= MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE;
-            }
 
-            PackageParser.Package pkg = parser.parsePackage(apkFile, 0, false);
-            if ((flagsBits & GET_SIGNATURES) != 0 || (flagsBits & GET_SIGNING_CERTIFICATES) != 0) {
-                PackageParser.collectCertificates(pkg, false /* skipVerify */);
-            }
-            return PackageParser.generatePackageInfo(pkg, null, (int) flagsBits, 0, 0, null,
-                    FrameworkPackageUserState.DEFAULT);
-        } catch (PackageParser.PackageParserException e) {
-            Log.w(TAG, "Failure to parse package archive", e);
+        @PackageInfoFlagsBits long flagsBits = flags.getValue();
+        if ((flagsBits & (MATCH_DIRECT_BOOT_UNAWARE | MATCH_DIRECT_BOOT_AWARE)) != 0) {
+            // Caller expressed an explicit opinion about what encryption
+            // aware/unaware components they want to see, so fall through and
+            // give them what they want
+        } else {
+            // Caller expressed no opinion, so match everything
+            flagsBits |= MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE;
+        }
+
+        int parserFlags = 0;
+        if ((flagsBits & (GET_SIGNATURES | GET_SIGNING_CERTIFICATES)) != 0) {
+            parserFlags |= PARSE_COLLECT_CERTIFICATES;
+        }
+
+        final PackageParser2 parser2 = new PackageParser2(/*separateProcesses*/ null,
+                /*displayMetrics*/ null,/*cacher*/ null,
+                new PackageParser2.Callback() {
+                    @Override
+                    public boolean hasFeature(String feature) {
+                        return PackageManager.this.hasSystemFeature(feature);
+                    }
+
+                    @NonNull
+                    @Override
+                    public Set<String> getHiddenApiWhitelistedApps() {
+                        return Collections.emptySet();
+                    }
+
+                    @NonNull
+                    @Override
+                    public Set<String> getInstallConstraintsAllowlist() {
+                        return Collections.emptySet();
+                    }
+
+                    @Override
+                    public boolean isChangeEnabled(long changeId,
+                            @androidx.annotation.NonNull ApplicationInfo appInfo) {
+                        return false;
+                    }
+                });
+
+        try {
+            ParsedPackage pp = parser2.parsePackage(apkFile, parserFlags, false);
+
+            return PackageInfoCommonUtils.generate(pp, flagsBits, UserHandle.myUserId());
+        } catch (PackageParserException e) {
+            Log.w(TAG, "Failure to parse package archive apkFile= " +apkFile);
             return null;
         }
     }
@@ -10970,7 +11024,7 @@
     }
 
     /**
-     * Returns the property defined in the given package's &lt;appliction&gt; tag.
+     * Returns the property defined in the given package's &lt;application&gt; tag.
      *
      * @throws NameNotFoundException if either the given package is not installed or if the
      * given property is not defined within the &lt;application&gt; tag.
@@ -11480,14 +11534,14 @@
     }
 
     /**
-     * Retrieve AndroidManifest.xml information for the given application apk path.
+     * Retrieve AndroidManifest.xml information for the given application apk file.
      *
      * <p>Example:
      *
      * <pre><code>
      * Bundle result;
      * try {
-     *     result = getContext().getPackageManager().parseAndroidManifest(apkFilePath,
+     *     result = getContext().getPackageManager().parseAndroidManifest(apkFile,
      *             xmlResourceParser -> {
      *                 Bundle bundle = new Bundle();
      *                 // Search the start tag
@@ -11516,9 +11570,10 @@
      *
      * Note: When the parserFunction is invoked, the client can read the AndroidManifest.xml
      * information by the XmlResourceParser object. After leaving the parserFunction, the
-     * XmlResourceParser object will be closed.
+     * XmlResourceParser object will be closed. The caller should also handle the exception for
+     * calling this method.
      *
-     * @param apkFilePath The path of an application apk file.
+     * @param apkFile The file of an application apk.
      * @param parserFunction The parserFunction will be invoked with the XmlResourceParser object
      *        after getting the AndroidManifest.xml of an application package.
      *
@@ -11529,7 +11584,7 @@
      */
     @FlaggedApi(android.content.pm.Flags.FLAG_GET_PACKAGE_INFO)
     @WorkerThread
-    public <T> T parseAndroidManifest(@NonNull String apkFilePath,
+    public <T> T parseAndroidManifest(@NonNull File apkFile,
             @NonNull Function<XmlResourceParser, T> parserFunction) throws IOException {
         throw new UnsupportedOperationException(
                 "parseAndroidManifest not implemented in subclass");
diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java
index 012b6c4..cdda12e 100644
--- a/core/java/android/content/pm/PermissionInfo.java
+++ b/core/java/android/content/pm/PermissionInfo.java
@@ -273,9 +273,6 @@
      * to the <code>retailDemo</code> value of
      * {@link android.R.attr#protectionLevel}.
      *
-     * @deprecated This flag has been replaced by the retail demo role and is a no-op since Android
-     *             V.
-     *
      * @hide
      */
     @SystemApi
diff --git a/core/java/android/content/pm/ProviderInfo.java b/core/java/android/content/pm/ProviderInfo.java
index 9e553db..de33fa8 100644
--- a/core/java/android/content/pm/ProviderInfo.java
+++ b/core/java/android/content/pm/ProviderInfo.java
@@ -89,6 +89,15 @@
     public static final int FLAG_VISIBLE_TO_INSTANT_APP = 0x100000;
 
     /**
+     * Bit in {@link #flags}: If set, this provider will only be available
+     * for the system user.
+     * Set from the android.R.attr#systemUserOnly attribute.
+     * In Sync with {@link ActivityInfo#FLAG_SYSTEM_USER_ONLY}
+     * @hide
+     */
+    public static final int FLAG_SYSTEM_USER_ONLY = ActivityInfo.FLAG_SYSTEM_USER_ONLY;
+
+    /**
      * Bit in {@link #flags}: If set, a single instance of the provider will
      * run for all users on the device.  Set from the
      * {@link android.R.attr#singleUser} attribute.
diff --git a/core/java/android/content/pm/ServiceInfo.java b/core/java/android/content/pm/ServiceInfo.java
index 4d704c3..2b378b1 100644
--- a/core/java/android/content/pm/ServiceInfo.java
+++ b/core/java/android/content/pm/ServiceInfo.java
@@ -17,6 +17,7 @@
 package android.content.pm;
 
 import android.Manifest;
+import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.RequiresPermission;
 import android.os.Parcel;
@@ -100,6 +101,14 @@
     public static final int FLAG_VISIBLE_TO_INSTANT_APP = 0x100000;
 
     /**
+     * @hide Bit in {@link #flags}: If set, this service will only be available
+     * for the system user.
+     * Set from the android.R.attr#systemUserOnly attribute.
+     * In Sync with {@link ActivityInfo#FLAG_SYSTEM_USER_ONLY}
+     */
+    public static final int FLAG_SYSTEM_USER_ONLY = ActivityInfo.FLAG_SYSTEM_USER_ONLY;
+
+    /**
      * Bit in {@link #flags}: If set, a single instance of the service will
      * run for all users on the device.  Set from the
      * {@link android.R.attr#singleUser} attribute.
@@ -471,6 +480,17 @@
     public static final int FOREGROUND_SERVICE_TYPE_FILE_MANAGEMENT = 1 << 12;
 
     /**
+     * Constant corresponding to {@code mediaProcessing} in
+     * the {@link android.R.attr#foregroundServiceType} attribute.
+     * Media processing use cases such as video or photo editing and processing.
+     */
+    @RequiresPermission(
+            value = Manifest.permission.FOREGROUND_SERVICE_MEDIA_PROCESSING
+    )
+    @FlaggedApi(Flags.FLAG_INTRODUCE_MEDIA_PROCESSING_TYPE)
+    public static final int FOREGROUND_SERVICE_TYPE_MEDIA_PROCESSING = 1 << 13;
+
+    /**
      * Constant corresponding to {@code specialUse} in
      * the {@link android.R.attr#foregroundServiceType} attribute.
      * Use cases that can't be categorized into any other foreground service types, but also
@@ -554,6 +574,7 @@
             FOREGROUND_SERVICE_TYPE_SYSTEM_EXEMPTED,
             FOREGROUND_SERVICE_TYPE_SHORT_SERVICE,
             FOREGROUND_SERVICE_TYPE_FILE_MANAGEMENT,
+            FOREGROUND_SERVICE_TYPE_MEDIA_PROCESSING,
             FOREGROUND_SERVICE_TYPE_SPECIAL_USE,
     })
     @Retention(RetentionPolicy.SOURCE)
@@ -640,6 +661,8 @@
                 return "shortService";
             case FOREGROUND_SERVICE_TYPE_FILE_MANAGEMENT:
                 return "fileManagement";
+            case FOREGROUND_SERVICE_TYPE_MEDIA_PROCESSING:
+                return "mediaProcessing";
             case FOREGROUND_SERVICE_TYPE_SPECIAL_USE:
                 return "specialUse";
             default:
diff --git a/core/java/android/content/pm/flags.aconfig b/core/java/android/content/pm/flags.aconfig
index 94bec35..a2cd3e1 100644
--- a/core/java/android/content/pm/flags.aconfig
+++ b/core/java/android/content/pm/flags.aconfig
@@ -131,3 +131,18 @@
     bug: "310801107"
     is_fixed_read_only: true
 }
+
+flag {
+    name: "introduce_media_processing_type"
+    namespace: "backstage_power"
+    description: "Add a new FGS type for media processing use cases."
+    bug: "317788011"
+}
+
+flag {
+    name: "encode_app_intent"
+    namespace: "package_manager_service"
+    description: "Feature flag to encode app intent."
+    bug: "281848623"
+}
+
diff --git a/core/java/android/content/pm/multiuser.aconfig b/core/java/android/content/pm/multiuser.aconfig
index 9a1796f..1036865 100644
--- a/core/java/android/content/pm/multiuser.aconfig
+++ b/core/java/android/content/pm/multiuser.aconfig
@@ -64,3 +64,17 @@
     bug: "296829976"
     is_fixed_read_only: true
 }
+
+flag {
+    name: "allow_resolver_sheet_for_private_space"
+    namespace: "profile_experiences"
+    description: "Add support for Private Space in resolver sheet"
+    bug: "307515485"
+}
+flag {
+    name: "enable_system_user_only_for_services_and_providers"
+    namespace: "multiuser"
+    description: "Enable systemUserOnly manifest attribute for services and providers."
+    bug: "302354856"
+    is_fixed_read_only: true
+}
diff --git a/core/java/android/content/res/Element.java b/core/java/android/content/res/Element.java
index e511469..89f4985 100644
--- a/core/java/android/content/res/Element.java
+++ b/core/java/android/content/res/Element.java
@@ -26,6 +26,8 @@
 
 import com.android.internal.R;
 
+import java.util.Set;
+
 /**
  * Defines the string attribute length and child tag count restrictions for a xml element.
  *
@@ -37,7 +39,11 @@
     private static final int MAX_ATTR_LEN_URL_COMPONENT = 256;
     private static final int MAX_ATTR_LEN_PERMISSION_GROUP = 256;
     private static final int MAX_ATTR_LEN_PACKAGE = 256;
-    private static final int MAX_ATTR_LEN_MIMETYPE = 512;
+    /**
+     * The mime type max length restriction here should match the restriction that is also
+     * placed in {@link android.content.pm.PackageManager#setMimeGroup(String, Set)}
+     */
+    private static final int MAX_ATTR_LEN_MIMETYPE = 255;
     private static final int MAX_ATTR_LEN_NAME = 1024;
     private static final int MAX_ATTR_LEN_PATH = 4000;
     private static final int MAX_ATTR_LEN_VALUE = 32_768;
@@ -103,6 +109,7 @@
     protected static final String TAG_ATTR_HOST = "host";
     protected static final String TAG_ATTR_MANAGE_SPACE_ACTIVITY = "manageSpaceActivity";
     protected static final String TAG_ATTR_MIMETYPE = "mimeType";
+    protected static final String TAG_ATTR_MIMEGROUP = "mimeGroup";
     protected static final String TAG_ATTR_NAME = "name";
     protected static final String TAG_ATTR_PACKAGE = "package";
     protected static final String TAG_ATTR_PATH = "path";
@@ -367,6 +374,7 @@
             case TAG_ATTR_BACKUP_AGENT:
             case TAG_ATTR_CATEGORY:
             case TAG_ATTR_MANAGE_SPACE_ACTIVITY:
+            case TAG_ATTR_MIMEGROUP:
             case TAG_ATTR_NAME:
             case TAG_ATTR_PARENT_ACTIVITY_NAME:
             case TAG_ATTR_PERMISSION:
@@ -520,6 +528,8 @@
                 return MAX_ATTR_LEN_URL_COMPONENT;
             case R.styleable.AndroidManifestData_mimeType:
                 return MAX_ATTR_LEN_MIMETYPE;
+            case R.styleable.AndroidManifestData_mimeGroup:
+                return MAX_ATTR_LEN_NAME;
             case R.styleable.AndroidManifestData_path:
             case R.styleable.AndroidManifestData_pathPattern:
             case R.styleable.AndroidManifestData_pathPrefix:
diff --git a/core/java/android/content/rollback/OWNERS b/core/java/android/content/rollback/OWNERS
index 3093fd6..8e5a0d8 100644
--- a/core/java/android/content/rollback/OWNERS
+++ b/core/java/android/content/rollback/OWNERS
@@ -1,5 +1,5 @@
-# Bug component: 557916
+# Bug component: 819107
 
-narayan@google.com
-nandana@google.com
-olilan@google.com
+ancr@google.com
+harshitmahajan@google.com
+robertogil@google.com
diff --git a/core/java/android/credentials/CredentialManager.java b/core/java/android/credentials/CredentialManager.java
index ad3ccc4..47ee76e 100644
--- a/core/java/android/credentials/CredentialManager.java
+++ b/core/java/android/credentials/CredentialManager.java
@@ -26,6 +26,7 @@
 import android.annotation.RequiresPermission;
 import android.annotation.SystemService;
 import android.annotation.TestApi;
+import android.app.ActivityOptions;
 import android.app.PendingIntent;
 import android.content.ComponentName;
 import android.content.Context;
@@ -37,6 +38,7 @@
 import android.os.RemoteException;
 import android.provider.DeviceConfig;
 import android.util.Log;
+import android.view.autofill.IAutoFillManagerClient;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -135,7 +137,8 @@
             @Nullable CancellationSignal cancellationSignal,
             @CallbackExecutor @NonNull Executor executor,
             @NonNull OutcomeReceiver<GetCandidateCredentialsResponse,
-                    GetCandidateCredentialsException> callback
+                    GetCandidateCredentialsException> callback,
+            @NonNull IAutoFillManagerClient clientCallback
     ) {
         requireNonNull(request, "request must not be null");
         requireNonNull(callingPackage, "callingPackage must not be null");
@@ -153,6 +156,7 @@
                     mService.getCandidateCredentials(
                             request,
                             new GetCandidateCredentialsTransport(executor, callback),
+                            clientCallback,
                             callingPackage);
         } catch (RemoteException e) {
             e.rethrowFromSystemServer();
@@ -752,7 +756,10 @@
         @Override
         public void onPendingIntent(PendingIntent pendingIntent) {
             try {
-                mContext.startIntentSender(pendingIntent.getIntentSender(), null, 0, 0, 0);
+                mContext.startIntentSender(pendingIntent.getIntentSender(), null, 0, 0, 0,
+                        ActivityOptions.makeBasic()
+                            .setPendingIntentBackgroundActivityStartMode(
+                                ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED).toBundle());
             } catch (IntentSender.SendIntentException e) {
                 Log.e(
                         TAG,
diff --git a/core/java/android/credentials/GetCandidateCredentialsResponse.java b/core/java/android/credentials/GetCandidateCredentialsResponse.java
index 1b130a9..530fead 100644
--- a/core/java/android/credentials/GetCandidateCredentialsResponse.java
+++ b/core/java/android/credentials/GetCandidateCredentialsResponse.java
@@ -18,6 +18,7 @@
 
 import android.annotation.Hide;
 import android.annotation.NonNull;
+import android.app.PendingIntent;
 import android.credentials.ui.GetCredentialProviderData;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -35,22 +36,39 @@
  */
 @Hide
 public final class GetCandidateCredentialsResponse implements Parcelable {
-    // TODO(b/299321990): Add members
-
     @NonNull
     private final List<GetCredentialProviderData> mCandidateProviderDataList;
 
+    private final PendingIntent mPendingIntent;
+
+    private final GetCredentialResponse mGetCredentialResponse;
+
     /**
      * @hide
      */
     @Hide
     public GetCandidateCredentialsResponse(
-            List<GetCredentialProviderData> candidateProviderDataList
+            GetCredentialResponse getCredentialResponse
+    ) {
+        mCandidateProviderDataList = null;
+        mPendingIntent = null;
+        mGetCredentialResponse = getCredentialResponse;
+    }
+
+    /**
+     * @hide
+     */
+    @Hide
+    public GetCandidateCredentialsResponse(
+            List<GetCredentialProviderData> candidateProviderDataList,
+            PendingIntent pendingIntent
     ) {
         Preconditions.checkCollectionNotEmpty(
                 candidateProviderDataList,
                 /*valueName=*/ "candidateProviderDataList");
         mCandidateProviderDataList = new ArrayList<>(candidateProviderDataList);
+        mPendingIntent = pendingIntent;
+        mGetCredentialResponse = null;
     }
 
     /**
@@ -62,17 +80,40 @@
         return mCandidateProviderDataList;
     }
 
+    /**
+     * Returns candidate provider data list.
+     *
+     * @hide
+     */
+    public GetCredentialResponse getGetCredentialResponse() {
+        return mGetCredentialResponse;
+    }
+
+    /**
+     * Returns candidate provider data list.
+     *
+     * @hide
+     */
+    public PendingIntent getPendingIntent() {
+        return mPendingIntent;
+    }
+
     protected GetCandidateCredentialsResponse(Parcel in) {
         List<GetCredentialProviderData> candidateProviderDataList = new ArrayList<>();
         in.readTypedList(candidateProviderDataList, GetCredentialProviderData.CREATOR);
         mCandidateProviderDataList = candidateProviderDataList;
 
         AnnotationValidations.validate(NonNull.class, null, mCandidateProviderDataList);
+
+        mPendingIntent = in.readTypedObject(PendingIntent.CREATOR);
+        mGetCredentialResponse = in.readTypedObject(GetCredentialResponse.CREATOR);
     }
 
     @Override
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeTypedList(mCandidateProviderDataList);
+        dest.writeTypedObject(mPendingIntent, flags);
+        dest.writeTypedObject(mGetCredentialResponse, flags);
     }
 
     @Override
diff --git a/core/java/android/credentials/ICredentialManager.aidl b/core/java/android/credentials/ICredentialManager.aidl
index d081576..726bc97 100644
--- a/core/java/android/credentials/ICredentialManager.aidl
+++ b/core/java/android/credentials/ICredentialManager.aidl
@@ -22,6 +22,7 @@
 import android.credentials.ClearCredentialStateRequest;
 import android.credentials.CreateCredentialRequest;
 import android.credentials.GetCandidateCredentialsRequest;
+import android.view.autofill.IAutoFillManagerClient;
 import android.credentials.GetCredentialRequest;
 import android.credentials.RegisterCredentialDescriptionRequest;
 import android.credentials.UnregisterCredentialDescriptionRequest;
@@ -47,7 +48,7 @@
 
     @nullable ICancellationSignal executeCreateCredential(in CreateCredentialRequest request, in ICreateCredentialCallback callback, String callingPackage);
 
-    @nullable ICancellationSignal getCandidateCredentials(in GetCredentialRequest request, in IGetCandidateCredentialsCallback callback, String callingPackage);
+    @nullable ICancellationSignal getCandidateCredentials(in GetCredentialRequest request, in IGetCandidateCredentialsCallback callback, in IAutoFillManagerClient clientCallback, String callingPackage);
 
     @nullable ICancellationSignal clearCredentialState(in ClearCredentialStateRequest request, in IClearCredentialStateCallback callback, String callingPackage);
 
diff --git a/core/java/android/credentials/ui/Constants.java b/core/java/android/credentials/ui/Constants.java
index 7092f29..37f850b 100644
--- a/core/java/android/credentials/ui/Constants.java
+++ b/core/java/android/credentials/ui/Constants.java
@@ -29,6 +29,13 @@
     public static final String EXTRA_RESULT_RECEIVER =
             "android.credentials.ui.extra.RESULT_RECEIVER";
 
+    /**
+     * The intent extra key for indicating whether the bottom sheet should be started directly
+     * on the 'All Options' screen.
+     */
+    public static final String EXTRA_REQ_FOR_ALL_OPTIONS =
+            "android.credentials.ui.extra.REQ_FOR_ALL_OPTIONS";
+
     /** The intent action for when the enabled Credential Manager providers has been updated. */
     public static final String CREDMAN_ENABLED_PROVIDERS_UPDATED =
             "android.credentials.ui.action.CREDMAN_ENABLED_PROVIDERS_UPDATED";
diff --git a/core/java/android/credentials/ui/IntentFactory.java b/core/java/android/credentials/ui/IntentFactory.java
index 5e8372d..49321d5 100644
--- a/core/java/android/credentials/ui/IntentFactory.java
+++ b/core/java/android/credentials/ui/IntentFactory.java
@@ -35,6 +35,31 @@
  */
 @TestApi
 public class IntentFactory {
+
+    /**
+     * Generate a new launch intent to the Credential Selector UI.
+     *
+     * @hide
+     */
+    @NonNull
+    public static Intent createCredentialSelectorIntent(
+            @NonNull RequestInfo requestInfo,
+            @SuppressLint("ConcreteCollection") // Concrete collection needed for marshalling.
+            @NonNull
+            ArrayList<ProviderData> enabledProviderDataList,
+            @SuppressLint("ConcreteCollection") // Concrete collection needed for marshalling.
+            @NonNull
+            ArrayList<DisabledProviderData> disabledProviderDataList,
+            @NonNull ResultReceiver resultReceiver,
+            boolean isRequestForAllOptions) {
+
+        Intent intent = createCredentialSelectorIntent(requestInfo, enabledProviderDataList,
+                disabledProviderDataList, resultReceiver);
+        intent.putExtra(Constants.EXTRA_REQ_FOR_ALL_OPTIONS, isRequestForAllOptions);
+
+        return intent;
+    }
+
     /** Generate a new launch intent to the Credential Selector UI. */
     @NonNull
     public static Intent createCredentialSelectorIntent(
diff --git a/core/java/android/database/sqlite/SQLiteConnection.java b/core/java/android/database/sqlite/SQLiteConnection.java
index b96d832..ecffe9e 100644
--- a/core/java/android/database/sqlite/SQLiteConnection.java
+++ b/core/java/android/database/sqlite/SQLiteConnection.java
@@ -121,8 +121,12 @@
     // The native SQLiteConnection pointer.  (FOR INTERNAL USE ONLY)
     private long mConnectionPtr;
 
+    // Restrict this connection to read-only operations.
     private boolean mOnlyAllowReadOnlyOperations;
 
+    // Allow this connection to treat updates to temporary tables as read-only operations.
+    private boolean mAllowTempTableRetry = Flags.sqliteAllowTempTables();
+
     // The number of times attachCancellationSignal has been called.
     // Because SQLite statement execution can be reentrant, we keep track of how many
     // times we have attempted to attach a cancellation signal to the connection so that
@@ -142,6 +146,7 @@
     private static native void nativeFinalizeStatement(long connectionPtr, long statementPtr);
     private static native int nativeGetParameterCount(long connectionPtr, long statementPtr);
     private static native boolean nativeIsReadOnly(long connectionPtr, long statementPtr);
+    private static native boolean nativeUpdatesTempOnly(long connectionPtr, long statementPtr);
     private static native int nativeGetColumnCount(long connectionPtr, long statementPtr);
     private static native String nativeGetColumnName(long connectionPtr, long statementPtr,
             int index);
@@ -1097,7 +1102,7 @@
         try {
             final int numParameters = nativeGetParameterCount(mConnectionPtr, statementPtr);
             final int type = DatabaseUtils.getSqlStatementTypeExtended(sql);
-            final boolean readOnly = nativeIsReadOnly(mConnectionPtr, statementPtr);
+            boolean readOnly = nativeIsReadOnly(mConnectionPtr, statementPtr);
             statement = obtainPreparedStatement(sql, statementPtr, numParameters, type, readOnly,
                     seqNum);
             if (!skipCache && isCacheable(type)) {
@@ -1265,13 +1270,20 @@
 
     /**
      * Verify that the statement is read-only, if the connection only allows read-only
-     * operations.
+     * operations.  If the connection allows updates to temporary tables, then the statement is
+     * read-only if the only updates are to temporary tables.
      * @param statement The statement to check.
      * @throws SQLiteException if the statement could update the database inside a read-only
      * transaction.
      */
     void throwIfStatementForbidden(PreparedStatement statement) {
         if (mOnlyAllowReadOnlyOperations && !statement.mReadOnly) {
+            if (mAllowTempTableRetry) {
+                statement.mReadOnly =
+                        nativeUpdatesTempOnly(mConnectionPtr, statement.mStatementPtr);
+                if (statement.mReadOnly) return;
+            }
+
             throw new SQLiteException("Cannot execute this statement because it "
                     + "might modify the database but the connection is read-only.");
         }
diff --git a/core/java/android/database/sqlite/flags.aconfig b/core/java/android/database/sqlite/flags.aconfig
index 62a5123..92ef9c2 100644
--- a/core/java/android/database/sqlite/flags.aconfig
+++ b/core/java/android/database/sqlite/flags.aconfig
@@ -7,3 +7,11 @@
      description: "SQLite APIs held back for Android 15"
      bug: "279043253"
 }
+
+flag {
+     name: "sqlite_allow_temp_tables"
+     namespace: "system_performance"
+     is_fixed_read_only: true
+     description: "Permit updates to TEMP tables in read-only transactions"
+     bug: "317993835"
+}
diff --git a/core/java/android/hardware/HardwareBuffer.java b/core/java/android/hardware/HardwareBuffer.java
index e714887..f5b3a7b 100644
--- a/core/java/android/hardware/HardwareBuffer.java
+++ b/core/java/android/hardware/HardwareBuffer.java
@@ -114,10 +114,16 @@
     /** Format: 8 bits red */
     @FlaggedApi(com.android.graphics.hwui.flags.Flags.FLAG_REQUESTED_FORMATS_V)
     public static final int R_8           = 0x38;
-    /** Format: 16 bits red */
+    /**
+     * Format: 16 bits red. Bits should be represented in unsigned integer, instead of the
+     * implicit unsigned normalized.
+     */
     @FlaggedApi(com.android.graphics.hwui.flags.Flags.FLAG_REQUESTED_FORMATS_V)
     public static final int R_16_UINT     = 0x39;
-    /** Format: 16 bits each red, green */
+    /**
+     * Format: 16 bits each red, green. Bits should be represented in unsigned integer,
+     * instead of the implicit unsigned normalized.
+     */
     @FlaggedApi(com.android.graphics.hwui.flags.Flags.FLAG_REQUESTED_FORMATS_V)
     public static final int RG_1616_UINT  = 0x3a;
     /** Format: 10 bits each red, green, blue, alpha */
diff --git a/core/java/android/hardware/SyncFence.java b/core/java/android/hardware/SyncFence.java
index d6052cd..c2440fb 100644
--- a/core/java/android/hardware/SyncFence.java
+++ b/core/java/android/hardware/SyncFence.java
@@ -16,6 +16,7 @@
 
 package android.hardware;
 
+import android.annotation.FlaggedApi;
 import android.annotation.NonNull;
 import android.media.Image;
 import android.media.ImageWriter;
@@ -26,6 +27,8 @@
 import android.os.Parcelable;
 import android.os.SystemClock;
 
+import com.android.window.flags.Flags;
+
 import libcore.util.NativeAllocationRegistry;
 
 import java.io.FileDescriptor;
@@ -121,6 +124,19 @@
         }
     }
 
+    /**
+     * Creates a copy of the SyncFence from an existing one.
+     * Both fences must be closed() independently.
+     */
+    @FlaggedApi(Flags.FLAG_SDK_DESIRED_PRESENT_TIME)
+    public SyncFence(@NonNull SyncFence other) {
+        this(other.mNativePtr);
+
+        if (mNativePtr != 0) {
+            nIncRef(mNativePtr);
+        }
+    }
+
     private SyncFence() {
         mCloser = () -> {};
     }
@@ -312,4 +328,5 @@
     private static native int nGetFd(long nPtr);
     private static native boolean nWait(long nPtr, long timeout);
     private static native long nGetSignalTime(long nPtr);
+    private static native void nIncRef(long nPtr);
 }
diff --git a/core/java/android/hardware/SystemSensorManager.java b/core/java/android/hardware/SystemSensorManager.java
index 40e03db..60ad8e8 100644
--- a/core/java/android/hardware/SystemSensorManager.java
+++ b/core/java/android/hardware/SystemSensorManager.java
@@ -86,6 +86,8 @@
     private static native long nativeCreate(String opPackageName);
     private static native boolean nativeGetSensorAtIndex(long nativeInstance,
             Sensor sensor, int index);
+    private static native boolean nativeGetDefaultDeviceSensorAtIndex(long nativeInstance,
+            Sensor sensor, int index);
     private static native void nativeGetDynamicSensors(long nativeInstance, List<Sensor> list);
     private static native void nativeGetRuntimeSensors(
             long nativeInstance, int deviceId, List<Sensor> list);
@@ -162,11 +164,14 @@
         // initialize the sensor list
         for (int index = 0;; ++index) {
             Sensor sensor = new Sensor();
-            if (!nativeGetSensorAtIndex(mNativeInstance, sensor, index)) break;
+            if (android.companion.virtual.flags.Flags.enableNativeVdm()) {
+                if (!nativeGetDefaultDeviceSensorAtIndex(mNativeInstance, sensor, index)) break;
+            } else {
+                if (!nativeGetSensorAtIndex(mNativeInstance, sensor, index)) break;
+            }
             mFullSensorsList.add(sensor);
             mHandleToSensor.put(sensor.getHandle(), sensor);
         }
-
     }
 
     /** @hide */
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 3ab889d..665d8d2 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -557,13 +557,15 @@
      * on a particular SessionConfiguration.</p>
      *
      * @return List of CameraCharacteristic keys containing characterisitics specific to a session
-     * configuration. For Android 15, this list only contains CONTROL_ZOOM_RATIO_RANGE.
+     * configuration. For Android 15, this list only contains CONTROL_ZOOM_RATIO_RANGE and
+     * SCALER_AVAILABLE_MAX_DIGITAL_ZOOM.
      */
     @NonNull
     @FlaggedApi(Flags.FLAG_FEATURE_COMBINATION_QUERY)
     public List<CameraCharacteristics.Key<?>> getAvailableSessionCharacteristicsKeys() {
         if (mAvailableSessionCharacteristicsKeys == null) {
-            mAvailableSessionCharacteristicsKeys = Arrays.asList(CONTROL_ZOOM_RATIO_RANGE);
+            mAvailableSessionCharacteristicsKeys =
+                    Arrays.asList(CONTROL_ZOOM_RATIO_RANGE, SCALER_AVAILABLE_MAX_DIGITAL_ZOOM);
         }
         return mAvailableSessionCharacteristicsKeys;
     }
diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
index 3affb73..0cd1c8c 100644
--- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
+++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
@@ -79,6 +79,8 @@
 import android.util.Range;
 import android.util.Size;
 
+import com.android.internal.camera.flags.Flags;
+
 import dalvik.annotation.optimization.FastNative;
 import dalvik.system.VMRuntime;
 
@@ -1795,49 +1797,57 @@
             return false;
         }
 
-        long[] tsArray = new long[samples.length];
-        float[] intrinsicsArray = new float[samples.length * 5];
-        for (int i = 0; i < samples.length; i++) {
-            tsArray[i] = samples[i].getTimestamp();
-            System.arraycopy(samples[i].getLensIntrinsics(), 0, intrinsicsArray, 5*i, 5);
+        if (Flags.concertMode()) {
+            long[] tsArray = new long[samples.length];
+            float[] intrinsicsArray = new float[samples.length * 5];
+            for (int i = 0; i < samples.length; i++) {
+                tsArray[i] = samples[i].getTimestampNanos();
+                System.arraycopy(samples[i].getLensIntrinsics(), 0, intrinsicsArray, 5 * i, 5);
 
+            }
+            setBase(CaptureResult.STATISTICS_LENS_INTRINSIC_SAMPLES, intrinsicsArray);
+            setBase(CaptureResult.STATISTICS_LENS_INTRINSIC_TIMESTAMPS, tsArray);
+
+            return true;
+        } else {
+            return false;
         }
-        setBase(CaptureResult.STATISTICS_LENS_INTRINSIC_SAMPLES, intrinsicsArray);
-        setBase(CaptureResult.STATISTICS_LENS_INTRINSIC_TIMESTAMPS, tsArray);
-
-        return true;
     }
 
     private LensIntrinsicsSample[] getLensIntrinsicSamples() {
-        long[] timestamps = getBase(CaptureResult.STATISTICS_LENS_INTRINSIC_TIMESTAMPS);
-        float[] intrinsics = getBase(CaptureResult.STATISTICS_LENS_INTRINSIC_SAMPLES);
+        if (Flags.concertMode()) {
+            long[] timestamps = getBase(CaptureResult.STATISTICS_LENS_INTRINSIC_TIMESTAMPS);
+            float[] intrinsics = getBase(CaptureResult.STATISTICS_LENS_INTRINSIC_SAMPLES);
 
-        if (timestamps == null) {
-            if (intrinsics != null) {
-                throw new AssertionError("timestamps is null but intrinsics is not");
+            if (timestamps == null) {
+                if (intrinsics != null) {
+                    throw new AssertionError("timestamps is null but intrinsics is not");
+                }
+
+                return null;
             }
 
+            if (intrinsics == null) {
+                throw new AssertionError("timestamps is not null but intrinsics is");
+            } else if ((intrinsics.length % 5) != 0) {
+                throw new AssertionError("intrinsics are not multiple of 5");
+            }
+
+            if ((intrinsics.length / 5) != timestamps.length) {
+                throw new AssertionError(String.format(
+                        "timestamps has %d entries but intrinsics has %d", timestamps.length,
+                        intrinsics.length / 5));
+            }
+
+            LensIntrinsicsSample[] samples = new LensIntrinsicsSample[timestamps.length];
+            for (int i = 0; i < timestamps.length; i++) {
+                float[] currentIntrinsic = Arrays.copyOfRange(intrinsics, 5 * i, 5 * i + 5);
+                samples[i] = new LensIntrinsicsSample(timestamps[i], currentIntrinsic);
+            }
+            return samples;
+        } else {
             return null;
         }
-
-        if (intrinsics == null) {
-            throw new AssertionError("timestamps is not null but intrinsics is");
-        } else if((intrinsics.length % 5) != 0) {
-            throw new AssertionError("intrinsics are not multiple of 5");
-        }
-
-        if ((intrinsics.length / 5) != timestamps.length) {
-            throw new AssertionError(String.format(
-                    "timestamps has %d entries but intrinsics has %d", timestamps.length,
-                    intrinsics.length / 5));
-        }
-
-        LensIntrinsicsSample[] samples = new LensIntrinsicsSample[timestamps.length];
-        for (int i = 0; i < timestamps.length; i++) {
-            float[] currentIntrinsic = Arrays.copyOfRange(intrinsics, 5*i, 5*i + 5);
-            samples[i] = new LensIntrinsicsSample(timestamps[i], currentIntrinsic);
-        }
-        return samples;
     }
 
     private Capability[] getExtendedSceneModeCapabilities() {
diff --git a/core/java/android/hardware/camera2/params/LensIntrinsicsSample.java b/core/java/android/hardware/camera2/params/LensIntrinsicsSample.java
index 575cbfa..9a4ec5c 100644
--- a/core/java/android/hardware/camera2/params/LensIntrinsicsSample.java
+++ b/core/java/android/hardware/camera2/params/LensIntrinsicsSample.java
@@ -37,16 +37,18 @@
      * Create a new {@link LensIntrinsicsSample}.
      *
      * <p>{@link LensIntrinsicsSample} contains the timestamp and the
-     * {@link CaptureResult#LENS_INTRINSIC_CALIBRATION} sample.
+     * {@link CaptureResult#LENS_INTRINSIC_CALIBRATION} sample.</p>
      *
-     * @param timestamp timestamp of the lens intrinsics sample.
-     * @param lensIntrinsics the lens intrinsic calibration for the sample.
+     * @param timestampNs timestamp in nanoseconds of the lens intrinsics sample. This uses the
+     *                  same time basis as {@link CaptureResult#SENSOR_TIMESTAMP}.
+     * @param lensIntrinsics the lens {@link CaptureResult#LENS_INTRINSIC_CALIBRATION intrinsic}
+     *                      calibration for the sample.
      *
      * @throws IllegalArgumentException if lensIntrinsics length is different from 5
      */
     @FlaggedApi(Flags.FLAG_CONCERT_MODE)
-    public LensIntrinsicsSample(final long timestamp, @NonNull final float[] lensIntrinsics) {
-        mTimestampNs = timestamp;
+    public LensIntrinsicsSample(final long timestampNs, @NonNull final float[] lensIntrinsics) {
+        mTimestampNs = timestampNs;
         Preconditions.checkArgument(lensIntrinsics.length == 5);
         mLensIntrinsics = lensIntrinsics;
     }
@@ -54,18 +56,18 @@
     /**
      * Get the timestamp in nanoseconds.
      *
-     *<p>The timestamps are in the same timebase as and comparable to
+     *<p>The timestamps are in the same time basis as and comparable to
      *{@link CaptureResult#SENSOR_TIMESTAMP android.sensor.timestamp}.</p>
      *
      * @return a long value (guaranteed to be finite)
      */
     @FlaggedApi(Flags.FLAG_CONCERT_MODE)
-    public long getTimestamp() {
+    public long getTimestampNanos() {
         return mTimestampNs;
     }
 
     /**
-     * Get the lens intrinsics calibration
+     * Get the lens {@link CaptureResult#LENS_INTRINSIC_CALIBRATION intrinsics} calibration
      *
      * @return a floating point value (guaranteed to be finite)
      * @see CaptureResult#LENS_INTRINSIC_CALIBRATION
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 134a510..8f0e0c9 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -1831,15 +1831,6 @@
         String KEY_POWER_THROTTLING_DATA = "power_throttling_data";
 
         /**
-         * Key for new power controller feature flag. If enabled new DisplayPowerController will
-         * be used.
-         * Read value via {@link android.provider.DeviceConfig#getBoolean(String, String, boolean)}
-         * with {@link android.provider.DeviceConfig#NAMESPACE_DISPLAY_MANAGER} as the namespace.
-         * @hide
-         */
-        String KEY_NEW_POWER_CONTROLLER = "use_newly_structured_display_power_controller";
-
-        /**
          * Key for normal brightness mode controller feature flag.
          * It enables NormalBrightnessModeController.
          * Read value via {@link android.provider.DeviceConfig#getBoolean(String, String, boolean)}
diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java
index ffd7212..64a62a9 100644
--- a/core/java/android/hardware/display/DisplayManagerInternal.java
+++ b/core/java/android/hardware/display/DisplayManagerInternal.java
@@ -751,6 +751,23 @@
          */
         boolean blockScreenOn(Runnable unblocker);
 
+        /**
+         * Get the brightness levels used to determine automatic brightness based on lux levels.
+         * @param mode The auto-brightness mode
+         *             (AutomaticBrightnessController.AutomaticBrightnessMode)
+         * @return The brightness levels for the specified mode. The values are between
+         * {@link PowerManager.BRIGHTNESS_MIN} and {@link PowerManager.BRIGHTNESS_MAX}.
+         */
+        float[] getAutoBrightnessLevels(int mode);
+
+        /**
+         * Get the lux levels used to determine automatic brightness.
+         * @param mode The auto-brightness mode
+         *             (AutomaticBrightnessController.AutomaticBrightnessMode)
+         * @return The lux levels for the specified mode
+         */
+        float[] getAutoBrightnessLuxLevels(int mode);
+
         /** Returns whether displayoffload supports the given display state. */
         static boolean isSupportedOffloadState(int displayState) {
             return Display.isSuspendedState(displayState);
diff --git a/core/java/android/hardware/display/VirtualDisplayConfig.java b/core/java/android/hardware/display/VirtualDisplayConfig.java
index 9e09759..56f69a6 100644
--- a/core/java/android/hardware/display/VirtualDisplayConfig.java
+++ b/core/java/android/hardware/display/VirtualDisplayConfig.java
@@ -450,11 +450,14 @@
          * automatically launched upon the display creation. If unset or set to {@code false}, the
          * display will not host any activities upon creation.</p>
          *
-         * <p>Note: setting to {@code true} requires the display to be trusted. If the display is
-         * not trusted, this property is ignored.</p>
+         * <p>Note: setting to {@code true} requires the display to be trusted and to not mirror
+         * content of other displays. If the display is not trusted, or if it mirrors content of
+         * other displays, this property is ignored.</p>
          *
          * @param isHomeSupported whether home activities are supported on the display
          * @see DisplayManager#VIRTUAL_DISPLAY_FLAG_TRUSTED
+         * @see DisplayManager#VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR
+         * @see DisplayManager#VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY
          * @hide
          */
         @FlaggedApi(android.companion.virtual.flags.Flags.FLAG_VDM_CUSTOM_HOME)
diff --git a/core/java/android/hardware/face/FaceAuthenticateOptions.java b/core/java/android/hardware/face/FaceAuthenticateOptions.java
index 1c6de04..518f902a 100644
--- a/core/java/android/hardware/face/FaceAuthenticateOptions.java
+++ b/core/java/android/hardware/face/FaceAuthenticateOptions.java
@@ -261,7 +261,7 @@
      * The reason for this operation when requested by the system (sysui),
      * otherwise AUTHENTICATE_REASON_UNKNOWN.
      *
-     * See frameworks/base/packages/SystemUI/src/com/android/keyguard/FaceAuthReason.kt
+     * See packages/SystemUI/src/com/android/systemui/deviceentry/shared/FaceAuthReason.kt
      * for more details about each reason.
      */
     @DataClass.Generated.Member
@@ -524,7 +524,7 @@
          * The reason for this operation when requested by the system (sysui),
          * otherwise AUTHENTICATE_REASON_UNKNOWN.
          *
-         * See frameworks/base/packages/SystemUI/src/com/android/keyguard/FaceAuthReason.kt
+         * See packages/SystemUI/src/com/android/systemui/deviceentry/shared/FaceAuthReason.kt
          * for more details about each reason.
          */
         @DataClass.Generated.Member
diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl
index 6626baf..7bea9ae 100644
--- a/core/java/android/hardware/input/IInputManager.aidl
+++ b/core/java/android/hardware/input/IInputManager.aidl
@@ -25,6 +25,7 @@
 import android.hardware.input.IInputDeviceBatteryState;
 import android.hardware.input.IKeyboardBacklightListener;
 import android.hardware.input.IKeyboardBacklightState;
+import android.hardware.input.IStickyModifierStateListener;
 import android.hardware.input.ITabletModeChangedListener;
 import android.hardware.input.TouchCalibration;
 import android.os.CombinedVibration;
@@ -241,4 +242,14 @@
     void unregisterKeyboardBacklightListener(IKeyboardBacklightListener listener);
 
     HostUsiVersion getHostUsiVersionFromDisplayConfig(int displayId);
+
+    @EnforcePermission("MONITOR_STICKY_MODIFIER_STATE")
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = "
+            + "android.Manifest.permission.MONITOR_STICKY_MODIFIER_STATE)")
+    void registerStickyModifierStateListener(IStickyModifierStateListener listener);
+
+    @EnforcePermission("MONITOR_STICKY_MODIFIER_STATE")
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = "
+            + "android.Manifest.permission.MONITOR_STICKY_MODIFIER_STATE)")
+    void unregisterStickyModifierStateListener(IStickyModifierStateListener listener);
 }
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/installstagedata/InstallStaging.java b/core/java/android/hardware/input/IStickyModifierStateListener.aidl
similarity index 62%
rename from packages/PackageInstaller/src/com/android/packageinstaller/v2/model/installstagedata/InstallStaging.java
rename to core/java/android/hardware/input/IStickyModifierStateListener.aidl
index a979cf8..bd139ab 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/installstagedata/InstallStaging.java
+++ b/core/java/android/hardware/input/IStickyModifierStateListener.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,14 +14,13 @@
  * limitations under the License.
  */
 
-package com.android.packageinstaller.v2.model.installstagedata;
+package android.hardware.input;
 
-public class InstallStaging extends InstallStage {
+/** @hide */
+oneway interface IStickyModifierStateListener {
 
-    private final int mStage = InstallStage.STAGE_STAGING;
-
-    @Override
-    public int getStageCode() {
-        return mStage;
-    }
+    /**
+     * Called when the sticky modifier state is changed when A11y Sticky keys feature is enabled
+     */
+    void onStickyModifierStateChanged(int modifierState, int lockedModifierState);
 }
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index f941ad8..4ebbde7 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -1297,6 +1297,42 @@
     }
 
     /**
+     * Registers a Sticky modifier state change listener to be notified about {@link
+     * StickyModifierState} changes.
+     *
+     * @param executor an executor on which the callback will be called
+     * @param listener the {@link StickyModifierStateListener}
+     * @throws IllegalArgumentException if {@code listener} has already been registered previously.
+     * @throws NullPointerException     if {@code listener} or {@code executor} is null.
+     * @hide
+     * @see #unregisterStickyModifierStateListener(StickyModifierStateListener)
+     */
+    @RequiresPermission(Manifest.permission.MONITOR_STICKY_MODIFIER_STATE)
+    public void registerStickyModifierStateListener(@NonNull Executor executor,
+            @NonNull StickyModifierStateListener listener) throws IllegalArgumentException {
+        if (!InputSettings.isAccessibilityStickyKeysFeatureEnabled()) {
+            return;
+        }
+        mGlobal.registerStickyModifierStateListener(executor, listener);
+    }
+
+    /**
+     * Unregisters a previously added Sticky modifier state change listener.
+     *
+     * @param listener the {@link StickyModifierStateListener}
+     * @hide
+     * @see #registerStickyModifierStateListener(Executor, StickyModifierStateListener)
+     */
+    @RequiresPermission(Manifest.permission.MONITOR_STICKY_MODIFIER_STATE)
+    public void unregisterStickyModifierStateListener(
+            @NonNull StickyModifierStateListener listener) {
+        if (!InputSettings.isAccessibilityStickyKeysFeatureEnabled()) {
+            return;
+        }
+        mGlobal.unregisterStickyModifierStateListener(listener);
+    }
+
+    /**
      * A callback used to be notified about battery state changes for an input device. The
      * {@link #onBatteryStateChanged(int, long, BatteryState)} method will be called once after the
      * listener is successfully registered to provide the initial battery state of the device.
@@ -1378,4 +1414,23 @@
         void onKeyboardBacklightChanged(
                 int deviceId, @NonNull KeyboardBacklightState state, boolean isTriggeredByKeyPress);
     }
+
+    /**
+     * A callback used to be notified about sticky modifier state changes when A11y Sticky keys
+     * feature is enabled.
+     *
+     * @see #registerStickyModifierStateListener(Executor, StickyModifierStateListener)
+     * @see #unregisterStickyModifierStateListener(StickyModifierStateListener)
+     * @hide
+     */
+    public interface StickyModifierStateListener {
+        /**
+         * Called when the sticky modifier state changes.
+         * This method will be called once after the listener is successfully registered to provide
+         * the initial modifier state.
+         *
+         * @param state the new sticky modifier state, never null.
+         */
+        void onStickyModifierStateChanged(@NonNull StickyModifierState state);
+    }
 }
diff --git a/core/java/android/hardware/input/InputManagerGlobal.java b/core/java/android/hardware/input/InputManagerGlobal.java
index 24a6911..7c104a0 100644
--- a/core/java/android/hardware/input/InputManagerGlobal.java
+++ b/core/java/android/hardware/input/InputManagerGlobal.java
@@ -27,6 +27,7 @@
 import android.hardware.input.InputManager.InputDeviceListener;
 import android.hardware.input.InputManager.KeyboardBacklightListener;
 import android.hardware.input.InputManager.OnTabletModeChangedListener;
+import android.hardware.input.InputManager.StickyModifierStateListener;
 import android.hardware.lights.Light;
 import android.hardware.lights.LightState;
 import android.hardware.lights.LightsManager;
@@ -52,6 +53,7 @@
 import android.view.InputEvent;
 import android.view.InputMonitor;
 import android.view.KeyCharacterMap;
+import android.view.KeyEvent;
 import android.view.PointerIcon;
 
 import com.android.internal.annotations.GuardedBy;
@@ -100,6 +102,14 @@
     @GuardedBy("mKeyboardBacklightListenerLock")
     @Nullable private IKeyboardBacklightListener mKeyboardBacklightListener;
 
+    private final Object mStickyModifierStateListenerLock = new Object();
+    @GuardedBy("mStickyModifierStateListenerLock")
+    @Nullable
+    private ArrayList<StickyModifierStateListenerDelegate> mStickyModifierStateListeners;
+    @GuardedBy("mStickyModifierStateListenerLock")
+    @Nullable
+    private IStickyModifierStateListener mStickyModifierStateListener;
+
     // InputDeviceSensorManager gets notified synchronously from the binder thread when input
     // devices change, so it must be synchronized with the input device listeners.
     @GuardedBy("mInputDeviceListeners")
@@ -905,6 +915,158 @@
         }
     }
 
+    private static final class StickyModifierStateListenerDelegate {
+        final InputManager.StickyModifierStateListener mListener;
+        final Executor mExecutor;
+
+        StickyModifierStateListenerDelegate(StickyModifierStateListener listener,
+                Executor executor) {
+            mListener = listener;
+            mExecutor = executor;
+        }
+
+        void notifyStickyModifierStateChange(int modifierState, int lockedModifierState) {
+            mExecutor.execute(() ->
+                    mListener.onStickyModifierStateChanged(
+                            new LocalStickyModifierState(modifierState, lockedModifierState)));
+        }
+    }
+
+    private class LocalStickyModifierStateListener extends IStickyModifierStateListener.Stub {
+
+        @Override
+        public void onStickyModifierStateChanged(int modifierState, int lockedModifierState) {
+            synchronized (mStickyModifierStateListenerLock) {
+                if (mStickyModifierStateListeners == null) return;
+                final int numListeners = mStickyModifierStateListeners.size();
+                for (int i = 0; i < numListeners; i++) {
+                    mStickyModifierStateListeners.get(i)
+                            .notifyStickyModifierStateChange(modifierState, lockedModifierState);
+                }
+            }
+        }
+    }
+
+    // Implementation of the android.hardware.input.StickyModifierState interface used to report
+    // the sticky modifier state via the StickyModifierStateListener interfaces.
+    private static final class LocalStickyModifierState extends StickyModifierState {
+
+        private final int mModifierState;
+        private final int mLockedModifierState;
+
+        LocalStickyModifierState(int modifierState, int lockedModifierState) {
+            mModifierState = modifierState;
+            mLockedModifierState = lockedModifierState;
+        }
+
+        @Override
+        public boolean isShiftModifierOn() {
+            return (mModifierState & KeyEvent.META_SHIFT_ON) != 0;
+        }
+
+        @Override
+        public boolean isShiftModifierLocked() {
+            return (mLockedModifierState & KeyEvent.META_SHIFT_ON) != 0;
+        }
+
+        @Override
+        public boolean isCtrlModifierOn() {
+            return (mModifierState & KeyEvent.META_CTRL_ON) != 0;
+        }
+
+        @Override
+        public boolean isCtrlModifierLocked() {
+            return (mLockedModifierState & KeyEvent.META_CTRL_ON) != 0;
+        }
+
+        @Override
+        public boolean isMetaModifierOn() {
+            return (mModifierState & KeyEvent.META_META_ON) != 0;
+        }
+
+        @Override
+        public boolean isMetaModifierLocked() {
+            return (mLockedModifierState & KeyEvent.META_META_ON) != 0;
+        }
+
+        @Override
+        public boolean isAltModifierOn() {
+            return (mModifierState & KeyEvent.META_ALT_LEFT_ON) != 0;
+        }
+
+        @Override
+        public boolean isAltModifierLocked() {
+            return (mLockedModifierState & KeyEvent.META_ALT_LEFT_ON) != 0;
+        }
+
+        @Override
+        public boolean isAltGrModifierOn() {
+            return (mModifierState & KeyEvent.META_ALT_RIGHT_ON) != 0;
+        }
+
+        @Override
+        public boolean isAltGrModifierLocked() {
+            return (mLockedModifierState & KeyEvent.META_ALT_RIGHT_ON) != 0;
+        }
+    }
+
+    /**
+     * @see InputManager#registerStickyModifierStateListener(Executor, StickyModifierStateListener)
+     */
+    @RequiresPermission(Manifest.permission.MONITOR_STICKY_MODIFIER_STATE)
+    void registerStickyModifierStateListener(@NonNull Executor executor,
+            @NonNull StickyModifierStateListener listener) throws IllegalArgumentException {
+        Objects.requireNonNull(executor, "executor should not be null");
+        Objects.requireNonNull(listener, "listener should not be null");
+
+        synchronized (mStickyModifierStateListenerLock) {
+            if (mStickyModifierStateListener == null) {
+                mStickyModifierStateListeners = new ArrayList<>();
+                mStickyModifierStateListener = new LocalStickyModifierStateListener();
+
+                try {
+                    mIm.registerStickyModifierStateListener(mStickyModifierStateListener);
+                } catch (RemoteException e) {
+                    throw e.rethrowFromSystemServer();
+                }
+            }
+            final int numListeners = mStickyModifierStateListeners.size();
+            for (int i = 0; i < numListeners; i++) {
+                if (mStickyModifierStateListeners.get(i).mListener == listener) {
+                    throw new IllegalArgumentException("Listener has already been registered!");
+                }
+            }
+            StickyModifierStateListenerDelegate delegate =
+                    new StickyModifierStateListenerDelegate(listener, executor);
+            mStickyModifierStateListeners.add(delegate);
+        }
+    }
+
+    /**
+     * @see InputManager#unregisterStickyModifierStateListener(StickyModifierStateListener)
+     */
+    @RequiresPermission(Manifest.permission.MONITOR_STICKY_MODIFIER_STATE)
+    void unregisterStickyModifierStateListener(
+            @NonNull StickyModifierStateListener listener) {
+        Objects.requireNonNull(listener, "listener should not be null");
+
+        synchronized (mStickyModifierStateListenerLock) {
+            if (mStickyModifierStateListeners == null) {
+                return;
+            }
+            mStickyModifierStateListeners.removeIf((delegate) -> delegate.mListener == listener);
+            if (mStickyModifierStateListeners.isEmpty()) {
+                try {
+                    mIm.unregisterStickyModifierStateListener(mStickyModifierStateListener);
+                } catch (RemoteException e) {
+                    throw e.rethrowFromSystemServer();
+                }
+                mStickyModifierStateListeners = null;
+                mStickyModifierStateListener = null;
+            }
+        }
+    }
+
     /**
      * @see InputManager#getKeyboardLayoutsForInputDevice(InputDeviceIdentifier)
      */
diff --git a/core/java/android/hardware/input/InputSettings.java b/core/java/android/hardware/input/InputSettings.java
index cb9c333..d939532 100644
--- a/core/java/android/hardware/input/InputSettings.java
+++ b/core/java/android/hardware/input/InputSettings.java
@@ -18,6 +18,7 @@
 
 import static com.android.hardware.input.Flags.keyboardA11yBounceKeysFlag;
 import static com.android.hardware.input.Flags.keyboardA11yStickyKeysFlag;
+import static com.android.input.flags.Flags.enableInputFilterRustImpl;
 
 import android.Manifest;
 import android.annotation.FloatRange;
@@ -338,6 +339,21 @@
     }
 
     /**
+     * Whether Accessibility bounce keys feature is enabled.
+     *
+     * <p>
+     * Bounce keys’ is an accessibility feature to aid users who have physical disabilities,
+     * that allows the user to configure the device to ignore rapid, repeated keypresses of the
+     * same key.
+     * </p>
+     *
+     * @hide
+     */
+    public static boolean isAccessibilityBounceKeysFeatureEnabled() {
+        return keyboardA11yBounceKeysFlag() && enableInputFilterRustImpl();
+    }
+
+    /**
      * Whether Accessibility bounce keys is enabled.
      *
      * <p>
@@ -364,10 +380,10 @@
      * @hide
      */
     public static int getAccessibilityBounceKeysThreshold(@NonNull Context context) {
-        if (!keyboardA11yBounceKeysFlag()) {
+        if (!isAccessibilityBounceKeysFeatureEnabled()) {
             return 0;
         }
-        return Settings.System.getIntForUser(context.getContentResolver(),
+        return Settings.Secure.getIntForUser(context.getContentResolver(),
                 Settings.Secure.ACCESSIBILITY_BOUNCE_KEYS, 0, UserHandle.USER_CURRENT);
     }
 
@@ -388,7 +404,7 @@
     @RequiresPermission(Manifest.permission.WRITE_SETTINGS)
     public static void setAccessibilityBounceKeysThreshold(@NonNull Context context,
             int thresholdTimeMillis) {
-        if (!keyboardA11yBounceKeysFlag()) {
+        if (!isAccessibilityBounceKeysFeatureEnabled()) {
             return;
         }
         if (thresholdTimeMillis < 0
@@ -397,12 +413,29 @@
                     "Provided Bounce keys threshold should be in range [0, "
                             + MAX_ACCESSIBILITY_BOUNCE_KEYS_THRESHOLD_MILLIS + "]");
         }
-        Settings.System.putIntForUser(context.getContentResolver(),
+        Settings.Secure.putIntForUser(context.getContentResolver(),
                 Settings.Secure.ACCESSIBILITY_BOUNCE_KEYS, thresholdTimeMillis,
                 UserHandle.USER_CURRENT);
     }
 
     /**
+     * Whether Accessibility sticky keys feature is enabled.
+     *
+     * <p>
+     * 'Sticky keys' is an accessibility feature that assists users who have physical
+     * disabilities or help users reduce repetitive strain injury. It serializes keystrokes
+     * instead of pressing multiple keys at a time, allowing the user to press and release a
+     * modifier key, such as Shift, Ctrl, Alt, or any other modifier key, and have it remain
+     * active until any other key is pressed.
+     * </p>
+     *
+     * @hide
+     */
+    public static boolean isAccessibilityStickyKeysFeatureEnabled() {
+        return keyboardA11yStickyKeysFlag() && enableInputFilterRustImpl();
+    }
+
+    /**
      * Whether Accessibility sticky keys is enabled.
      *
      * <p>
@@ -416,10 +449,10 @@
      * @hide
      */
     public static boolean isAccessibilityStickyKeysEnabled(@NonNull Context context) {
-        if (!keyboardA11yStickyKeysFlag()) {
+        if (!isAccessibilityStickyKeysFeatureEnabled()) {
             return false;
         }
-        return Settings.System.getIntForUser(context.getContentResolver(),
+        return Settings.Secure.getIntForUser(context.getContentResolver(),
                 Settings.Secure.ACCESSIBILITY_STICKY_KEYS, 0, UserHandle.USER_CURRENT) != 0;
     }
 
@@ -439,10 +472,10 @@
     @RequiresPermission(Manifest.permission.WRITE_SETTINGS)
     public static void setAccessibilityStickyKeysEnabled(@NonNull Context context,
             boolean enabled) {
-        if (!keyboardA11yStickyKeysFlag()) {
+        if (!isAccessibilityStickyKeysFeatureEnabled()) {
             return;
         }
-        Settings.System.putIntForUser(context.getContentResolver(),
+        Settings.Secure.putIntForUser(context.getContentResolver(),
                 Settings.Secure.ACCESSIBILITY_STICKY_KEYS, enabled ? 1 : 0,
                 UserHandle.USER_CURRENT);
     }
diff --git a/core/java/android/hardware/input/StickyModifierState.java b/core/java/android/hardware/input/StickyModifierState.java
new file mode 100644
index 0000000..a3f7a0a
--- /dev/null
+++ b/core/java/android/hardware/input/StickyModifierState.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.input;
+
+/**
+ * The StickyModifierState class is a representation of a modifier state when A11y Sticky keys
+ * feature is enabled
+ *
+ * @hide
+ */
+public abstract class StickyModifierState {
+
+    /**
+     * Represents whether current sticky modifier state includes 'Shift' modifier.
+     * <p> If {@code true} the next {@link android.view.KeyEvent} will contain 'Shift' modifier in
+     * its metaState.
+     *
+     * @return whether Shift modifier key is on.
+     */
+    public abstract boolean isShiftModifierOn();
+
+    /**
+     * Represents whether current sticky modifier state includes 'Shift' modifier, and it is
+     * locked.
+     * <p> If {@code true} any subsequent {@link android.view.KeyEvent} will contain 'Shift'
+     * modifier in its metaState and this state will remain sticky (will not be cleared), until
+     * user presses 'Shift' key again to clear the locked state.
+     *
+     * @return whether Shift modifier key is locked.
+     */
+    public abstract boolean isShiftModifierLocked();
+
+    /**
+     * Represents whether current sticky modifier state includes 'Ctrl' modifier.
+     * <p> If {@code true} the next {@link android.view.KeyEvent} will contain 'Ctrl' modifier in
+     * its metaState.
+     *
+     * @return whether Ctrl modifier key is on.
+     */
+    public abstract boolean isCtrlModifierOn();
+
+    /**
+     * Represents whether current sticky modifier state includes 'Ctrl' modifier, and it is
+     * locked.
+     * <p> If {@code true} any subsequent {@link android.view.KeyEvent} will contain 'Ctrl'
+     * modifier in its metaState and this state will remain sticky (will not be cleared), until
+     * user presses 'Ctrl' key again to clear the locked state.
+     *
+     * @return whether Ctrl modifier key is locked.
+     */
+    public abstract boolean isCtrlModifierLocked();
+
+    /**
+     * Represents whether current sticky modifier state includes 'Meta' modifier.
+     * <p> If {@code true} the next {@link android.view.KeyEvent} will contain 'Meta' modifier in
+     * its metaState.
+     *
+     * @return whether Meta modifier key is on.
+     */
+    public abstract boolean isMetaModifierOn();
+
+    /**
+     * Represents whether current sticky modifier state includes 'Meta' modifier, and it is
+     * locked.
+     * <p> If {@code true} any subsequent {@link android.view.KeyEvent} will contain 'Meta'
+     * modifier in its metaState and this state will remain sticky (will not be cleared), until
+     * user presses 'Meta' key again to clear the locked state.
+     *
+     * @return whether Meta modifier key is locked.
+     */
+    public abstract boolean isMetaModifierLocked();
+
+    /**
+     * Represents whether current sticky modifier state includes 'Alt' modifier.
+     * <p> If {@code true} the next {@link android.view.KeyEvent} will contain 'Alt' modifier in
+     * its metaState.
+     *
+     * @return whether Alt modifier key is on.
+     */
+    public abstract boolean isAltModifierOn();
+
+    /**
+     * Represents whether current sticky modifier state includes 'Alt' modifier, and it is
+     * locked.
+     * <p> If {@code true} any subsequent {@link android.view.KeyEvent} will contain 'Alt'
+     * modifier in its metaState and this state will remain sticky (will not be cleared), until
+     * user presses 'Alt' key again to clear the locked state.
+     *
+     * @return whether Alt modifier key is locked.
+     */
+    public abstract boolean isAltModifierLocked();
+
+    /**
+     * Represents whether current sticky modifier state includes 'AltGr' modifier.
+     * <p> If {@code true} the next {@link android.view.KeyEvent} will contain 'AltGr' modifier in
+     * its metaState.
+     *
+     * @return whether AltGr modifier key is on.
+     */
+    public abstract boolean isAltGrModifierOn();
+
+    /**
+     * Represents whether current sticky modifier state includes 'AltGr' modifier, and it is
+     * locked.
+     * <p> If {@code true} any subsequent {@link android.view.KeyEvent} will contain 'AltGr'
+     * modifier in its metaState and this state will remain sticky (will not be cleared), until
+     * user presses 'AltGr' key again to clear the locked state.
+     *
+     * @return whether AltGr modifier key is locked.
+     */
+    public abstract boolean isAltGrModifierLocked();
+}
+
diff --git a/core/java/android/hardware/radio/ProgramList.java b/core/java/android/hardware/radio/ProgramList.java
index c5167db..a3a2a2e 100644
--- a/core/java/android/hardware/radio/ProgramList.java
+++ b/core/java/android/hardware/radio/ProgramList.java
@@ -304,11 +304,7 @@
      *
      * @param id primary identifier of a program to fetch
      * @return the program info, or null if there is no such program on the list
-     *
-     * @deprecated Use {@link #getProgramInfos(ProgramSelector.Identifier)} to get all programs
-     * with the given primary identifier
      */
-    @Deprecated
     public @Nullable RadioManager.ProgramInfo get(@NonNull ProgramSelector.Identifier id) {
         Map<UniqueProgramIdentifier, RadioManager.ProgramInfo> entries;
         synchronized (mLock) {
diff --git a/core/java/android/hardware/radio/ProgramSelector.java b/core/java/android/hardware/radio/ProgramSelector.java
index 7e5c141..4c95e02 100644
--- a/core/java/android/hardware/radio/ProgramSelector.java
+++ b/core/java/android/hardware/radio/ProgramSelector.java
@@ -312,20 +312,14 @@
     public static final int IDENTIFIER_TYPE_DRMO_FREQUENCY = 10;
     /**
      * 1: AM, 2:FM
-     * @deprecated use {@link #IDENTIFIER_TYPE_DRMO_FREQUENCY} instead
      */
-    @Deprecated
     public static final int IDENTIFIER_TYPE_DRMO_MODULATION = 11;
     /**
      * 32bit primary identifier for SiriusXM Satellite Radio.
-     *
-     * @deprecated SiriusXM Satellite Radio is not supported
      */
     public static final int IDENTIFIER_TYPE_SXM_SERVICE_ID = 12;
     /**
      * 0-999 range
-     *
-     * @deprecated SiriusXM Satellite Radio is not supported
      */
     public static final int IDENTIFIER_TYPE_SXM_CHANNEL = 13;
     /**
diff --git a/core/java/android/hardware/radio/RadioManager.java b/core/java/android/hardware/radio/RadioManager.java
index f0f7e8a..41f21ef 100644
--- a/core/java/android/hardware/radio/RadioManager.java
+++ b/core/java/android/hardware/radio/RadioManager.java
@@ -166,12 +166,7 @@
      * analog handover state managed from the HAL implementation side.
      *
      * <p>Some radio technologies may not support this, i.e. DAB.
-     *
-     * @deprecated Use {@link #CONFIG_FORCE_ANALOG_FM} instead. If {@link #CONFIG_FORCE_ANALOG_FM}
-     * is supported in HAL, {@link RadioTuner#setConfigFlag} and {@link RadioTuner#isConfigFlagSet}
-     * with CONFIG_FORCE_ANALOG will set/get the value of {@link #CONFIG_FORCE_ANALOG_FM}.
      */
-    @Deprecated
     public static final int CONFIG_FORCE_ANALOG = 2;
     /**
      * Forces the digital playback for the supporting radio technology.
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 18d3e5e..71698e4 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -127,6 +127,7 @@
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.ExtractedText;
 import android.view.inputmethod.ExtractedTextRequest;
+import android.view.inputmethod.Flags;
 import android.view.inputmethod.ImeTracker;
 import android.view.inputmethod.InlineSuggestionsRequest;
 import android.view.inputmethod.InlineSuggestionsResponse;
@@ -388,6 +389,9 @@
     private long mStylusHwSessionsTimeout = STYLUS_HANDWRITING_IDLE_TIMEOUT_MS;
     private Runnable mStylusWindowIdleTimeoutRunnable;
     private long mStylusWindowIdleTimeoutForTest;
+    /** Tracks last {@link MotionEvent#getToolType(int)} used for {@link MotionEvent#ACTION_DOWN}.
+     **/
+    private int mLastUsedToolType;
 
     /**
      * Returns whether {@link InputMethodService} is responsible for rendering the back button and
@@ -1005,7 +1009,7 @@
          */
         @Override
         public void updateEditorToolType(@ToolType int toolType) {
-            onUpdateEditorToolType(toolType);
+            updateEditorToolTypeInternal(toolType);
         }
 
         /**
@@ -1249,6 +1253,14 @@
         rootView.setSystemGestureExclusionRects(exclusionRects);
     }
 
+    private void updateEditorToolTypeInternal(int toolType) {
+        if (Flags.useHandwritingListenerForTooltype()) {
+            mLastUsedToolType = toolType;
+            mInputEditorInfo.setInitialToolType(toolType);
+        }
+        onUpdateEditorToolType(toolType);
+    }
+
     /**
      * Concrete implementation of
      * {@link AbstractInputMethodService.AbstractInputMethodSessionImpl} that provides
@@ -3110,6 +3122,9 @@
                 null /* icProto */);
         mInputStarted = true;
         mStartedInputConnection = ic;
+        if (Flags.useHandwritingListenerForTooltype()) {
+            editorInfo.setInitialToolType(mLastUsedToolType);
+        }
         mInputEditorInfo = editorInfo;
         initialize();
         mInlineSuggestionSessionController.notifyOnStartInput(
@@ -3354,6 +3369,10 @@
      *         had not seen the event at all.
      */
     public boolean onKeyDown(int keyCode, KeyEvent event) {
+        if (Flags.useHandwritingListenerForTooltype()) {
+            // any KeyEvent keyDown should reset last toolType.
+            updateEditorToolTypeInternal(MotionEvent.TOOL_TYPE_UNKNOWN);
+        }
         if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
             final ExtractEditText eet = getExtractEditTextIfVisible();
             if (eet != null && eet.handleBackInTextActionModeIfNeeded(event)) {
diff --git a/core/java/android/net/LocalSocket.java b/core/java/android/net/LocalSocket.java
index b69410c..a86396c 100644
--- a/core/java/android/net/LocalSocket.java
+++ b/core/java/android/net/LocalSocket.java
@@ -196,7 +196,8 @@
     }
 
     /**
-     * Retrieves the input stream for this instance.
+     * Retrieves the input stream for this instance. Closing this stream is equivalent to closing
+     * the entire socket and its associated streams using {@link #close()}.
      *
      * @return input stream
      * @throws IOException if socket has been closed or cannot be created.
@@ -207,7 +208,8 @@
     }
 
     /**
-     * Retrieves the output stream for this instance.
+     * Retrieves the output stream for this instance. Closing this stream is equivalent to closing
+     * the entire socket and its associated streams using {@link #close()}.
      *
      * @return output stream
      * @throws IOException if socket has been closed or cannot be created.
diff --git a/core/java/android/net/Uri.java b/core/java/android/net/Uri.java
index 70de477..05a3e18 100644
--- a/core/java/android/net/Uri.java
+++ b/core/java/android/net/Uri.java
@@ -21,6 +21,7 @@
 import android.annotation.SystemApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Intent;
+import android.content.pm.Flags;
 import android.os.Environment;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -1971,6 +1972,42 @@
     }
 
     /**
+     * Encodes a value it wasn't already encoded.
+     *
+     * @param value string to encode
+     * @param allow characters to allow
+     * @return encoded value
+     * @hide
+     */
+    public static String encodeIfNotEncoded(@Nullable String value, @Nullable String allow) {
+        if (value == null) return null;
+        if (!Flags.encodeAppIntent() || isEncoded(value, allow)) return value;
+        return encode(value, allow);
+    }
+
+    /**
+     * Returns true if the given string is already encoded to safe characters.
+     *
+     * @param value string to check
+     * @param allow characters to allow
+     * @return true if the string is already encoded or false if it should be encoded
+     */
+    private static boolean isEncoded(@Nullable String value, @Nullable String allow) {
+        if (value == null) return true;
+        for (int index = 0; index < value.length(); index++) {
+            char c = value.charAt(index);
+
+            // Allow % because that's the prefix for an encoded character. This method will fail
+            // for decoded strings whose onlyinvalid character is %, but it's assumed that % alone
+            // cannot cause malicious behavior in the framework.
+            if (!isAllowed(c, allow) && c != '%') {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
      * Decodes '%'-escaped octets in the given string using the UTF-8 scheme.
      * Replaces invalid octets with the unicode replacement character
      * ("\\uFFFD").
@@ -1988,6 +2025,18 @@
     }
 
     /**
+     * Decodes a string if it was encoded, indicated by containing a %.
+     * @param value encoded string to decode
+     * @return decoded value
+     * @hide
+     */
+    public static String decodeIfNeeded(@Nullable String value) {
+        if (value == null) return null;
+        if (Flags.encodeAppIntent() && value.contains("%")) return decode(value);
+        return value;
+    }
+
+    /**
      * Support for part implementations.
      */
     static abstract class AbstractPart {
diff --git a/core/java/android/net/vcn/VcnManager.java b/core/java/android/net/vcn/VcnManager.java
index 70cf973..561db9c 100644
--- a/core/java/android/net/vcn/VcnManager.java
+++ b/core/java/android/net/vcn/VcnManager.java
@@ -102,6 +102,24 @@
     public static final String VCN_NETWORK_SELECTION_WIFI_EXIT_RSSI_THRESHOLD_KEY =
             "vcn_network_selection_wifi_exit_rssi_threshold";
 
+    /**
+     * Key for the interval to poll IpSecTransformState for packet loss monitoring
+     *
+     * @hide
+     */
+    @NonNull
+    public static final String VCN_NETWORK_SELECTION_POLL_IPSEC_STATE_INTERVAL_SECONDS_KEY =
+            "vcn_network_selection_poll_ipsec_state_interval_seconds";
+
+    /**
+     * Key for the threshold of IPSec packet loss rate
+     *
+     * @hide
+     */
+    @NonNull
+    public static final String VCN_NETWORK_SELECTION_IPSEC_PACKET_LOSS_PERCENT_THRESHOLD_KEY =
+            "vcn_network_selection_ipsec_packet_loss_percent_threshold";
+
     // TODO: Add separate signal strength thresholds for 2.4 GHz and 5GHz
 
     /**
@@ -115,6 +133,20 @@
             "vcn_restricted_transports";
 
     /**
+     * Key for number of seconds to wait before entering safe mode
+     *
+     * <p>A VcnGatewayConnection will enter safe mode when it takes over the configured timeout to
+     * enter {@link ConnectedState}.
+     *
+     * <p>Defaults to 30, unless overridden by carrier config
+     *
+     * @hide
+     */
+    @NonNull
+    public static final String VCN_SAFE_MODE_TIMEOUT_SECONDS_KEY =
+            "vcn_safe_mode_timeout_seconds_key";
+
+    /**
      * Key for maximum number of parallel SAs for tunnel aggregation
      *
      * <p>If set to a value > 1, multiple tunnels will be set up, and inbound traffic will be
@@ -134,7 +166,10 @@
             new String[] {
                 VCN_NETWORK_SELECTION_WIFI_ENTRY_RSSI_THRESHOLD_KEY,
                 VCN_NETWORK_SELECTION_WIFI_EXIT_RSSI_THRESHOLD_KEY,
+                VCN_NETWORK_SELECTION_POLL_IPSEC_STATE_INTERVAL_SECONDS_KEY,
+                VCN_NETWORK_SELECTION_IPSEC_PACKET_LOSS_PERCENT_THRESHOLD_KEY,
                 VCN_RESTRICTED_TRANSPORTS_INT_ARRAY_KEY,
+                VCN_SAFE_MODE_TIMEOUT_SECONDS_KEY,
                 VCN_TUNNEL_AGGREGATION_SA_COUNT_MAX_KEY,
             };
 
diff --git a/core/java/android/net/vcn/flags.aconfig b/core/java/android/net/vcn/flags.aconfig
index 6956916..7afd721 100644
--- a/core/java/android/net/vcn/flags.aconfig
+++ b/core/java/android/net/vcn/flags.aconfig
@@ -5,4 +5,18 @@
     namespace: "vcn"
     description: "Feature flag for safe mode configurability"
     bug: "276358140"
+}
+
+flag {
+    name: "safe_mode_timeout_config"
+    namespace: "vcn"
+    description: "Feature flag for adjustable safe mode timeout"
+    bug: "317406085"
+}
+
+flag{
+    name: "network_metric_monitor"
+    namespace: "vcn"
+    description: "Feature flag for enabling network metric monitor"
+    bug: "282996138"
 }
\ No newline at end of file
diff --git a/core/java/android/nfc/INfcAdapter.aidl b/core/java/android/nfc/INfcAdapter.aidl
index f6beec1..286cf28 100644
--- a/core/java/android/nfc/INfcAdapter.aidl
+++ b/core/java/android/nfc/INfcAdapter.aidl
@@ -30,8 +30,10 @@
 import android.nfc.INfcUnlockHandler;
 import android.nfc.ITagRemovedCallback;
 import android.nfc.INfcDta;
+import android.nfc.INfcWlcStateListener;
 import android.nfc.NfcAntennaInfo;
 import android.os.Bundle;
+import android.nfc.WlcLDeviceInfo;
 
 /**
  * @hide
@@ -86,4 +88,13 @@
     boolean enableReaderOption(boolean enable);
     boolean isObserveModeSupported();
     boolean setObserveMode(boolean enabled);
+
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)")
+    boolean enableWlc(boolean enable);
+    boolean isWlcEnabled();
+    void registerWlcStateListener(in INfcWlcStateListener listener);
+    void unregisterWlcStateListener(in INfcWlcStateListener listener);
+    WlcLDeviceInfo getWlcLDeviceInfo();
+
+    void updateDiscoveryTechnology(IBinder b, int pollFlags, int listenFlags);
 }
diff --git a/core/java/android/nfc/INfcCardEmulation.aidl b/core/java/android/nfc/INfcCardEmulation.aidl
index 191385a..f4b4604 100644
--- a/core/java/android/nfc/INfcCardEmulation.aidl
+++ b/core/java/android/nfc/INfcCardEmulation.aidl
@@ -43,4 +43,7 @@
     ApduServiceInfo getPreferredPaymentService(int userHandle);
     boolean setServiceEnabledForCategoryOther(int userHandle, in ComponentName app, boolean status);
     boolean isDefaultPaymentRegistered();
+
+    boolean overrideRoutingTable(int userHandle, String protocol, String technology);
+    boolean recoverRoutingTable(int userHandle);
 }
diff --git a/telephony/java/android/telephony/satellite/ISatelliteStateCallback.aidl b/core/java/android/nfc/INfcWlcStateListener.aidl
similarity index 65%
copy from telephony/java/android/telephony/satellite/ISatelliteStateCallback.aidl
copy to core/java/android/nfc/INfcWlcStateListener.aidl
index cd9d81e..c2b7075 100644
--- a/telephony/java/android/telephony/satellite/ISatelliteStateCallback.aidl
+++ b/core/java/android/nfc/INfcWlcStateListener.aidl
@@ -14,17 +14,17 @@
  * limitations under the License.
  */
 
-package android.telephony.satellite;
+package android.nfc;
 
+import android.nfc.WlcLDeviceInfo;
 /**
- * Interface for satellite state change callback.
  * @hide
  */
-oneway interface ISatelliteStateCallback {
-    /**
-     * Indicates that the satellite modem state has changed.
-     *
-     * @param state The current satellite modem state.
-     */
-    void onSatelliteModemStateChanged(in int state);
+oneway interface INfcWlcStateListener {
+  /**
+   * Called whenever NFC WLC state changes
+   *
+   * @param wlcLDeviceInfo NFC wlc listener information
+   */
+  void onWlcStateChanged(in WlcLDeviceInfo wlcLDeviceInfo);
 }
diff --git a/core/java/android/nfc/NfcActivityManager.java b/core/java/android/nfc/NfcActivityManager.java
index 8d75cac..f03fc0a 100644
--- a/core/java/android/nfc/NfcActivityManager.java
+++ b/core/java/android/nfc/NfcActivityManager.java
@@ -112,6 +112,9 @@
         Bundle readerModeExtras = null;
         Binder token;
 
+        int mPollTech = NfcAdapter.FLAG_USE_ALL_TECH;
+        int mListenTech = NfcAdapter.FLAG_USE_ALL_TECH;
+
         public NfcActivityState(Activity activity) {
             if (activity.isDestroyed()) {
                 throw new IllegalStateException("activity is already destroyed");
@@ -132,6 +135,9 @@
             readerModeFlags = 0;
             readerModeExtras = null;
             token = null;
+
+            mPollTech = NfcAdapter.FLAG_USE_ALL_TECH;
+            mListenTech = NfcAdapter.FLAG_USE_ALL_TECH;
         }
         @Override
         public String toString() {
@@ -278,6 +284,9 @@
         int readerModeFlags = 0;
         Bundle readerModeExtras = null;
         Binder token;
+        int pollTech;
+        int listenTech;
+
         synchronized (NfcActivityManager.this) {
             NfcActivityState state = findActivityState(activity);
             if (DBG) Log.d(TAG, "onResume() for " + activity + " " + state);
@@ -286,9 +295,15 @@
             token = state.token;
             readerModeFlags = state.readerModeFlags;
             readerModeExtras = state.readerModeExtras;
+
+            pollTech = state.mPollTech;
+            listenTech = state.mListenTech;
         }
         if (readerModeFlags != 0) {
             setReaderMode(token, readerModeFlags, readerModeExtras);
+        } else if (listenTech != NfcAdapter.FLAG_USE_ALL_TECH
+                || pollTech != NfcAdapter.FLAG_USE_ALL_TECH) {
+            changeDiscoveryTech(token, pollTech, listenTech);
         }
         requestNfcServiceCallback();
     }
@@ -298,6 +313,9 @@
     public void onActivityPaused(Activity activity) {
         boolean readerModeFlagsSet;
         Binder token;
+        int pollTech;
+        int listenTech;
+
         synchronized (NfcActivityManager.this) {
             NfcActivityState state = findActivityState(activity);
             if (DBG) Log.d(TAG, "onPause() for " + activity + " " + state);
@@ -305,10 +323,17 @@
             state.resumed = false;
             token = state.token;
             readerModeFlagsSet = state.readerModeFlags != 0;
+
+            pollTech = state.mPollTech;
+            listenTech = state.mListenTech;
         }
         if (readerModeFlagsSet) {
             // Restore default p2p modes
             setReaderMode(token, 0, null);
+        } else if (listenTech != NfcAdapter.FLAG_USE_ALL_TECH
+                || pollTech != NfcAdapter.FLAG_USE_ALL_TECH) {
+            changeDiscoveryTech(token,
+                    NfcAdapter.FLAG_USE_ALL_TECH, NfcAdapter.FLAG_USE_ALL_TECH);
         }
     }
 
@@ -333,4 +358,53 @@
         }
     }
 
+    /** setDiscoveryTechnology() implementation */
+    public void setDiscoveryTech(Activity activity, int pollTech, int listenTech) {
+        boolean isResumed;
+        Binder token;
+        boolean readerModeFlagsSet;
+        synchronized (NfcActivityManager.this) {
+            NfcActivityState state = getActivityState(activity);
+            readerModeFlagsSet = state.readerModeFlags != 0;
+            state.mListenTech = listenTech;
+            state.mPollTech = pollTech;
+            token = state.token;
+            isResumed = state.resumed;
+        }
+        if (!readerModeFlagsSet && isResumed) {
+            changeDiscoveryTech(token, pollTech, listenTech);
+        } else if (readerModeFlagsSet) {
+            throw new IllegalStateException("Cannot be used when the Reader Mode is enabled");
+        }
+    }
+
+    /** resetDiscoveryTechnology() implementation */
+    public void resetDiscoveryTech(Activity activity) {
+        boolean isResumed;
+        Binder token;
+        boolean readerModeFlagsSet;
+        synchronized (NfcActivityManager.this) {
+            NfcActivityState state = getActivityState(activity);
+            readerModeFlagsSet = state.readerModeFlags != 0;
+            state.mListenTech = NfcAdapter.FLAG_USE_ALL_TECH;
+            state.mPollTech = NfcAdapter.FLAG_USE_ALL_TECH;
+            token = state.token;
+            isResumed = state.resumed;
+        }
+        if (readerModeFlagsSet) {
+            disableReaderMode(activity);
+        } else if (isResumed) {
+            changeDiscoveryTech(token, NfcAdapter.FLAG_USE_ALL_TECH, NfcAdapter.FLAG_USE_ALL_TECH);
+        }
+
+    }
+
+    private void changeDiscoveryTech(Binder token, int pollTech, int listenTech) {
+        try {
+            NfcAdapter.sService.updateDiscoveryTechnology(token, pollTech, listenTech);
+        } catch (RemoteException e) {
+            mAdapter.attemptDeadServiceRecovery(e);
+        }
+    }
+
 }
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index f407fb7..75f5491 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -75,6 +75,7 @@
     static final String TAG = "NFC";
 
     private final NfcControllerAlwaysOnListener mControllerAlwaysOnListener;
+    private final NfcWlcStateListener mNfcWlcStateListener;
 
     /**
      * Intent to start an activity when a tag with NDEF payload is discovered.
@@ -332,6 +333,19 @@
      */
     public static final int FLAG_READER_NFC_BARCODE = 0x10;
 
+    /** @hide */
+    @IntDef(flag = true, prefix = {"FLAG_READER_"}, value = {
+        FLAG_READER_KEEP,
+        FLAG_READER_DISABLE,
+        FLAG_READER_NFC_A,
+        FLAG_READER_NFC_B,
+        FLAG_READER_NFC_F,
+        FLAG_READER_NFC_V,
+        FLAG_READER_NFC_BARCODE
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface PollTechnology {}
+
     /**
      * Flag for use with {@link #enableReaderMode(Activity, ReaderCallback, int, Bundle)}.
      * <p>
@@ -359,6 +373,76 @@
     public static final String EXTRA_READER_PRESENCE_CHECK_DELAY = "presence";
 
     /**
+     * Flag for use with {@link #setDiscoveryTechnology(Activity, int, int)}.
+     * <p>
+     * Setting this flag enables listening for Nfc-A technology.
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_SET_DISCOVERY_TECH)
+    public static final int FLAG_LISTEN_NFC_PASSIVE_A = 0x1;
+
+    /**
+     * Flag for use with {@link #setDiscoveryTechnology(Activity, int, int)}.
+     * <p>
+     * Setting this flag enables listening for Nfc-B technology.
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_SET_DISCOVERY_TECH)
+    public static final int FLAG_LISTEN_NFC_PASSIVE_B = 1 << 1;
+
+    /**
+     * Flag for use with {@link #setDiscoveryTechnology(Activity, int, int)}.
+     * <p>
+     * Setting this flag enables listening for Nfc-F technology.
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_SET_DISCOVERY_TECH)
+    public static final int FLAG_LISTEN_NFC_PASSIVE_F = 1 << 2;
+
+    /**
+     * Flags for use with {@link #setDiscoveryTechnology(Activity, int, int)}.
+     * <p>
+     * Setting this flag disables listening.
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_SET_DISCOVERY_TECH)
+    public static final int FLAG_LISTEN_DISABLE = 0x0;
+
+    /**
+     * Flags for use with {@link #setDiscoveryTechnology(Activity, int, int)}.
+     * <p>
+     * Setting this flag disables polling.
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_SET_DISCOVERY_TECH)
+    public static final int FLAG_READER_DISABLE = 0x0;
+
+    /**
+     * Flags for use with {@link #setDiscoveryTechnology(Activity, int, int)}.
+     * <p>
+     * Setting this flag makes listening to use current flags.
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_SET_DISCOVERY_TECH)
+    public static final int FLAG_LISTEN_KEEP = -1;
+
+    /**
+     * Flags for use with {@link #setDiscoveryTechnology(Activity, int, int)}.
+     * <p>
+     * Setting this flag makes polling to use current flags.
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_SET_DISCOVERY_TECH)
+    public static final int FLAG_READER_KEEP = -1;
+
+    /** @hide */
+    public static final int FLAG_USE_ALL_TECH = 0xff;
+
+    /** @hide */
+    @IntDef(flag = true, prefix = {"FLAG_LISTEN_"}, value = {
+        FLAG_LISTEN_KEEP,
+        FLAG_LISTEN_DISABLE,
+        FLAG_LISTEN_NFC_PASSIVE_A,
+        FLAG_LISTEN_NFC_PASSIVE_B,
+        FLAG_LISTEN_NFC_PASSIVE_F
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ListenTechnology {}
+
+    /**
      * @hide
      * @removed
      */
@@ -436,10 +520,13 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface TagIntentAppPreferenceResult {}
 
-    // Guarded by NfcAdapter.class
+    // Guarded by sLock
     static boolean sIsInitialized = false;
     static boolean sHasNfcFeature;
     static boolean sHasCeFeature;
+    static boolean sHasNfcWlcFeature;
+
+    static Object sLock = new Object();
 
     // Final after first constructor, except for
     // attemptDeadServiceRecovery() when NFC crashes - we accept a best effort
@@ -650,8 +737,9 @@
                     || pm.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION_NFCF)
                     || pm.hasSystemFeature(PackageManager.FEATURE_NFC_OFF_HOST_CARD_EMULATION_UICC)
                     || pm.hasSystemFeature(PackageManager.FEATURE_NFC_OFF_HOST_CARD_EMULATION_ESE);
+            sHasNfcWlcFeature = pm.hasSystemFeature(PackageManager.FEATURE_NFC_CHARGING);
             /* is this device meant to have NFC */
-            if (!sHasNfcFeature && !sHasCeFeature) {
+            if (!sHasNfcFeature && !sHasCeFeature && !sHasNfcWlcFeature) {
                 Log.v(TAG, "this device does not have NFC support");
                 throw new UnsupportedOperationException();
             }
@@ -724,8 +812,8 @@
         if (context == null) {
             throw new IllegalArgumentException("context cannot be null");
         }
-        context = context.getApplicationContext();
-        if (context == null) {
+        Context applicationContext = context.getApplicationContext();
+        if (applicationContext == null) {
             throw new IllegalArgumentException(
                     "context not associated with any application (using a mock context?)");
         }
@@ -776,6 +864,7 @@
         mTagRemovedListener = null;
         mLock = new Object();
         mControllerAlwaysOnListener = new NfcControllerAlwaysOnListener(getService());
+        mNfcWlcStateListener = new NfcWlcStateListener(getService());
     }
 
     /**
@@ -944,7 +1033,8 @@
                 Log.e(TAG, "Failed to recover NFC Service.");
             }
         }
-        return serviceState && (isTagReadingEnabled() || isCardEmulationEnabled());
+        return serviceState
+                && (isTagReadingEnabled() || isCardEmulationEnabled() || sHasNfcWlcFeature);
     }
 
     /**
@@ -1230,7 +1320,7 @@
     @java.lang.Deprecated
     @UnsupportedAppUsage
     public void setBeamPushUris(Uri[] uris, Activity activity) {
-        synchronized (NfcAdapter.class) {
+        synchronized (sLock) {
             if (!sHasNfcFeature) {
                 throw new UnsupportedOperationException();
             }
@@ -1300,7 +1390,7 @@
     @java.lang.Deprecated
     @UnsupportedAppUsage
     public void setBeamPushUrisCallback(CreateBeamUrisCallback callback, Activity activity) {
-        synchronized (NfcAdapter.class) {
+        synchronized (sLock) {
             if (!sHasNfcFeature) {
                 throw new UnsupportedOperationException();
             }
@@ -1385,7 +1475,7 @@
     @UnsupportedAppUsage
     public void setNdefPushMessage(NdefMessage message, Activity activity,
             Activity ... activities) {
-        synchronized (NfcAdapter.class) {
+        synchronized (sLock) {
             if (!sHasNfcFeature) {
                 throw new UnsupportedOperationException();
             }
@@ -1399,7 +1489,7 @@
     @SystemApi
     @UnsupportedAppUsage
     public void setNdefPushMessage(NdefMessage message, Activity activity, int flags) {
-        synchronized (NfcAdapter.class) {
+        synchronized (sLock) {
             if (!sHasNfcFeature) {
                 throw new UnsupportedOperationException();
             }
@@ -1478,7 +1568,7 @@
     @UnsupportedAppUsage
     public void setNdefPushMessageCallback(CreateNdefMessageCallback callback, Activity activity,
             Activity ... activities) {
-        synchronized (NfcAdapter.class) {
+        synchronized (sLock) {
             if (!sHasNfcFeature) {
                 throw new UnsupportedOperationException();
             }
@@ -1529,7 +1619,7 @@
     @UnsupportedAppUsage
     public void setOnNdefPushCompleteCallback(OnNdefPushCompleteCallback callback,
             Activity activity, Activity ... activities) {
-        synchronized (NfcAdapter.class) {
+        synchronized (sLock) {
             if (!sHasNfcFeature) {
                 throw new UnsupportedOperationException();
             }
@@ -1572,7 +1662,7 @@
      */
     public void enableForegroundDispatch(Activity activity, PendingIntent intent,
             IntentFilter[] filters, String[][] techLists) {
-        synchronized (NfcAdapter.class) {
+        synchronized (sLock) {
             if (!sHasNfcFeature) {
                 throw new UnsupportedOperationException();
             }
@@ -1607,7 +1697,7 @@
      * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
      */
     public void disableForegroundDispatch(Activity activity) {
-        synchronized (NfcAdapter.class) {
+        synchronized (sLock) {
             if (!sHasNfcFeature) {
                 throw new UnsupportedOperationException();
             }
@@ -1643,7 +1733,7 @@
      */
     public void enableReaderMode(Activity activity, ReaderCallback callback, int flags,
             Bundle extras) {
-        synchronized (NfcAdapter.class) {
+        synchronized (sLock) {
             if (!sHasNfcFeature) {
                 throw new UnsupportedOperationException();
             }
@@ -1660,7 +1750,7 @@
      * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
      */
     public void disableReaderMode(Activity activity) {
-        synchronized (NfcAdapter.class) {
+        synchronized (sLock) {
             if (!sHasNfcFeature) {
                 throw new UnsupportedOperationException();
             }
@@ -1688,7 +1778,7 @@
     @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
     @SuppressLint("VisiblySynchronized")
     public void setReaderMode(boolean enablePolling) {
-        synchronized (NfcAdapter.class) {
+        synchronized (sLock) {
             if (!sHasNfcFeature) {
                 throw new UnsupportedOperationException();
             }
@@ -1703,6 +1793,80 @@
     }
 
     /**
+     * Set the NFC controller to enable specific poll/listen technologies,
+     * as specified in parameters, while this Activity is in the foreground.
+     *
+     * Use {@link #FLAG_READER_KEEP} to keep current polling technology.
+     * Use {@link #FLAG_LISTEN_KEEP} to keep current listenig technology.
+     * Use {@link #FLAG_READER_DISABLE} to disable polling.
+     * Use {@link #FLAG_LISTEN_DISABLE} to disable listening.
+     * Also refer to {@link #resetDiscoveryTechnology(Activity)} to restore these changes.
+     * </p>
+     * The pollTechnology, listenTechnology parameters can be one or several of below list.
+     * <pre>
+     *                    Poll                    Listen
+     *  Passive A         0x01   (NFC_A)           0x01  (NFC_PASSIVE_A)
+     *  Passive B         0x02   (NFC_B)           0x02  (NFC_PASSIVE_B)
+     *  Passive F         0x04   (NFC_F)           0x04  (NFC_PASSIVE_F)
+     *  ISO 15693         0x08   (NFC_V)             -
+     *  Kovio             0x10   (NFC_BARCODE)       -
+     * </pre>
+     * <p>Example usage in an Activity that requires to disable poll,
+     * keep current listen technologies:
+     * <pre>
+     * protected void onResume() {
+     *     mNfcAdapter = NfcAdapter.getDefaultAdapter(getApplicationContext());
+     *     mNfcAdapter.setDiscoveryTechnology(this,
+     *         NfcAdapter.FLAG_READER_DISABLE, NfcAdapter.FLAG_LISTEN_KEEP);
+     * }</pre></p>
+     * @param activity The Activity that requests NFC controller to enable specific technologies.
+     * @param pollTechnology Flags indicating poll technologies.
+     * @param listenTechnology Flags indicating listen technologies.
+     * @throws UnsupportedOperationException if FEATURE_NFC,
+     * FEATURE_NFC_HOST_CARD_EMULATION, FEATURE_NFC_HOST_CARD_EMULATION_NFCF are unavailable.
+     */
+
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_SET_DISCOVERY_TECH)
+    public void setDiscoveryTechnology(@NonNull Activity activity,
+            @PollTechnology int pollTechnology, @ListenTechnology int listenTechnology) {
+        if (listenTechnology == FLAG_LISTEN_DISABLE) {
+            synchronized (sLock) {
+                if (!sHasNfcFeature) {
+                    throw new UnsupportedOperationException();
+                }
+            }
+            mNfcActivityManager.enableReaderMode(activity, null, pollTechnology, null);
+            return;
+        }
+        if (pollTechnology == FLAG_READER_DISABLE) {
+            synchronized (sLock) {
+                if (!sHasCeFeature) {
+                    throw new UnsupportedOperationException();
+                }
+            }
+        } else {
+            synchronized (sLock) {
+                if (!sHasNfcFeature || !sHasCeFeature) {
+                    throw new UnsupportedOperationException();
+                }
+            }
+        }
+        mNfcActivityManager.setDiscoveryTech(activity, pollTechnology, listenTechnology);
+    }
+
+    /**
+     * Restore the poll/listen technologies of NFC controller,
+     * which were changed by {@link #setDiscoveryTechnology(Activity , int , int)}
+     *
+     * @param activity The Activity that requests to changed technologies.
+     */
+
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_SET_DISCOVERY_TECH)
+    public void resetDiscoveryTechnology(@NonNull Activity activity) {
+        mNfcActivityManager.resetDiscoveryTech(activity);
+    }
+
+    /**
      * Manually invoke Android Beam to share data.
      *
      * <p>The Android Beam animation is normally only shown when two NFC-capable
@@ -1732,7 +1896,7 @@
     @java.lang.Deprecated
     @UnsupportedAppUsage
     public boolean invokeBeam(Activity activity) {
-        synchronized (NfcAdapter.class) {
+        synchronized (sLock) {
             if (!sHasNfcFeature) {
                 throw new UnsupportedOperationException();
             }
@@ -1770,7 +1934,7 @@
     @Deprecated
     @UnsupportedAppUsage
     public void enableForegroundNdefPush(Activity activity, NdefMessage message) {
-        synchronized (NfcAdapter.class) {
+        synchronized (sLock) {
             if (!sHasNfcFeature) {
                 throw new UnsupportedOperationException();
             }
@@ -1800,7 +1964,7 @@
     @Deprecated
     @UnsupportedAppUsage
     public void disableForegroundNdefPush(Activity activity) {
-        synchronized (NfcAdapter.class) {
+        synchronized (sLock) {
             if (!sHasNfcFeature) {
                 throw new UnsupportedOperationException();
             }
@@ -2080,7 +2244,7 @@
     @java.lang.Deprecated
     @UnsupportedAppUsage
     public boolean isNdefPushEnabled() {
-        synchronized (NfcAdapter.class) {
+        synchronized (sLock) {
             if (!sHasNfcFeature) {
                 throw new UnsupportedOperationException();
             }
@@ -2194,7 +2358,7 @@
     @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
     public boolean addNfcUnlockHandler(final NfcUnlockHandler unlockHandler,
                                        String[] tagTechnologies) {
-        synchronized (NfcAdapter.class) {
+        synchronized (sLock) {
             if (!sHasNfcFeature) {
                 throw new UnsupportedOperationException();
             }
@@ -2243,7 +2407,7 @@
     @SystemApi
     @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
     public boolean removeNfcUnlockHandler(NfcUnlockHandler unlockHandler) {
-        synchronized (NfcAdapter.class) {
+        synchronized (sLock) {
             if (!sHasNfcFeature) {
                 throw new UnsupportedOperationException();
             }
@@ -2587,4 +2751,159 @@
             return false;
         }
     }
+
+    /**
+     * Sets NFC charging feature.
+     * <p>This API is for the Settings application.
+     * @return True if successful
+     * @hide
+     */
+    @SystemApi
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_CHARGING)
+    @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
+    public boolean enableWlc(boolean enable) {
+        if (!sHasNfcWlcFeature) {
+            throw new UnsupportedOperationException();
+        }
+        try {
+            return sService.enableWlc(enable);
+        } catch (RemoteException e) {
+            attemptDeadServiceRecovery(e);
+            // Try one more time
+            if (sService == null) {
+                Log.e(TAG, "Failed to recover NFC Service.");
+                return false;
+            }
+            try {
+                return sService.enableWlc(enable);
+            } catch (RemoteException ee) {
+                Log.e(TAG, "Failed to recover NFC Service.");
+            }
+            return false;
+        }
+    }
+
+    /**
+     * Checks NFC charging feature is enabled.
+     *
+     * @return True if NFC charging is enabled, false otherwise
+     * @throws UnsupportedOperationException if FEATURE_NFC_CHARGING
+     * is unavailable
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_CHARGING)
+    public boolean isWlcEnabled() {
+        if (!sHasNfcWlcFeature) {
+            throw new UnsupportedOperationException();
+        }
+        try {
+            return sService.isWlcEnabled();
+        } catch (RemoteException e) {
+            attemptDeadServiceRecovery(e);
+            // Try one more time
+            if (sService == null) {
+                Log.e(TAG, "Failed to recover NFC Service.");
+                return false;
+            }
+            try {
+                return sService.isWlcEnabled();
+            } catch (RemoteException ee) {
+                Log.e(TAG, "Failed to recover NFC Service.");
+            }
+            return false;
+        }
+    }
+
+    /**
+     * A listener to be invoked when NFC controller always on state changes.
+     * <p>Register your {@code ControllerAlwaysOnListener} implementation with {@link
+     * NfcAdapter#registerWlcStateListener} and disable it with {@link
+     * NfcAdapter#unregisterWlcStateListenerListener}.
+     * @see #registerWlcStateListener
+     * @hide
+     */
+    @SystemApi
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_CHARGING)
+    public interface WlcStateListener {
+        /**
+         * Called on NFC WLC state changes
+         */
+        void onWlcStateChanged(@NonNull WlcLDeviceInfo wlcLDeviceInfo);
+    }
+
+    /**
+     * Register a {@link WlcStateListener} to listen for NFC WLC state changes
+     * <p>The provided listener will be invoked by the given {@link Executor}.
+     *
+     * @param executor an {@link Executor} to execute given listener
+     * @param listener user implementation of the {@link WlcStateListener}
+     * @throws UnsupportedOperationException if FEATURE_NFC_CHARGING
+     * is unavailable
+     *
+     * @hide
+     */
+    @SystemApi
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_CHARGING)
+    public void registerWlcStateListener(
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull WlcStateListener listener) {
+        if (!sHasNfcWlcFeature) {
+            throw new UnsupportedOperationException();
+        }
+        mNfcWlcStateListener.register(executor, listener);
+    }
+
+    /**
+     * Unregister the specified {@link WlcStateListener}
+     * <p>The same {@link WlcStateListener} object used when calling
+     * {@link #registerWlcStateListener(Executor, WlcStateListener)}
+     * must be used.
+     *
+     * <p>Listeners are automatically unregistered when application process goes away
+     *
+     * @param listener user implementation of the {@link WlcStateListener}a
+     * @throws UnsupportedOperationException if FEATURE_NFC_CHARGING
+     * is unavailable
+     *
+     * @hide
+     */
+    @SystemApi
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_CHARGING)
+    public void unregisterWlcStateListener(
+            @NonNull WlcStateListener listener) {
+        if (!sHasNfcWlcFeature) {
+            throw new UnsupportedOperationException();
+        }
+        mNfcWlcStateListener.unregister(listener);
+    }
+
+    /**
+     * Returns information on the NFC charging listener device
+     *
+     * @return Information on the NFC charging listener device
+     * @throws UnsupportedOperationException if FEATURE_NFC_CHARGING
+     * is unavailable
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_CHARGING)
+    @Nullable
+    public WlcLDeviceInfo getWlcLDeviceInfo() {
+        if (!sHasNfcWlcFeature) {
+            throw new UnsupportedOperationException();
+        }
+        try {
+            return sService.getWlcLDeviceInfo();
+        } catch (RemoteException e) {
+            attemptDeadServiceRecovery(e);
+            // Try one more time
+            if (sService == null) {
+                Log.e(TAG, "Failed to recover NFC Service.");
+                return null;
+            }
+            try {
+                return sService.getWlcLDeviceInfo();
+            } catch (RemoteException ee) {
+                Log.e(TAG, "Failed to recover NFC Service.");
+            }
+            return null;
+        }
+    }
 }
diff --git a/core/java/android/nfc/NfcWlcStateListener.java b/core/java/android/nfc/NfcWlcStateListener.java
new file mode 100644
index 0000000..8d79310
--- /dev/null
+++ b/core/java/android/nfc/NfcWlcStateListener.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.nfc;
+
+import android.annotation.NonNull;
+import android.nfc.NfcAdapter.WlcStateListener;
+import android.os.Binder;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.Executor;
+
+/**
+ * @hide
+ */
+public class NfcWlcStateListener extends INfcWlcStateListener.Stub {
+    private static final String TAG = NfcWlcStateListener.class.getSimpleName();
+
+    private final INfcAdapter mAdapter;
+
+    private final Map<WlcStateListener, Executor> mListenerMap = new HashMap<>();
+
+    private WlcLDeviceInfo mCurrentState = null;
+    private boolean mIsRegistered = false;
+
+    public NfcWlcStateListener(@NonNull INfcAdapter adapter) {
+        mAdapter = adapter;
+    }
+
+    /**
+     * Register a {@link WlcStateListener} with this
+     * {@link WlcStateListener}
+     *
+     * @param executor an {@link Executor} to execute given listener
+     * @param listener user implementation of the {@link WlcStateListener}
+     */
+    public void register(@NonNull Executor executor, @NonNull WlcStateListener listener) {
+        synchronized (this) {
+            if (mListenerMap.containsKey(listener)) {
+                return;
+            }
+
+            mListenerMap.put(listener, executor);
+
+            if (!mIsRegistered) {
+                try {
+                    mAdapter.registerWlcStateListener(this);
+                    mIsRegistered = true;
+                } catch (RemoteException e) {
+                    Log.w(TAG, "Failed to register");
+                }
+            }
+        }
+    }
+
+    /**
+     * Unregister the specified {@link WlcStateListener}
+     *
+     * @param listener user implementation of the {@link WlcStateListener}
+     */
+    public void unregister(@NonNull WlcStateListener listener) {
+        synchronized (this) {
+            if (!mListenerMap.containsKey(listener)) {
+                return;
+            }
+
+            mListenerMap.remove(listener);
+
+            if (mListenerMap.isEmpty() && mIsRegistered) {
+                try {
+                    mAdapter.unregisterWlcStateListener(this);
+                } catch (RemoteException e) {
+                    Log.w(TAG, "Failed to unregister");
+                }
+                mIsRegistered = false;
+            }
+        }
+    }
+
+    private void sendCurrentState(@NonNull WlcStateListener listener) {
+        synchronized (this) {
+            Executor executor = mListenerMap.get(listener);
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                executor.execute(() -> listener.onWlcStateChanged(
+                        mCurrentState));
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+    }
+
+    @Override
+    public void onWlcStateChanged(@NonNull WlcLDeviceInfo wlcLDeviceInfo) {
+        synchronized (this) {
+            mCurrentState = wlcLDeviceInfo;
+
+            for (WlcStateListener cb : mListenerMap.keySet()) {
+                sendCurrentState(cb);
+            }
+        }
+    }
+}
+
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/UninstallActionListener.java b/core/java/android/nfc/WlcLDeviceInfo.aidl
similarity index 71%
copy from packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/UninstallActionListener.java
copy to core/java/android/nfc/WlcLDeviceInfo.aidl
index b8a9355..33143fe 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/UninstallActionListener.java
+++ b/core/java/android/nfc/WlcLDeviceInfo.aidl
@@ -5,7 +5,7 @@
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
- *      https://www.apache.org/licenses/LICENSE-2.0
+ *      http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
@@ -14,11 +14,6 @@
  * limitations under the License.
  */
 
-package com.android.packageinstaller.v2.ui;
+package android.nfc;
 
-public interface UninstallActionListener {
-
-    void onPositiveResponse(boolean keepData);
-
-    void onNegativeResponse();
-}
+parcelable WlcLDeviceInfo;
diff --git a/core/java/android/nfc/WlcLDeviceInfo.java b/core/java/android/nfc/WlcLDeviceInfo.java
new file mode 100644
index 0000000..016431e
--- /dev/null
+++ b/core/java/android/nfc/WlcLDeviceInfo.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.nfc;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Contains information of the nfc wireless charging listener device information.
+ */
+@FlaggedApi(Flags.FLAG_ENABLE_NFC_CHARGING)
+public final class WlcLDeviceInfo implements Parcelable {
+    public static final int DISCONNECTED = 1;
+
+    public static final int CONNECTED_CHARGING = 2;
+
+    public static final int CONNECTED_DISCHARGING = 3;
+
+    private double mProductId;
+    private double mTemperature;
+    private double mBatteryLevel;
+    private int mState;
+
+    public WlcLDeviceInfo(double productId, double temperature, double batteryLevel, int state) {
+        this.mProductId = productId;
+        this.mTemperature = temperature;
+        this.mBatteryLevel = batteryLevel;
+        this.mState = state;
+    }
+
+    /**
+     * ProductId of the WLC listener device.
+     */
+    public double getProductId() {
+        return mProductId;
+    }
+
+    /**
+     * Temperature of the WLC listener device.
+     */
+    public double getTemperature() {
+        return mTemperature;
+    }
+
+    /**
+     * BatteryLevel of the WLC listener device.
+     */
+    public double getBatteryLevel() {
+        return mBatteryLevel;
+    }
+
+    /**
+     * State of the WLC listener device.
+     */
+    public int getState() {
+        return mState;
+    }
+
+    private WlcLDeviceInfo(Parcel in) {
+        this.mProductId = in.readDouble();
+        this.mTemperature = in.readDouble();
+        this.mBatteryLevel = in.readDouble();
+        this.mState = in.readInt();
+    }
+
+    public static final @NonNull Parcelable.Creator<WlcLDeviceInfo> CREATOR =
+            new Parcelable.Creator<WlcLDeviceInfo>() {
+                @Override
+                public WlcLDeviceInfo createFromParcel(Parcel in) {
+                    return new WlcLDeviceInfo(in);
+                }
+
+                @Override
+                public WlcLDeviceInfo[] newArray(int size) {
+                    return new WlcLDeviceInfo[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeDouble(mProductId);
+        dest.writeDouble(mTemperature);
+        dest.writeDouble(mBatteryLevel);
+        dest.writeDouble(mState);
+    }
+}
diff --git a/core/java/android/nfc/cardemulation/ApduServiceInfo.java b/core/java/android/nfc/cardemulation/ApduServiceInfo.java
index bd087f9..41dee3a 100644
--- a/core/java/android/nfc/cardemulation/ApduServiceInfo.java
+++ b/core/java/android/nfc/cardemulation/ApduServiceInfo.java
@@ -21,10 +21,10 @@
 package android.nfc.cardemulation;
 
 import android.annotation.FlaggedApi;
-import android.compat.annotation.UnsupportedAppUsage;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
@@ -374,7 +374,7 @@
         // Set uid
         mUid = si.applicationInfo.uid;
 
-        mCategoryOtherServiceEnabled = false;    // support other category
+        mCategoryOtherServiceEnabled = true;    // support other category
 
     }
 
diff --git a/core/java/android/nfc/cardemulation/CardEmulation.java b/core/java/android/nfc/cardemulation/CardEmulation.java
index 58b6179..81eab71 100644
--- a/core/java/android/nfc/cardemulation/CardEmulation.java
+++ b/core/java/android/nfc/cardemulation/CardEmulation.java
@@ -69,7 +69,12 @@
      * specified in {@link #EXTRA_CATEGORY}. There is an optional
      * extra field using {@link Intent#EXTRA_USER} to specify
      * the {@link UserHandle} of the user that owns the app.
+     *
+     * @deprecated Please use {@link android.app.role.RoleManager#createRequestRoleIntent(String)}
+     * with {@link android.app.role.RoleManager#ROLE_WALLET} parameter
+     * and {@link Activity#startActivityForResult(Intent, int)} instead.
      */
+    @Deprecated
     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
     public static final String ACTION_CHANGE_DEFAULT =
             "android.nfc.cardemulation.action.ACTION_CHANGE_DEFAULT";
@@ -998,9 +1003,118 @@
         }
     }
 
+     /**
+      * Setting NFC controller routing table, which includes Protocol Route and Technology Route,
+      * while this Activity is in the foreground.
+      *
+      * The parameter set to null can be used to keep current values for that entry.
+      * <p>
+      * Example usage in an Activity that requires to set proto route to "ESE" and keep tech route:
+      * <pre>
+      * protected void onResume() {
+      *     mNfcAdapter.overrideRoutingTable(this , "ESE" , null);
+      * }</pre>
+      * </p>
+      * Also activities must call this method when it goes to the background,
+      * with all parameters set to null.
+      * @param activity The Activity that requests NFC controller routing table to be changed.
+      * @param protocol ISO-DEP route destination, which can be "DH" or "UICC" or "ESE".
+      * @param technology Tech-A, Tech-B route destination, which can be "DH" or "UICC" or "ESE".
+      * @return true if operation is successful and false otherwise
+      *
+      * This is a high risk API and only included to support mainline effort
+      * @hide
+      */
+    public boolean overrideRoutingTable(Activity activity, String protocol, String technology) {
+        if (activity == null) {
+            throw new NullPointerException("activity or service or category is null");
+        }
+        if (!activity.isResumed()) {
+            throw new IllegalArgumentException("Activity must be resumed.");
+        }
+        try {
+            return sService.overrideRoutingTable(UserHandle.myUserId(), protocol, technology);
+        } catch (RemoteException e) {
+            // Try one more time
+            recoverService();
+            if (sService == null) {
+                Log.e(TAG, "Failed to recover CardEmulationService.");
+                return false;
+            }
+            try {
+                return sService.overrideRoutingTable(UserHandle.myUserId(), protocol, technology);
+            } catch (RemoteException ee) {
+                Log.e(TAG, "Failed to reach CardEmulationService.");
+                return false;
+            }
+        }
+    }
+
+    /**
+     * Restore the NFC controller routing table,
+     * which was changed by {@link #overrideRoutingTable(Activity, String, String)}
+     *
+     * @param activity The Activity that requested NFC controller routing table to be changed.
+     * @return true if operation is successful and false otherwise
+     *
+     * @hide
+     */
+    public boolean recoverRoutingTable(Activity activity) {
+        if (activity == null) {
+            throw new NullPointerException("activity is null");
+        }
+        if (!activity.isResumed()) {
+            throw new IllegalArgumentException("Activity must be resumed.");
+        }
+        try {
+            return sService.recoverRoutingTable(UserHandle.myUserId());
+        } catch (RemoteException e) {
+            // Try one more time
+            recoverService();
+            if (sService == null) {
+                Log.e(TAG, "Failed to recover CardEmulationService.");
+                return false;
+            }
+            try {
+                return sService.recoverRoutingTable(UserHandle.myUserId());
+            } catch (RemoteException ee) {
+                Log.e(TAG, "Failed to reach CardEmulationService.");
+                return false;
+            }
+        }
+    }
+
     void recoverService() {
         NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mContext);
         sService = adapter.getCardEmulationService();
     }
 
+    /**
+     * Returns the {@link Settings.Secure#NFC_PAYMENT_DEFAULT_COMPONENT} for the given user.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.NFC_PREFERRED_PAYMENT_INFO)
+    @FlaggedApi(android.permission.flags.Flags.FLAG_WALLET_ROLE_ENABLED)
+    @Nullable
+    public ApduServiceInfo getPreferredPaymentService() {
+        try {
+            return sService.getPreferredPaymentService(mContext.getUser().getIdentifier());
+        } catch (RemoteException e) {
+            // Try one more time
+            recoverService();
+            if (sService == null) {
+                Log.e(TAG, "Failed to recover CardEmulationService.");
+                return null;
+            }
+            try {
+                return sService.getPreferredPaymentService(mContext.getUser().getIdentifier());
+            } catch (RemoteException ee) {
+                Log.e(TAG, "Failed to reach CardEmulationService.");
+                return null;
+            }
+        }
+    }
+
 }
diff --git a/core/java/android/nfc/flags.aconfig b/core/java/android/nfc/flags.aconfig
index 17e0427..01a4570 100644
--- a/core/java/android/nfc/flags.aconfig
+++ b/core/java/android/nfc/flags.aconfig
@@ -48,3 +48,24 @@
     description: "Enable NFC Polling Loop Notifications ST shim"
     bug: "294217286"
 }
+
+flag {
+    name: "enable_tag_detection_broadcasts"
+    namespace: "nfc"
+    description: "Enable sending broadcasts to Wallet role holder when a tag enters/leaves the field."
+    bug: "306203494"
+}
+
+flag {
+    name: "enable_nfc_charging"
+    namespace: "nfc"
+    description: "Flag for NFC charging changes"
+    bug: "292143899"
+}
+
+flag {
+    name: "enable_nfc_set_discovery_tech"
+    namespace: "nfc"
+    description: "Flag for NFC set discovery tech API"
+    bug: "300351519"
+}
diff --git a/core/java/android/os/AggregateBatteryConsumer.java b/core/java/android/os/AggregateBatteryConsumer.java
index c5f5614..67e2195 100644
--- a/core/java/android/os/AggregateBatteryConsumer.java
+++ b/core/java/android/os/AggregateBatteryConsumer.java
@@ -33,6 +33,7 @@
  *
  * {@hide}
  */
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
 public final class AggregateBatteryConsumer extends BatteryConsumer {
     static final int CONSUMER_TYPE_AGGREGATE = 0;
 
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 1a0ce70..b68b94d 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -37,6 +37,7 @@
 import android.service.batterystats.BatteryStatsServiceDumpHistoryProto;
 import android.service.batterystats.BatteryStatsServiceDumpProto;
 import android.telephony.CellSignalStrength;
+import android.telephony.ModemActivityInfo;
 import android.telephony.ServiceState;
 import android.telephony.TelephonyManager;
 import android.text.format.DateFormat;
@@ -83,6 +84,7 @@
  * except where indicated otherwise.
  * @hide
  */
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
 public abstract class BatteryStats {
 
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
@@ -463,6 +465,7 @@
     /**
      * State for keeping track of long counting information.
      */
+    @android.ravenwood.annotation.RavenwoodKeepWholeClass
     public static abstract class LongCounter {
 
         /**
@@ -2724,12 +2727,6 @@
      */
     public abstract int getMobileRadioActiveUnknownCount(int which);
 
-    public static final int DATA_CONNECTION_OUT_OF_SERVICE = 0;
-    public static final int DATA_CONNECTION_EMERGENCY_SERVICE =
-            TelephonyManager.getAllNetworkTypes().length + 1;
-    public static final int DATA_CONNECTION_OTHER = DATA_CONNECTION_EMERGENCY_SERVICE + 1;
-
-
     static final String[] DATA_CONNECTION_NAMES = {
         "oos", "gprs", "edge", "umts", "cdma", "evdo_0", "evdo_A",
         "1xrtt", "hsdpa", "hsupa", "hspa", "iden", "evdo_b", "lte",
@@ -2737,9 +2734,28 @@
         "emngcy", "other"
     };
 
+    public static final int DATA_CONNECTION_OUT_OF_SERVICE = 0;
+    public static final int DATA_CONNECTION_EMERGENCY_SERVICE = getEmergencyNetworkConnectionType();
+    public static final int DATA_CONNECTION_OTHER = DATA_CONNECTION_EMERGENCY_SERVICE + 1;
+
     @UnsupportedAppUsage
     public static final int NUM_DATA_CONNECTION_TYPES = DATA_CONNECTION_OTHER + 1;
 
+    @android.ravenwood.annotation.RavenwoodReplace
+    private static int getEmergencyNetworkConnectionType() {
+        int count = TelephonyManager.getAllNetworkTypes().length;
+        if (DATA_CONNECTION_NAMES.length != count + 3) {        // oos, emngcy, other
+            throw new IllegalStateException(
+                    "DATA_CONNECTION_NAMES length does not match network type count. "
+                    + "Expected: " + (count + 3) + ", actual:" + DATA_CONNECTION_NAMES.length);
+        }
+        return count + 1;
+    }
+
+    private static int getEmergencyNetworkConnectionType$ravenwood() {
+        return DATA_CONNECTION_NAMES.length - 2;
+    }
+
     /**
      * Returns the time in microseconds that the phone has been running with
      * the given data connection.
@@ -9015,4 +9031,44 @@
                 (lhs, rhs) -> Double.compare(rhs.millisecondsPerPacket, lhs.millisecondsPerPacket));
         return uidMobileRadioStats;
     }
+
+    @android.ravenwood.annotation.RavenwoodReplace
+    @VisibleForTesting
+    protected static boolean isLowRamDevice() {
+        return ActivityManager.isLowRamDeviceStatic();
+    }
+
+    protected static boolean isLowRamDevice$ravenwood() {
+        return false;
+    }
+
+    @android.ravenwood.annotation.RavenwoodReplace
+    @VisibleForTesting
+    protected static int getCellSignalStrengthLevelCount() {
+        return CellSignalStrength.getNumSignalStrengthLevels();
+    }
+
+    protected static int getCellSignalStrengthLevelCount$ravenwood() {
+        return 5;
+    }
+
+    @android.ravenwood.annotation.RavenwoodReplace
+    @VisibleForTesting
+    protected static int getModemTxPowerLevelCount() {
+        return ModemActivityInfo.getNumTxPowerLevels();
+    }
+
+    protected static int getModemTxPowerLevelCount$ravenwood() {
+        return 5;
+    }
+
+    @android.ravenwood.annotation.RavenwoodReplace
+    @VisibleForTesting
+    protected static boolean isKernelStatsAvailable() {
+        return true;
+    }
+
+    protected static boolean isKernelStatsAvailable$ravenwood() {
+        return false;
+    }
 }
diff --git a/core/java/android/os/BatteryUsageStats.java b/core/java/android/os/BatteryUsageStats.java
index 511f464..90d82e7 100644
--- a/core/java/android/os/BatteryUsageStats.java
+++ b/core/java/android/os/BatteryUsageStats.java
@@ -56,6 +56,7 @@
  *
  * @hide
  */
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
 public final class BatteryUsageStats implements Parcelable, Closeable {
 
     /**
diff --git a/core/java/android/os/BatteryUsageStatsQuery.java b/core/java/android/os/BatteryUsageStatsQuery.java
index 32840d4..203ef47 100644
--- a/core/java/android/os/BatteryUsageStatsQuery.java
+++ b/core/java/android/os/BatteryUsageStatsQuery.java
@@ -28,6 +28,7 @@
  *
  * @hide
  */
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
 public final class BatteryUsageStatsQuery implements Parcelable {
 
     @NonNull
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index a9b7257..5871717 100755
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -1315,9 +1315,7 @@
         if (IS_ENG) return true;
 
         if (IS_TREBLE_ENABLED) {
-            // If we can run this code, the device should already pass AVB.
-            // So, we don't need to check AVB here.
-            int result = VintfObject.verifyWithoutAvb();
+            int result = VintfObject.verifyBuildAtBoot();
 
             if (result != 0) {
                 Slog.e(TAG, "Vendor interface is incompatible, error="
diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java
index 8fcff78..3149de4 100644
--- a/core/java/android/os/GraphicsEnvironment.java
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -673,6 +673,7 @@
         if (anglePkg.isEmpty()) {
             return;
         }
+        intent.setPackage(anglePkg);
 
         context.sendOrderedBroadcast(intent, null, new BroadcastReceiver() {
             @Override
diff --git a/core/java/android/os/HwNoService.java b/core/java/android/os/HwNoService.java
index 117c3ad..0840314 100644
--- a/core/java/android/os/HwNoService.java
+++ b/core/java/android/os/HwNoService.java
@@ -16,37 +16,127 @@
 
 package android.os;
 
+import android.hidl.manager.V1_2.IServiceManager;
+import android.util.Log;
+
+import java.util.ArrayList;
+
 /**
  * A fake hwservicemanager that is used locally when HIDL isn't supported on the device.
  *
  * @hide
  */
-final class HwNoService implements IHwBinder, IHwInterface {
-    /** @hide */
-    @Override
-    public void transact(int code, HwParcel request, HwParcel reply, int flags) {}
+final class HwNoService extends IServiceManager.Stub implements IHwBinder, IHwInterface {
+    private static final String TAG = "HwNoService";
 
     /** @hide */
     @Override
-    public IHwInterface queryLocalInterface(String descriptor) {
-        return new HwNoService();
+    public String toString() {
+        return "[HwNoService]";
     }
 
-    /** @hide */
     @Override
-    public boolean linkToDeath(DeathRecipient recipient, long cookie) {
+    public android.hidl.base.V1_0.IBase get(String fqName, String name)
+            throws android.os.RemoteException {
+        Log.i(TAG, "get " + fqName + "/" + name + " with no hwservicemanager");
+        return null;
+    }
+
+    @Override
+    public boolean add(String name, android.hidl.base.V1_0.IBase service)
+            throws android.os.RemoteException {
+        Log.i(TAG, "get " + name + " with no hwservicemanager");
+        return false;
+    }
+
+    @Override
+    public byte getTransport(String fqName, String name) throws android.os.RemoteException {
+        Log.i(TAG, "getTransoport " + fqName + "/" + name + " with no hwservicemanager");
+        return 0x0;
+    }
+
+    @Override
+    public java.util.ArrayList<String> list() throws android.os.RemoteException {
+        Log.i(TAG, "list with no hwservicemanager");
+        return new ArrayList<String>();
+    }
+
+    @Override
+    public java.util.ArrayList<String> listByInterface(String fqName)
+            throws android.os.RemoteException {
+        Log.i(TAG, "listByInterface with no hwservicemanager");
+        return new ArrayList<String>();
+    }
+
+    @Override
+    public boolean registerForNotifications(
+            String fqName, String name, android.hidl.manager.V1_0.IServiceNotification callback)
+            throws android.os.RemoteException {
+        Log.i(TAG, "registerForNotifications with no hwservicemanager");
         return true;
     }
 
-    /** @hide */
     @Override
-    public boolean unlinkToDeath(DeathRecipient recipient) {
+    public ArrayList<android.hidl.manager.V1_0.IServiceManager.InstanceDebugInfo> debugDump()
+            throws android.os.RemoteException {
+        Log.i(TAG, "debugDump with no hwservicemanager");
+        return new ArrayList<android.hidl.manager.V1_0.IServiceManager.InstanceDebugInfo>();
+    }
+
+    @Override
+    public void registerPassthroughClient(String fqName, String name)
+            throws android.os.RemoteException {
+        Log.i(TAG, "registerPassthroughClient with no hwservicemanager");
+    }
+
+    @Override
+    public boolean unregisterForNotifications(
+            String fqName, String name, android.hidl.manager.V1_0.IServiceNotification callback)
+            throws android.os.RemoteException {
+        Log.i(TAG, "unregisterForNotifications with no hwservicemanager");
         return true;
     }
 
-    /** @hide */
     @Override
-    public IHwBinder asBinder() {
-        return this;
+    public boolean registerClientCallback(
+            String fqName,
+            String name,
+            android.hidl.base.V1_0.IBase server,
+            android.hidl.manager.V1_2.IClientCallback cb)
+            throws android.os.RemoteException {
+        Log.i(
+                TAG,
+                "registerClientCallback for " + fqName + "/" + name + " with no hwservicemanager");
+        return true;
+    }
+
+    @Override
+    public boolean unregisterClientCallback(
+            android.hidl.base.V1_0.IBase server, android.hidl.manager.V1_2.IClientCallback cb)
+            throws android.os.RemoteException {
+        Log.i(TAG, "unregisterClientCallback with no hwservicemanager");
+        return true;
+    }
+
+    @Override
+    public boolean addWithChain(
+            String name, android.hidl.base.V1_0.IBase service, java.util.ArrayList<String> chain)
+            throws android.os.RemoteException {
+        Log.i(TAG, "addWithChain with no hwservicemanager");
+        return true;
+    }
+
+    @Override
+    public java.util.ArrayList<String> listManifestByInterface(String fqName)
+            throws android.os.RemoteException {
+        Log.i(TAG, "listManifestByInterface for " + fqName + " with no hwservicemanager");
+        return new ArrayList<String>();
+    }
+
+    @Override
+    public boolean tryUnregister(String fqName, String name, android.hidl.base.V1_0.IBase service)
+            throws android.os.RemoteException {
+        Log.i(TAG, "tryUnregister for " + fqName + "/" + name + " with no hwservicemanager");
+        return true;
     }
 }
diff --git a/core/java/android/os/ISystemConfig.aidl b/core/java/android/os/ISystemConfig.aidl
index 61b24aa..b7649ba 100644
--- a/core/java/android/os/ISystemConfig.aidl
+++ b/core/java/android/os/ISystemConfig.aidl
@@ -52,4 +52,9 @@
      * @see SystemConfigManager#getDefaultVrComponents
      */
     List<ComponentName> getDefaultVrComponents();
+
+    /**
+     * @see SystemConfigManager#getPreventUserDisablePackages
+     */
+    List<String> getPreventUserDisablePackages();
 }
diff --git a/core/java/android/os/MessageQueue.java b/core/java/android/os/MessageQueue.java
index c60f949..fbec518 100644
--- a/core/java/android/os/MessageQueue.java
+++ b/core/java/android/os/MessageQueue.java
@@ -57,6 +57,7 @@
 
     @UnsupportedAppUsage
     Message mMessages;
+    private Message mLast;
     @UnsupportedAppUsage
     private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();
     private SparseArray<FileDescriptorRecord> mFileDescriptorRecords;
@@ -66,6 +67,10 @@
     // Indicates whether next() is blocked waiting in pollOnce() with a non-zero timeout.
     private boolean mBlocked;
 
+    // Tracks the number of async message. We use this in enqueueMessage() to avoid searching the
+    // queue for async messages when inserting a message at the tail.
+    private int mAsyncMessageCount;
+
     // The next barrier token.
     // Barriers are indicated by messages with a null target whose arg1 field carries the token.
     @UnsupportedAppUsage
@@ -364,12 +369,21 @@
                         mBlocked = false;
                         if (prevMsg != null) {
                             prevMsg.next = msg.next;
+                            if (prevMsg.next == null) {
+                                mLast = prevMsg;
+                            }
                         } else {
                             mMessages = msg.next;
+                            if (msg.next == null) {
+                                mLast = null;
+                            }
                         }
                         msg.next = null;
                         if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                         msg.markInUse();
+                        if (msg.isAsynchronous()) {
+                            mAsyncMessageCount--;
+                        }
                         return msg;
                     }
                 } else {
@@ -492,6 +506,14 @@
             msg.when = when;
             msg.arg1 = token;
 
+            if (Flags.messageQueueTailTracking() && mLast != null && mLast.when <= when) {
+                /* Message goes to tail of list */
+                mLast.next = msg;
+                mLast = msg;
+                msg.next = null;
+                return token;
+            }
+
             Message prev = null;
             Message p = mMessages;
             if (when != 0) {
@@ -500,6 +522,12 @@
                     p = p.next;
                 }
             }
+
+            if (p == null) {
+                /* We reached the tail of the list, or list is empty. */
+                mLast = msg;
+            }
+
             if (prev != null) { // invariant: p == prev.next
                 msg.next = p;
                 prev.next = msg;
@@ -540,9 +568,15 @@
             final boolean needWake;
             if (prev != null) {
                 prev.next = p.next;
+                if (prev.next == null) {
+                    mLast = prev;
+                }
                 needWake = false;
             } else {
                 mMessages = p.next;
+                if (mMessages == null) {
+                    mLast = null;
+                }
                 needWake = mMessages == null || mMessages.target != null;
             }
             p.recycleUnchecked();
@@ -582,24 +616,77 @@
                 msg.next = p;
                 mMessages = msg;
                 needWake = mBlocked;
-            } else {
-                // Inserted within the middle of the queue.  Usually we don't have to wake
-                // up the event queue unless there is a barrier at the head of the queue
-                // and the message is the earliest asynchronous message in the queue.
-                needWake = mBlocked && p.target == null && msg.isAsynchronous();
-                Message prev;
-                for (;;) {
-                    prev = p;
-                    p = p.next;
-                    if (p == null || when < p.when) {
-                        break;
-                    }
-                    if (needWake && p.isAsynchronous()) {
-                        needWake = false;
-                    }
+                if (p == null) {
+                    mLast = mMessages;
                 }
-                msg.next = p; // invariant: p == prev.next
-                prev.next = msg;
+            } else {
+                // Message is to be inserted at tail or middle of queue. Usually we don't have to
+                // wake up the event queue unless there is a barrier at the head of the queue and
+                // the message is the earliest asynchronous message in the queue.
+                //
+                // For readability, we split this portion of the function into two blocks based on
+                // whether tail tracking is enabled. This has a minor implication for the case
+                // where tail tracking is disabled. See the comment below.
+                if (Flags.messageQueueTailTracking()) {
+                    needWake = mBlocked && p.target == null && msg.isAsynchronous()
+                        && mAsyncMessageCount == 0;
+                    if (when >= mLast.when) {
+                        msg.next = null;
+                        mLast.next = msg;
+                        mLast = msg;
+                    } else {
+                        // Inserted within the middle of the queue.
+                        Message prev;
+                        for (;;) {
+                            prev = p;
+                            p = p.next;
+                            if (p == null || when < p.when) {
+                                break;
+                            }
+                        }
+                        if (p == null) {
+                            /* Inserting at tail of queue */
+                            mLast = msg;
+                        }
+                        msg.next = p; // invariant: p == prev.next
+                        prev.next = msg;
+                    }
+                } else {
+                    needWake = mBlocked && p.target == null && msg.isAsynchronous();
+                    Message prev;
+                    for (;;) {
+                        prev = p;
+                        p = p.next;
+                        if (p == null || when < p.when) {
+                            break;
+                        }
+                        if (needWake && p.isAsynchronous()) {
+                            needWake = false;
+                        }
+                    }
+                    msg.next = p; // invariant: p == prev.next
+                    prev.next = msg;
+
+                    /*
+                     * If this block is executing then we have a build without tail tracking -
+                     * specifically: Flags.messageQueueTailTracking() == false. This is determined
+                     * at build time so the flag won't change on us during runtime.
+                     *
+                     * Since we don't want to pepper the code with extra checks, we only check
+                     * for tail tracking when we might use mLast. Otherwise, we continue to update
+                     * mLast as the tail of the list.
+                     *
+                     * In this case however we are not maintaining mLast correctly. Since we never
+                     * use it, this is fine. However, we run the risk of leaking a reference.
+                     * So set mLast to null in this case to avoid any Message leaks. The other
+                     * sites will never use the value so we are safe against null pointer derefs.
+                     */
+                    mLast = null;
+                }
+            }
+
+            if (msg.isAsynchronous()) {
+                mAsyncMessageCount++;
             }
 
             // We can assume mPtr != 0 because mQuitting is false.
@@ -692,10 +779,17 @@
                    && (object == null || p.obj == object)) {
                 Message n = p.next;
                 mMessages = n;
+                if (p.isAsynchronous()) {
+                    mAsyncMessageCount--;
+                }
                 p.recycleUnchecked();
                 p = n;
             }
 
+            if (p == null) {
+                mLast = mMessages;
+            }
+
             // Remove all messages after front.
             while (p != null) {
                 Message n = p.next;
@@ -703,8 +797,14 @@
                     if (n.target == h && n.what == what
                         && (object == null || n.obj == object)) {
                         Message nn = n.next;
+                        if (n.isAsynchronous()) {
+                            mAsyncMessageCount--;
+                        }
                         n.recycleUnchecked();
                         p.next = nn;
+                        if (p.next == null) {
+                            mLast = p;
+                        }
                         continue;
                     }
                 }
@@ -726,10 +826,17 @@
                    && (object == null || object.equals(p.obj))) {
                 Message n = p.next;
                 mMessages = n;
+                if (p.isAsynchronous()) {
+                    mAsyncMessageCount--;
+                }
                 p.recycleUnchecked();
                 p = n;
             }
 
+            if (p == null) {
+                mLast = mMessages;
+            }
+
             // Remove all messages after front.
             while (p != null) {
                 Message n = p.next;
@@ -737,8 +844,14 @@
                     if (n.target == h && n.what == what
                         && (object == null || object.equals(n.obj))) {
                         Message nn = n.next;
+                        if (n.isAsynchronous()) {
+                            mAsyncMessageCount--;
+                        }
                         n.recycleUnchecked();
                         p.next = nn;
+                        if (p.next == null) {
+                            mLast = p;
+                        }
                         continue;
                     }
                 }
@@ -760,10 +873,17 @@
                    && (object == null || p.obj == object)) {
                 Message n = p.next;
                 mMessages = n;
+                if (p.isAsynchronous()) {
+                    mAsyncMessageCount--;
+                }
                 p.recycleUnchecked();
                 p = n;
             }
 
+            if (p == null) {
+                mLast = mMessages;
+            }
+
             // Remove all messages after front.
             while (p != null) {
                 Message n = p.next;
@@ -771,8 +891,14 @@
                     if (n.target == h && n.callback == r
                         && (object == null || n.obj == object)) {
                         Message nn = n.next;
+                        if (n.isAsynchronous()) {
+                            mAsyncMessageCount--;
+                        }
                         n.recycleUnchecked();
                         p.next = nn;
+                        if (p.next == null) {
+                            mLast = p;
+                        }
                         continue;
                     }
                 }
@@ -794,10 +920,17 @@
                    && (object == null || object.equals(p.obj))) {
                 Message n = p.next;
                 mMessages = n;
+                if (p.isAsynchronous()) {
+                    mAsyncMessageCount--;
+                }
                 p.recycleUnchecked();
                 p = n;
             }
 
+            if (p == null) {
+                mLast = mMessages;
+            }
+
             // Remove all messages after front.
             while (p != null) {
                 Message n = p.next;
@@ -805,8 +938,14 @@
                     if (n.target == h && n.callback == r
                         && (object == null || object.equals(n.obj))) {
                         Message nn = n.next;
+                        if (n.isAsynchronous()) {
+                            mAsyncMessageCount--;
+                        }
                         n.recycleUnchecked();
                         p.next = nn;
+                        if (p.next == null) {
+                            mLast = p;
+                        }
                         continue;
                     }
                 }
@@ -829,18 +968,31 @@
                     && (object == null || p.obj == object)) {
                 Message n = p.next;
                 mMessages = n;
+                if (p.isAsynchronous()) {
+                    mAsyncMessageCount--;
+                }
                 p.recycleUnchecked();
                 p = n;
             }
 
+            if (p == null) {
+                mLast = mMessages;
+            }
+
             // Remove all messages after front.
             while (p != null) {
                 Message n = p.next;
                 if (n != null) {
                     if (n.target == h && (object == null || n.obj == object)) {
                         Message nn = n.next;
+                        if (n.isAsynchronous()) {
+                            mAsyncMessageCount--;
+                        }
                         n.recycleUnchecked();
                         p.next = nn;
+                        if (p.next == null) {
+                            mLast = p;
+                        }
                         continue;
                     }
                 }
@@ -862,18 +1014,31 @@
                     && (object == null || object.equals(p.obj))) {
                 Message n = p.next;
                 mMessages = n;
+                if (p.isAsynchronous()) {
+                    mAsyncMessageCount--;
+                }
                 p.recycleUnchecked();
                 p = n;
             }
 
+            if (p == null) {
+                mLast = mMessages;
+            }
+
             // Remove all messages after front.
             while (p != null) {
                 Message n = p.next;
                 if (n != null) {
                     if (n.target == h && (object == null || object.equals(n.obj))) {
                         Message nn = n.next;
+                        if (n.isAsynchronous()) {
+                            mAsyncMessageCount--;
+                        }
                         n.recycleUnchecked();
                         p.next = nn;
+                        if (p.next == null) {
+                            mLast = p;
+                        }
                         continue;
                     }
                 }
@@ -890,6 +1055,8 @@
             p = n;
         }
         mMessages = null;
+        mLast = null;
+        mAsyncMessageCount = 0;
     }
 
     private void removeAllFutureMessagesLocked() {
@@ -911,9 +1078,14 @@
                     p = n;
                 }
                 p.next = null;
+                mLast = p;
+
                 do {
                     p = n;
                     n = p.next;
+                    if (p.isAsynchronous()) {
+                        mAsyncMessageCount--;
+                    }
                     p.recycleUnchecked();
                 } while (n != null);
             }
diff --git a/core/java/android/os/OWNERS b/core/java/android/os/OWNERS
index d3f2c7a..eb5b511 100644
--- a/core/java/android/os/OWNERS
+++ b/core/java/android/os/OWNERS
@@ -94,4 +94,8 @@
 per-file Temperature.java = file:/THERMAL_OWNERS
 
 # SecurityStateManager
-per-file *SecurityStateManager* = file:/SECURITY_STATE_OWNERS
\ No newline at end of file
+per-file *SecurityStateManager* = file:/SECURITY_STATE_OWNERS
+
+# SystemConfig
+per-file ISystemConfig.aidl = file:/PACKAGE_MANAGER_OWNERS
+per-file SystemConfigManager.java = file:/PACKAGE_MANAGER_OWNERS
diff --git a/core/java/android/os/PerformanceHintManager.java b/core/java/android/os/PerformanceHintManager.java
index e005910..746278f 100644
--- a/core/java/android/os/PerformanceHintManager.java
+++ b/core/java/android/os/PerformanceHintManager.java
@@ -149,13 +149,47 @@
         @TestApi
         public static final int CPU_LOAD_RESUME = 3;
 
+        /**
+         * This hint indicates an increase in GPU workload intensity. It means that
+         * this hint session needs extra GPU resources to meet the target duration.
+         * This hint must be sent before reporting the actual duration to the session.
+         *
+         * @hide
+         */
+        @TestApi
+        @FlaggedApi(Flags.FLAG_ADPF_GPU_REPORT_ACTUAL_WORK_DURATION)
+        public static final int GPU_LOAD_UP = 5;
+
+        /**
+         * This hint indicates a decrease in GPU workload intensity. It means that
+         * this hint session can reduce GPU resources and still meet the target duration.
+         *
+         * @hide
+         */
+        @TestApi
+        @FlaggedApi(Flags.FLAG_ADPF_GPU_REPORT_ACTUAL_WORK_DURATION)
+        public static final int GPU_LOAD_DOWN = 6;
+
+        /**
+        * This hint indicates an upcoming GPU workload that is completely changed and
+        * unknown. It means that the hint session should reset GPU resources to a known
+        * baseline to prepare for an arbitrary load, and must wake up if inactive.
+         *
+         * @hide
+         */
+        @TestApi
+        @FlaggedApi(Flags.FLAG_ADPF_GPU_REPORT_ACTUAL_WORK_DURATION)
+        public static final int GPU_LOAD_RESET = 7;
+
         /** @hide */
         @Retention(RetentionPolicy.SOURCE)
         @IntDef(prefix = {"CPU_LOAD_"}, value = {
             CPU_LOAD_UP,
             CPU_LOAD_DOWN,
             CPU_LOAD_RESET,
-            CPU_LOAD_RESUME
+            CPU_LOAD_RESUME,
+            GPU_LOAD_UP,
+            GPU_LOAD_DOWN
         })
         public @interface Hint {}
 
@@ -170,7 +204,7 @@
         }
 
         /**
-         * Updates this session's target duration for each cycle of work.
+         * Updates this session's target total duration for each cycle of work.
          *
          * @param targetDurationNanos the new desired duration in nanoseconds
          */
diff --git a/core/java/android/os/PowerComponents.java b/core/java/android/os/PowerComponents.java
index 9c11ad4..164e265 100644
--- a/core/java/android/os/PowerComponents.java
+++ b/core/java/android/os/PowerComponents.java
@@ -40,6 +40,7 @@
  *
  * @hide
  */
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
 class PowerComponents {
     private final BatteryConsumer.BatteryConsumerData mData;
 
diff --git a/core/java/android/os/PowerMonitorReadings.java b/core/java/android/os/PowerMonitorReadings.java
index bb677d5..a0ab066 100644
--- a/core/java/android/os/PowerMonitorReadings.java
+++ b/core/java/android/os/PowerMonitorReadings.java
@@ -71,7 +71,7 @@
      */
     @FlaggedApi("com.android.server.power.optimization.power_monitor_api")
     @ElapsedRealtimeLong
-    public long getTimestamp(@NonNull PowerMonitor powerMonitor) {
+    public long getTimestampMillis(@NonNull PowerMonitor powerMonitor) {
         int offset = Arrays.binarySearch(mPowerMonitors, powerMonitor, POWER_MONITOR_COMPARATOR);
         if (offset >= 0) {
             return mTimestampsMs[offset];
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index fc8523e..dd0436c 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -19,8 +19,10 @@
 import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
 
 import android.annotation.ElapsedRealtimeLong;
+import android.annotation.FlaggedApi;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
 import android.annotation.UptimeMillisLong;
@@ -36,6 +38,7 @@
 
 import com.android.internal.os.SomeArgs;
 import com.android.internal.util.Preconditions;
+import com.android.sdksandbox.flags.Flags;
 
 import dalvik.system.VMRuntime;
 
@@ -757,7 +760,6 @@
                                                   @Nullable String invokeWith,
                                                   @Nullable String packageName,
                                                   @Nullable long[] disabledCompatChanges,
-                                                  boolean bindMountSyspropOverrides,
                                                   @Nullable String[] zygoteArgs) {
         // Webview zygote can't access app private data files, so doesn't need to know its data
         // info.
@@ -767,7 +769,7 @@
                     /*zygotePolicyFlags=*/ ZYGOTE_POLICY_FLAG_EMPTY, /*isTopApp=*/ false,
                 disabledCompatChanges, /* pkgDataInfoMap */ null,
                 /* whitelistedDataInfoMap */ null, /* bindMountAppsData */ false,
-                /* bindMountAppStorageDirs */ false, bindMountSyspropOverrides, zygoteArgs);
+                /* bindMountAppStorageDirs */ false, /* bindMountSyspropOverrides */ false, zygoteArgs);
     }
 
     /**
@@ -979,12 +981,10 @@
     }
 
     /**
-     * Returns whether the provided UID belongs to a SDK sandbox process.
-     *
-     * @hide
+     * Returns whether the provided UID belongs to an  sdk sandbox process
+     * @see android.app.sdksandbox.SdkSandboxManager
      */
-    @SystemApi(client = MODULE_LIBRARIES)
-    @TestApi
+    @SuppressLint("UnflaggedApi") // promoting from @SystemApi.
     @android.ravenwood.annotation.RavenwoodKeep
     public static final boolean isSdkSandboxUid(int uid) {
         uid = UserHandle.getAppId(uid);
@@ -992,15 +992,20 @@
     }
 
     /**
+     * Returns the app uid corresponding to an sdk sandbox uid.
+     * @see android.app.sdksandbox.SdkSandboxManager
      *
-     * Returns the app process corresponding to an sdk sandbox process.
+     * @param uid the sdk sandbox uid
+     * @return the app uid for the given sdk sandbox uid
      *
-     * @hide
+     * @throws IllegalArgumentException if input is not an sdk sandbox uid
      */
-    @SystemApi(client = MODULE_LIBRARIES)
-    @TestApi
+    @SuppressLint("UnflaggedApi") // promoting from @SystemApi.
     @android.ravenwood.annotation.RavenwoodKeep
     public static final int getAppUidForSdkSandboxUid(int uid) {
+        if (!isSdkSandboxUid(uid)) {
+            throw new IllegalArgumentException("Input UID is not an SDK sandbox UID");
+        }
         return uid - (FIRST_SDK_SANDBOX_UID - FIRST_APPLICATION_UID);
     }
 
@@ -1013,11 +1018,30 @@
     @SystemApi(client = MODULE_LIBRARIES)
     @TestApi
     @android.ravenwood.annotation.RavenwoodKeep
+    // TODO(b/318651609): Deprecate once Process#getSdkSandboxUidForAppUid is rolled out to 100%
     public static final int toSdkSandboxUid(int uid) {
         return uid + (FIRST_SDK_SANDBOX_UID - FIRST_APPLICATION_UID);
     }
 
     /**
+     * Returns the sdk sandbox uid corresponding to an app uid.
+     * @see android.app.sdksandbox.SdkSandboxManager
+     *
+     * @param uid the app uid
+     * @return the sdk sandbox uid for the given app uid
+     *
+     * @throws IllegalArgumentException if input is not an app uid
+     */
+    @FlaggedApi(Flags.FLAG_SDK_SANDBOX_UID_TO_APP_UID_API)
+    @android.ravenwood.annotation.RavenwoodKeep
+    public static final int getSdkSandboxUidForAppUid(int uid) {
+        if (!isApplicationUid(uid)) {
+            throw new IllegalArgumentException("Input UID is not an app UID");
+        }
+        return uid + (FIRST_SDK_SANDBOX_UID - FIRST_APPLICATION_UID);
+    }
+
+    /**
      * Returns whether the current process is a sdk sandbox process.
      */
     @android.ravenwood.annotation.RavenwoodKeep
diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java
index f71c269..07f7690 100644
--- a/core/java/android/os/RecoverySystem.java
+++ b/core/java/android/os/RecoverySystem.java
@@ -18,8 +18,6 @@
 
 import static android.view.Display.DEFAULT_DISPLAY;
 
-import static java.nio.charset.StandardCharsets.UTF_8;
-
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -47,11 +45,8 @@
 import android.util.Log;
 import android.view.Display;
 
-import libcore.io.Streams;
-
 import java.io.ByteArrayInputStream;
 import java.io.File;
-import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.FileWriter;
 import java.io.IOException;
@@ -75,7 +70,6 @@
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipFile;
-import java.util.zip.ZipInputStream;
 
 import sun.security.pkcs.PKCS7;
 import sun.security.pkcs.SignerInfo;
@@ -426,72 +420,43 @@
         } finally {
             raf.close();
         }
-
-        // Additionally verify the package compatibility.
-        if (!readAndVerifyPackageCompatibilityEntry(packageFile)) {
-            throw new SignatureException("package compatibility verification failed");
-        }
     }
 
     /**
      * Verifies the compatibility entry from an {@link InputStream}.
      *
-     * @return the verification result.
+     * @param inputStream The stream that contains the package compatibility info.
+     * @throws IOException Never.
+     * @return {@code true}.
+     * @deprecated This function no longer checks {@code inputStream} and
+     *   unconditionally returns true. Instead, check compatibility when the
+     *   OTA package is generated.
      */
-    @UnsupportedAppUsage
+    @Deprecated
+    @UnsupportedAppUsage(
+            publicAlternatives = "Use {@code true} directly",
+            maxTargetSdk = Build.VERSION_CODES.VANILLA_ICE_CREAM)
     private static boolean verifyPackageCompatibility(InputStream inputStream) throws IOException {
-        ArrayList<String> list = new ArrayList<>();
-        ZipInputStream zis = new ZipInputStream(inputStream);
-        ZipEntry entry;
-        while ((entry = zis.getNextEntry()) != null) {
-            long entrySize = entry.getSize();
-            if (entrySize > Integer.MAX_VALUE || entrySize < 0) {
-                throw new IOException(
-                        "invalid entry size (" + entrySize + ") in the compatibility file");
-            }
-            byte[] bytes = new byte[(int) entrySize];
-            Streams.readFully(zis, bytes);
-            list.add(new String(bytes, UTF_8));
-        }
-        if (list.isEmpty()) {
-            throw new IOException("no entries found in the compatibility file");
-        }
-        return (VintfObject.verify(list.toArray(new String[list.size()])) == 0);
-    }
-
-    /**
-     * Reads and verifies the compatibility entry in an OTA zip package. The compatibility entry is
-     * a zip file (inside the OTA package zip).
-     *
-     * @return {@code true} if the entry doesn't exist or verification passes.
-     */
-    private static boolean readAndVerifyPackageCompatibilityEntry(File packageFile)
-            throws IOException {
-        try (ZipFile zip = new ZipFile(packageFile)) {
-            ZipEntry entry = zip.getEntry("compatibility.zip");
-            if (entry == null) {
-                return true;
-            }
-            InputStream inputStream = zip.getInputStream(entry);
-            return verifyPackageCompatibility(inputStream);
-        }
+        return true;
     }
 
     /**
      * Verifies the package compatibility info against the current system.
      *
      * @param compatibilityFile the {@link File} that contains the package compatibility info.
-     * @throws IOException if there were any errors reading the compatibility file.
-     * @return the compatibility verification result.
+     * @throws IOException Never.
+     * @return {@code true}
+     * @deprecated This function no longer checks {@code compatibilityFile} and
+     *   unconditionally returns true. Instead, check compatibility when the
+     *   OTA package is generated.
      *
      * {@hide}
      */
+    @Deprecated
     @SystemApi
     @SuppressLint("RequiresPermission")
     public static boolean verifyPackageCompatibility(File compatibilityFile) throws IOException {
-        try (InputStream inputStream = new FileInputStream(compatibilityFile)) {
-            return verifyPackageCompatibility(inputStream);
-        }
+        return true;
     }
 
     /**
diff --git a/core/java/android/os/ServiceSpecificException.java b/core/java/android/os/ServiceSpecificException.java
index 49ce40b..df503e8 100644
--- a/core/java/android/os/ServiceSpecificException.java
+++ b/core/java/android/os/ServiceSpecificException.java
@@ -18,6 +18,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
 
 /**
  * An exception specific to a service.
@@ -33,6 +34,7 @@
  * @hide
  */
 @SystemApi
+@RavenwoodKeepWholeClass
 public class ServiceSpecificException extends RuntimeException {
     public final int errorCode;
 
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index e32a8f3..8c8af0e 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -2373,13 +2373,29 @@
     /** Assume locked until we hear otherwise */
     private static volatile boolean sCeStorageUnlocked = false;
 
+    /**
+     * Avoid (potentially) costly and repeated lookups to the same mount service.
+     * Note that we don't use the Singleton wrapper as lookup may fail early during boot.
+     */
+    private static volatile IStorageManager sStorageManager;
+
     private static boolean isCeStorageUnlocked(int userId) {
-        final IStorageManager storage = IStorageManager.Stub
+        IStorageManager storage = sStorageManager;
+        if (storage == null) {
+            storage = IStorageManager.Stub
                 .asInterface(ServiceManager.getService("mount"));
+            // As the queried handle may be null early during boot, only stash valid handles,
+            // avoiding races with concurrent service queries.
+            if (storage != null) {
+                sStorageManager = storage;
+            }
+        }
         if (storage != null) {
             try {
                 return storage.isCeStorageUnlocked(userId);
             } catch (RemoteException ignored) {
+                // Conservatively clear the ref, allowing refresh if the remote process restarts.
+                sStorageManager = null;
             }
         }
         return false;
diff --git a/core/java/android/os/SystemConfigManager.java b/core/java/android/os/SystemConfigManager.java
index 77843d9..21ffbf1 100644
--- a/core/java/android/os/SystemConfigManager.java
+++ b/core/java/android/os/SystemConfigManager.java
@@ -161,4 +161,18 @@
         }
         return Collections.emptyList();
     }
+
+    /**
+     * Return the packages that are prevented from being disabled, where if
+     * disabled it would result in a non-functioning system or similar.
+     * @hide
+     */
+    @NonNull
+    public List<String> getPreventUserDisablePackages() {
+        try {
+            return mInterface.getPreventUserDisablePackages();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
 }
diff --git a/core/java/android/os/UidBatteryConsumer.java b/core/java/android/os/UidBatteryConsumer.java
index 3eea94e..53af838 100644
--- a/core/java/android/os/UidBatteryConsumer.java
+++ b/core/java/android/os/UidBatteryConsumer.java
@@ -37,6 +37,7 @@
  *
  * @hide
  */
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
 public final class UidBatteryConsumer extends BatteryConsumer {
 
     static final int CONSUMER_TYPE_UID = 1;
@@ -223,6 +224,7 @@
     /**
      * Builder for UidBatteryConsumer.
      */
+    @android.ravenwood.annotation.RavenwoodKeepWholeClass
     public static final class Builder extends BaseBuilder<Builder> {
         private static final String PACKAGE_NAME_UNINITIALIZED = "";
         private final BatteryStats.Uid mBatteryStatsUid;
diff --git a/core/java/android/os/UpdateEngineStable.java b/core/java/android/os/UpdateEngineStable.java
new file mode 100644
index 0000000..9e2593e
--- /dev/null
+++ b/core/java/android/os/UpdateEngineStable.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.annotation.IntDef;
+
+/**
+ * UpdateEngineStable handles calls to the update engine stalbe which takes care of A/B OTA updates.
+ * This interface has lesser functionalities than UpdateEngine and doesn't allow cancel.
+ *
+ * <p>The minimal flow is:
+ *
+ * <ol>
+ *   <li>Create a new UpdateEngineStable instance.
+ *   <li>Call {@link #bind}, provide callback function.
+ *   <li>Call {@link #applyPayloadFd}.
+ * </ol>
+ *
+ * The APIs defined in this class and UpdateEngineStableCallback class must be in sync with the ones
+ * in {@code system/update_engine/stable/android/os/IUpdateEngineStable.aidl} and {@code
+ * ssystem/update_engine/stable/android/os/IUpdateEngineStableCallback.aidl}.
+ *
+ * @hide
+ */
+public class UpdateEngineStable {
+    private static final String TAG = "UpdateEngineStable";
+
+    private static final String UPDATE_ENGINE_STABLE_SERVICE =
+            "android.os.UpdateEngineStableService";
+
+    /**
+     * Error codes from update engine upon finishing a call to {@link applyPayloadFd}. Values will
+     * be passed via the callback function {@link
+     * UpdateEngineStableCallback#onPayloadApplicationComplete}. Values must agree with the ones in
+     * {@code system/update_engine/common/error_code.h}.
+     */
+    /** @hide */
+    @IntDef(
+            value = {
+                UpdateEngine.ErrorCodeConstants.SUCCESS,
+                UpdateEngine.ErrorCodeConstants.ERROR,
+                UpdateEngine.ErrorCodeConstants.FILESYSTEM_COPIER_ERROR,
+                UpdateEngine.ErrorCodeConstants.POST_INSTALL_RUNNER_ERROR,
+                UpdateEngine.ErrorCodeConstants.PAYLOAD_MISMATCHED_TYPE_ERROR,
+                UpdateEngine.ErrorCodeConstants.INSTALL_DEVICE_OPEN_ERROR,
+                UpdateEngine.ErrorCodeConstants.KERNEL_DEVICE_OPEN_ERROR,
+                UpdateEngine.ErrorCodeConstants.DOWNLOAD_TRANSFER_ERROR,
+                UpdateEngine.ErrorCodeConstants.PAYLOAD_HASH_MISMATCH_ERROR,
+                UpdateEngine.ErrorCodeConstants.PAYLOAD_SIZE_MISMATCH_ERROR,
+                UpdateEngine.ErrorCodeConstants.DOWNLOAD_PAYLOAD_VERIFICATION_ERROR,
+                UpdateEngine.ErrorCodeConstants.PAYLOAD_TIMESTAMP_ERROR,
+                UpdateEngine.ErrorCodeConstants.UPDATED_BUT_NOT_ACTIVE,
+                UpdateEngine.ErrorCodeConstants.NOT_ENOUGH_SPACE,
+                UpdateEngine.ErrorCodeConstants.DEVICE_CORRUPTED,
+            })
+    public @interface ErrorCode {}
+
+    private final IUpdateEngineStable mUpdateEngineStable;
+    private IUpdateEngineStableCallback mUpdateEngineStableCallback = null;
+    private final Object mUpdateEngineStableCallbackLock = new Object();
+
+    /**
+     * Creates a new instance.
+     *
+     * @hide
+     */
+    public UpdateEngineStable() {
+        mUpdateEngineStable =
+                IUpdateEngineStable.Stub.asInterface(
+                        ServiceManager.getService(UPDATE_ENGINE_STABLE_SERVICE));
+        if (mUpdateEngineStable == null) {
+            throw new IllegalStateException("Failed to find " + UPDATE_ENGINE_STABLE_SERVICE);
+        }
+    }
+
+    /**
+     * Prepares this instance for use. The callback will be notified on any status change, and when
+     * the update completes. A handler can be supplied to control which thread runs the callback, or
+     * null.
+     *
+     * @hide
+     */
+    public boolean bind(final UpdateEngineStableCallback callback, final Handler handler) {
+        synchronized (mUpdateEngineStableCallbackLock) {
+            mUpdateEngineStableCallback =
+                    new IUpdateEngineStableCallback.Stub() {
+                        @Override
+                        public void onStatusUpdate(final int status, final float percent) {
+                            if (handler != null) {
+                                handler.post(
+                                        new Runnable() {
+                                            @Override
+                                            public void run() {
+                                                callback.onStatusUpdate(status, percent);
+                                            }
+                                        });
+                            } else {
+                                callback.onStatusUpdate(status, percent);
+                            }
+                        }
+
+                        @Override
+                        public void onPayloadApplicationComplete(final int errorCode) {
+                            if (handler != null) {
+                                handler.post(
+                                        new Runnable() {
+                                            @Override
+                                            public void run() {
+                                                callback.onPayloadApplicationComplete(errorCode);
+                                            }
+                                        });
+                            } else {
+                                callback.onPayloadApplicationComplete(errorCode);
+                            }
+                        }
+
+                        @Override
+                        public int getInterfaceVersion() {
+                            return super.VERSION;
+                        }
+
+                        @Override
+                        public String getInterfaceHash() {
+                            return super.HASH;
+                        }
+                    };
+
+            try {
+                return mUpdateEngineStable.bind(mUpdateEngineStableCallback);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+    }
+
+    /**
+     * Equivalent to {@code bind(callback, null)}.
+     *
+     * @hide
+     */
+    public boolean bind(final UpdateEngineStableCallback callback) {
+        return bind(callback, null);
+    }
+
+    /**
+     * Applies payload from given ParcelFileDescriptor. Usage is same as UpdateEngine#applyPayload
+     *
+     * @hide
+     */
+    public void applyPayloadFd(
+            ParcelFileDescriptor fd, long offset, long size, String[] headerKeyValuePairs) {
+        try {
+            mUpdateEngineStable.applyPayloadFd(fd, offset, size, headerKeyValuePairs);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Unbinds the last bound callback function.
+     *
+     * @hide
+     */
+    public boolean unbind() {
+        synchronized (mUpdateEngineStableCallbackLock) {
+            if (mUpdateEngineStableCallback == null) {
+                return true;
+            }
+            try {
+                boolean result = mUpdateEngineStable.unbind(mUpdateEngineStableCallback);
+                mUpdateEngineStableCallback = null;
+                return result;
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+    }
+}
diff --git a/core/java/android/os/UpdateEngineStableCallback.java b/core/java/android/os/UpdateEngineStableCallback.java
new file mode 100644
index 0000000..4bcfb4b
--- /dev/null
+++ b/core/java/android/os/UpdateEngineStableCallback.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+/**
+ * Callback function for UpdateEngineStable. Used to keep the caller up to date with progress, so
+ * the UI (if any) can be updated.
+ *
+ * <p>The APIs defined in this class and UpdateEngineStable class must be in sync with the ones in
+ * system/update_engine/stable/android/os/IUpdateEngineStable.aidl and
+ * system/update_engine/stable/android/os/IUpdateEngineStableCallback.aidl.
+ *
+ * <p>{@hide}
+ */
+public abstract class UpdateEngineStableCallback {
+
+    /**
+     * Invoked when anything changes. The value of {@code status} will be one of the values from
+     * {@link UpdateEngine.UpdateStatusConstants}, and {@code percent} will be valid
+     *
+     * @hide
+     */
+    public abstract void onStatusUpdate(int status, float percent);
+
+    /**
+     * Invoked when the payload has been applied, whether successfully or unsuccessfully. The value
+     * of {@code errorCode} will be one of the values from {@link UpdateEngine.ErrorCodeConstants}.
+     *
+     * @hide
+     */
+    public abstract void onPayloadApplicationComplete(@UpdateEngineStable.ErrorCode int errorCode);
+}
diff --git a/core/java/android/os/VintfObject.java b/core/java/android/os/VintfObject.java
index 1f11197..4fc5131 100644
--- a/core/java/android/os/VintfObject.java
+++ b/core/java/android/os/VintfObject.java
@@ -18,7 +18,6 @@
 
 import android.annotation.NonNull;
 import android.annotation.TestApi;
-import android.util.Slog;
 
 import java.util.Map;
 
@@ -44,44 +43,8 @@
     public static native String[] report();
 
     /**
-     * Verify that the given metadata for an OTA package is compatible with
-     * this device.
-     *
-     * @param packageInfo a list of serialized form of HalManifest's /
-     * CompatibilityMatri'ces (XML).
-     * @return = 0 if success (compatible)
-     *         &gt; 0 if incompatible
-     *         &lt; 0 if any error (mount partition fails, illformed XML, etc.)
-     *
-     * @deprecated Checking compatibility against an OTA package is no longer
-     * supported because the format of VINTF metadata in the OTA package may not
-     * be recognized by the current system.
-     *
-     * <p>
-     * <ul>
-     * <li>This function always returns 0 for non-empty {@code packageInfo}.
-     * </li>
-     * <li>This function returns the result of {@link #verifyWithoutAvb} for
-     * null or empty {@code packageInfo}.</li>
-     * </ul>
-     *
-     * @hide
-     */
-    @Deprecated
-    public static int verify(String[] packageInfo) {
-        if (packageInfo != null && packageInfo.length > 0) {
-            Slog.w(LOG_TAG, "VintfObject.verify() with non-empty packageInfo is deprecated. "
-                    + "Skipping compatibility checks for update package.");
-            return 0;
-        }
-        Slog.w(LOG_TAG, "VintfObject.verify() is deprecated. Call verifyWithoutAvb() instead.");
-        return verifyWithoutAvb();
-    }
-
-    /**
-     * Verify Vintf compatibility on the device without checking AVB
-     * (Android Verified Boot). It is useful to verify a running system
-     * image where AVB check is irrelevant.
+     * Verify Vintf compatibility on the device at boot time. Certain checks
+     * like kernel checks, AVB checks are disabled.
      *
      * @return = 0 if success (compatible)
      *         > 0 if incompatible
@@ -89,7 +52,7 @@
      *
      * @hide
      */
-    public static native int verifyWithoutAvb();
+    public static native int verifyBuildAtBoot();
 
     /**
      * @return a list of HAL names and versions that is supported by this
diff --git a/core/java/android/os/WorkDuration.java b/core/java/android/os/WorkDuration.java
index 4fdc34f..2ebcd83 100644
--- a/core/java/android/os/WorkDuration.java
+++ b/core/java/android/os/WorkDuration.java
@@ -26,7 +26,7 @@
  * in each component, see
  * {@link PerformanceHintManager.Session#reportActualWorkDuration(WorkDuration)}.
  *
- * All timings should be in {@link SystemClock#elapsedRealtimeNanos()}.
+ * All timings should be in {@link SystemClock#uptimeNanos()} and measured in wall time.
  */
 @FlaggedApi(Flags.FLAG_ADPF_GPU_REPORT_ACTUAL_WORK_DURATION)
 public final class WorkDuration implements Parcelable {
@@ -50,17 +50,9 @@
 
     public WorkDuration() {}
 
-    public WorkDuration(long workPeriodStartTimestampNanos,
-                      long actualTotalDurationNanos,
-                      long actualCpuDurationNanos,
-                      long actualGpuDurationNanos) {
-        mWorkPeriodStartTimestampNanos = workPeriodStartTimestampNanos;
-        mActualTotalDurationNanos = actualTotalDurationNanos;
-        mActualCpuDurationNanos = actualCpuDurationNanos;
-        mActualGpuDurationNanos = actualGpuDurationNanos;
-    }
-
     /**
+     * Constructor for testing.
+     *
      * @hide
      */
     public WorkDuration(long workPeriodStartTimestampNanos,
@@ -86,7 +78,7 @@
     /**
      * Sets the work period start timestamp in nanoseconds.
      *
-     * All timings should be in {@link SystemClock#elapsedRealtimeNanos()}.
+     * All timings should be in {@link SystemClock#uptimeNanos()}.
      */
     public void setWorkPeriodStartTimestampNanos(long workPeriodStartTimestampNanos) {
         if (workPeriodStartTimestampNanos <= 0) {
@@ -99,7 +91,7 @@
     /**
      * Sets the actual total duration in nanoseconds.
      *
-     * All timings should be in {@link SystemClock#elapsedRealtimeNanos()}.
+     * All timings should be in {@link SystemClock#uptimeNanos()}.
      */
     public void setActualTotalDurationNanos(long actualTotalDurationNanos) {
         if (actualTotalDurationNanos <= 0) {
@@ -111,7 +103,7 @@
     /**
      * Sets the actual CPU duration in nanoseconds.
      *
-     * All timings should be in {@link SystemClock#elapsedRealtimeNanos()}.
+     * All timings should be in {@link SystemClock#uptimeNanos()}.
      */
     public void setActualCpuDurationNanos(long actualCpuDurationNanos) {
         if (actualCpuDurationNanos <= 0) {
@@ -123,7 +115,7 @@
     /**
      * Sets the actual GPU duration in nanoseconds.
      *
-     * All timings should be in {@link SystemClock#elapsedRealtimeNanos()}.
+     * All timings should be in {@link SystemClock#uptimeNanos()}.
      */
     public void setActualGpuDurationNanos(long actualGpuDurationNanos) {
         if (actualGpuDurationNanos < 0) {
@@ -135,7 +127,7 @@
     /**
      * Returns the work period start timestamp based in nanoseconds.
      *
-     * All timings should be in {@link SystemClock#elapsedRealtimeNanos()}.
+     * All timings should be in {@link SystemClock#uptimeNanos()}.
      */
     public long getWorkPeriodStartTimestampNanos() {
         return mWorkPeriodStartTimestampNanos;
@@ -144,7 +136,7 @@
     /**
      * Returns the actual total duration in nanoseconds.
      *
-     * All timings should be in {@link SystemClock#elapsedRealtimeNanos()}.
+     * All timings should be in {@link SystemClock#uptimeNanos()}.
      */
     public long getActualTotalDurationNanos() {
         return mActualTotalDurationNanos;
@@ -153,7 +145,7 @@
     /**
      * Returns the actual CPU duration in nanoseconds.
      *
-     * All timings should be in {@link SystemClock#elapsedRealtimeNanos()}.
+     * All timings should be in {@link SystemClock#uptimeNanos()}.
      */
     public long getActualCpuDurationNanos() {
         return mActualCpuDurationNanos;
@@ -162,7 +154,7 @@
     /**
      * Returns the actual GPU duration in nanoseconds.
      *
-     * All timings should be in {@link SystemClock#elapsedRealtimeNanos()}.
+     * All timings should be in {@link SystemClock#uptimeNanos()}.
      */
     public long getActualGpuDurationNanos() {
         return mActualGpuDurationNanos;
diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java
index c14810b..f3496e7 100644
--- a/core/java/android/os/ZygoteProcess.java
+++ b/core/java/android/os/ZygoteProcess.java
@@ -425,6 +425,8 @@
                 throw new ZygoteStartFailedEx("Embedded newlines not allowed");
             } else if (arg.indexOf('\r') >= 0) {
                 throw new ZygoteStartFailedEx("Embedded carriage returns not allowed");
+            } else if (arg.indexOf('\u0000') >= 0) {
+                throw new ZygoteStartFailedEx("Embedded nulls not allowed");
             }
         }
 
@@ -965,6 +967,14 @@
             return true;
         }
 
+        for (/* NonNull */ String s : mApiDenylistExemptions) {
+            // indexOf() is intrinsified and faster than contains().
+            if (s.indexOf('\n') >= 0 || s.indexOf('\r') >= 0 || s.indexOf('\u0000') >= 0) {
+                Slog.e(LOG_TAG, "Failed to set API denylist exemptions: Bad character");
+                mApiDenylistExemptions = Collections.emptyList();
+                return false;
+            }
+        }
         try {
             state.mZygoteOutputWriter.write(Integer.toString(mApiDenylistExemptions.size() + 1));
             state.mZygoteOutputWriter.newLine();
diff --git a/core/java/android/os/flags.aconfig b/core/java/android/os/flags.aconfig
index 83d237d..0db90bf 100644
--- a/core/java/android/os/flags.aconfig
+++ b/core/java/android/os/flags.aconfig
@@ -91,3 +91,18 @@
     is_fixed_read_only: true
     bug: "315037695"
 }
+
+flag {
+    name: "strict_mode_restricted_network"
+    namespace: "backstage_power"
+    description: "Guards StrictMode APIs for detecting restricted network access."
+    bug: "317250784"
+}
+
+flag {
+    name: "message_queue_tail_tracking"
+    namespace: "system_performance"
+    description: "track tail of message queue."
+    bug: "305311707"
+    is_fixed_read_only: true
+}
diff --git a/core/java/android/os/health/SystemHealthManager.java b/core/java/android/os/health/SystemHealthManager.java
index 2d53341..322a8e6 100644
--- a/core/java/android/os/health/SystemHealthManager.java
+++ b/core/java/android/os/health/SystemHealthManager.java
@@ -25,8 +25,8 @@
 import android.os.BatteryStats;
 import android.os.Build;
 import android.os.Bundle;
-import android.os.Handler;
 import android.os.IPowerStatsService;
+import android.os.OutcomeReceiver;
 import android.os.PowerMonitor;
 import android.os.PowerMonitorReadings;
 import android.os.Process;
@@ -39,6 +39,7 @@
 import java.util.Arrays;
 import java.util.Comparator;
 import java.util.List;
+import java.util.concurrent.Executor;
 import java.util.function.Consumer;
 
 /**
@@ -161,12 +162,12 @@
      * (on-device power rail monitor) rails and modeled energy consumers.  If ODPM is unsupported
      * on this device this method delivers an empty list.
      *
-     * @param handler  optional Handler to deliver the callback. If not supplied, the callback
+     * @param executor optional Handler to deliver the callback. If not supplied, the callback
      *                 may be invoked on an arbitrary thread.
      * @param onResult callback for the result
      */
     @FlaggedApi("com.android.server.power.optimization.power_monitor_api")
-    public void getSupportedPowerMonitors(@Nullable Handler handler,
+    public void getSupportedPowerMonitors(@Nullable Executor executor,
             @NonNull Consumer<List<PowerMonitor>> onResult) {
         final List<PowerMonitor> result;
         synchronized (mPowerMonitorsLock) {
@@ -180,15 +181,15 @@
             }
         }
         if (result != null) {
-            if (handler != null) {
-                handler.post(() -> onResult.accept(result));
+            if (executor != null) {
+                executor.execute(() -> onResult.accept(result));
             } else {
                 onResult.accept(result);
             }
             return;
         }
         try {
-            mPowerStats.getSupportedPowerMonitors(new ResultReceiver(handler) {
+            mPowerStats.getSupportedPowerMonitors(new ResultReceiver(null) {
                 @Override
                 protected void onReceiveResult(int resultCode, Bundle resultData) {
                     PowerMonitor[] array = resultData.getParcelableArray(
@@ -197,7 +198,11 @@
                     synchronized (mPowerMonitorsLock) {
                         mPowerMonitorsInfo = result;
                     }
-                    onResult.accept(result);
+                    if (executor != null) {
+                        executor.execute(()-> onResult.accept(result));
+                    } else {
+                        onResult.accept(result);
+                    }
                 }
             });
         } catch (RemoteException e) {
@@ -213,17 +218,22 @@
      * monitors.
      *
      * @param powerMonitors power monitors to be retrieved.
-     * @param handler       optional Handler to deliver the callbacks. If not supplied, the callback
-     *                      may be invoked on an arbitrary thread.
-     * @param onSuccess     callback for the result
-     * @param onError       callback invoked in case of an error
+     * @param executor      optional Executor to deliver the callbacks. If not supplied, the
+     *                      callback may be invoked on an arbitrary thread.
+     * @param onResult      callback for the result
      */
     @FlaggedApi("com.android.server.power.optimization.power_monitor_api")
     public void getPowerMonitorReadings(@NonNull List<PowerMonitor> powerMonitors,
-            @Nullable Handler handler, @NonNull Consumer<PowerMonitorReadings> onSuccess,
-            @NonNull Consumer<RuntimeException> onError) {
+            @Nullable Executor executor,
+            @NonNull OutcomeReceiver<PowerMonitorReadings, RuntimeException> onResult) {
         if (mPowerStats == null) {
-            onError.accept(new IllegalArgumentException("Unsupported power monitor"));
+            IllegalArgumentException error =
+                    new IllegalArgumentException("Unsupported power monitor");
+            if (executor != null) {
+                executor.execute(() -> onResult.onError(error));
+            } else {
+                onResult.onError(error);
+            }
             return;
         }
 
@@ -235,18 +245,31 @@
             indices[i] = powerMonitorsArray[i].index;
         }
         try {
-            mPowerStats.getPowerMonitorReadings(indices, new ResultReceiver(handler) {
+            mPowerStats.getPowerMonitorReadings(indices, new ResultReceiver(null) {
                 @Override
                 protected void onReceiveResult(int resultCode, Bundle resultData) {
                     if (resultCode == IPowerStatsService.RESULT_SUCCESS) {
-                        onSuccess.accept(new PowerMonitorReadings(powerMonitorsArray,
+                        PowerMonitorReadings result = new PowerMonitorReadings(powerMonitorsArray,
                                 resultData.getLongArray(IPowerStatsService.KEY_ENERGY),
-                                resultData.getLongArray(IPowerStatsService.KEY_TIMESTAMPS)));
-                    } else if (resultCode == IPowerStatsService.RESULT_UNSUPPORTED_POWER_MONITOR) {
-                        onError.accept(new IllegalArgumentException("Unsupported power monitor"));
+                                resultData.getLongArray(IPowerStatsService.KEY_TIMESTAMPS));
+                        if (executor != null) {
+                            executor.execute(() -> onResult.onResult(result));
+                        } else {
+                            onResult.onResult(result);
+                        }
                     } else {
-                        onError.accept(new IllegalStateException(
-                                "Unrecognized result code " + resultCode));
+                        RuntimeException error;
+                        if (resultCode == IPowerStatsService.RESULT_UNSUPPORTED_POWER_MONITOR) {
+                            error = new IllegalArgumentException("Unsupported power monitor");
+                        } else {
+                            error = new IllegalStateException(
+                                    "Unrecognized result code " + resultCode);
+                        }
+                        if (executor != null) {
+                            executor.execute(() -> onResult.onError(error));
+                        } else {
+                            onResult.onError(error);
+                        }
                     }
                 }
             });
diff --git a/core/java/android/os/storage/IStorageManager.aidl b/core/java/android/os/storage/IStorageManager.aidl
index 3ecf74e..54ed73c 100644
--- a/core/java/android/os/storage/IStorageManager.aidl
+++ b/core/java/android/os/storage/IStorageManager.aidl
@@ -134,16 +134,16 @@
     @EnforcePermission("MOUNT_UNMOUNT_FILESYSTEMS")
     void setDebugFlags(int flags, int mask) = 60;
     @EnforcePermission("STORAGE_INTERNAL")
-    void createUserStorageKeys(int userId, int serialNumber, boolean ephemeral) = 61;
+    void createUserStorageKeys(int userId, boolean ephemeral) = 61;
     @EnforcePermission("STORAGE_INTERNAL")
     void destroyUserStorageKeys(int userId) = 62;
     @EnforcePermission("STORAGE_INTERNAL")
-    void unlockCeStorage(int userId, int serialNumber, in byte[] secret) = 63;
+    void unlockCeStorage(int userId, in byte[] secret) = 63;
     @EnforcePermission("STORAGE_INTERNAL")
     void lockCeStorage(int userId) = 64;
     boolean isCeStorageUnlocked(int userId) = 65;
     @EnforcePermission("STORAGE_INTERNAL")
-    void prepareUserStorage(in String volumeUuid, int userId, int serialNumber, int flags) = 66;
+    void prepareUserStorage(in String volumeUuid, int userId, int flags) = 66;
     @EnforcePermission("STORAGE_INTERNAL")
     void destroyUserStorage(in String volumeUuid, int userId, int flags) = 67;
     @EnforcePermission("STORAGE_INTERNAL")
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 78a12f7..9587db1 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -1602,14 +1602,13 @@
      * This is only intended to be called by UserManagerService, as part of creating a user.
      *
      * @param userId ID of the user
-     * @param serialNumber serial number of the user
      * @param ephemeral whether the user is ephemeral
      * @throws RuntimeException on error.  The user's keys already existing is considered an error.
      * @hide
      */
-    public void createUserStorageKeys(int userId, int serialNumber, boolean ephemeral) {
+    public void createUserStorageKeys(int userId, boolean ephemeral) {
         try {
-            mStorageManager.createUserStorageKeys(userId, serialNumber, ephemeral);
+            mStorageManager.createUserStorageKeys(userId, ephemeral);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1653,9 +1652,9 @@
     }
 
     /** {@hide} */
-    public void prepareUserStorage(String volumeUuid, int userId, int serialNumber, int flags) {
+    public void prepareUserStorage(String volumeUuid, int userId, int flags) {
         try {
-            mStorageManager.prepareUserStorage(volumeUuid, userId, serialNumber, flags);
+            mStorageManager.prepareUserStorage(volumeUuid, userId, flags);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/permission/flags.aconfig b/core/java/android/permission/flags.aconfig
index d09c229..60143cc 100644
--- a/core/java/android/permission/flags.aconfig
+++ b/core/java/android/permission/flags.aconfig
@@ -64,3 +64,17 @@
   description: "enable Permission PREPARE_FACTORY_RESET."
   bug: "302016478"
 }
+
+flag {
+    name: "retail_demo_role_enabled"
+    namespace: "permissions"
+    description: "default retail demo role holder"
+    bug: "274132354"
+}
+
+flag {
+    name: "wallet_role_enabled"
+    namespace: "wallet_integration"
+    description: "This flag is used to enabled the Wallet Role for all users on the device"
+    bug: "283989236"
+}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 942ce971..47065e1 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -108,10 +108,10 @@
 import java.lang.annotation.Target;
 import java.lang.reflect.Field;
 import java.net.URISyntaxException;
-import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Locale;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
@@ -3585,10 +3585,12 @@
                     || applicationInfo.isSignedWithPlatformKey();
         }
 
-        public ArrayMap<String, String> getStringsForPrefix(ContentResolver cr, String prefix,
-                List<String> names) {
+        private ArrayMap<String, String> getStringsForPrefixStripPrefix(
+                ContentResolver cr, String prefix, String[] names) {
             String namespace = prefix.substring(0, prefix.length() - 1);
             ArrayMap<String, String> keyValues = new ArrayMap<>();
+            int substringLength = prefix.length();
+
             int currentGeneration = -1;
             boolean needsGenerationTracker = false;
 
@@ -3601,22 +3603,30 @@
                                     + " type:" + mUri.getPath()
                                     + " in package:" + cr.getPackageName());
                         }
+                        // When a generation number changes, remove cached values, remove the old
+                        // generation tracker and request a new one
+                        generationTracker.destroy();
+                        mGenerationTrackers.remove(prefix);
                         for (int i = mValues.size() - 1; i >= 0; i--) {
                             String key = mValues.keyAt(i);
                             if (key.startsWith(prefix)) {
                                 mValues.remove(key);
                             }
                         }
+                        needsGenerationTracker = true;
                     } else {
                         boolean prefixCached = mValues.containsKey(prefix);
                         if (prefixCached) {
                             if (DEBUG) {
                                 Log.i(TAG, "Cache hit for prefix:" + prefix);
                             }
-                            if (!names.isEmpty()) {
+                            if (names.length > 0) {
                                 for (String name : names) {
+                                    // mValues can contain "null" values, need to use containsKey.
                                     if (mValues.containsKey(name)) {
-                                        keyValues.put(name, mValues.get(name));
+                                        keyValues.put(
+                                                name.substring(substringLength),
+                                                mValues.get(name));
                                     }
                                 }
                             } else {
@@ -3625,7 +3635,10 @@
                                     // Explicitly exclude the prefix as it is only there to
                                     // signal that the prefix has been cached.
                                     if (key.startsWith(prefix) && !key.equals(prefix)) {
-                                        keyValues.put(key, mValues.get(key));
+                                        String value = mValues.valueAt(i);
+                                        keyValues.put(
+                                                key.substring(substringLength),
+                                                value);
                                     }
                                 }
                             }
@@ -3685,14 +3698,22 @@
                 Map<String, String> flagsToValues =
                         (HashMap) b.getSerializable(Settings.NameValueTable.VALUE, java.util.HashMap.class);
                 // Only the flags requested by the caller
-                if (!names.isEmpty()) {
-                    for (Map.Entry<String, String> flag : flagsToValues.entrySet()) {
-                        if (names.contains(flag.getKey())) {
-                            keyValues.put(flag.getKey(), flag.getValue());
+                if (names.length > 0) {
+                    for (String name : names) {
+                        // flagsToValues can contain "null" values, need to use containsKey.
+                        if (flagsToValues.containsKey(name)) {
+                            keyValues.put(
+                                    name.substring(substringLength),
+                                    flagsToValues.get(name));
                         }
                     }
                 } else {
-                    keyValues.putAll(flagsToValues);
+                    keyValues.ensureCapacity(keyValues.size() + flagsToValues.size());
+                    for (Map.Entry<String, String> flag : flagsToValues.entrySet()) {
+                        keyValues.put(
+                                flag.getKey().substring(substringLength),
+                                flag.getValue());
+                    }
                 }
 
                 synchronized (NameValueCache.this) {
@@ -7428,6 +7449,20 @@
         public static final String DEFAULT_INPUT_METHOD = "default_input_method";
 
         /**
+         * Used only by {@link com.android.server.inputmethod.InputMethodManagerService} as a
+         * temporary data store of {@link #DEFAULT_INPUT_METHOD} while a virtual-device-specific
+         * input method is set as default.</p>
+         *
+         * <p>This should be considered to be an implementation detail of
+         * {@link com.android.server.inputmethod.InputMethodManagerService}.  Other system
+         * components should never rely on this value.</p>
+         *
+         * @see #DEFAULT_INPUT_METHOD
+         * @hide
+         */
+        public static final String DEFAULT_DEVICE_INPUT_METHOD = "default_device_input_method";
+
+        /**
          * Setting to record the input method subtype used by default, holding the ID
          * of the desired method.
          */
@@ -10123,8 +10158,12 @@
 
         /**
          * The default NFC payment component
+         *
+         * @deprecated please use {@link android.app.role.RoleManager#getRoleHolders(String)}
+         * with {@link android.app.role.RoleManager#ROLE_WALLET} parameter.
          * @hide
          */
+        @Deprecated
         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
         public static final String NFC_PAYMENT_DEFAULT_COMPONENT = "nfc_payment_default_component";
 
@@ -12141,6 +12180,36 @@
          */
         public static final String HIDE_PRIVATESPACE_ENTRY_POINT = "hide_privatespace_entry_point";
 
+        /** @hide */
+        public static final int PRIVATE_SPACE_AUTO_LOCK_ON_DEVICE_LOCK = 0;
+        /** @hide */
+        public static final int PRIVATE_SPACE_AUTO_LOCK_AFTER_INACTIVITY = 1;
+        /** @hide */
+        public static final int PRIVATE_SPACE_AUTO_LOCK_NEVER = 2;
+
+        /**
+         * The different auto lock options for private space.
+         *
+         * @hide
+         */
+        @IntDef(prefix = {"PRIVATE_SPACE_AUTO_LOCK_"}, value = {
+                PRIVATE_SPACE_AUTO_LOCK_ON_DEVICE_LOCK,
+                PRIVATE_SPACE_AUTO_LOCK_AFTER_INACTIVITY,
+                PRIVATE_SPACE_AUTO_LOCK_NEVER,
+        })
+        @Retention(RetentionPolicy.SOURCE)
+        public @interface PrivateSpaceAutoLockOption {
+        }
+
+
+        /**
+         *  Store auto lock value for private space.
+         *  The possible values are defined in {@link PrivateSpaceAutoLockOption}.
+         *
+         * @hide
+         */
+        public static final String PRIVATE_SPACE_AUTO_LOCK = "private_space_auto_lock";
+
         /**
          * These entries are considered common between the personal and the managed profile,
          * since the managed profile doesn't get to change them.
@@ -12158,6 +12227,7 @@
             CLONE_TO_MANAGED_PROFILE.add(SHOW_IME_WITH_HARD_KEYBOARD);
             CLONE_TO_MANAGED_PROFILE.add(ACCESSIBILITY_BOUNCE_KEYS);
             CLONE_TO_MANAGED_PROFILE.add(NOTIFICATION_BUBBLES);
+            CLONE_TO_MANAGED_PROFILE.add(NOTIFICATION_HISTORY_ENABLED);
         }
 
         /** @hide */
@@ -14347,6 +14417,19 @@
                 "mute_alarm_stream_with_ringer_mode";
 
         /**
+         * The user's choice for whether or not Alarm stream should always be muted with Ringer.
+         *
+         * <p>Note that this is different from {@link #MUTE_ALARM_STREAM_WITH_RINGER_MODE}, which
+         * controls the real state of whether or not the Alarm stream and Ringer association occurs.
+         * The two Settings are not necessarily equal, if the final decision for the association
+         * depends on factors beyond the user's preference.
+         *
+         * @hide
+         */
+        public static final String MUTE_ALARM_STREAM_WITH_RINGER_MODE_USER_PREFERENCE =
+                "mute_alarm_stream_with_ringer_mode_user_preference";
+
+        /**
          * Overlay display devices setting.
          * The associated value is a specially formatted string that describes the
          * size and density of simulated secondary display devices.
@@ -15021,6 +15104,16 @@
                 "foreground_service_starts_logging_enabled";
 
         /**
+         * Describes whether AM's AppProfiler should collect PSS even if RSS is the default. This
+         * can be set by a user in developer settings.
+         * Default: 0
+         * @hide
+         */
+        @Readable
+        public static final String FORCE_ENABLE_PSS_PROFILING =
+                "force_enable_pss_profiling";
+
+        /**
          * @hide
          * @see com.android.server.appbinding.AppBindingConstants
          */
@@ -19628,6 +19721,24 @@
              */
             public static final String WEAR_POWER_ANOMALY_SERVICE_ENABLED =
                     "wear_power_anomaly_service_enabled";
+
+            /**
+             * A boolean that tracks whether Wrist Detection Auto-Locking is enabled.
+             *
+             * @hide
+             */
+            @Readable
+            public static final String WRIST_DETECTION_AUTO_LOCKING_ENABLED =
+                    "wear_wrist_detection_auto_locking_enabled";
+
+            /**
+             * Whether consistent notification blocking experience is enabled.
+             *
+             * @hide
+             */
+            @Readable
+            public static final String CONSISTENT_NOTIFICATION_BLOCKING_ENABLED =
+                    "consistent_notification_blocking_enabled";
         }
     }
 
@@ -19788,21 +19899,15 @@
         @RequiresPermission(Manifest.permission.READ_DEVICE_CONFIG)
         public static Map<String, String> getStrings(@NonNull ContentResolver resolver,
                 @NonNull String namespace, @NonNull List<String> names) {
-            List<String> compositeNames = new ArrayList<>(names.size());
-            for (String name : names) {
-                compositeNames.add(createCompositeName(namespace, name));
+            String[] compositeNames = new String[names.size()];
+            for (int i = 0, size = names.size(); i < size; ++i) {
+                compositeNames[i] = createCompositeName(namespace, names.get(i));
             }
 
             String prefix = createPrefix(namespace);
-            ArrayMap<String, String> rawKeyValues = sNameValueCache.getStringsForPrefix(
+
+            ArrayMap<String, String> keyValues = sNameValueCache.getStringsForPrefixStripPrefix(
                     resolver, prefix, compositeNames);
-            int size = rawKeyValues.size();
-            int substringLength = prefix.length();
-            ArrayMap<String, String> keyValues = new ArrayMap<>(size);
-            for (int i = 0; i < size; ++i) {
-                keyValues.put(rawKeyValues.keyAt(i).substring(substringLength),
-                        rawKeyValues.valueAt(i));
-            }
             return keyValues;
         }
 
@@ -20128,12 +20233,13 @@
         private static String createCompositeName(@NonNull String namespace, @NonNull String name) {
             Preconditions.checkNotNull(namespace);
             Preconditions.checkNotNull(name);
-            return createPrefix(namespace) + name;
+            var sb = new StringBuilder(namespace.length() + 1 + name.length());
+            return sb.append(namespace).append('/').append(name).toString();
         }
 
         private static String createPrefix(@NonNull String namespace) {
             Preconditions.checkNotNull(namespace);
-            return namespace + "/";
+            return namespace + '/';
         }
 
         private static Uri createNamespaceUri(@NonNull String namespace) {
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index e4af2da..d47ff2e 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -368,11 +368,13 @@
          * <p>
          * As of Android 11 apps will need specific permission to query other packages. To use
          * this method an app must include in their AndroidManifest:
+         * <pre>{@code
          * <queries>
          *   <intent>
          *     <action android:name="android.provider.Telephony.SMS_DELIVER"/>
          *   </intent>
          * </queries>
+         * }</pre>
          * Which will allow them to query packages which declare intent filters that include
          * the {@link android.provider.Telephony.Sms.Intents#SMS_DELIVER_ACTION} intent.
          * </p>
diff --git a/core/java/android/security/flags.aconfig b/core/java/android/security/flags.aconfig
index b56bef3..30524a1 100644
--- a/core/java/android/security/flags.aconfig
+++ b/core/java/android/security/flags.aconfig
@@ -50,3 +50,11 @@
     description: "Collect sepolicy hash from sysfs"
     bug: "308471499"
 }
+
+flag {
+    name: "frp_enforcement"
+    namespace: "android_hw_security"
+    description: "This flag controls whether PDB enforces FRP"
+    bug: "290312729"
+    is_fixed_read_only: true
+}
diff --git a/core/java/android/security/responsible_apis_flags.aconfig b/core/java/android/security/responsible_apis_flags.aconfig
index 4e5588c..fe6c4a4 100644
--- a/core/java/android/security/responsible_apis_flags.aconfig
+++ b/core/java/android/security/responsible_apis_flags.aconfig
@@ -20,3 +20,10 @@
     description: "Enables toasts when ASM restrictions are triggered"
     bug: "230590090"
 }
+
+flag {
+    name: "content_uri_permission_apis"
+    namespace: "responsible_apis"
+    description: "Enables the content URI permission APIs"
+    bug: "293467489"
+}
diff --git a/core/java/android/service/autofill/AutofillService.java b/core/java/android/service/autofill/AutofillService.java
index cb1b5d3..5ad2502 100644
--- a/core/java/android/service/autofill/AutofillService.java
+++ b/core/java/android/service/autofill/AutofillService.java
@@ -37,6 +37,7 @@
 import android.view.autofill.AutofillId;
 import android.view.autofill.AutofillManager;
 import android.view.autofill.AutofillValue;
+import android.view.autofill.IAutoFillManagerClient;
 
 import com.android.internal.os.IResultReceiver;
 
@@ -621,6 +622,23 @@
                     new FillCallback(callback, request.getId())));
         }
 
+
+        @Override
+        public void onFillCredentialRequest(FillRequest request, IFillCallback callback,
+                IAutoFillManagerClient autofillClientCallback) {
+            ICancellationSignal transport = CancellationSignal.createTransport();
+            try {
+                callback.onCancellable(transport);
+            } catch (RemoteException e) {
+                e.rethrowFromSystemServer();
+            }
+            mHandler.sendMessage(obtainMessage(
+                    AutofillService::onFillCredentialRequest,
+                    AutofillService.this, request, CancellationSignal.fromTransport(transport),
+                    new FillCallback(callback, request.getId()),
+                    autofillClientCallback));
+        }
+
         @Override
         public void onSaveRequest(SaveRequest request, ISaveCallback callback) {
             mHandler.sendMessage(obtainMessage(
@@ -683,6 +701,15 @@
             @NonNull CancellationSignal cancellationSignal, @NonNull FillCallback callback);
 
     /**
+     * Variant of onFillRequest for internal credential manager proxy autofill service only.
+     *
+     * @hide
+     */
+    public void onFillCredentialRequest(@NonNull FillRequest request,
+            @NonNull CancellationSignal cancellationSignal, @NonNull FillCallback callback,
+            IAutoFillManagerClient autofillClientCallback) {}
+
+    /**
      * Called when the user requests the service to save the contents of a screen.
      *
      * <p>If the service could not handle the request right away&mdash;for example, because it must
diff --git a/core/java/android/service/autofill/IAutoFillService.aidl b/core/java/android/service/autofill/IAutoFillService.aidl
index d88e094..03ead32 100644
--- a/core/java/android/service/autofill/IAutoFillService.aidl
+++ b/core/java/android/service/autofill/IAutoFillService.aidl
@@ -20,6 +20,7 @@
 import android.service.autofill.IFillCallback;
 import android.service.autofill.ISaveCallback;
 import android.service.autofill.SaveRequest;
+import android.view.autofill.IAutoFillManagerClient;
 import com.android.internal.os.IResultReceiver;
 
 /**
@@ -30,6 +31,8 @@
 oneway interface IAutoFillService {
     void onConnectedStateChanged(boolean connected);
     void onFillRequest(in FillRequest request, in IFillCallback callback);
+    void onFillCredentialRequest(in FillRequest request, in IFillCallback callback,
+    in IAutoFillManagerClient client);
     void onSaveRequest(in SaveRequest request, in ISaveCallback callback);
     void onSavedPasswordCountRequest(in IResultReceiver receiver);
 }
diff --git a/core/java/android/service/contentcapture/ContentCaptureService.java b/core/java/android/service/contentcapture/ContentCaptureService.java
index 78248d9..e1965ef 100644
--- a/core/java/android/service/contentcapture/ContentCaptureService.java
+++ b/core/java/android/service/contentcapture/ContentCaptureService.java
@@ -53,7 +53,6 @@
 import android.view.contentcapture.DataRemovalRequest;
 import android.view.contentcapture.DataShareRequest;
 import android.view.contentcapture.IContentCaptureDirectManager;
-import android.view.contentcapture.MainContentCaptureSession;
 
 import com.android.internal.os.IResultReceiver;
 import com.android.internal.util.FrameworkStatsLog;
@@ -724,7 +723,7 @@
             final Bundle extras;
             if (binder != null) {
                 extras = new Bundle();
-                extras.putBinder(MainContentCaptureSession.EXTRA_BINDER, binder);
+                extras.putBinder(ContentCaptureSession.EXTRA_BINDER, binder);
             } else {
                 extras = null;
             }
diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java
index 7d9c0a3..2d657c2 100644
--- a/core/java/android/service/dreams/DreamService.java
+++ b/core/java/android/service/dreams/DreamService.java
@@ -445,6 +445,19 @@
     }
 
     /**
+     * Retrieves the current {@link android.app.Activity} associated with the dream.
+     * This method behaves similarly to calling {@link android.app.Activity#getActivity()}.
+     *
+     * @return The current activity, or null if the dream is not associated with an activity
+     * or not started.
+     *
+     * @hide
+     */
+    public Activity getActivity() {
+        return mActivity;
+    }
+
+    /**
      * Inflates a layout resource and set it to be the content view for this Dream.
      * Behaves similarly to {@link android.app.Activity#setContentView(int)}.
      *
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 92c516c..7658af5 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -42,6 +42,7 @@
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.Icon;
+import android.os.BadParcelableException;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
@@ -1056,7 +1057,7 @@
             ParceledListSlice<StatusBarNotification> parceledList = getNotificationInterface()
                     .getActiveNotificationsFromListener(mWrapper, keys, trim);
             return cleanUpNotificationList(parceledList);
-        } catch (android.os.RemoteException ex) {
+        } catch (android.os.RemoteException | BadParcelableException ex) {
             Log.v(TAG, "Unable to contact notification manager", ex);
         }
         return null;
diff --git a/core/java/android/service/notification/ZenDeviceEffects.java b/core/java/android/service/notification/ZenDeviceEffects.java
index 0e82b6c..03ebae5 100644
--- a/core/java/android/service/notification/ZenDeviceEffects.java
+++ b/core/java/android/service/notification/ZenDeviceEffects.java
@@ -17,12 +17,16 @@
 package android.service.notification;
 
 import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.TestApi;
 import android.app.Flags;
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.Objects;
 
@@ -33,6 +37,76 @@
 @FlaggedApi(Flags.FLAG_MODES_API)
 public final class ZenDeviceEffects implements Parcelable {
 
+    /** Used to track which rule variables have been modified by the user.
+     * Should be checked against the bitmask {@link #getUserModifiedFields()}.
+     * @hide
+     */
+    @IntDef(flag = true, prefix = { "FIELD_" }, value = {
+            FIELD_GRAYSCALE,
+            FIELD_SUPPRESS_AMBIENT_DISPLAY,
+            FIELD_DIM_WALLPAPER,
+            FIELD_NIGHT_MODE,
+            FIELD_DISABLE_AUTO_BRIGHTNESS,
+            FIELD_DISABLE_TAP_TO_WAKE,
+            FIELD_DISABLE_TILT_TO_WAKE,
+            FIELD_DISABLE_TOUCH,
+            FIELD_MINIMIZE_RADIO_USAGE,
+            FIELD_MAXIMIZE_DOZE,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ModifiableField {}
+
+    /**
+     * @hide
+     */
+    @TestApi
+    public static final int FIELD_GRAYSCALE = 1 << 0;
+    /**
+     * @hide
+     */
+    @TestApi
+    public static final int FIELD_SUPPRESS_AMBIENT_DISPLAY = 1 << 1;
+    /**
+     * @hide
+     */
+    @TestApi
+    public static final int FIELD_DIM_WALLPAPER = 1 << 2;
+    /**
+     * @hide
+     */
+    @TestApi
+    public static final int FIELD_NIGHT_MODE = 1 << 3;
+    /**
+     * @hide
+     */
+    @TestApi
+    public static final int FIELD_DISABLE_AUTO_BRIGHTNESS = 1 << 4;
+    /**
+     * @hide
+     */
+    @TestApi
+    public static final int FIELD_DISABLE_TAP_TO_WAKE = 1 << 5;
+    /**
+     * @hide
+     */
+    @TestApi
+    public static final int FIELD_DISABLE_TILT_TO_WAKE = 1 << 6;
+    /**
+     * @hide
+     */
+    @TestApi
+    public static final int FIELD_DISABLE_TOUCH = 1 << 7;
+    /**
+     * @hide
+     */
+    @TestApi
+    public static final int FIELD_MINIMIZE_RADIO_USAGE = 1 << 8;
+    /**
+     * @hide
+     */
+    @TestApi
+    public static final int FIELD_MAXIMIZE_DOZE = 1 << 9;
+
     private final boolean mGrayscale;
     private final boolean mSuppressAmbientDisplay;
     private final boolean mDimWallpaper;
@@ -45,10 +119,13 @@
     private final boolean mMinimizeRadioUsage;
     private final boolean mMaximizeDoze;
 
+    private final @ModifiableField int mUserModifiedFields; // Bitwise representation
+
     private ZenDeviceEffects(boolean grayscale, boolean suppressAmbientDisplay,
             boolean dimWallpaper, boolean nightMode, boolean disableAutoBrightness,
             boolean disableTapToWake, boolean disableTiltToWake, boolean disableTouch,
-            boolean minimizeRadioUsage, boolean maximizeDoze) {
+            boolean minimizeRadioUsage, boolean maximizeDoze,
+            @ModifiableField int userModifiedFields) {
         mGrayscale = grayscale;
         mSuppressAmbientDisplay = suppressAmbientDisplay;
         mDimWallpaper = dimWallpaper;
@@ -59,6 +136,7 @@
         mDisableTouch = disableTouch;
         mMinimizeRadioUsage = minimizeRadioUsage;
         mMaximizeDoze = maximizeDoze;
+        mUserModifiedFields = userModifiedFields;
     }
 
     @Override
@@ -75,14 +153,15 @@
                 && this.mDisableTiltToWake == that.mDisableTiltToWake
                 && this.mDisableTouch == that.mDisableTouch
                 && this.mMinimizeRadioUsage == that.mMinimizeRadioUsage
-                && this.mMaximizeDoze == that.mMaximizeDoze;
+                && this.mMaximizeDoze == that.mMaximizeDoze
+                && this.mUserModifiedFields == that.mUserModifiedFields;
     }
 
     @Override
     public int hashCode() {
         return Objects.hash(mGrayscale, mSuppressAmbientDisplay, mDimWallpaper, mNightMode,
                 mDisableAutoBrightness, mDisableTapToWake, mDisableTiltToWake, mDisableTouch,
-                mMinimizeRadioUsage, mMaximizeDoze);
+                mMinimizeRadioUsage, mMaximizeDoze, mUserModifiedFields);
     }
 
     @Override
@@ -98,7 +177,43 @@
         if (mDisableTouch) effects.add("disableTouch");
         if (mMinimizeRadioUsage) effects.add("minimizeRadioUsage");
         if (mMaximizeDoze) effects.add("maximizeDoze");
-        return "[" + String.join(", ", effects) + "]";
+        return "[" + String.join(", ", effects) + "]"
+                + " userModifiedFields: " + modifiedFieldsToString(mUserModifiedFields);
+    }
+
+    private String modifiedFieldsToString(int bitmask) {
+        ArrayList<String> modified = new ArrayList<>();
+        if ((bitmask & FIELD_GRAYSCALE) != 0) {
+            modified.add("FIELD_GRAYSCALE");
+        }
+        if ((bitmask & FIELD_SUPPRESS_AMBIENT_DISPLAY) != 0) {
+            modified.add("FIELD_SUPPRESS_AMBIENT_DISPLAY");
+        }
+        if ((bitmask & FIELD_DIM_WALLPAPER) != 0) {
+            modified.add("FIELD_DIM_WALLPAPER");
+        }
+        if ((bitmask & FIELD_NIGHT_MODE) != 0) {
+            modified.add("FIELD_NIGHT_MODE");
+        }
+        if ((bitmask & FIELD_DISABLE_AUTO_BRIGHTNESS) != 0) {
+            modified.add("FIELD_DISABLE_AUTO_BRIGHTNESS");
+        }
+        if ((bitmask & FIELD_DISABLE_TAP_TO_WAKE) != 0) {
+            modified.add("FIELD_DISABLE_TAP_TO_WAKE");
+        }
+        if ((bitmask & FIELD_DISABLE_TILT_TO_WAKE) != 0) {
+            modified.add("FIELD_DISABLE_TILT_TO_WAKE");
+        }
+        if ((bitmask & FIELD_DISABLE_TOUCH) != 0) {
+            modified.add("FIELD_DISABLE_TOUCH");
+        }
+        if ((bitmask & FIELD_MINIMIZE_RADIO_USAGE) != 0) {
+            modified.add("FIELD_MINIMIZE_RADIO_USAGE");
+        }
+        if ((bitmask & FIELD_MAXIMIZE_DOZE) != 0) {
+            modified.add("FIELD_MAXIMIZE_DOZE");
+        }
+        return "{" + String.join(",", modified) + "}";
     }
 
     /**
@@ -194,9 +309,10 @@
     public static final Creator<ZenDeviceEffects> CREATOR = new Creator<ZenDeviceEffects>() {
         @Override
         public ZenDeviceEffects createFromParcel(Parcel in) {
-            return new ZenDeviceEffects(in.readBoolean(), in.readBoolean(), in.readBoolean(),
+            return new ZenDeviceEffects(in.readBoolean(),
                     in.readBoolean(), in.readBoolean(), in.readBoolean(), in.readBoolean(),
-                    in.readBoolean(), in.readBoolean(), in.readBoolean());
+                    in.readBoolean(), in.readBoolean(), in.readBoolean(), in.readBoolean(),
+                    in.readBoolean(), in.readInt());
         }
 
         @Override
@@ -205,6 +321,16 @@
         }
     };
 
+    /**
+     * Gets the bitmask representing which fields are user modified. Bits are set using
+     * {@link ModifiableField}.
+     * @hide
+     */
+    @TestApi
+    public @ModifiableField int getUserModifiedFields() {
+        return mUserModifiedFields;
+    }
+
     @Override
     public int describeContents() {
         return 0;
@@ -222,6 +348,7 @@
         dest.writeBoolean(mDisableTouch);
         dest.writeBoolean(mMinimizeRadioUsage);
         dest.writeBoolean(mMaximizeDoze);
+        dest.writeInt(mUserModifiedFields);
     }
 
     /** Builder class for {@link ZenDeviceEffects} objects. */
@@ -238,6 +365,7 @@
         private boolean mDisableTouch;
         private boolean mMinimizeRadioUsage;
         private boolean mMaximizeDoze;
+        private @ModifiableField int mUserModifiedFields;
 
         /**
          * Instantiates a new {@link ZenPolicy.Builder} with all effects set to default (disabled).
@@ -260,6 +388,7 @@
             mDisableTouch = zenDeviceEffects.shouldDisableTouch();
             mMinimizeRadioUsage = zenDeviceEffects.shouldMinimizeRadioUsage();
             mMaximizeDoze = zenDeviceEffects.shouldMaximizeDoze();
+            mUserModifiedFields = zenDeviceEffects.mUserModifiedFields;
         }
 
         /**
@@ -381,12 +510,24 @@
             return this;
         }
 
+        /**
+         * Sets the bitmask representing which fields are user modified. See the FIELD_ constants.
+         * @hide
+         */
+        @TestApi
+        @NonNull
+        public Builder setUserModifiedFields(@ModifiableField int userModifiedFields) {
+            mUserModifiedFields = userModifiedFields;
+            return this;
+        }
+
         /** Builds a {@link ZenDeviceEffects} object based on the builder's state. */
         @NonNull
         public ZenDeviceEffects build() {
-            return new ZenDeviceEffects(mGrayscale, mSuppressAmbientDisplay, mDimWallpaper,
-                    mNightMode, mDisableAutoBrightness, mDisableTapToWake, mDisableTiltToWake,
-                    mDisableTouch, mMinimizeRadioUsage, mMaximizeDoze);
+            return new ZenDeviceEffects(mGrayscale,
+                    mSuppressAmbientDisplay, mDimWallpaper, mNightMode, mDisableAutoBrightness,
+                    mDisableTapToWake, mDisableTiltToWake, mDisableTouch, mMinimizeRadioUsage,
+                    mMaximizeDoze, mUserModifiedFields);
         }
     }
 }
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index fcdc5fe..54248be 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -64,6 +64,7 @@
 import java.io.IOException;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.time.Instant;
 import java.util.Arrays;
 import java.util.Calendar;
 import java.util.Date;
@@ -205,6 +206,7 @@
     private static final String ALLOW_ATT_CONV = "convos";
     private static final String ALLOW_ATT_CONV_FROM = "convosFrom";
     private static final String ALLOW_ATT_CHANNELS = "channels";
+    private static final String USER_MODIFIED_FIELDS = "policyUserModifiedFields";
     private static final String DISALLOW_TAG = "disallow";
     private static final String DISALLOW_ATT_VISUAL_EFFECTS = "visualEffects";
     private static final String STATE_TAG = "state";
@@ -232,6 +234,7 @@
 
     private static final String MANUAL_TAG = "manual";
     private static final String AUTOMATIC_TAG = "automatic";
+    private static final String AUTOMATIC_DELETED_TAG = "deleted";
 
     private static final String RULE_ATT_ID = "ruleId";
     private static final String RULE_ATT_ENABLED = "enabled";
@@ -247,8 +250,10 @@
     private static final String RULE_ATT_MODIFIED = "modified";
     private static final String RULE_ATT_ALLOW_MANUAL = "userInvokable";
     private static final String RULE_ATT_TYPE = "type";
+    private static final String RULE_ATT_USER_MODIFIED_FIELDS = "userModifiedFields";
     private static final String RULE_ATT_ICON = "rule_icon";
     private static final String RULE_ATT_TRIGGER_DESC = "triggerDesc";
+    private static final String RULE_ATT_DELETION_INSTANT = "deletionInstant";
 
     private static final String DEVICE_EFFECT_DISPLAY_GRAYSCALE = "zdeDisplayGrayscale";
     private static final String DEVICE_EFFECT_SUPPRESS_AMBIENT_DISPLAY =
@@ -261,6 +266,7 @@
     private static final String DEVICE_EFFECT_DISABLE_TOUCH = "zdeDisableTouch";
     private static final String DEVICE_EFFECT_MINIMIZE_RADIO_USAGE = "zdeMinimizeRadioUsage";
     private static final String DEVICE_EFFECT_MAXIMIZE_DOZE = "zdeMaximizeDoze";
+    private static final String DEVICE_EFFECT_USER_MODIFIED_FIELDS = "zdeUserModifiedFields";
 
     @UnsupportedAppUsage
     public boolean allowAlarms = DEFAULT_ALLOW_ALARMS;
@@ -289,6 +295,10 @@
     @UnsupportedAppUsage
     public ArrayMap<String, ZenRule> automaticRules = new ArrayMap<>();
 
+    // Note: Map is *pkg|conditionId* (see deletedRuleKey()) -> ZenRule,
+    // unlike automaticRules (which is id -> rule).
+    public final ArrayMap<String, ZenRule> deletedRules = new ArrayMap<>();
+
     @UnsupportedAppUsage
     public ZenModeConfig() {
     }
@@ -303,15 +313,9 @@
         allowMessagesFrom = source.readInt();
         user = source.readInt();
         manualRule = source.readParcelable(null, ZenRule.class);
-        final int len = source.readInt();
-        if (len > 0) {
-            final String[] ids = new String[len];
-            final ZenRule[] rules = new ZenRule[len];
-            source.readStringArray(ids);
-            source.readTypedArray(rules, ZenRule.CREATOR);
-            for (int i = 0; i < len; i++) {
-                automaticRules.put(ids[i], rules[i]);
-            }
+        readRulesFromParcel(automaticRules, source);
+        if (Flags.modesApi()) {
+            readRulesFromParcel(deletedRules, source);
         }
         allowAlarms = source.readInt() == 1;
         allowMedia = source.readInt() == 1;
@@ -325,6 +329,19 @@
         }
     }
 
+    private static void readRulesFromParcel(ArrayMap<String, ZenRule> ruleMap, Parcel source) {
+        final int len = source.readInt();
+        if (len > 0) {
+            final String[] ids = new String[len];
+            final ZenRule[] rules = new ZenRule[len];
+            source.readStringArray(ids);
+            source.readTypedArray(rules, ZenRule.CREATOR);
+            for (int i = 0; i < len; i++) {
+                ruleMap.put(ids[i], rules[i]);
+            }
+        }
+    }
+
     @Override
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeInt(allowCalls ? 1 : 0);
@@ -336,19 +353,9 @@
         dest.writeInt(allowMessagesFrom);
         dest.writeInt(user);
         dest.writeParcelable(manualRule, 0);
-        if (!automaticRules.isEmpty()) {
-            final int len = automaticRules.size();
-            final String[] ids = new String[len];
-            final ZenRule[] rules = new ZenRule[len];
-            for (int i = 0; i < len; i++) {
-                ids[i] = automaticRules.keyAt(i);
-                rules[i] = automaticRules.valueAt(i);
-            }
-            dest.writeInt(len);
-            dest.writeStringArray(ids);
-            dest.writeTypedArray(rules, 0);
-        } else {
-            dest.writeInt(0);
+        writeRulesToParcel(automaticRules, dest);
+        if (Flags.modesApi()) {
+            writeRulesToParcel(deletedRules, dest);
         }
         dest.writeInt(allowAlarms ? 1 : 0);
         dest.writeInt(allowMedia ? 1 : 0);
@@ -362,6 +369,23 @@
         }
     }
 
+    private static void writeRulesToParcel(ArrayMap<String, ZenRule> ruleMap, Parcel dest) {
+        if (!ruleMap.isEmpty()) {
+            final int len = ruleMap.size();
+            final String[] ids = new String[len];
+            final ZenRule[] rules = new ZenRule[len];
+            for (int i = 0; i < len; i++) {
+                ids[i] = ruleMap.keyAt(i);
+                rules[i] = ruleMap.valueAt(i);
+            }
+            dest.writeInt(len);
+            dest.writeStringArray(ids);
+            dest.writeTypedArray(rules, 0);
+        } else {
+            dest.writeInt(0);
+        }
+    }
+
     @Override
     public String toString() {
         StringBuilder sb = new StringBuilder(ZenModeConfig.class.getSimpleName()).append('[')
@@ -386,23 +410,26 @@
         } else {
             sb.append(",areChannelsBypassingDnd=").append(areChannelsBypassingDnd);
         }
-        return sb.append(",\nautomaticRules=").append(rulesToString())
-                .append(",\nmanualRule=").append(manualRule)
-                .append(']').toString();
+        sb.append(",\nautomaticRules=").append(rulesToString(automaticRules))
+                .append(",\nmanualRule=").append(manualRule);
+        if (Flags.modesApi()) {
+            sb.append(",\ndeletedRules=").append(rulesToString(deletedRules));
+        }
+        return sb.append(']').toString();
     }
 
-    private String rulesToString() {
-        if (automaticRules.isEmpty()) {
+    private static String rulesToString(ArrayMap<String, ZenRule> ruleList) {
+        if (ruleList.isEmpty()) {
             return "{}";
         }
 
-        StringBuilder buffer = new StringBuilder(automaticRules.size() * 28);
+        StringBuilder buffer = new StringBuilder(ruleList.size() * 28);
         buffer.append("{\n");
-        for (int i = 0; i < automaticRules.size(); i++) {
+        for (int i = 0; i < ruleList.size(); i++) {
             if (i > 0) {
                 buffer.append(",\n");
             }
-            Object value = automaticRules.valueAt(i);
+            Object value = ruleList.valueAt(i);
             buffer.append(value);
         }
         buffer.append('}');
@@ -484,7 +511,9 @@
                 && other.allowConversations == allowConversations
                 && other.allowConversationsFrom == allowConversationsFrom;
         if (Flags.modesApi()) {
-            return eq && other.allowPriorityChannels == allowPriorityChannels;
+            return eq
+                    && Objects.equals(other.deletedRules, deletedRules)
+                    && other.allowPriorityChannels == allowPriorityChannels;
         }
         return eq;
     }
@@ -641,12 +670,20 @@
                             DEFAULT_SUPPRESSED_VISUAL_EFFECTS);
                 } else if (MANUAL_TAG.equals(tag)) {
                     rt.manualRule = readRuleXml(parser);
-                } else if (AUTOMATIC_TAG.equals(tag)) {
+                } else if (AUTOMATIC_TAG.equals(tag)
+                        || (Flags.modesApi() && AUTOMATIC_DELETED_TAG.equals(tag))) {
                     final String id = parser.getAttributeValue(null, RULE_ATT_ID);
                     final ZenRule automaticRule = readRuleXml(parser);
                     if (id != null && automaticRule != null) {
                         automaticRule.id = id;
-                        rt.automaticRules.put(id, automaticRule);
+                        if (Flags.modesApi() && AUTOMATIC_DELETED_TAG.equals(tag)) {
+                            String deletedRuleKey = deletedRuleKey(automaticRule);
+                            if (deletedRuleKey != null) {
+                                rt.deletedRules.put(deletedRuleKey, automaticRule);
+                            }
+                        } else if (AUTOMATIC_TAG.equals(tag)) {
+                            rt.automaticRules.put(id, automaticRule);
+                        }
                     }
                 } else if (STATE_TAG.equals(tag)) {
                     rt.areChannelsBypassingDnd = safeBoolean(parser,
@@ -657,13 +694,24 @@
         throw new IllegalStateException("Failed to reach END_DOCUMENT");
     }
 
+    /** Generates the map key used for a {@link ZenRule} in {@link #deletedRules}. */
+    @Nullable
+    public static String deletedRuleKey(ZenRule rule) {
+        if (rule.pkg != null && rule.conditionId != null) {
+            return rule.pkg + "|" + rule.conditionId.toString();
+        } else {
+            return null;
+        }
+    }
+
     /**
      * Writes XML of current ZenModeConfig
      * @param out serializer
      * @param version uses XML_VERSION if version is null
      * @throws IOException
      */
-    public void writeXml(TypedXmlSerializer out, Integer version) throws IOException {
+    public void writeXml(TypedXmlSerializer out, Integer version, boolean forBackup)
+            throws IOException {
         out.startTag(null, ZEN_TAG);
         out.attribute(null, ZEN_ATT_VERSION, version == null
                 ? Integer.toString(XML_VERSION) : Integer.toString(version));
@@ -704,6 +752,15 @@
             writeRuleXml(automaticRule, out);
             out.endTag(null, AUTOMATIC_TAG);
         }
+        if (Flags.modesApi() && !forBackup) {
+            for (int i = 0; i < deletedRules.size(); i++) {
+                final ZenRule deletedRule = deletedRules.valueAt(i);
+                out.startTag(null, AUTOMATIC_DELETED_TAG);
+                out.attribute(null, RULE_ATT_ID, deletedRule.id);
+                writeRuleXml(deletedRule, out);
+                out.endTag(null, AUTOMATIC_DELETED_TAG);
+            }
+        }
 
         out.startTag(null, STATE_TAG);
         out.attributeBoolean(null, STATE_ATT_CHANNELS_BYPASSING_DND, areChannelsBypassingDnd);
@@ -748,6 +805,12 @@
             rt.iconResName = parser.getAttributeValue(null, RULE_ATT_ICON);
             rt.triggerDescription = parser.getAttributeValue(null, RULE_ATT_TRIGGER_DESC);
             rt.type = safeInt(parser, RULE_ATT_TYPE, AutomaticZenRule.TYPE_UNKNOWN);
+            rt.userModifiedFields = safeInt(parser, RULE_ATT_USER_MODIFIED_FIELDS, 0);
+            Long deletionInstant = tryParseLong(
+                    parser.getAttributeValue(null, RULE_ATT_DELETION_INSTANT), null);
+            if (deletionInstant != null) {
+                rt.deletionInstant = Instant.ofEpochMilli(deletionInstant);
+            }
         }
         return rt;
     }
@@ -794,6 +857,11 @@
                 out.attribute(null, RULE_ATT_TRIGGER_DESC, rule.triggerDescription);
             }
             out.attributeInt(null, RULE_ATT_TYPE, rule.type);
+            out.attributeInt(null, RULE_ATT_USER_MODIFIED_FIELDS, rule.userModifiedFields);
+            if (rule.deletionInstant != null) {
+                out.attributeLong(null, RULE_ATT_DELETION_INSTANT,
+                        rule.deletionInstant.toEpochMilli());
+            }
         }
     }
 
@@ -856,6 +924,7 @@
                 builder.allowChannels(channels);
                 policySet = true;
             }
+            builder.setUserModifiedFields(safeInt(parser, USER_MODIFIED_FIELDS, 0));
         }
 
         if (calls != ZenPolicy.PEOPLE_TYPE_UNSET) {
@@ -968,6 +1037,7 @@
 
         if (Flags.modesApi()) {
             writeZenPolicyState(ALLOW_ATT_CHANNELS, policy.getAllowedChannels(), out);
+            out.attributeInt(null, USER_MODIFIED_FIELDS, policy.getUserModifiedFields());
         }
     }
 
@@ -993,6 +1063,7 @@
         }
     }
 
+    @FlaggedApi(Flags.FLAG_MODES_API)
     @Nullable
     private static ZenDeviceEffects readZenDeviceEffectsXml(TypedXmlPullParser parser) {
         ZenDeviceEffects deviceEffects = new ZenDeviceEffects.Builder()
@@ -1012,11 +1083,13 @@
                 .setShouldMinimizeRadioUsage(
                         safeBoolean(parser, DEVICE_EFFECT_MINIMIZE_RADIO_USAGE, false))
                 .setShouldMaximizeDoze(safeBoolean(parser, DEVICE_EFFECT_MAXIMIZE_DOZE, false))
+                .setUserModifiedFields(safeInt(parser, DEVICE_EFFECT_USER_MODIFIED_FIELDS, 0))
                 .build();
 
         return deviceEffects.hasEffects() ? deviceEffects : null;
     }
 
+    @FlaggedApi(Flags.FLAG_MODES_API)
     private static void writeZenDeviceEffectsXml(ZenDeviceEffects deviceEffects,
             TypedXmlSerializer out) throws IOException {
         writeBooleanIfTrue(out, DEVICE_EFFECT_DISPLAY_GRAYSCALE,
@@ -1035,6 +1108,8 @@
         writeBooleanIfTrue(out, DEVICE_EFFECT_MINIMIZE_RADIO_USAGE,
                 deviceEffects.shouldMinimizeRadioUsage());
         writeBooleanIfTrue(out, DEVICE_EFFECT_MAXIMIZE_DOZE, deviceEffects.shouldMaximizeDoze());
+        out.attributeInt(null, DEVICE_EFFECT_USER_MODIFIED_FIELDS,
+                deviceEffects.getUserModifiedFields());
     }
 
     private static void writeBooleanIfTrue(TypedXmlSerializer out, String att, boolean value)
@@ -1985,6 +2060,8 @@
         public String triggerDescription;
         public String iconResName;
         public boolean allowManualInvocation;
+        public int userModifiedFields;
+        @Nullable public Instant deletionInstant; // Only set on deleted rules.
 
         public ZenRule() { }
 
@@ -2017,9 +2094,25 @@
                 iconResName = source.readString();
                 triggerDescription = source.readString();
                 type = source.readInt();
+                userModifiedFields = source.readInt();
+                if (source.readInt() == 1) {
+                    deletionInstant = Instant.ofEpochMilli(source.readLong());
+                }
             }
         }
 
+        /**
+         * @see AutomaticZenRule#canUpdate()
+         */
+        @FlaggedApi(Flags.FLAG_MODES_API)
+        public boolean canBeUpdatedByApp() {
+            // The rule is considered updateable if its bitmask has no user modifications, and
+            // the bitmasks of the policy and device effects have no modification.
+            return userModifiedFields == 0
+                    && (zenPolicy == null || zenPolicy.getUserModifiedFields() == 0)
+                    && (zenDeviceEffects == null || zenDeviceEffects.getUserModifiedFields() == 0);
+        }
+
         @Override
         public int describeContents() {
             return 0;
@@ -2064,6 +2157,13 @@
                 dest.writeString(iconResName);
                 dest.writeString(triggerDescription);
                 dest.writeInt(type);
+                dest.writeInt(userModifiedFields);
+                if (deletionInstant != null) {
+                    dest.writeInt(1);
+                    dest.writeLong(deletionInstant.toEpochMilli());
+                } else {
+                    dest.writeInt(0);
+                }
             }
         }
 
@@ -2092,7 +2192,11 @@
                         .append(",allowManualInvocation=").append(allowManualInvocation)
                         .append(",iconResName=").append(iconResName)
                         .append(",triggerDescription=").append(triggerDescription)
-                        .append(",type=").append(type);
+                        .append(",type=").append(type)
+                        .append(",userModifiedFields=").append(userModifiedFields);
+                if (deletionInstant != null) {
+                    sb.append(",deletionInstant=").append(deletionInstant);
+                }
             }
 
             return sb.append(']').toString();
@@ -2151,7 +2255,9 @@
                         && other.allowManualInvocation == allowManualInvocation
                         && Objects.equals(other.iconResName, iconResName)
                         && Objects.equals(other.triggerDescription, triggerDescription)
-                        && other.type == type;
+                        && other.type == type
+                        && other.userModifiedFields == userModifiedFields
+                        && Objects.equals(other.deletionInstant, deletionInstant);
             }
 
             return finalEquals;
@@ -2163,7 +2269,7 @@
                 return Objects.hash(enabled, snoozing, name, zenMode, conditionId, condition,
                         component, configurationActivity, pkg, id, enabler, zenPolicy,
                         zenDeviceEffects, modified, allowManualInvocation, iconResName,
-                        triggerDescription, type);
+                        triggerDescription, type, userModifiedFields, deletionInstant);
             }
             return Objects.hash(enabled, snoozing, name, zenMode, conditionId, condition,
                     component, configurationActivity, pkg, id, enabler, zenPolicy, modified);
diff --git a/core/java/android/service/notification/ZenModeDiff.java b/core/java/android/service/notification/ZenModeDiff.java
index d87e758..91ef11c 100644
--- a/core/java/android/service/notification/ZenModeDiff.java
+++ b/core/java/android/service/notification/ZenModeDiff.java
@@ -30,6 +30,11 @@
 /**
  * ZenModeDiff is a utility class meant to encapsulate the diff between ZenModeConfigs and their
  * subcomponents (automatic and manual ZenRules).
+ *
+ * <p>Note that this class is intended to detect <em>meaningful</em> differences, so objects that
+ * are not identical (as per their {@code equals()} implementation) can still produce an empty diff
+ * if only "metadata" fields are updated.
+ *
  * @hide
  */
 public class ZenModeDiff {
diff --git a/core/java/android/service/notification/ZenPolicy.java b/core/java/android/service/notification/ZenPolicy.java
index b1680ab..8477eb7 100644
--- a/core/java/android/service/notification/ZenPolicy.java
+++ b/core/java/android/service/notification/ZenPolicy.java
@@ -20,6 +20,8 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.annotation.TestApi;
 import android.app.Flags;
 import android.app.Notification;
 import android.app.NotificationChannel;
@@ -32,6 +34,7 @@
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.List;
 import java.util.Objects;
 
 /**
@@ -41,12 +44,148 @@
  * a device is in Do Not Disturb mode.
  */
 public final class ZenPolicy implements Parcelable {
-    private ArrayList<Integer> mPriorityCategories;
-    private ArrayList<Integer> mVisualEffects;
+
+    /** Used to track which rule variables have been modified by the user.
+     * Should be checked against the bitmask {@link #getUserModifiedFields()}.
+     * @hide
+     */
+    @IntDef(flag = true, prefix = { "FIELD_" }, value = {
+            FIELD_MESSAGES,
+            FIELD_CALLS,
+            FIELD_CONVERSATIONS,
+            FIELD_ALLOW_CHANNELS,
+            FIELD_PRIORITY_CATEGORY_REMINDERS,
+            FIELD_PRIORITY_CATEGORY_EVENTS,
+            FIELD_PRIORITY_CATEGORY_REPEAT_CALLERS,
+            FIELD_PRIORITY_CATEGORY_ALARMS,
+            FIELD_PRIORITY_CATEGORY_MEDIA,
+            FIELD_PRIORITY_CATEGORY_SYSTEM,
+            FIELD_VISUAL_EFFECT_FULL_SCREEN_INTENT,
+            FIELD_VISUAL_EFFECT_LIGHTS,
+            FIELD_VISUAL_EFFECT_PEEK,
+            FIELD_VISUAL_EFFECT_STATUS_BAR,
+            FIELD_VISUAL_EFFECT_BADGE,
+            FIELD_VISUAL_EFFECT_AMBIENT,
+            FIELD_VISUAL_EFFECT_NOTIFICATION_LIST,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ModifiableField {}
+
+    /**
+     * Covers modifications to MESSAGE_SENDERS and PRIORITY_CATEGORY_MESSAGES, which are set at
+     * the same time.
+     * @hide
+     */
+    @TestApi
+    @FlaggedApi(Flags.FLAG_MODES_API)
+    public static final int FIELD_MESSAGES = 1 << 0;
+    /**
+     * Covers modifications to CALL_SENDERS and PRIORITY_CATEGORY_CALLS, which are set at
+     * the same time.
+     * @hide
+     */
+    @TestApi
+    @FlaggedApi(Flags.FLAG_MODES_API)
+    public static final int FIELD_CALLS = 1 << 1;
+    /**
+     * Covers modifications to CONVERSATION_SENDERS and PRIORITY_CATEGORY_CONVERSATIONS, which are
+     * set at the same time.
+     * @hide
+     */
+    @TestApi
+    @FlaggedApi(Flags.FLAG_MODES_API)
+    public static final int FIELD_CONVERSATIONS = 1 << 2;
+    /**
+     * @hide
+     */
+    @TestApi
+    @FlaggedApi(Flags.FLAG_MODES_API)
+    public static final int FIELD_ALLOW_CHANNELS = 1 << 3;
+    /**
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_MODES_API)
+    public static final int FIELD_PRIORITY_CATEGORY_REMINDERS = 1 << 4;
+    /**
+     * @hide
+     */
+    @TestApi
+    @FlaggedApi(Flags.FLAG_MODES_API)
+    public static final int FIELD_PRIORITY_CATEGORY_EVENTS = 1 << 5;
+    /**
+     * @hide
+     */
+    @TestApi
+    @FlaggedApi(Flags.FLAG_MODES_API)
+    public static final int FIELD_PRIORITY_CATEGORY_REPEAT_CALLERS = 1 << 6;
+    /**
+     * @hide
+     */
+    @TestApi
+    @FlaggedApi(Flags.FLAG_MODES_API)
+    public static final int FIELD_PRIORITY_CATEGORY_ALARMS = 1 << 7;
+    /**
+     * @hide
+     */
+    @TestApi
+    @FlaggedApi(Flags.FLAG_MODES_API)
+    public static final int FIELD_PRIORITY_CATEGORY_MEDIA = 1 << 8;
+    /**
+     * @hide
+     */
+    @TestApi
+    @FlaggedApi(Flags.FLAG_MODES_API)
+    public static final int FIELD_PRIORITY_CATEGORY_SYSTEM = 1 << 9;
+    /**
+     * @hide
+     */
+    @TestApi
+    @FlaggedApi(Flags.FLAG_MODES_API)
+    public static final int FIELD_VISUAL_EFFECT_FULL_SCREEN_INTENT = 1 << 10;
+    /**
+     * @hide
+     */
+    @TestApi
+    @FlaggedApi(Flags.FLAG_MODES_API)
+    public static final int FIELD_VISUAL_EFFECT_LIGHTS = 1 << 11;
+    /**
+     * @hide
+     */
+    @TestApi
+    @FlaggedApi(Flags.FLAG_MODES_API)
+    public static final int FIELD_VISUAL_EFFECT_PEEK = 1 << 12;
+    /**
+     * @hide
+     */
+    @TestApi
+    @FlaggedApi(Flags.FLAG_MODES_API)
+    public static final int FIELD_VISUAL_EFFECT_STATUS_BAR = 1 << 13;
+    /**
+     * @hide
+     */
+    @TestApi
+    @FlaggedApi(Flags.FLAG_MODES_API)
+    public static final int FIELD_VISUAL_EFFECT_BADGE = 1 << 14;
+    /**
+     * @hide
+     */
+    @TestApi
+    @FlaggedApi(Flags.FLAG_MODES_API)
+    public static final int FIELD_VISUAL_EFFECT_AMBIENT = 1 << 15;
+    /**
+     * @hide
+     */
+    @TestApi
+    @FlaggedApi(Flags.FLAG_MODES_API)
+    public static final int FIELD_VISUAL_EFFECT_NOTIFICATION_LIST = 1 << 16;
+
+    private List<Integer> mPriorityCategories;
+    private List<Integer> mVisualEffects;
     private @PeopleType int mPriorityMessages = PEOPLE_TYPE_UNSET;
     private @PeopleType int mPriorityCalls = PEOPLE_TYPE_UNSET;
     private @ConversationSenders int mConversationSenders = CONVERSATION_SENDERS_UNSET;
     private @ChannelType int mAllowChannels = CHANNEL_TYPE_UNSET;
+    private final @ModifiableField int mUserModifiedFields; // Bitwise representation
 
     /** @hide */
     @IntDef(prefix = { "PRIORITY_CATEGORY_" }, value = {
@@ -249,6 +388,22 @@
     public ZenPolicy() {
         mPriorityCategories = new ArrayList<>(Collections.nCopies(NUM_PRIORITY_CATEGORIES, 0));
         mVisualEffects = new ArrayList<>(Collections.nCopies(NUM_VISUAL_EFFECTS, 0));
+        mUserModifiedFields = 0;
+    }
+
+    /** @hide */
+    @FlaggedApi(Flags.FLAG_MODES_API)
+    public ZenPolicy(List<Integer> priorityCategories, List<Integer> visualEffects,
+                     @PeopleType int priorityMessages, @PeopleType int priorityCalls,
+                     @ConversationSenders int conversationSenders, @ChannelType int allowChannels,
+                     @ModifiableField int userModifiedFields) {
+        mPriorityCategories = priorityCategories;
+        mVisualEffects = visualEffects;
+        mPriorityMessages = priorityMessages;
+        mPriorityCalls = priorityCalls;
+        mConversationSenders = conversationSenders;
+        mAllowChannels = allowChannels;
+        mUserModifiedFields = userModifiedFields;
     }
 
     /**
@@ -256,7 +411,7 @@
      * @return {@link #CONVERSATION_SENDERS_UNSET}, {@link #CONVERSATION_SENDERS_ANYONE},
      * {@link #CONVERSATION_SENDERS_IMPORTANT}, {@link #CONVERSATION_SENDERS_NONE}.
      */
-    public @PeopleType int getPriorityConversationSenders() {
+    public @ConversationSenders int getPriorityConversationSenders() {
         return mConversationSenders;
     }
 
@@ -473,6 +628,8 @@
      * is not set, it is (@link STATE_UNSET} and will not change the current set policy.
      */
     public static final class Builder {
+        private @ModifiableField int mUserModifiedFields;
+
         private ZenPolicy mZenPolicy;
 
         public Builder() {
@@ -482,9 +639,14 @@
         /**
          * @hide
          */
-        public Builder(ZenPolicy policy) {
+        @SuppressLint("UnflaggedApi")
+        @TestApi
+        public Builder(@Nullable ZenPolicy policy) {
             if (policy != null) {
                 mZenPolicy = policy.copy();
+                if (Flags.modesApi()) {
+                    mUserModifiedFields = policy.mUserModifiedFields;
+                }
             } else {
                 mZenPolicy = new ZenPolicy();
             }
@@ -494,7 +656,15 @@
          * Builds the current ZenPolicy.
          */
         public @NonNull ZenPolicy build() {
-            return mZenPolicy.copy();
+            if (Flags.modesApi()) {
+                return new ZenPolicy(new ArrayList<Integer>(mZenPolicy.mPriorityCategories),
+                        new ArrayList<Integer>(mZenPolicy.mVisualEffects),
+                        mZenPolicy.mPriorityMessages, mZenPolicy.mPriorityCalls,
+                        mZenPolicy.mConversationSenders, mZenPolicy.mAllowChannels,
+                        mUserModifiedFields);
+            } else {
+                return mZenPolicy.copy();
+            }
         }
 
         /**
@@ -850,6 +1020,28 @@
             mZenPolicy.mAllowChannels = channelType;
             return this;
         }
+
+        /**
+         * Sets the user modified fields bitmask.
+         * @hide
+         */
+        @TestApi
+        @FlaggedApi(Flags.FLAG_MODES_API)
+        public @NonNull Builder setUserModifiedFields(@ModifiableField int userModifiedFields) {
+            mUserModifiedFields = userModifiedFields;
+            return this;
+        }
+    }
+
+    /**
+     Gets the bitmask representing which fields are user modified. Bits are set using
+     * {@link ModifiableField}.
+     * @hide
+     */
+    @TestApi
+    @FlaggedApi(Flags.FLAG_MODES_API)
+    public @ModifiableField int getUserModifiedFields() {
+        return mUserModifiedFields;
     }
 
     @Override
@@ -861,39 +1053,49 @@
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeList(mPriorityCategories);
         dest.writeList(mVisualEffects);
-        dest.writeInt(mPriorityCalls);
         dest.writeInt(mPriorityMessages);
+        dest.writeInt(mPriorityCalls);
         dest.writeInt(mConversationSenders);
         if (Flags.modesApi()) {
             dest.writeInt(mAllowChannels);
+            dest.writeInt(mUserModifiedFields);
         }
     }
 
-    public static final @android.annotation.NonNull Parcelable.Creator<ZenPolicy> CREATOR =
-            new Parcelable.Creator<ZenPolicy>() {
-        @Override
-        public ZenPolicy createFromParcel(Parcel source) {
-            ZenPolicy policy = new ZenPolicy();
-            policy.mPriorityCategories = trimList(
-                    source.readArrayList(Integer.class.getClassLoader(), java.lang.Integer.class),
-                    NUM_PRIORITY_CATEGORIES);
-            policy.mVisualEffects = trimList(
-                    source.readArrayList(Integer.class.getClassLoader(), java.lang.Integer.class),
-                    NUM_VISUAL_EFFECTS);
-            policy.mPriorityCalls = source.readInt();
-            policy.mPriorityMessages = source.readInt();
-            policy.mConversationSenders = source.readInt();
-            if (Flags.modesApi()) {
-                policy.mAllowChannels = source.readInt();
-            }
-            return policy;
-        }
+    public static final @NonNull Creator<ZenPolicy> CREATOR =
+            new Creator<ZenPolicy>() {
+                @Override
+                public ZenPolicy createFromParcel(Parcel source) {
+                    ZenPolicy policy;
+                    if (Flags.modesApi()) {
+                        policy = new ZenPolicy(
+                                trimList(source.readArrayList(Integer.class.getClassLoader(),
+                                        Integer.class), NUM_PRIORITY_CATEGORIES),
+                                trimList(source.readArrayList(Integer.class.getClassLoader(),
+                                        Integer.class), NUM_VISUAL_EFFECTS),
+                                source.readInt(), source.readInt(), source.readInt(),
+                                source.readInt(), source.readInt()
+                        );
+                    } else {
+                        policy = new ZenPolicy();
+                        policy.mPriorityCategories =
+                                trimList(source.readArrayList(Integer.class.getClassLoader(),
+                                        Integer.class), NUM_PRIORITY_CATEGORIES);
+                        policy.mVisualEffects =
+                                trimList(source.readArrayList(Integer.class.getClassLoader(),
+                                        Integer.class), NUM_VISUAL_EFFECTS);
+                        policy.mPriorityMessages = source.readInt();
+                        policy.mPriorityCalls = source.readInt();
+                        policy.mConversationSenders = source.readInt();
+                    }
+                    return policy;
+                }
 
-        @Override
-        public ZenPolicy[] newArray(int size) {
-            return new ZenPolicy[size];
-        }
-    };
+                @Override
+                public ZenPolicy[] newArray(int size) {
+                    return new ZenPolicy[size];
+                }
+            };
 
     @Override
     public String toString() {
@@ -907,10 +1109,69 @@
                         conversationTypeToString(mConversationSenders));
         if (Flags.modesApi()) {
             sb.append(", allowChannels=").append(channelTypeToString(mAllowChannels));
+            sb.append(", userModifiedFields=")
+                    .append(modifiedFieldsToString(mUserModifiedFields));
         }
         return sb.append('}').toString();
     }
 
+    @FlaggedApi(Flags.FLAG_MODES_API)
+    private String modifiedFieldsToString(@ModifiableField int bitmask) {
+        ArrayList<String> modified = new ArrayList<>();
+        if ((bitmask & FIELD_MESSAGES) != 0) {
+            modified.add("FIELD_MESSAGES");
+        }
+        if ((bitmask & FIELD_CALLS) != 0) {
+            modified.add("FIELD_CALLS");
+        }
+        if ((bitmask & FIELD_CONVERSATIONS) != 0) {
+            modified.add("FIELD_CONVERSATIONS");
+        }
+        if ((bitmask & FIELD_ALLOW_CHANNELS) != 0) {
+            modified.add("FIELD_ALLOW_CHANNELS");
+        }
+        if ((bitmask & FIELD_PRIORITY_CATEGORY_REMINDERS) != 0) {
+            modified.add("FIELD_PRIORITY_CATEGORY_REMINDERS");
+        }
+        if ((bitmask & FIELD_PRIORITY_CATEGORY_EVENTS) != 0) {
+            modified.add("FIELD_PRIORITY_CATEGORY_EVENTS");
+        }
+        if ((bitmask & FIELD_PRIORITY_CATEGORY_REPEAT_CALLERS) != 0) {
+            modified.add("FIELD_PRIORITY_CATEGORY_REPEAT_CALLERS");
+        }
+        if ((bitmask & FIELD_PRIORITY_CATEGORY_ALARMS) != 0) {
+            modified.add("FIELD_PRIORITY_CATEGORY_ALARMS");
+        }
+        if ((bitmask & FIELD_PRIORITY_CATEGORY_MEDIA) != 0) {
+            modified.add("FIELD_PRIORITY_CATEGORY_MEDIA");
+        }
+        if ((bitmask & FIELD_PRIORITY_CATEGORY_SYSTEM) != 0) {
+            modified.add("FIELD_PRIORITY_CATEGORY_SYSTEM");
+        }
+        if ((bitmask & FIELD_VISUAL_EFFECT_FULL_SCREEN_INTENT) != 0) {
+            modified.add("FIELD_VISUAL_EFFECT_FULL_SCREEN_INTENT");
+        }
+        if ((bitmask & FIELD_VISUAL_EFFECT_LIGHTS) != 0) {
+            modified.add("FIELD_VISUAL_EFFECT_LIGHTS");
+        }
+        if ((bitmask & FIELD_VISUAL_EFFECT_PEEK) != 0) {
+            modified.add("FIELD_VISUAL_EFFECT_PEEK");
+        }
+        if ((bitmask & FIELD_VISUAL_EFFECT_STATUS_BAR) != 0) {
+            modified.add("FIELD_VISUAL_EFFECT_STATUS_BAR");
+        }
+        if ((bitmask & FIELD_VISUAL_EFFECT_BADGE) != 0) {
+            modified.add("FIELD_VISUAL_EFFECT_BADGE");
+        }
+        if ((bitmask & FIELD_VISUAL_EFFECT_AMBIENT) != 0) {
+            modified.add("FIELD_VISUAL_EFFECT_AMBIENT");
+        }
+        if ((bitmask & FIELD_VISUAL_EFFECT_NOTIFICATION_LIST) != 0) {
+            modified.add("FIELD_VISUAL_EFFECT_NOTIFICATION_LIST");
+        }
+        return "{" + String.join(",", modified) + "}";
+    }
+
     // Returns a list containing the first maxLength elements of the input list if the list is
     // longer than that size. For the lists in ZenPolicy, this should not happen unless the input
     // is corrupt.
@@ -1066,7 +1327,8 @@
                 && other.mPriorityMessages == mPriorityMessages
                 && other.mConversationSenders == mConversationSenders;
         if (Flags.modesApi()) {
-            return eq && other.mAllowChannels == mAllowChannels;
+            return eq && other.mAllowChannels == mAllowChannels
+                    && other.mUserModifiedFields == mUserModifiedFields;
         }
         return eq;
     }
@@ -1075,13 +1337,13 @@
     public int hashCode() {
         if (Flags.modesApi()) {
             return Objects.hash(mPriorityCategories, mVisualEffects, mPriorityCalls,
-                    mPriorityMessages, mConversationSenders, mAllowChannels);
+                    mPriorityMessages, mConversationSenders, mAllowChannels, mUserModifiedFields);
         }
         return Objects.hash(mPriorityCategories, mVisualEffects, mPriorityCalls, mPriorityMessages,
                 mConversationSenders);
     }
 
-    private @ZenPolicy.State int getZenPolicyPriorityCategoryState(@PriorityCategory int
+    private @State int getZenPolicyPriorityCategoryState(@PriorityCategory int
             category) {
         switch (category) {
             case PRIORITY_CATEGORY_REMINDERS:
@@ -1106,7 +1368,7 @@
         return -1;
     }
 
-    private @ZenPolicy.State int getZenPolicyVisualEffectState(@VisualEffect int effect) {
+    private @State int getZenPolicyVisualEffectState(@VisualEffect int effect) {
         switch (effect) {
             case VISUAL_EFFECT_FULL_SCREEN_INTENT:
                 return getVisualEffectFullScreenIntent();
diff --git a/core/java/android/service/notification/flags.aconfig b/core/java/android/service/notification/flags.aconfig
index a2ade6a..3008b8d 100644
--- a/core/java/android/service/notification/flags.aconfig
+++ b/core/java/android/service/notification/flags.aconfig
@@ -21,3 +21,11 @@
   description: "This flag controls the redacting of sensitive notifications from untrusted NotificationListenerServices"
   bug: "306271190"
 }
+
+flag {
+  name: "callstyle_callback_api"
+  namespace: "systemui"
+  description: "Guards the new CallStyleNotificationEventsCallback"
+  bug: "305095040"
+  is_fixed_read_only: true
+}
\ No newline at end of file
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 54116a2..1a2be15 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -26,6 +26,8 @@
 import static android.view.View.SYSTEM_UI_FLAG_VISIBLE;
 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
 
+import static com.android.window.flags.Flags.noConsecutiveVisibilityEvents;
+
 import android.animation.AnimationHandler;
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -1431,27 +1433,36 @@
                         }
 
                         if (didSurface && !mReportedVisible) {
-                            // This wallpaper is currently invisible, but its
-                            // surface has changed.  At this point let's tell it
-                            // again that it is invisible in case the report about
-                            // the surface caused it to start running.  We really
-                            // don't want wallpapers running when not visible.
                             if (mIsCreating) {
-                                // Some wallpapers will ignore this call if they
-                                // had previously been told they were invisble,
-                                // so if we are creating a new surface then toggle
-                                // the state to get them to notice.
-                                if (DEBUG) Log.v(TAG, "onVisibilityChanged(true) at surface: "
-                                        + this);
-                                Trace.beginSection("WPMS.Engine.onVisibilityChanged-true");
-                                onVisibilityChanged(true);
+                                // The surface has been created, but the wallpaper isn't visible.
+                                // Trigger onVisibilityChanged(true) then onVisibilityChanged(false)
+                                // to make sure the wallpaper is stopped even after the events
+                                // onSurfaceCreated() and onSurfaceChanged().
+                                if (noConsecutiveVisibilityEvents()) {
+                                    if (DEBUG) Log.v(TAG, "toggling onVisibilityChanged");
+                                    Trace.beginSection("WPMS.Engine.onVisibilityChanged-true");
+                                    onVisibilityChanged(true);
+                                    Trace.endSection();
+                                    Trace.beginSection("WPMS.Engine.onVisibilityChanged-false");
+                                    onVisibilityChanged(false);
+                                    Trace.endSection();
+                                } else {
+                                    if (DEBUG) {
+                                        Log.v(TAG, "onVisibilityChanged(true) at surface: " + this);
+                                    }
+                                    Trace.beginSection("WPMS.Engine.onVisibilityChanged-true");
+                                    onVisibilityChanged(true);
+                                    Trace.endSection();
+                                }
+                            }
+                            if (!noConsecutiveVisibilityEvents()) {
+                                if (DEBUG) {
+                                    Log.v(TAG, "onVisibilityChanged(false) at surface: " + this);
+                                }
+                                Trace.beginSection("WPMS.Engine.onVisibilityChanged-false");
+                                onVisibilityChanged(false);
                                 Trace.endSection();
                             }
-                            if (DEBUG) Log.v(TAG, "onVisibilityChanged(false) at surface: "
-                                        + this);
-                            Trace.beginSection("WPMS.Engine.onVisibilityChanged-false");
-                            onVisibilityChanged(false);
-                            Trace.endSection();
                         }
                     } finally {
                         mIsCreating = false;
diff --git a/core/java/android/service/wearable/OWNERS b/core/java/android/service/wearable/OWNERS
index 073e2d7..eca48b7 100644
--- a/core/java/android/service/wearable/OWNERS
+++ b/core/java/android/service/wearable/OWNERS
@@ -1,3 +1 @@
-charliewang@google.com
-oni@google.com
-volnov@google.com
\ No newline at end of file
+include /core/java/android/app/wearable/OWNERS
\ No newline at end of file
diff --git a/core/java/android/speech/RecognizerIntent.java b/core/java/android/speech/RecognizerIntent.java
index 118d028..1ca7ac7 100644
--- a/core/java/android/speech/RecognizerIntent.java
+++ b/core/java/android/speech/RecognizerIntent.java
@@ -16,6 +16,9 @@
 
 package android.speech;
 
+import static android.speech.flags.Flags.FLAG_MULTILANG_EXTRA_LAUNCH;
+
+import android.annotation.FlaggedApi;
 import android.app.Activity;
 import android.content.ActivityNotFoundException;
 import android.content.BroadcastReceiver;
@@ -653,4 +656,30 @@
      */
     public static final String EXTRA_LANGUAGE_SWITCH_ALLOWED_LANGUAGES =
             "android.speech.extra.LANGUAGE_SWITCH_ALLOWED_LANGUAGES";
+
+    /**
+     * Optional integer to use for {@link #EXTRA_ENABLE_LANGUAGE_SWITCH}. If set, the language
+     * switch will be deactivated when LANGUAGE_SWITCH_MAX_SWITCHES reached.
+     *
+     * <p> Depending on the recognizer implementation, this flag may have no effect.
+     *
+     * @see #EXTRA_ENABLE_LANGUAGE_SWITCH
+     */
+    @FlaggedApi(FLAG_MULTILANG_EXTRA_LAUNCH)
+    public static final String EXTRA_LANGUAGE_SWITCH_MAX_SWITCHES =
+            "android.speech.extra.LANGUAGE_SWITCH_MAX_SWITCHES";
+
+    /**
+     * Optional integer to use for {@link #EXTRA_ENABLE_LANGUAGE_SWITCH}. If set, the language
+     * switch will only be activated for this value of ms of audio since the START_OF_SPEECH. This
+     * could provide a more stable recognition result when the language switch is only required in
+     * the beginning of the session.
+     *
+     * <p> Depending on the recognizer implementation, this flag may have no effect.
+     *
+     * @see #EXTRA_ENABLE_LANGUAGE_SWITCH
+     */
+    @FlaggedApi(FLAG_MULTILANG_EXTRA_LAUNCH)
+    public static final String EXTRA_LANGUAGE_SWITCH_INITIAL_ACTIVE_DURATION_TIME_MILLIS =
+            "android.speech.extra.LANGUAGE_SWITCH_INITIAL_ACTIVE_DURATION_TIME_MILLIS";
 }
diff --git a/core/java/android/speech/flags/speech_flags.aconfig b/core/java/android/speech/flags/speech_flags.aconfig
new file mode 100644
index 0000000..fd80127
--- /dev/null
+++ b/core/java/android/speech/flags/speech_flags.aconfig
@@ -0,0 +1,8 @@
+package: "android.speech.flags"
+
+flag {
+    name: "multilang_extra_launch"
+    namespace: "machine_learning"
+    description: "Feature flag for adding new extra for multi-lang feature"
+    bug: "312489931"
+}
diff --git a/core/java/android/text/BoringLayout.java b/core/java/android/text/BoringLayout.java
index 4c81888..a6d3bb4 100644
--- a/core/java/android/text/BoringLayout.java
+++ b/core/java/android/text/BoringLayout.java
@@ -454,7 +454,7 @@
             line.set(paint, source, 0, source.length(), Layout.DIR_LEFT_TO_RIGHT,
                     Layout.DIRS_ALL_LEFT_TO_RIGHT, false, null,
                     mEllipsizedStart, mEllipsizedStart + mEllipsizedCount, useFallbackLineSpacing);
-            mMax = (int) Math.ceil(line.metrics(null, null, false));
+            mMax = (int) Math.ceil(line.metrics(null, null, false, null));
             TextLine.recycle(line);
         }
 
@@ -603,7 +603,7 @@
                 0 /* ellipsisStart, 0 since text has not been ellipsized at this point */,
                 0 /* ellipsisEnd, 0 since text has not been ellipsized at this point */,
                 useFallbackLineSpacing);
-        fm.width = (int) Math.ceil(line.metrics(fm, fm.mDrawingBounds, false));
+        fm.width = (int) Math.ceil(line.metrics(fm, fm.mDrawingBounds, false, null));
         TextLine.recycle(line);
 
         return fm;
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index c9906cc..eca848a 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -18,6 +18,7 @@
 
 import static com.android.text.flags.Flags.FLAG_FIX_LINE_HEIGHT_FOR_LOCALE;
 import static com.android.text.flags.Flags.FLAG_USE_BOUNDS_FOR_WIDTH;
+import static com.android.text.flags.Flags.FLAG_INTER_CHARACTER_JUSTIFICATION;
 
 import android.annotation.FlaggedApi;
 import android.annotation.FloatRange;
@@ -50,8 +51,10 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.text.BreakIterator;
 import java.util.Arrays;
 import java.util.List;
+import java.util.Locale;
 
 /**
  * A base class that manages text layout in visual elements on
@@ -669,7 +672,8 @@
             int start = previousLineEnd;
             previousLineEnd = getLineStart(lineNum + 1);
             final boolean justify = isJustificationRequired(lineNum);
-            int end = getLineVisibleEnd(lineNum, start, previousLineEnd);
+            int end = getLineVisibleEnd(lineNum, start, previousLineEnd,
+                    true /* trailingSpaceAtLastLineIsVisible */);
             paint.setStartHyphenEdit(getStartHyphenEdit(lineNum));
             paint.setEndHyphenEdit(getEndHyphenEdit(lineNum));
 
@@ -1056,7 +1060,7 @@
             if (isJustificationRequired(line)) {
                 tl.justify(getJustifyWidth(line));
             }
-            tl.metrics(null, rectF, false);
+            tl.metrics(null, rectF, false, null);
 
             float lineLeft = rectF.left;
             float lineRight = rectF.right;
@@ -1456,7 +1460,7 @@
         tl.set(mPaint, mText, start, end, dir, directions, hasTab, tabStops,
                 getEllipsisStart(line), getEllipsisStart(line) + getEllipsisCount(line),
                 isFallbackLineSpacingEnabled());
-        float wid = tl.measure(offset - start, trailing, null, null);
+        float wid = tl.measure(offset - start, trailing, null, null, null);
         TextLine.recycle(tl);
 
         if (clamped && wid > mWidth) {
@@ -1792,12 +1796,69 @@
         if (isJustificationRequired(line)) {
             tl.justify(getJustifyWidth(line));
         }
-        final float width = tl.metrics(null, null, mUseBoundsForWidth);
+        final float width = tl.metrics(null, null, mUseBoundsForWidth, null);
         TextLine.recycle(tl);
         return width;
     }
 
     /**
+     * Returns the number of letter spacing unit in the line.
+     *
+     * <p>
+     * This API returns a number of letters that is a target of letter spacing. The letter spacing
+     * won't be added to the middle of the characters that are needed to be treated as a single,
+     * e.g., ligatured or conjunct form. Note that this value is different from the number of]
+     * grapheme clusters that is calculated by {@link BreakIterator#getCharacterInstance(Locale)}.
+     * For example, if the "fi" is ligatured, the ligatured form is treated as single uni and letter
+     * spacing is not added, but it has two separate grapheme cluster.
+     *
+     * <p>
+     * This value is used for calculating the letter spacing amount for the justification because
+     * the letter spacing is applied between clusters. For example, if extra {@code W} pixels needed
+     * to be filled by letter spacing, the amount of letter spacing to be applied is
+     * {@code W}/(letter spacing unit count - 1) px.
+     *
+     * @param line the index of the line
+     * @param includeTrailingWhitespace whether to include trailing whitespace
+     * @return the number of cluster count in the line.
+     */
+    @IntRange(from = 0)
+    @FlaggedApi(FLAG_INTER_CHARACTER_JUSTIFICATION)
+    public int getLineLetterSpacingUnitCount(@IntRange(from = 0) int line,
+            boolean includeTrailingWhitespace) {
+        final int start = getLineStart(line);
+        final int end = includeTrailingWhitespace ? getLineEnd(line)
+                : getLineVisibleEnd(line, getLineStart(line), getLineStart(line + 1),
+                        false  // trailingSpaceAtLastLineIsVisible: Treating trailing whitespaces at
+                               // the last line as a invisible chars for single line justification.
+                );
+
+        final Directions directions = getLineDirections(line);
+        // Returned directions can actually be null
+        if (directions == null) {
+            return 0;
+        }
+        final int dir = getParagraphDirection(line);
+
+        final TextLine tl = TextLine.obtain();
+        final TextPaint paint = mWorkPaint;
+        paint.set(mPaint);
+        paint.setStartHyphenEdit(getStartHyphenEdit(line));
+        paint.setEndHyphenEdit(getEndHyphenEdit(line));
+        tl.set(paint, mText, start, end, dir, directions,
+                false, null, // tab width is not used for cluster counting.
+                getEllipsisStart(line), getEllipsisStart(line) + getEllipsisCount(line),
+                isFallbackLineSpacingEnabled());
+        if (mLineInfo == null) {
+            mLineInfo = new TextLine.LineInfo();
+        }
+        mLineInfo.setClusterCount(0);
+        tl.metrics(null, null, mUseBoundsForWidth, mLineInfo);
+        TextLine.recycle(tl);
+        return mLineInfo.getClusterCount();
+    }
+
+    /**
      * Returns the signed horizontal extent of the specified line, excluding
      * leading margin.  If full is false, excludes trailing whitespace.
      * @param line the index of the line
@@ -1823,7 +1884,7 @@
         if (isJustificationRequired(line)) {
             tl.justify(getJustifyWidth(line));
         }
-        final float width = tl.metrics(null, null, mUseBoundsForWidth);
+        final float width = tl.metrics(null, null, mUseBoundsForWidth, null);
         TextLine.recycle(tl);
         return width;
     }
@@ -2432,14 +2493,21 @@
      * is not counted) on the specified line.
      */
     public int getLineVisibleEnd(int line) {
-        return getLineVisibleEnd(line, getLineStart(line), getLineStart(line+1));
+        return getLineVisibleEnd(line, getLineStart(line), getLineStart(line + 1),
+                true /* trailingSpaceAtLastLineIsVisible */);
     }
 
-    private int getLineVisibleEnd(int line, int start, int end) {
+    private int getLineVisibleEnd(int line, int start, int end,
+            boolean trailingSpaceAtLastLineIsVisible) {
         CharSequence text = mText;
         char ch;
-        if (line == getLineCount() - 1) {
-            return end;
+
+        // Historically, trailing spaces at the last line is counted as visible. However, this
+        // doesn't work well for justification.
+        if (trailingSpaceAtLastLineIsVisible) {
+            if (line == getLineCount() - 1) {
+                return end;
+            }
         }
 
         for (; end > start; end--) {
@@ -2939,7 +3007,7 @@
             tl.set(paint, text, start, end, dir, directions, hasTabs, tabStops,
                     0 /* ellipsisStart */, 0 /* ellipsisEnd */,
                     false /* use fallback line spacing. unused */);
-            return margin + Math.abs(tl.metrics(null, null, useBoundsForWidth));
+            return margin + Math.abs(tl.metrics(null, null, useBoundsForWidth, null));
         } finally {
             TextLine.recycle(tl);
             if (mt != null) {
@@ -3337,6 +3405,8 @@
     private boolean mUseBoundsForWidth;
     private @Nullable Paint.FontMetrics mMinimumFontMetrics;
 
+    private TextLine.LineInfo mLineInfo = null;
+
     /** @hide */
     @IntDef(prefix = { "DIR_" }, value = {
             DIR_LEFT_TO_RIGHT,
diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java
index f9abec0..135935c 100644
--- a/core/java/android/text/TextLine.java
+++ b/core/java/android/text/TextLine.java
@@ -76,6 +76,21 @@
     private RectF mTmpRectForPaintAPI;
     private Rect mTmpRectForPrecompute;
 
+    // Recycling object for Paint APIs. Do not use outside getRunAdvances method.
+    private Paint.RunInfo mRunInfo;
+
+    public static final class LineInfo {
+        private int mClusterCount;
+
+        public int getClusterCount() {
+            return mClusterCount;
+        }
+
+        public void setClusterCount(int clusterCount) {
+            mClusterCount = clusterCount;
+        }
+    };
+
     private boolean mUseFallbackExtent = false;
 
     // The start and end of a potentially existing ellipsis on this text line.
@@ -270,7 +285,7 @@
             // width.
             return;
         }
-        final float width = Math.abs(measure(end, false, null, null));
+        final float width = Math.abs(measure(end, false, null, null, null));
         mAddedWidthForJustify = (justifyWidth - width) / spaces;
         mIsJustifying = true;
     }
@@ -315,10 +330,12 @@
      * @param drawBounds output parameter for drawing bounding box. optional.
      * @param returnDrawWidth true for returning width of the bounding box, false for returning
      *                       total advances.
+     * @param lineInfo an optional output parameter for filling line information.
      * @return the signed width of the line
      */
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-    public float metrics(FontMetricsInt fmi, @Nullable RectF drawBounds, boolean returnDrawWidth) {
+    public float metrics(FontMetricsInt fmi, @Nullable RectF drawBounds, boolean returnDrawWidth,
+            @Nullable LineInfo lineInfo) {
         if (returnDrawWidth) {
             if (drawBounds == null) {
                 if (mTmpRectForMeasure == null) {
@@ -327,7 +344,7 @@
                 drawBounds = mTmpRectForMeasure;
             }
             drawBounds.setEmpty();
-            float w = measure(mLen, false, fmi, drawBounds);
+            float w = measure(mLen, false, fmi, drawBounds, lineInfo);
             float boundsWidth = drawBounds.width();
             if (Math.abs(w) > boundsWidth) {
                 return w;
@@ -337,7 +354,7 @@
                 return Math.signum(w) * boundsWidth;
             }
         } else {
-            return measure(mLen, false, fmi, drawBounds);
+            return measure(mLen, false, fmi, drawBounds, lineInfo);
         }
     }
 
@@ -407,12 +424,13 @@
      *                 the edge of the preceding run's edge. See example above.
      * @param fmi receives metrics information about the requested character, can be null
      * @param drawBounds output parameter for drawing bounding box. optional.
+     * @param lineInfo an optional output parameter for filling line information.
      * @return the signed graphical offset from the leading margin to the requested character edge.
      *         The positive value means the offset is right from the leading edge. The negative
      *         value means the offset is left from the leading edge.
      */
     public float measure(@IntRange(from = 0) int offset, boolean trailing,
-            @NonNull FontMetricsInt fmi, @Nullable RectF drawBounds) {
+            @NonNull FontMetricsInt fmi, @Nullable RectF drawBounds, @Nullable LineInfo lineInfo) {
         if (offset > mLen) {
             throw new IndexOutOfBoundsException(
                     "offset(" + offset + ") should be less than line limit(" + mLen + ")");
@@ -437,16 +455,16 @@
 
                     if (targetIsInThisSegment && sameDirection) {
                         return h + measureRun(segStart, offset, j, runIsRtl, fmi, drawBounds, null,
-                                0, h);
+                                0, h, lineInfo);
                     }
 
                     final float segmentWidth = measureRun(segStart, j, j, runIsRtl, fmi, drawBounds,
-                            null, 0, h);
+                            null, 0, h, lineInfo);
                     h += sameDirection ? segmentWidth : -segmentWidth;
 
                     if (targetIsInThisSegment) {
                         return h + measureRun(segStart, offset, j, runIsRtl, null, null,  null, 0,
-                                h);
+                                h, lineInfo);
                     }
 
                     if (j != runLimit) {  // charAt(j) == TAB_CHAR
@@ -537,7 +555,8 @@
                     final boolean sameDirection = (mDir == Layout.DIR_RIGHT_TO_LEFT) == runIsRtl;
 
                     final float segmentWidth =
-                            measureRun(segStart, j, j, runIsRtl, null, null, advances, segStart, 0);
+                            measureRun(segStart, j, j, runIsRtl, null, null, advances, segStart, 0,
+                                    null);
 
                     final float oldh = h;
                     h += sameDirection ? segmentWidth : -segmentWidth;
@@ -578,7 +597,7 @@
     }
 
     /**
-     * @see #measure(int, boolean, FontMetricsInt, RectF)
+     * @see #measure(int, boolean, FontMetricsInt, RectF, LineInfo)
      * @return The measure results for all possible offsets
      */
     @VisibleForTesting
@@ -610,7 +629,7 @@
                     final float previousSegEndHorizontal = measurement[segStart];
                     final float width =
                             measureRun(segStart, j, j, runIsRtl, fmi, null, measurement, segStart,
-                                    0);
+                                    0, null);
                     horizontal += sameDirection ? width : -width;
 
                     float currHorizontal = sameDirection ? oldHorizontal : horizontal;
@@ -675,14 +694,14 @@
             boolean needWidth) {
 
         if ((mDir == Layout.DIR_LEFT_TO_RIGHT) == runIsRtl) {
-            float w = -measureRun(start, limit, limit, runIsRtl, null, null, null, 0, 0);
+            float w = -measureRun(start, limit, limit, runIsRtl, null, null, null, 0, 0, null);
             handleRun(start, limit, limit, runIsRtl, c, null, x + w, top,
-                    y, bottom, null, null, false, null, 0);
+                    y, bottom, null, null, false, null, 0, null);
             return w;
         }
 
         return handleRun(start, limit, limit, runIsRtl, c, null, x, top,
-                y, bottom, null, null, needWidth, null, 0);
+                y, bottom, null, null, needWidth, null, 0, null);
     }
 
     /**
@@ -698,19 +717,20 @@
      * @param advances receives the advance information about the requested run, can be null.
      * @param advancesIndex the start index to fill in the advance information.
      * @param x horizontal offset of the run.
+     * @param lineInfo an optional output parameter for filling line information.
      * @return the signed width from the start of the run to the leading edge
      * of the character at offset, based on the run (not paragraph) direction
      */
     private float measureRun(int start, int offset, int limit, boolean runIsRtl,
             @Nullable FontMetricsInt fmi, @Nullable RectF drawBounds, @Nullable float[] advances,
-            int advancesIndex, float x) {
+            int advancesIndex, float x, @Nullable LineInfo lineInfo) {
         if (drawBounds != null && (mDir == Layout.DIR_LEFT_TO_RIGHT) == runIsRtl) {
-            float w = -measureRun(start, offset, limit, runIsRtl, null, null, null, 0, 0);
+            float w = -measureRun(start, offset, limit, runIsRtl, null, null, null, 0, 0, null);
             return handleRun(start, offset, limit, runIsRtl, null, null, x + w, 0, 0, 0, fmi,
-                    drawBounds, true, advances, advancesIndex);
+                    drawBounds, true, advances, advancesIndex, lineInfo);
         }
         return handleRun(start, offset, limit, runIsRtl, null, null, x, 0, 0, 0, fmi, drawBounds,
-                true, advances, advancesIndex);
+                true, advances, advancesIndex, lineInfo);
     }
 
     /**
@@ -729,14 +749,14 @@
             int limit, boolean runIsRtl, float x, boolean needWidth) {
 
         if ((mDir == Layout.DIR_LEFT_TO_RIGHT) == runIsRtl) {
-            float w = -measureRun(start, limit, limit, runIsRtl, null, null, null, 0, 0);
+            float w = -measureRun(start, limit, limit, runIsRtl, null, null, null, 0, 0, null);
             handleRun(start, limit, limit, runIsRtl, null, consumer, x + w, 0, 0, 0, null, null,
-                    false, null, 0);
+                    false, null, 0, null);
             return w;
         }
 
         return handleRun(start, limit, limit, runIsRtl, null, consumer, x, 0, 0, 0, null, null,
-                needWidth, null, 0);
+                needWidth, null, 0, null);
     }
 
 
@@ -1077,16 +1097,35 @@
 
     private float getRunAdvance(TextPaint wp, int start, int end, int contextStart, int contextEnd,
             boolean runIsRtl, int offset, @Nullable float[] advances, int advancesIndex,
-            RectF drawingBounds) {
+            RectF drawingBounds, @Nullable LineInfo lineInfo) {
+        if (lineInfo != null) {
+            if (mRunInfo == null) {
+                mRunInfo = new Paint.RunInfo();
+            }
+            mRunInfo.setClusterCount(0);
+        } else {
+            mRunInfo = null;
+        }
         if (mCharsValid) {
-            return wp.getRunCharacterAdvance(mChars, start, end, contextStart, contextEnd,
-                    runIsRtl, offset, advances, advancesIndex, drawingBounds);
+            float r = wp.getRunCharacterAdvance(mChars, start, end, contextStart, contextEnd,
+                    runIsRtl, offset, advances, advancesIndex, drawingBounds, mRunInfo);
+            if (lineInfo != null) {
+                lineInfo.setClusterCount(lineInfo.getClusterCount() + mRunInfo.getClusterCount());
+            }
+            return r;
         } else {
             final int delta = mStart;
-            if (mComputed == null || advances != null) {
-                return wp.getRunCharacterAdvance(mText, delta + start, delta + end,
+            // TODO: Add cluster information to the PrecomputedText for better performance of
+            // justification.
+            if (mComputed == null || advances != null || lineInfo != null) {
+                float r = wp.getRunCharacterAdvance(mText, delta + start, delta + end,
                         delta + contextStart, delta + contextEnd, runIsRtl,
-                        delta + offset, advances, advancesIndex, drawingBounds);
+                        delta + offset, advances, advancesIndex, drawingBounds, mRunInfo);
+                if (lineInfo != null) {
+                    lineInfo.setClusterCount(
+                            lineInfo.getClusterCount() + mRunInfo.getClusterCount());
+                }
+                return r;
             } else {
                 if (drawingBounds != null) {
                     if (mTmpRectForPrecompute == null) {
@@ -1120,6 +1159,7 @@
      * @param decorations the list of locations and paremeters for drawing decorations
      * @param advances receives the advance information about the requested run, can be null.
      * @param advancesIndex the start index to fill in the advance information.
+     * @param lineInfo an optional output parameter for filling line information.
      * @return the signed width of the run based on the run direction; only
      * valid if needWidth is true
      */
@@ -1128,7 +1168,7 @@
             Canvas c, TextShaper.GlyphsConsumer consumer, float x, int top, int y, int bottom,
             FontMetricsInt fmi, RectF drawBounds, boolean needWidth, int offset,
             @Nullable ArrayList<DecorationInfo> decorations,
-            @Nullable float[] advances, int advancesIndex) {
+            @Nullable float[] advances, int advancesIndex, @Nullable LineInfo lineInfo) {
 
         if (mIsJustifying) {
             wp.setWordSpacing(mAddedWidthForJustify);
@@ -1155,7 +1195,8 @@
                 mTmpRectForPaintAPI = new RectF();
             }
             totalWidth = getRunAdvance(wp, start, end, contextStart, contextEnd, runIsRtl, offset,
-                    advances, advancesIndex, drawBounds == null ? null : mTmpRectForPaintAPI);
+                    advances, advancesIndex, drawBounds == null ? null : mTmpRectForPaintAPI,
+                    lineInfo);
             if (drawBounds != null) {
                 if (runIsRtl) {
                     mTmpRectForPaintAPI.offset(x - totalWidth, 0);
@@ -1206,9 +1247,9 @@
                     final int decorationStart = Math.max(info.start, start);
                     final int decorationEnd = Math.min(info.end, offset);
                     float decorationStartAdvance = getRunAdvance(wp, start, end, contextStart,
-                            contextEnd, runIsRtl, decorationStart, null, 0, null);
+                            contextEnd, runIsRtl, decorationStart, null, 0, null, null);
                     float decorationEndAdvance = getRunAdvance(wp, start, end, contextStart,
-                            contextEnd, runIsRtl, decorationEnd, null, 0, null);
+                            contextEnd, runIsRtl, decorationEnd, null, 0, null, null);
                     final float decorationXLeft, decorationXRight;
                     if (runIsRtl) {
                         decorationXLeft = rightX - decorationEndAdvance;
@@ -1377,6 +1418,7 @@
      * @param needWidth true if the width is required
      * @param advances receives the advance information about the requested run, can be null.
      * @param advancesIndex the start index to fill in the advance information.
+     * @param lineInfo an optional output parameter for filling line information.
      * @return the signed width of the run based on the run direction; only
      * valid if needWidth is true
      */
@@ -1384,7 +1426,7 @@
             int limit, boolean runIsRtl, Canvas c,
             TextShaper.GlyphsConsumer consumer, float x, int top, int y,
             int bottom, FontMetricsInt fmi, RectF drawBounds, boolean needWidth,
-            @Nullable float[] advances, int advancesIndex) {
+            @Nullable float[] advances, int advancesIndex, @Nullable LineInfo lineInfo) {
 
         if (measureLimit < start || measureLimit > limit) {
             throw new IndexOutOfBoundsException("measureLimit (" + measureLimit + ") is out of "
@@ -1431,7 +1473,7 @@
             wp.setEndHyphenEdit(adjustEndHyphenEdit(limit, wp.getEndHyphenEdit()));
             return handleText(wp, start, limit, start, limit, runIsRtl, c, consumer, x, top,
                     y, bottom, fmi, drawBounds, needWidth, measureLimit, null, advances,
-                    advancesIndex);
+                    advancesIndex, lineInfo);
         }
 
         // Shaping needs to take into account context up to metric boundaries,
@@ -1523,7 +1565,7 @@
                             consumer, x, top, y, bottom, fmi, drawBounds,
                             needWidth || activeEnd < measureLimit,
                             Math.min(activeEnd, mlimit), mDecorations,
-                            advances, advancesIndex + activeStart - start);
+                            advances, advancesIndex + activeStart - start, lineInfo);
 
                     activeStart = j;
                     activePaint.set(wp);
@@ -1551,7 +1593,7 @@
             x += handleText(activePaint, activeStart, activeEnd, i, inext, runIsRtl, c, consumer, x,
                     top, y, bottom, fmi, drawBounds, needWidth || activeEnd < measureLimit,
                     Math.min(activeEnd, mlimit), mDecorations,
-                    advances, advancesIndex + activeStart - start);
+                    advances, advancesIndex + activeStart - start, lineInfo);
         }
 
         return x - originalX;
diff --git a/core/java/android/view/DisplayAddress.java b/core/java/android/view/DisplayAddress.java
index 99e811a..c3c4ab5 100644
--- a/core/java/android/view/DisplayAddress.java
+++ b/core/java/android/view/DisplayAddress.java
@@ -138,6 +138,30 @@
             out.writeLong(mPhysicalDisplayId);
         }
 
+        /**
+         * This method is meant to check to see if the ports match
+         * @param a1 Address to compare
+         * @param a2 Address to compare
+         *
+         * @return true if the arguments have the same port, and at least one does not specify
+         *         a model.
+         */
+        public static boolean isPortMatch(DisplayAddress a1, DisplayAddress a2) {
+            // Both displays must be of type Physical
+            if (!(a1 instanceof Physical && a2 instanceof Physical)) {
+                return false;
+            }
+            Physical p1 = (Physical) a1;
+            Physical p2 = (Physical) a2;
+
+            // If both addresses specify a model, fallback to a basic match check (which
+            // also checks the port).
+            if (p1.getModel() != null && p2.getModel() != null) {
+                return p1.equals(p2);
+            }
+            return p1.getPort() == p2.getPort();
+        }
+
         private Physical(long physicalDisplayId) {
             mPhysicalDisplayId = physicalDisplayId;
         }
diff --git a/core/java/android/view/DisplayCutout.java b/core/java/android/view/DisplayCutout.java
index 712d1d6..f819c9b 100644
--- a/core/java/android/view/DisplayCutout.java
+++ b/core/java/android/view/DisplayCutout.java
@@ -16,6 +16,7 @@
 
 package android.view;
 
+import static android.content.res.Resources.ID_NULL;
 import static android.util.DisplayMetrics.DENSITY_DEFAULT;
 import static android.util.DisplayMetrics.DENSITY_DEVICE_STABLE;
 import static android.view.DisplayCutoutProto.BOUND_BOTTOM;
@@ -23,8 +24,12 @@
 import static android.view.DisplayCutoutProto.BOUND_RIGHT;
 import static android.view.DisplayCutoutProto.BOUND_TOP;
 import static android.view.DisplayCutoutProto.INSETS;
+import static android.view.DisplayCutoutProto.SIDE_OVERRIDES;
 import static android.view.DisplayCutoutProto.WATERFALL_INSETS;
 import static android.view.Surface.ROTATION_0;
+import static android.view.Surface.ROTATION_180;
+import static android.view.Surface.ROTATION_270;
+import static android.view.Surface.ROTATION_90;
 
 import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE;
 
@@ -49,6 +54,7 @@
 import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.window.flags.Flags;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -112,6 +118,9 @@
     private static float sCachedPhysicalPixelDisplaySizeRatio;
 
     @GuardedBy("CACHE_LOCK")
+    private static int[] sCachedSideOverrides;
+
+    @GuardedBy("CACHE_LOCK")
     private static CutoutPathParserInfo sCachedCutoutPathParserInfo;
     @GuardedBy("CACHE_LOCK")
     private static Path sCachedCutoutPath;
@@ -150,6 +159,15 @@
      */
     public static final int BOUNDS_POSITION_LENGTH = 4;
 
+    private static final int INVALID_SIDE_OVERRIDE = -1;
+    private static final String SIDE_STRING_TOP = "top";
+    private static final String SIDE_STRING_BOTTOM = "bottom";
+    private static final String SIDE_STRING_RIGHT = "right";
+    private static final String SIDE_STRING_LEFT = "left";
+
+    // The side index is always under the natural rotation of the device.
+    private int[] mSideOverrides;
+
     /** @hide */
     @IntDef(prefix = { "BOUNDS_POSITION_" }, value = {
             BOUNDS_POSITION_LEFT,
@@ -402,8 +420,36 @@
     // TODO(b/73953958): @VisibleForTesting(visibility = PRIVATE)
     public DisplayCutout(@NonNull Insets safeInsets, @Nullable Rect boundLeft,
             @Nullable Rect boundTop, @Nullable Rect boundRight, @Nullable Rect boundBottom) {
-        this(safeInsets.toRect(), Insets.NONE, boundLeft, boundTop, boundRight, boundBottom, null,
-                true);
+        this(getCopyOrRef(safeInsets.toRect(), true), Insets.NONE,
+                new Bounds(boundLeft, boundTop, boundRight, boundBottom, true), null, null);
+    }
+
+    /**
+     * Creates a DisplayCutout instance.
+     *
+     * <p>Note that this is only useful for tests. For production code, developers should always
+     * use a {@link DisplayCutout} obtained from the system.</p>
+     *
+     * @param safeInsets the insets from each edge which avoid the display cutout as returned by
+     *                   {@link #getSafeInsetTop()} etc.
+     * @param boundLeft the left bounding rect of the display cutout in pixels. If null is passed,
+     *                  it's treated as an empty rectangle (0,0)-(0,0).
+     * @param boundTop the top bounding rect of the display cutout in pixels.  If null is passed,
+     *                  it's treated as an empty rectangle (0,0)-(0,0).
+     * @param boundRight the right bounding rect of the display cutout in pixels.  If null is
+     *                  passed, it's treated as an empty rectangle (0,0)-(0,0).
+     * @param boundBottom the bottom bounding rect of the display cutout in pixels.  If null is
+     *                   passed, it's treated as an empty rectangle (0,0)-(0,0).
+     * @param waterfallInsets the insets for the curved areas in waterfall display.
+     * @param info the cutout path parser info.
+     * @hide
+     */
+    @VisibleForTesting
+    public DisplayCutout(@NonNull Insets safeInsets, @Nullable Rect boundLeft,
+            @Nullable Rect boundTop, @Nullable Rect boundRight, @Nullable Rect boundBottom,
+            @NonNull Insets waterfallInsets, @Nullable CutoutPathParserInfo info) {
+        this(getCopyOrRef(safeInsets.toRect(), true), waterfallInsets,
+                new Bounds(boundLeft, boundTop, boundRight, boundBottom, true), info, null);
     }
 
     /**
@@ -428,9 +474,11 @@
      */
     public DisplayCutout(@NonNull Insets safeInsets, @Nullable Rect boundLeft,
             @Nullable Rect boundTop, @Nullable Rect boundRight, @Nullable Rect boundBottom,
-            @NonNull Insets waterfallInsets, @Nullable CutoutPathParserInfo info) {
-        this(safeInsets.toRect(), waterfallInsets, boundLeft, boundTop, boundRight, boundBottom,
-                info, true);
+            @NonNull Insets waterfallInsets, @Nullable CutoutPathParserInfo info,
+            @Nullable int[] sideOverrides) {
+        this(safeInsets.toRect(), waterfallInsets,
+                new Bounds(boundLeft, boundTop, boundRight, boundBottom, true),
+                info, sideOverrides);
     }
 
     /**
@@ -454,8 +502,8 @@
     public DisplayCutout(@NonNull Insets safeInsets, @Nullable Rect boundLeft,
             @Nullable Rect boundTop, @Nullable Rect boundRight, @Nullable Rect boundBottom,
             @NonNull Insets waterfallInsets) {
-        this(safeInsets.toRect(), waterfallInsets, boundLeft, boundTop, boundRight, boundBottom,
-                null, true);
+        this(getCopyOrRef(safeInsets.toRect(), true), waterfallInsets,
+                new Bounds(boundLeft, boundTop, boundRight, boundBottom, true), null, null);
     }
 
     /**
@@ -473,8 +521,8 @@
     // TODO(b/73953958): @VisibleForTesting(visibility = PRIVATE)
     @Deprecated
     public DisplayCutout(@Nullable Rect safeInsets, @Nullable List<Rect> boundingRects) {
-        this(safeInsets, Insets.NONE, extractBoundsFromList(safeInsets, boundingRects), null,
-                true /* copyArguments */);
+        this(getCopyOrRef(safeInsets, true), Insets.NONE,
+                new Bounds(extractBoundsFromList(safeInsets, boundingRects), true), null, null);
     }
 
     /**
@@ -498,26 +546,29 @@
     private DisplayCutout(Rect safeInsets, Insets waterfallInsets, Rect boundLeft, Rect boundTop,
             Rect boundRight, Rect boundBottom, CutoutPathParserInfo info,
             boolean copyArguments) {
-        mSafeInsets = getCopyOrRef(safeInsets, copyArguments);
-        mWaterfallInsets = waterfallInsets == null ? Insets.NONE : waterfallInsets;
-        mBounds = new Bounds(boundLeft, boundTop, boundRight, boundBottom, copyArguments);
-        mCutoutPathParserInfo = info == null ? EMPTY_PARSER_INFO : info;
+        this(getCopyOrRef(safeInsets, copyArguments), waterfallInsets,
+                new Bounds(boundLeft, boundTop, boundRight, boundBottom, copyArguments), info,
+                null);
     }
 
     private DisplayCutout(Rect safeInsets, Insets waterfallInsets, Rect[] bounds,
             CutoutPathParserInfo info, boolean copyArguments) {
-        mSafeInsets = getCopyOrRef(safeInsets, copyArguments);
-        mWaterfallInsets = waterfallInsets == null ? Insets.NONE : waterfallInsets;
-        mBounds = new Bounds(bounds, copyArguments);
-        mCutoutPathParserInfo = info == null ? EMPTY_PARSER_INFO : info;
+        this(getCopyOrRef(safeInsets, copyArguments), waterfallInsets,
+                new Bounds(bounds, copyArguments), info, null);
     }
 
     private DisplayCutout(Rect safeInsets, Insets waterfallInsets, Bounds bounds,
             CutoutPathParserInfo info) {
+        this(safeInsets, waterfallInsets, bounds, info, null);
+    }
+
+    private DisplayCutout(Rect safeInsets, Insets waterfallInsets, Bounds bounds,
+            CutoutPathParserInfo info, int[] sideOverrides) {
         mSafeInsets = safeInsets;
         mWaterfallInsets = waterfallInsets == null ? Insets.NONE : waterfallInsets;
         mBounds = bounds;
         mCutoutPathParserInfo = info == null ? EMPTY_PARSER_INFO : info;
+        mSideOverrides = sideOverrides;
     }
 
     private static Rect getCopyOrRef(Rect r, boolean copyArguments) {
@@ -795,6 +846,7 @@
         result = 48271 * result + mBounds.hashCode();
         result = 48271 * result + mWaterfallInsets.hashCode();
         result = 48271 * result + mCutoutPathParserInfo.hashCode();
+        result = 48271 * result + Arrays.hashCode(mSideOverrides);
         return result;
     }
 
@@ -807,7 +859,8 @@
             DisplayCutout c = (DisplayCutout) o;
             return mSafeInsets.equals(c.mSafeInsets) && mBounds.equals(c.mBounds)
                     && mWaterfallInsets.equals(c.mWaterfallInsets)
-                    && mCutoutPathParserInfo.equals(c.mCutoutPathParserInfo);
+                    && mCutoutPathParserInfo.equals(c.mCutoutPathParserInfo)
+                    && Arrays.equals(mSideOverrides, c.mSideOverrides);
         }
         return false;
     }
@@ -818,9 +871,48 @@
                 + " waterfall=" + mWaterfallInsets
                 + " boundingRect={" + mBounds + "}"
                 + " cutoutPathParserInfo={" + mCutoutPathParserInfo + "}"
+                + " sideOverrides=" + sideOverridesToString(mSideOverrides)
                 + "}";
     }
 
+    private static String sideOverridesToString(int[] sideOverrides) {
+        if (sideOverrides == null) {
+            return "null";
+        }
+        final StringBuilder sb = new StringBuilder();
+        sb.append("{");
+        final int length = sideOverrides.length;
+        if (length != BOUNDS_POSITION_LENGTH) {
+            sb.append("length=").append(sideOverrides.length).append(". ");
+        }
+        boolean hasContent = false;
+        for (int i = ROTATION_0; i < length; i++) {
+            final int override = sideOverrides[i];
+            if (override != INVALID_SIDE_OVERRIDE) {
+                if (hasContent) {
+                    sb.append(", ");
+                }
+                sb.append(Surface.rotationToString(i)).append(": ");
+                switch(override) {
+                    case BOUNDS_POSITION_LEFT:
+                        sb.append(SIDE_STRING_LEFT);
+                        break;
+                    case BOUNDS_POSITION_TOP:
+                        sb.append(SIDE_STRING_TOP);
+                        break;
+                    case BOUNDS_POSITION_RIGHT:
+                        sb.append(SIDE_STRING_RIGHT);
+                        break;
+                    case BOUNDS_POSITION_BOTTOM:
+                        sb.append(SIDE_STRING_BOTTOM);
+                        break;
+                }
+                hasContent = true;
+            }
+        }
+        return sb.append("}").toString();
+    }
+
     /**
      * @hide
      */
@@ -832,6 +924,11 @@
         mBounds.getRect(BOUNDS_POSITION_RIGHT).dumpDebug(proto, BOUND_RIGHT);
         mBounds.getRect(BOUNDS_POSITION_BOTTOM).dumpDebug(proto, BOUND_BOTTOM);
         mWaterfallInsets.toRect().dumpDebug(proto, WATERFALL_INSETS);
+        if (mSideOverrides != null) {
+            for (int sideOverride : mSideOverrides) {
+                proto.write(SIDE_OVERRIDES, sideOverride);
+            }
+        }
         proto.end(token);
     }
 
@@ -899,7 +996,7 @@
      */
     public DisplayCutout replaceSafeInsets(Rect safeInsets) {
         return new DisplayCutout(new Rect(safeInsets), mWaterfallInsets, mBounds,
-                mCutoutPathParserInfo);
+                mCutoutPathParserInfo, mSideOverrides);
     }
 
     private static int atLeastZero(int value) {
@@ -1031,8 +1128,10 @@
         Insets insets;
         final int index = DisplayUtils.getDisplayUniqueIdConfigIndex(res, displayUniqueId);
         final TypedArray array = res.obtainTypedArray(R.array.config_waterfallCutoutArray);
-        if (index >= 0 && index < array.length() && array.getResourceId(index, 0) > 0) {
-            final int resourceId = array.getResourceId(index, 0);
+        final int resourceId = index >= 0 && index < array.length()
+                ? array.getResourceId(index, ID_NULL)
+                : ID_NULL;
+        if (resourceId != ID_NULL) {
             final TypedArray waterfall = res.obtainTypedArray(resourceId);
             insets = Insets.of(
                     waterfall.getDimensionPixelSize(0 /* waterfall left edge size */, 0),
@@ -1047,6 +1146,48 @@
         return insets;
     }
 
+    private static int[] getDisplayCutoutSideOverrides(Resources res, String displayUniqueId)
+            throws IllegalArgumentException {
+        if (!Flags.movableCutoutConfiguration()) {
+            return null;
+        }
+        final int index = DisplayUtils.getDisplayUniqueIdConfigIndex(res, displayUniqueId);
+        final TypedArray array = res.obtainTypedArray(
+                R.array.config_displayCutoutSideOverrideArray);
+        final int resourceId = index >= 0 && index < array.length()
+                ? array.getResourceId(index, ID_NULL)
+                : ID_NULL;
+        final String[] rawOverrides = resourceId != ID_NULL
+                ? array.getResources().getStringArray(resourceId)
+                : res.getStringArray(R.array.config_mainBuiltInDisplayCutoutSideOverride);
+        array.recycle();
+        final int[] override = new int[]{INVALID_SIDE_OVERRIDE, INVALID_SIDE_OVERRIDE,
+                INVALID_SIDE_OVERRIDE, INVALID_SIDE_OVERRIDE};
+        for (String rawOverride : rawOverrides) {
+            int rotation;
+            String[] split = rawOverride.split(" *, *");
+            switch (split[0]) {
+                case "0" -> rotation = ROTATION_0;
+                case "90" -> rotation = ROTATION_90;
+                case "180" -> rotation = ROTATION_180;
+                case "270" -> rotation = ROTATION_270;
+                default -> throw new IllegalArgumentException("Invalid side override definition: "
+                            + rawOverride);
+            }
+            int side;
+            switch (split[1]) {
+                case SIDE_STRING_LEFT -> side = BOUNDS_POSITION_LEFT;
+                case SIDE_STRING_TOP -> side = BOUNDS_POSITION_TOP;
+                case SIDE_STRING_RIGHT -> side = BOUNDS_POSITION_RIGHT;
+                case SIDE_STRING_BOTTOM -> side = BOUNDS_POSITION_BOTTOM;
+                default -> throw new IllegalArgumentException("Invalid side override definition: "
+                        + rawOverride);
+            }
+            override[rotation] = side;
+        }
+        return override;
+    }
+
     /**
      * Creates the display cutout according to
      * @android:string/config_mainBuiltInDisplayCutoutRectApproximation, which is the closest
@@ -1060,7 +1201,8 @@
                 getDisplayCutoutApproximationRect(res, displayUniqueId), physicalDisplayWidth,
                 physicalDisplayHeight, displayWidth, displayHeight,
                 DENSITY_DEVICE_STABLE / (float) DENSITY_DEFAULT,
-                getWaterfallInsets(res, displayUniqueId)).second;
+                getWaterfallInsets(res, displayUniqueId),
+                getDisplayCutoutSideOverrides(res, displayUniqueId)).second;
     }
 
     /**
@@ -1070,17 +1212,17 @@
      */
     @VisibleForTesting(visibility = PRIVATE)
     public static DisplayCutout fromSpec(String pathSpec, int displayWidth,
-            int displayHeight, float density, Insets waterfallInsets) {
+            int displayHeight, float density, Insets waterfallInsets, int[] sideOverrides) {
         return pathAndDisplayCutoutFromSpec(
                 pathSpec, null, displayWidth, displayHeight, displayWidth, displayHeight, density,
-                waterfallInsets).second;
+                waterfallInsets, sideOverrides).second;
     }
 
     /**
      * Gets the cutout path and the corresponding DisplayCutout instance from the spec string.
      *
-     * @param pathSpec the spec string read from config_mainBuiltInDisplayCutout.
-     * @param rectSpec the spec string read from config_mainBuiltInDisplayCutoutRectApproximation.
+     * @param pathSpec the spec string read from config for certain display.
+     * @param rectSpec the rect approximation spec string read from config for certain display.
      * @param physicalDisplayWidth the max physical display width the display supports.
      * @param physicalDisplayHeight the max physical display height the display supports.
      * @param displayWidth the display width.
@@ -1091,7 +1233,8 @@
      */
     private static Pair<Path, DisplayCutout> pathAndDisplayCutoutFromSpec(
             String pathSpec, String rectSpec, int physicalDisplayWidth, int physicalDisplayHeight,
-            int displayWidth, int displayHeight, float density, Insets waterfallInsets) {
+            int displayWidth, int displayHeight, float density, Insets waterfallInsets,
+            int[] sideOverrides) {
         // Always use the rect approximation spec to create the cutout if it's not null because
         // transforming and sending a Region constructed from a path is very costly.
         String spec = rectSpec != null ? rectSpec : pathSpec;
@@ -1107,7 +1250,8 @@
                     && sCachedDisplayHeight == displayHeight
                     && sCachedDensity == density
                     && waterfallInsets.equals(sCachedWaterfallInsets)
-                    && sCachedPhysicalPixelDisplaySizeRatio == physicalPixelDisplaySizeRatio) {
+                    && sCachedPhysicalPixelDisplaySizeRatio == physicalPixelDisplaySizeRatio
+                    && Arrays.equals(sCachedSideOverrides, sideOverrides)) {
                 return sCachedCutout;
             }
         }
@@ -1123,7 +1267,6 @@
         final Rect boundRight = cutoutSpec.getRightBound();
         final Rect boundBottom = cutoutSpec.getBottomBound();
 
-
         if (!waterfallInsets.equals(Insets.NONE)) {
             safeInset.set(
                     Math.max(waterfallInsets.left, safeInset.left),
@@ -1135,10 +1278,21 @@
         final CutoutPathParserInfo cutoutPathParserInfo = new CutoutPathParserInfo(
                 displayWidth, displayHeight, physicalDisplayWidth, physicalDisplayHeight, density,
                 pathSpec.trim(), ROTATION_0, 1f /* scale */, physicalPixelDisplaySizeRatio);
+        final int sideOverride = getSideOverride(sideOverrides, ROTATION_0);
+        final Rect[] bounds = new Bounds(boundLeft, boundTop, boundRight, boundBottom, false)
+                .getRects();
+        final int rotateDistance = getRotationToOverride(sideOverride, bounds,
+                ROTATION_0 /* defaultRotation */);
+        if (rotateDistance != ROTATION_0) {
+            Collections.rotate(Arrays.asList(bounds), rotateDistance);
+        }
+        final Rect safeInsets = DisplayCutout.computeSafeInsets(displayWidth, displayHeight,
+                waterfallInsets, bounds);
+        final DisplayCutout cutout = new DisplayCutout(safeInsets, waterfallInsets,
+                new Bounds(bounds[BOUNDS_POSITION_LEFT], bounds[BOUNDS_POSITION_TOP],
+                        bounds[BOUNDS_POSITION_RIGHT], bounds[BOUNDS_POSITION_BOTTOM], false),
+                cutoutPathParserInfo, sideOverrides);
 
-        final DisplayCutout cutout = new DisplayCutout(
-                safeInset, waterfallInsets, boundLeft, boundTop, boundRight, boundBottom,
-                cutoutPathParserInfo , false /* copyArguments */);
         final Pair<Path, DisplayCutout> result = new Pair<>(cutoutSpec.getPath(), cutout);
         synchronized (CACHE_LOCK) {
             sCachedSpec = spec;
@@ -1148,6 +1302,7 @@
             sCachedCutout = result;
             sCachedWaterfallInsets = waterfallInsets;
             sCachedPhysicalPixelDisplaySizeRatio = physicalPixelDisplaySizeRatio;
+            sCachedSideOverrides = sideOverrides;
         }
         return result;
     }
@@ -1181,7 +1336,10 @@
             if (newBounds[i].isEmpty()) continue;
             RotationUtils.rotateBounds(newBounds[i], displayBounds, rotation);
         }
-        Collections.rotate(Arrays.asList(newBounds), -rotation);
+        final int defaultRotation = -rotation;
+        final int override = getSideOverride(mSideOverrides, toRotation);
+        Collections.rotate(Arrays.asList(newBounds),
+                getRotationToOverride(override, newBounds, defaultRotation));
         final CutoutPathParserInfo info = getCutoutPathParserInfo();
         final CutoutPathParserInfo newInfo = new CutoutPathParserInfo(
                 info.getDisplayWidth(), info.getDisplayHeight(), info.getPhysicalDisplayWidth(),
@@ -1193,51 +1351,87 @@
         final DisplayCutout tmp =
                 DisplayCutout.constructDisplayCutout(newBounds, waterfallInsets, newInfo);
         final Rect safeInsets = DisplayCutout.computeSafeInsets(endWidth, endHeight, tmp);
+        tmp.mSideOverrides = mSideOverrides;
         return tmp.replaceSafeInsets(safeInsets);
     }
 
+    private static int getSideOverride(int[] sideOverrides, @Rotation int rotation) {
+        if (sideOverrides == null || sideOverrides.length != 4) {
+            return INVALID_SIDE_OVERRIDE;
+        }
+        return sideOverrides[rotation];
+    }
+
+    /** @return the rotation needed to rotate from the original side to the overridden one. */
+    private static @Rotation int getRotationToOverride(int sideOverride, Rect[] bounds,
+            @Rotation int defaultRotation) {
+        if (sideOverride == INVALID_SIDE_OVERRIDE) {
+            return defaultRotation;
+        }
+        int side = -1;
+        for (int i = 0; i <= BOUNDS_POSITION_BOTTOM; i++) {
+            if (bounds[i].isEmpty()) {
+                continue;
+            }
+            if (side != -1) {
+                // We don't rotate at all when there are multiple non empty cutout bounds.
+                return defaultRotation;
+            }
+            side = i;
+        }
+        if (side == -1) {
+            return defaultRotation;
+        }
+        int rotation = sideOverride - side;
+        if (rotation < 0) {
+            rotation += 4;
+        }
+        return rotation;
+    }
+
     /**
      * Compute the insets derived from a cutout. This is usually used to populate the safe-insets
      * of the cutout via {@link #replaceSafeInsets}.
      * @hide
      */
     public static Rect computeSafeInsets(int displayW, int displayH, DisplayCutout cutout) {
+        return computeSafeInsets(displayW, displayH, cutout.getWaterfallInsets(),
+                cutout.getBoundingRectsAll());
+    }
+
+    private static Rect computeSafeInsets(int displayW, int displayH, Insets waterFallInsets,
+            Rect[] bounds) {
         if (displayW == displayH) {
             throw new UnsupportedOperationException("not implemented: display=" + displayW + "x"
-                    + displayH + " cutout=" + cutout);
+                    + displayH + " bounding rects=" + Arrays.toString(bounds));
         }
 
-        int leftInset = Math.max(cutout.getWaterfallInsets().left, findCutoutInsetForSide(
-                displayW, displayH, cutout.getBoundingRectLeft(), Gravity.LEFT));
-        int topInset = Math.max(cutout.getWaterfallInsets().top, findCutoutInsetForSide(
-                displayW, displayH, cutout.getBoundingRectTop(), Gravity.TOP));
-        int rightInset = Math.max(cutout.getWaterfallInsets().right, findCutoutInsetForSide(
-                displayW, displayH, cutout.getBoundingRectRight(), Gravity.RIGHT));
-        int bottomInset = Math.max(cutout.getWaterfallInsets().bottom, findCutoutInsetForSide(
-                displayW, displayH, cutout.getBoundingRectBottom(), Gravity.BOTTOM));
+        int leftInset = Math.max(waterFallInsets.left, findCutoutInsetForSide(
+                displayW, displayH, bounds[BOUNDS_POSITION_LEFT], Gravity.LEFT));
+        int topInset = Math.max(waterFallInsets.top, findCutoutInsetForSide(
+                displayW, displayH, bounds[BOUNDS_POSITION_TOP], Gravity.TOP));
+        int rightInset = Math.max(waterFallInsets.right, findCutoutInsetForSide(
+                displayW, displayH, bounds[BOUNDS_POSITION_RIGHT], Gravity.RIGHT));
+        int bottomInset = Math.max(waterFallInsets.bottom, findCutoutInsetForSide(
+                displayW, displayH, bounds[BOUNDS_POSITION_BOTTOM], Gravity.BOTTOM));
 
         return new Rect(leftInset, topInset, rightInset, bottomInset);
     }
 
-    private static int findCutoutInsetForSide(int displayW, int displayH, Rect boundingRect,
-            int gravity) {
+    private static int findCutoutInsetForSide(int displayW, int displayH,
+            @NonNull Rect boundingRect, int gravity) {
         if (boundingRect.isEmpty()) {
             return 0;
         }
 
         int inset = 0;
-        switch (gravity) {
-            case Gravity.TOP:
-                return Math.max(inset, boundingRect.bottom);
-            case Gravity.BOTTOM:
-                return Math.max(inset, displayH - boundingRect.top);
-            case Gravity.LEFT:
-                return Math.max(inset, boundingRect.right);
-            case Gravity.RIGHT:
-                return Math.max(inset, displayW - boundingRect.left);
-            default:
-                throw new IllegalArgumentException("unknown gravity: " + gravity);
-        }
+        return switch (gravity) {
+            case Gravity.TOP -> Math.max(inset, boundingRect.bottom);
+            case Gravity.BOTTOM -> Math.max(inset, displayH - boundingRect.top);
+            case Gravity.LEFT -> Math.max(inset, boundingRect.right);
+            case Gravity.RIGHT -> Math.max(inset, displayW - boundingRect.left);
+            default -> throw new IllegalArgumentException("unknown gravity: " + gravity);
+        };
     }
 
     /**
@@ -1293,6 +1487,7 @@
                 out.writeInt(cutout.mCutoutPathParserInfo.getRotation());
                 out.writeFloat(cutout.mCutoutPathParserInfo.getScale());
                 out.writeFloat(cutout.mCutoutPathParserInfo.getPhysicalPixelDisplaySizeRatio());
+                out.writeIntArray(cutout.mSideOverrides);
             }
         }
 
@@ -1348,9 +1543,10 @@
             final CutoutPathParserInfo info = new CutoutPathParserInfo(
                     displayWidth, displayHeight, physicalDisplayWidth, physicalDisplayHeight,
                     density, cutoutSpec, rotation, scale, physicalPixelDisplaySizeRatio);
+            final int[] sideOverrides = in.createIntArray();
 
-            return new DisplayCutout(
-                    safeInsets, waterfallInsets, bounds, info, false /* copyArguments */);
+            return new DisplayCutout(safeInsets, waterfallInsets,
+                        new Bounds(bounds, false /* copyArguments */), info, sideOverrides);
         }
 
         public DisplayCutout get() {
@@ -1382,8 +1578,10 @@
                     mInner.mCutoutPathParserInfo.getRotation(),
                     scale,
                     mInner.mCutoutPathParserInfo.getPhysicalPixelDisplaySizeRatio());
+            final int[] sideOverrides = mInner.mSideOverrides;
 
-            mInner = new DisplayCutout(safeInsets, Insets.of(waterfallInsets), bounds, info);
+            mInner = new DisplayCutout(safeInsets, Insets.of(waterfallInsets), bounds, info,
+                    sideOverrides);
         }
 
         @Override
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index 9bf43a3..1d81be1 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -370,4 +370,14 @@
     boolean transferEmbeddedTouchFocusToHost(IWindow embeddedWindow);
 
     boolean transferHostTouchGestureToEmbedded(IWindow hostWindow, IBinder transferTouchToken);
+
+    /**
+     * Moves the focus to the adjacent window if there is one in the given direction. This can only
+     * move the focus to the window in the same leaf task.
+     *
+     * @param fromWindow The calling window that the focus is moved from.
+     * @param direction The {@link android.view.View.FocusDirection} that the new focus should go.
+     * @return {@code true} if the focus changes. Otherwise, {@code false}.
+     */
+    boolean moveFocusToAdjacentWindow(IWindow fromWindow, int direction);
 }
diff --git a/core/java/android/view/InputEventReceiver.java b/core/java/android/view/InputEventReceiver.java
index 19e6836..9c430cd 100644
--- a/core/java/android/view/InputEventReceiver.java
+++ b/core/java/android/view/InputEventReceiver.java
@@ -54,6 +54,7 @@
             InputChannel inputChannel, MessageQueue messageQueue);
     private static native void nativeDispose(long receiverPtr);
     private static native void nativeFinishInputEvent(long receiverPtr, int seq, boolean handled);
+    private static native boolean nativeProbablyHasInput(long receiverPtr);
     private static native void nativeReportTimeline(long receiverPtr, int inputEventId,
             long gpuCompletedTime, long presentTime);
     private static native boolean nativeConsumeBatchedInputEvents(long receiverPtr,
@@ -92,6 +93,17 @@
     }
 
     /**
+     * Checks the receiver for input availability.
+     * May return false negatives.
+     */
+    public boolean probablyHasInput() {
+        if (mReceiverPtr == 0) {
+            return false;
+        }
+        return nativeProbablyHasInput(mReceiverPtr);
+    }
+
+    /**
      * Disposes the receiver.
      * Must be called on the same Looper thread to which the receiver is attached.
      */
diff --git a/core/java/android/view/InsetsAnimationThreadControlRunner.java b/core/java/android/view/InsetsAnimationThreadControlRunner.java
index f7b9aa2..079991a 100644
--- a/core/java/android/view/InsetsAnimationThreadControlRunner.java
+++ b/core/java/android/view/InsetsAnimationThreadControlRunner.java
@@ -76,7 +76,7 @@
             Trace.asyncTraceEnd(Trace.TRACE_TAG_VIEW,
                     "InsetsAsyncAnimation: " + WindowInsets.Type.toString(runner.getTypes()),
                     runner.getTypes());
-            releaseControls(mControl.getControls());
+            InsetsController.releaseControls(mControl.getControls());
             mMainThreadHandler.post(() ->
                     mOuterCallbacks.notifyFinished(InsetsAnimationThreadControlRunner.this, shown));
         }
@@ -130,12 +130,6 @@
         });
     }
 
-    private void releaseControls(SparseArray<InsetsSourceControl> controls) {
-        for (int i = controls.size() - 1; i >= 0; i--) {
-            controls.valueAt(i).release(SurfaceControl::release);
-        }
-    }
-
     @Override
     @UiThread
     public void dumpDebug(ProtoOutputStream proto, long fieldId) {
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 147c15b..dd09157 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -1353,6 +1353,9 @@
                 });
             }
 
+            // The leashes are copied, but they won't be used.
+            releaseControls(controls);
+
             // The requested visibilities should be delayed as well. Otherwise, we might override
             // the insets visibility before playing animation.
             setRequestedVisibleTypes(mReportedRequestedVisibleTypes, types);
@@ -1422,6 +1425,12 @@
         }
     }
 
+    static void releaseControls(SparseArray<InsetsSourceControl> controls) {
+        for (int i = controls.size() - 1; i >= 0; i--) {
+            controls.valueAt(i).release(SurfaceControl::release);
+        }
+    }
+
     // TODO(b/242962223): Make this setter restrictive.
     @Override
     public void setSystemDrivenInsetsAnimationLoggingListener(
diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java
index ad0bf7c..7850554 100644
--- a/core/java/android/view/Surface.java
+++ b/core/java/android/view/Surface.java
@@ -274,7 +274,8 @@
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(prefix = {"FRAME_RATE_CATEGORY_"},
             value = {FRAME_RATE_CATEGORY_DEFAULT, FRAME_RATE_CATEGORY_NO_PREFERENCE,
-                    FRAME_RATE_CATEGORY_LOW, FRAME_RATE_CATEGORY_NORMAL, FRAME_RATE_CATEGORY_HIGH})
+                    FRAME_RATE_CATEGORY_LOW, FRAME_RATE_CATEGORY_NORMAL,
+                    FRAME_RATE_CATEGORY_HIGH_HINT, FRAME_RATE_CATEGORY_HIGH})
     public @interface FrameRateCategory {}
 
     // From native_window.h or window.h. Keep these in sync.
@@ -308,11 +309,21 @@
     public static final int FRAME_RATE_CATEGORY_NORMAL = 3;
 
     /**
+     * Hints that, as a result of a user interaction, an animation is likely to start.
+     * This category is a signal that a user interaction heuristic determined the need of a
+     * high refresh rate, and is not an explicit request from the app.
+     * As opposed to {@link #FRAME_RATE_CATEGORY_HIGH}, this vote may be ignored in favor of
+     * more explicit votes.
+     * @hide
+     */
+    public static final int FRAME_RATE_CATEGORY_HIGH_HINT = 4;
+
+    /**
      * Indicates a frame rate suitable for animations that require a high frame rate, which may
      * increase smoothness but may also increase power usage.
      * @hide
      */
-    public static final int FRAME_RATE_CATEGORY_HIGH = 4;
+    public static final int FRAME_RATE_CATEGORY_HIGH = 5;
 
     /**
      * Create an empty surface, which will later be filled in by readFromParcel().
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index b957b31..674f22c 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -28,6 +28,7 @@
 
 import android.Manifest;
 import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
 import android.annotation.FloatRange;
 import android.annotation.IntDef;
 import android.annotation.IntRange;
@@ -71,6 +72,7 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.Preconditions;
 import com.android.internal.util.VirtualRefBasePtr;
+import com.android.window.flags.Flags;
 
 import dalvik.system.CloseGuard;
 
@@ -277,6 +279,8 @@
     private static native int nativeGetLayerId(long nativeObject);
     private static native void nativeAddTransactionCommittedListener(long nativeObject,
             TransactionCommittedListener listener);
+    private static native void nativeAddTransactionCompletedListener(long nativeObject,
+            Consumer<TransactionStats> listener);
     private static native void nativeSanitize(long transactionObject, int pid, int uid);
     private static native void nativeSetDestinationFrame(long transactionObj, long nativeObject,
             int l, int t, int r, int b);
@@ -290,6 +294,10 @@
     private static native void nativeClearTrustedPresentationCallback(long transactionObj,
             long nativeObject);
     private static native StalledTransactionInfo nativeGetStalledTransactionInfo(int pid);
+    private static native void nativeSetDesiredPresentTime(long transactionObj,
+                                                           long desiredPresentTime);
+    private static native void nativeSetFrameTimeline(long transactionObj,
+                                                           long vsyncId);
 
     /**
      * Transforms that can be applied to buffers as they are displayed to a window.
@@ -2550,6 +2558,50 @@
     }
 
     /**
+     * Transaction stats given to the listener registered in
+     * {@link SurfaceControl.Transaction#addTransactionCompletedListener}
+     */
+    @FlaggedApi(Flags.FLAG_SDK_DESIRED_PRESENT_TIME)
+    public static final class TransactionStats {
+        private long mLatchTime;
+        private SyncFence mSyncFence;
+
+        // called from native
+        private TransactionStats(long latchTime, long presentFencePtr) {
+            mLatchTime = latchTime;
+            mSyncFence = new SyncFence(presentFencePtr);
+        }
+
+        /**
+         * Close the TransactionStats. Called by the framework when the listener returns.
+         * @hide
+         */
+        public void close() {
+            mSyncFence.close();
+        }
+
+        /**
+         * Returns the timestamp of when the frame was latched by the framework and queued for
+         * presentation.
+         */
+        @FlaggedApi(Flags.FLAG_SDK_DESIRED_PRESENT_TIME)
+        public long getLatchTime() {
+            return mLatchTime;
+        }
+
+        /**
+         * Returns a new SyncFence that signals when the transaction has been presented.
+         * The caller takes ownership of the fence and is responsible for closing
+         * it by calling {@link SyncFence#close}.
+         * If a device does not support present fences, an empty fence will be returned.
+         */
+        @FlaggedApi(Flags.FLAG_SDK_DESIRED_PRESENT_TIME)
+        public @NonNull SyncFence getPresentFence() {
+            return new SyncFence(mSyncFence);
+        }
+    };
+
+    /**
      * Threshold values that are sent with
      * {@link Transaction#setTrustedPresentationCallback(SurfaceControl,
      * TrustedPresentationThresholds, Executor, Consumer)}
@@ -4185,12 +4237,35 @@
         }
 
         /**
-         * Sets the frame timeline vsync id received from choreographer
-         * {@link Choreographer#getVsyncId()} that corresponds to the transaction submitted on that
-         * surface control.
+         * Sets the frame timeline to use in SurfaceFlinger.
          *
-         * @hide
+         * A frame timeline should be chosen based on the frame deadline the application
+         * can meet when rendering the frame and the application's desired presentation time.
+         * By setting a frame timeline, SurfaceFlinger tries to present the frame at the
+         * corresponding expected presentation time.
+         *
+         * To receive frame timelines, a callback must be posted to Choreographer using
+         * {@link Choreographer#postVsyncCallback} The vsyncId can then be extracted from the
+         * {@link Choreographer.FrameTimeline#getVsyncId}.
+         *
+         * @param vsyncId The vsync ID received from Choreographer, setting the frame's
+         *                presentation target to the corresponding expected presentation time
+         *                and deadline from the frame to be rendered. A stale or invalid value
+         *                will be ignored.
+         *
          */
+        @FlaggedApi(Flags.FLAG_SDK_DESIRED_PRESENT_TIME)
+        @NonNull
+        public Transaction setFrameTimeline(long vsyncId) {
+            if (!Flags.sdkDesiredPresentTime()) {
+                Log.w(TAG, "addTransactionCompletedListener was called but flag is disabled");
+                return this;
+            }
+            nativeSetFrameTimelineVsync(mNativeObject, vsyncId);
+            return this;
+        }
+
+        /** @hide */
         @NonNull
         public Transaction setFrameTimelineVsync(long frameTimelineVsyncId) {
             nativeSetFrameTimelineVsync(mNativeObject, frameTimelineVsyncId);
@@ -4207,6 +4282,9 @@
          * to avoid dropping frames (overwriting transactions), and unable to use timestamps (Which
          * provide a more efficient solution), then this method provides a method to pace your
          * transaction application.
+         * The listener is invoked once the transaction is applied, and never again. Multiple
+         * listeners can be added to the same transaction, however the order the listeners will
+         * be called is not guaranteed.
          *
          * @param executor The executor that the callback should be invoked on.
          * @param listener The callback that will be invoked when the transaction has been
@@ -4223,6 +4301,33 @@
         }
 
         /**
+         * Request to add a TransactionCompletedListener.
+         *
+         * The listener is invoked when transaction is presented, and never again. Multiple
+         * listeners can be added to the same transaction, however the order the listeners will
+         * be called is not guaranteed.
+         *
+         * @param executor The executor that the callback should be invoked on.
+         * @param listener The callback that will be invoked when the transaction has been
+         *                 completed.
+         */
+        @FlaggedApi(Flags.FLAG_SDK_DESIRED_PRESENT_TIME)
+        @NonNull
+        public Transaction addTransactionCompletedListener(
+                @NonNull @CallbackExecutor Executor executor,
+                @NonNull Consumer<TransactionStats> listener) {
+
+            if (!Flags.sdkDesiredPresentTime()) {
+                Log.w(TAG, "addTransactionCompletedListener was called but flag is disabled");
+                return this;
+            }
+            Consumer<TransactionStats> listenerInner = stats -> executor.execute(
+                                    () -> listener.andThen(TransactionStats::close).accept(stats));
+            nativeAddTransactionCompletedListener(mNativeObject, listenerInner);
+            return this;
+        }
+
+        /**
          * Sets a callback to receive feedback about the presentation of a {@link SurfaceControl}.
          * When the {@link SurfaceControl} is presented according to the passed in
          * {@link TrustedPresentationThresholds}, it is said to "enter the state", and receives the
@@ -4321,6 +4426,30 @@
         }
 
         /**
+         * Specifies a desiredPresentTime for the transaction. The framework will try to present
+         * the transaction at or after the time specified.
+         *
+         * Transactions will not be presented until all of their acquire fences have signaled even
+         * if the app requests an earlier present time.
+         *
+         * If an earlier transaction has a desired present time of x, and a later transaction has
+         * a desired present time that is before x, the later transaction will not preempt the
+         * earlier transaction.
+         *
+         * @param desiredPresentTime The desired time (in CLOCK_MONOTONIC) for the transaction.
+         * @return This transaction
+         */
+        @FlaggedApi(Flags.FLAG_SDK_DESIRED_PRESENT_TIME)
+        @NonNull
+        public Transaction setDesiredPresentTime(long desiredPresentTime) {
+            if (!Flags.sdkDesiredPresentTime()) {
+                Log.w(TAG, "addTransactionCompletedListener was called but flag is disabled");
+                return this;
+            }
+            nativeSetDesiredPresentTime(mNativeObject, desiredPresentTime);
+            return this;
+        }
+        /**
          * Writes the transaction to parcel, clearing the transaction as if it had been applied so
          * it can be used to store future transactions. It's the responsibility of the parcel
          * reader to apply the original transaction.
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index ec99459..257ecc5 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -32,6 +32,7 @@
 import static android.view.displayhash.DisplayHashResultCallback.EXTRA_DISPLAY_HASH_ERROR_CODE;
 import static android.view.flags.Flags.FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY;
 import static android.view.flags.Flags.FLAG_VIEW_VELOCITY_API;
+import static android.view.flags.Flags.enableUseMeasureCacheDuringForceLayout;
 import static android.view.flags.Flags.toolkitMetricsForFrameRateDecision;
 import static android.view.flags.Flags.toolkitSetFrameRateReadOnly;
 import static android.view.flags.Flags.viewVelocityApi;
@@ -955,6 +956,12 @@
     private static boolean sAlwaysRemeasureExactly = false;
 
     /**
+     * When true makes it possible to use onMeasure caches also when the force layout flag is
+     * enabled. This helps avoiding multiple measures in the same frame with the same dimensions.
+     */
+    private static boolean sUseMeasureCacheDuringForceLayoutFlagValue;
+
+    /**
      * Allow setForeground/setBackground to be called (and ignored) on a textureview,
      * without throwing
      */
@@ -2396,6 +2403,7 @@
 
         sToolkitSetFrameRateReadOnlyFlagValue = toolkitSetFrameRateReadOnly();
         sToolkitMetricsForFrameRateDecisionFlagValue = toolkitMetricsForFrameRateDecision();
+        sUseMeasureCacheDuringForceLayoutFlagValue = enableUseMeasureCacheDuringForceLayout();
     }
 
     /**
@@ -10760,11 +10768,11 @@
             return;
         }
 
-        session.internalNotifyViewTreeEvent(/* started= */ true);
+        session.notifyViewTreeEvent(/* started= */ true);
         try {
             dispatchProvideContentCaptureStructure();
         } finally {
-            session.internalNotifyViewTreeEvent(/* started= */ false);
+            session.notifyViewTreeEvent(/* started= */ false);
         }
     }
 
@@ -22848,6 +22856,36 @@
     }
 
     /**
+     * Determines whether an unprocessed input event is available on the window.
+     *
+     * This is only a performance hint (a.k.a. the Input Hint) and may return false negative
+     * results.  Callers should not rely on availability of the input event based on the return
+     * value of this method.
+     *
+     * The Input Hint functionality is experimental, and can be removed in the future OS releases.
+     *
+     * This method only returns nontrivial results on a View that is attached to a Window. Such View
+     * can be acquired using `Activity.getWindow().getDecorView()`, and only after the view
+     * hierarchy is attached (via {@link android.app.Activity#setContentView(android.view.View)}).
+     *
+     * In multi-window mode the View can provide the Input Hint only for the window it is attached
+     * to. Therefore, checking input availability for the whole application would require asking
+     * for the hint from more than one View.
+     *
+     * The initial implementation does not return false positives, but callers should not rely on
+     * it: false positives may occur in future OS releases.
+     *
+     * @hide
+     */
+    public boolean probablyHasInput() {
+        ViewRootImpl viewRootImpl = getViewRootImpl();
+        if (viewRootImpl == null) {
+            return false;
+        }
+        return viewRootImpl.probablyHasInput();
+    }
+
+    /**
      * Destroys all hardware rendering resources. This method is invoked
      * when the system needs to reclaim resources. Upon execution of this
      * method, you should free any OpenGL resources created by the view.
@@ -27417,7 +27455,13 @@
 
             resolveRtlPropertiesIfNeeded();
 
-            int cacheIndex = forceLayout ? -1 : mMeasureCache.indexOfKey(key);
+            int cacheIndex;
+            if (sUseMeasureCacheDuringForceLayoutFlagValue) {
+                cacheIndex =  mMeasureCache.indexOfKey(key);
+            } else {
+                cacheIndex = forceLayout ? -1 : mMeasureCache.indexOfKey(key);
+            }
+
             if (cacheIndex < 0 || sIgnoreMeasureCache) {
                 if (isTraversalTracingEnabled()) {
                     Trace.beginSection(mTracingStrings.onMeasure);
@@ -33126,6 +33170,10 @@
     public void setFrameContentVelocity(float pixelsPerSecond) {
         if (viewVelocityApi()) {
             mFrameContentVelocity = Math.abs(pixelsPerSecond);
+
+            if (sToolkitMetricsForFrameRateDecisionFlagValue) {
+                Trace.setCounter("Set frame velocity", (long) mFrameContentVelocity);
+            }
         }
     }
 
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 287c7b2..fbefbf3 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -49,6 +49,7 @@
 import android.os.Bundle;
 import android.os.Parcelable;
 import android.os.SystemClock;
+import android.service.autofill.Flags;
 import android.util.AttributeSet;
 import android.util.IntArray;
 import android.util.Log;
@@ -3752,7 +3753,16 @@
                         && !child.isActivityDeniedForAutofillForUnimportantView())
                     || (shouldIncludeAllChildrenViewWithAutofillTypeNotNone(afm)
                         && child.getAutofillType() != AUTOFILL_TYPE_NONE)
-                    || shouldIncludeAllChildrenViews(afm)){
+                    || shouldIncludeAllChildrenViews(afm)
+                    || (Flags.includeInvisibleViewGroupInAssistStructure()
+                    && child instanceof ViewGroup && child.getVisibility() != View.VISIBLE)) {
+                // If the child is a ViewGroup object and its visibility is not visible, include
+                // it as part of the assist structure. The children of these invisible ViewGroup
+                // objects are parsed and included in the assist structure. When the Autofill
+                // Provider determines the visibility of these children, it looks at their
+                // visibility as well as their parent's visibility. Omitting invisible parents
+                // will lead to the Autofill Provider incorrectly assuming that these children
+                // of invisible parents are actually visible.
                 list.add(child);
             } else if (child instanceof ViewGroup) {
                 ((ViewGroup) child).populateChildrenForAutofill(list, flags);
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 1f81a64..c66f3c8 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -16,6 +16,7 @@
 
 package android.view;
 
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
 import static android.content.pm.ActivityInfo.OVERRIDE_SANDBOX_VIEW_BOUNDS_APIS;
 import static android.graphics.HardwareRenderer.SYNC_CONTEXT_IS_STOPPED;
 import static android.graphics.HardwareRenderer.SYNC_LOST_SURFACE_REWARD_IF_FOUND;
@@ -111,6 +112,7 @@
 import android.app.ResourcesManager;
 import android.app.WindowConfiguration;
 import android.app.compat.CompatChanges;
+import android.app.servertransaction.WindowStateResizeItem;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ClipData;
 import android.content.ClipDescription;
@@ -208,7 +210,6 @@
 import android.view.autofill.AutofillManager;
 import android.view.contentcapture.ContentCaptureManager;
 import android.view.contentcapture.ContentCaptureSession;
-import android.view.contentcapture.MainContentCaptureSession;
 import android.view.inputmethod.ImeTracker;
 import android.view.inputmethod.InputMethodManager;
 import android.widget.Scroller;
@@ -2012,26 +2013,24 @@
     }
 
     /** Handles messages {@link #MSG_RESIZED} and {@link #MSG_RESIZED_REPORT}. */
-    private void handleResized(int msg, SomeArgs args) {
+    private void handleResized(ClientWindowFrames frames, boolean reportDraw,
+            MergedConfiguration mergedConfiguration, InsetsState insetsState, boolean forceLayout,
+            boolean alwaysConsumeSystemBars, int displayId, int syncSeqId, boolean dragResizing) {
         if (!mAdded) {
             return;
         }
 
-        final ClientWindowFrames frames = (ClientWindowFrames) args.arg1;
-        final MergedConfiguration mergedConfiguration = (MergedConfiguration) args.arg2;
         CompatibilityInfo.applyOverrideScaleIfNeeded(mergedConfiguration);
-        final boolean forceNextWindowRelayout = args.argi1 != 0;
-        final int displayId = args.argi3;
-        final boolean dragResizing = args.argi5 != 0;
-
         final Rect frame = frames.frame;
         final Rect displayFrame = frames.displayFrame;
         final Rect attachedFrame = frames.attachedFrame;
         if (mTranslator != null) {
+            mTranslator.translateInsetsStateInScreenToAppWindow(insetsState);
             mTranslator.translateRectInScreenToAppWindow(frame);
             mTranslator.translateRectInScreenToAppWindow(displayFrame);
             mTranslator.translateRectInScreenToAppWindow(attachedFrame);
         }
+        mInsetsController.onStateChanged(insetsState);
         final float compatScale = frames.compatScale;
         final boolean frameChanged = !mWinFrame.equals(frame);
         final boolean configChanged = !mLastReportedMergedConfiguration.equals(mergedConfiguration);
@@ -2040,8 +2039,8 @@
         final boolean displayChanged = mDisplay.getDisplayId() != displayId;
         final boolean compatScaleChanged = mTmpFrames.compatScale != compatScale;
         final boolean dragResizingChanged = mPendingDragResizing != dragResizing;
-        if (msg == MSG_RESIZED && !frameChanged && !configChanged && !attachedFrameChanged
-                && !displayChanged && !forceNextWindowRelayout
+        if (!reportDraw && !frameChanged && !configChanged && !attachedFrameChanged
+                && !displayChanged && !forceLayout
                 && !compatScaleChanged && !dragResizingChanged) {
             return;
         }
@@ -2073,11 +2072,11 @@
             }
         }
 
-        mForceNextWindowRelayout |= forceNextWindowRelayout;
-        mPendingAlwaysConsumeSystemBars = args.argi2 != 0;
-        mSyncSeqId = args.argi4 > mSyncSeqId ? args.argi4 : mSyncSeqId;
+        mForceNextWindowRelayout |= forceLayout;
+        mPendingAlwaysConsumeSystemBars = alwaysConsumeSystemBars;
+        mSyncSeqId = syncSeqId > mSyncSeqId ? syncSeqId : mSyncSeqId;
 
-        if (msg == MSG_RESIZED_REPORT) {
+        if (reportDraw) {
             reportNextDraw("resized");
         }
 
@@ -4104,7 +4103,7 @@
 
         final ContentCaptureManager manager = mAttachInfo.mContentCaptureManager;
         if (manager != null && mAttachInfo.mContentCaptureEvents != null) {
-            final MainContentCaptureSession session = manager.getMainContentCaptureSession();
+            final ContentCaptureSession session = manager.getMainContentCaptureSession();
             session.notifyContentCaptureEvents(mAttachInfo.mContentCaptureEvents);
         }
         mAttachInfo.mContentCaptureEvents = null;
@@ -5021,7 +5020,7 @@
 
             // Initial dispatch of window bounds to content capture
             if (mAttachInfo.mContentCaptureManager != null) {
-                MainContentCaptureSession session =
+                ContentCaptureSession session =
                         mAttachInfo.mContentCaptureManager.getMainContentCaptureSession();
                 session.notifyWindowBoundsChanged(session.getId(),
                         getConfiguration().windowConfiguration.getBounds());
@@ -6232,8 +6231,17 @@
                 case MSG_RESIZED:
                 case MSG_RESIZED_REPORT: {
                     final SomeArgs args = (SomeArgs) msg.obj;
-                    mInsetsController.onStateChanged((InsetsState) args.arg3);
-                    handleResized(msg.what, args);
+                    final ClientWindowFrames frames = (ClientWindowFrames) args.arg1;
+                    final boolean reportDraw = msg.what == MSG_RESIZED_REPORT;
+                    final MergedConfiguration mergedConfiguration = (MergedConfiguration) args.arg2;
+                    final InsetsState insetsState = (InsetsState) args.arg3;
+                    final boolean forceLayout = args.argi1 != 0;
+                    final boolean alwaysConsumeSystemBars = args.argi2 != 0;
+                    final int displayId = args.argi3;
+                    final int syncSeqId = args.argi4;
+                    final boolean dragResizing = args.argi5 != 0;
+                    handleResized(frames, reportDraw, mergedConfiguration, insetsState, forceLayout,
+                            alwaysConsumeSystemBars, displayId, syncSeqId, dragResizing);
                     args.recycle();
                     break;
                 }
@@ -6936,7 +6944,11 @@
         }
 
         private int doOnBackKeyEvent(KeyEvent keyEvent) {
-            OnBackInvokedCallback topCallback = getOnBackInvokedDispatcher().getTopCallback();
+            WindowOnBackInvokedDispatcher dispatcher = getOnBackInvokedDispatcher();
+            OnBackInvokedCallback topCallback = dispatcher.getTopCallback();
+            if (dispatcher.isDispatching()) {
+                return FINISH_NOT_HANDLED;
+            }
             if (topCallback instanceof OnBackAnimationCallback) {
                 final OnBackAnimationCallback animationCallback =
                         (OnBackAnimationCallback) topCallback;
@@ -7225,7 +7237,7 @@
         }
 
         private boolean performFocusNavigation(KeyEvent event) {
-            int direction = 0;
+            @FocusDirection int direction = 0;
             switch (event.getKeyCode()) {
                 case KeyEvent.KEYCODE_DPAD_LEFT:
                     if (event.hasNoModifiers()) {
@@ -7277,6 +7289,8 @@
                                             isFastScrolling));
                             return true;
                         }
+                    } else if (moveFocusToAdjacentWindow(direction)) {
+                        return true;
                     }
 
                     // Give the focused view a last chance to handle the dpad key.
@@ -7286,12 +7300,26 @@
                 } else {
                     if (mView.restoreDefaultFocus()) {
                         return true;
+                    } else if (moveFocusToAdjacentWindow(direction)) {
+                        return true;
                     }
                 }
             }
             return false;
         }
 
+        private boolean moveFocusToAdjacentWindow(@FocusDirection int direction) {
+            if (getConfiguration().windowConfiguration.getWindowingMode()
+                    != WINDOWING_MODE_MULTI_WINDOW) {
+                return false;
+            }
+            try {
+                return mWindowSession.moveFocusToAdjacentWindow(mWindow, direction);
+            } catch (RemoteException e) {
+                return false;
+            }
+        }
+
         private boolean performKeyboardGroupNavigation(int direction) {
             final View focused = mView.findFocus();
             if (focused == null && mView.restoreDefaultFocus()) {
@@ -8797,7 +8825,7 @@
         mSurfaceControl.setTransformHint(transformHint);
 
         if (mAttachInfo.mContentCaptureManager != null) {
-            MainContentCaptureSession mainSession = mAttachInfo.mContentCaptureManager
+            ContentCaptureSession mainSession = mAttachInfo.mContentCaptureManager
                     .getMainContentCaptureSession();
             mainSession.notifyWindowBoundsChanged(mainSession.getId(),
                     getConfiguration().windowConfiguration.getBounds());
@@ -9379,20 +9407,8 @@
             boolean alwaysConsumeSystemBars, int displayId, int syncSeqId, boolean dragResizing) {
         Message msg = mHandler.obtainMessage(reportDraw ? MSG_RESIZED_REPORT : MSG_RESIZED);
         SomeArgs args = SomeArgs.obtain();
-        final boolean sameProcessCall = (Binder.getCallingPid() == android.os.Process.myPid());
-        if (sameProcessCall) {
-            insetsState = new InsetsState(insetsState, true /* copySource */);
-        }
-        if (mTranslator != null) {
-            mTranslator.translateInsetsStateInScreenToAppWindow(insetsState);
-        }
-        if (insetsState.isSourceOrDefaultVisible(ID_IME, Type.ime())) {
-            ImeTracing.getInstance().triggerClientDump("ViewRootImpl#dispatchResized",
-                    getInsetsController().getHost().getInputMethodManager(), null /* icProto */);
-        }
-        args.arg1 = sameProcessCall ? new ClientWindowFrames(frames) : frames;
-        args.arg2 = sameProcessCall && mergedConfiguration != null
-                ? new MergedConfiguration(mergedConfiguration) : mergedConfiguration;
+        args.arg1 = frames;
+        args.arg2 = mergedConfiguration;
         args.arg3 = insetsState;
         args.argi1 = forceLayout ? 1 : 0;
         args.argi2 = alwaysConsumeSystemBars ? 1 : 0;
@@ -10559,6 +10575,18 @@
     }
 
     /**
+     * Checks the input event receiver for input availability.
+     * May return false negatives.
+     * @hide
+     */
+    public boolean probablyHasInput() {
+        if (mInputEventReceiver == null) {
+            return false;
+        }
+        return mInputEventReceiver.probablyHasInput();
+    }
+
+    /**
      * Adds a scroll capture callback to this window.
      *
      * @param callback the callback to add
@@ -10815,9 +10843,10 @@
         }
     }
 
-    static class W extends IWindow.Stub {
+    static class W extends IWindow.Stub implements WindowStateResizeItem.ResizeListener {
         private final WeakReference<ViewRootImpl> mViewAncestor;
         private final IWindowSession mWindowSession;
+        private boolean mIsFromResizeItem;
 
         W(ViewRootImpl viewAncestor) {
             mViewAncestor = new WeakReference<ViewRootImpl>(viewAncestor);
@@ -10825,17 +10854,46 @@
         }
 
         @Override
+        public void onExecutingWindowStateResizeItem() {
+            mIsFromResizeItem = true;
+        }
+
+        @Override
         public void resized(ClientWindowFrames frames, boolean reportDraw,
                 MergedConfiguration mergedConfiguration, InsetsState insetsState,
                 boolean forceLayout, boolean alwaysConsumeSystemBars, int displayId, int syncSeqId,
                 boolean dragResizing) {
+            final boolean isFromResizeItem = mIsFromResizeItem;
+            mIsFromResizeItem = false;
             // Although this is a AIDL method, it will only be triggered in local process through
             // either WindowStateResizeItem or WindowlessWindowManager.
             final ViewRootImpl viewAncestor = mViewAncestor.get();
-            if (viewAncestor != null) {
-                viewAncestor.dispatchResized(frames, reportDraw, mergedConfiguration, insetsState,
-                        forceLayout, alwaysConsumeSystemBars, displayId, syncSeqId, dragResizing);
+            if (viewAncestor == null) {
+                return;
             }
+            if (insetsState.isSourceOrDefaultVisible(ID_IME, Type.ime())) {
+                ImeTracing.getInstance().triggerClientDump("ViewRootImpl.W#resized",
+                        viewAncestor.getInsetsController().getHost().getInputMethodManager(),
+                        null /* icProto */);
+            }
+            // If the UI thread is the same as the current thread that is dispatching
+            // WindowStateResizeItem, then it can run directly.
+            if (isFromResizeItem && viewAncestor.mHandler.getLooper()
+                    == ActivityThread.currentActivityThread().getLooper()) {
+                viewAncestor.handleResized(frames, reportDraw, mergedConfiguration, insetsState,
+                        forceLayout, alwaysConsumeSystemBars, displayId, syncSeqId, dragResizing);
+                return;
+            }
+            // The the parameters from WindowStateResizeItem are already copied.
+            final boolean needCopy =
+                    !isFromResizeItem && (Binder.getCallingPid() == Process.myPid());
+            if (needCopy) {
+                insetsState = new InsetsState(insetsState, true /* copySource */);
+                frames = new ClientWindowFrames(frames);
+                mergedConfiguration = new MergedConfiguration(mergedConfiguration);
+            }
+            viewAncestor.dispatchResized(frames, reportDraw, mergedConfiguration, insetsState,
+                    forceLayout, alwaysConsumeSystemBars, displayId, syncSeqId, dragResizing);
         }
 
         @Override
@@ -11910,6 +11968,12 @@
 
                 if (syncBuffer) {
                     boolean result = mBlastBufferQueue.syncNextTransaction(transaction -> {
+                        Runnable timeoutRunnable = () -> Log.e(mTag,
+                                "Failed to submit the sync transaction after 4s. Likely to ANR "
+                                        + "soon");
+                        mHandler.postDelayed(timeoutRunnable, 4L * Build.HW_TIMEOUT_MULTIPLIER);
+                        transaction.addTransactionCommittedListener(mSimpleExecutor,
+                                () -> mHandler.removeCallbacks(timeoutRunnable));
                         surfaceSyncGroup.addTransaction(transaction);
                         surfaceSyncGroup.markSyncReady();
                     });
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index f76822f..d8fa415 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -972,10 +972,8 @@
      *     android:value="false"/&gt;
      * &lt;/application&gt;
      * </pre>
-     *
-     * @hide
      */
-    // TODO(b/274924641): Make this public API.
+    @FlaggedApi(Flags.FLAG_APP_COMPAT_PROPERTIES_API)
     String PROPERTY_COMPAT_ALLOW_IGNORING_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED =
             "android.window.PROPERTY_COMPAT_ALLOW_IGNORING_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED";
 
@@ -1262,7 +1260,7 @@
      * <p>When this compat override is enabled the min aspect ratio given in the app's manifest can
      * be overridden by the device manufacturer using their discretion to improve display
      * compatibility unless the app's manifest value is higher. This treatment will also apply if
-     * no min aspect ratio value is provided in the manifest. These treatments can apply only in
+     * no min aspect ratio value is provided in the manifest. These treatments can apply either in
      * specific cases (e.g. device is in portrait) or each time the app is displayed on screen.
      *
      * <p>Setting this property to {@code false} informs the system that the app must be
@@ -1309,9 +1307,8 @@
      *     android:value="true|false"/&gt;
      * &lt;/application&gt;
      * </pre>
-     * @hide
      */
-    // TODO(b/280052089): Make this public API.
+    @FlaggedApi(Flags.FLAG_APP_COMPAT_PROPERTIES_API)
     String PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES =
             "android.window.PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES";
 
@@ -1497,6 +1494,30 @@
             "android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED";
 
     /**
+     * Activity or Application level {@link android.content.pm.PackageManager.Property
+     * PackageManager.Property} for an app to declare that System UI should be shown for this
+     * app/component to allow it to be launched as multiple instances.  This property only affects
+     * SystemUI behavior and does _not_ affect whether a component can actually be launched into
+     * multiple instances, which is determined by the Activity's {@code launchMode} or the launching
+     * Intent's flags.  If the property is set on the Application, then all components within that
+     * application will use that value unless specified per component.
+     *
+     * The value must be a boolean string.
+     *
+     * <p><b>Syntax:</b>
+     * <pre>
+     * &lt;activity&gt;
+     *   &lt;property
+     *     android:name="android.window.PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI"
+     *     android:value="true|false"/&gt;
+     * &lt;/activity&gt;
+     * </pre>
+     */
+    @FlaggedApi(Flags.FLAG_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI)
+    public static final String PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI =
+            "android.window.PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI";
+
+    /**
      * Request for app's keyboard shortcuts to be retrieved asynchronously.
      *
      * @param receiver The callback to be triggered when the result is ready.
@@ -3166,6 +3187,12 @@
         public static final int PRIVATE_FLAG_DISABLE_WALLPAPER_TOUCH_EVENTS = 1 << 10;
 
         /**
+         * Flag to indicate that the window is forcibly to go edge-to-edge.
+         * @hide
+         */
+        public static final int PRIVATE_FLAG_EDGE_TO_EDGE_ENFORCED = 1 << 11;
+
+        /**
          * Flag to indicate that the window frame should be the requested frame adding the display
          * cutout frame. This will only be applied if a specific size smaller than the parent frame
          * is given, and the window is covering the display cutout. The extended frame will not be
@@ -3341,6 +3368,7 @@
                 PRIVATE_FLAG_SYSTEM_ERROR,
                 PRIVATE_FLAG_OPTIMIZE_MEASURE,
                 PRIVATE_FLAG_DISABLE_WALLPAPER_TOUCH_EVENTS,
+                PRIVATE_FLAG_EDGE_TO_EDGE_ENFORCED,
                 PRIVATE_FLAG_LAYOUT_SIZE_EXTENDED_BY_CUTOUT,
                 PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY,
                 PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME,
@@ -3403,6 +3431,10 @@
                         equals = PRIVATE_FLAG_DISABLE_WALLPAPER_TOUCH_EVENTS,
                         name = "DISABLE_WALLPAPER_TOUCH_EVENTS"),
                 @ViewDebug.FlagToString(
+                        mask = PRIVATE_FLAG_EDGE_TO_EDGE_ENFORCED,
+                        equals = PRIVATE_FLAG_EDGE_TO_EDGE_ENFORCED,
+                        name = "EDGE_TO_EDGE_ENFORCED"),
+                @ViewDebug.FlagToString(
                         mask = PRIVATE_FLAG_LAYOUT_SIZE_EXTENDED_BY_CUTOUT,
                         equals = PRIVATE_FLAG_LAYOUT_SIZE_EXTENDED_BY_CUTOUT,
                         name = "LAYOUT_SIZE_EXTENDED_BY_CUTOUT"),
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index d6ac562..b95e459 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -30,6 +30,7 @@
 import android.os.RemoteException;
 import android.util.Log;
 import android.util.MergedConfiguration;
+import android.view.View.FocusDirection;
 import android.view.WindowInsets.Type.InsetsType;
 import android.window.ClientWindowFrames;
 import android.window.OnBackInvokedCallbackInfo;
@@ -665,6 +666,13 @@
         return false;
     }
 
+    @Override
+    public boolean moveFocusToAdjacentWindow(IWindow fromWindow, @FocusDirection int direction) {
+        Log.e(TAG, "Received request to moveFocusToAdjacentWindow on"
+                + " WindowlessWindowManager. We shouldn't get here!");
+        return false;
+    }
+
     void setParentInterface(@Nullable ISurfaceControlViewHostParent parentInterface) {
         IBinder oldInterface = mParentInterface == null ? null : mParentInterface.asBinder();
         IBinder newInterface = parentInterface == null ? null : parentInterface.asBinder();
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index a38092a..49d2ceb 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -2067,10 +2067,10 @@
     }
 
     /**
-     * Start sequence (infinite) type of flash notification. Use
-     * {@code Context.getOpPackageName()} as the identifier of this flash notification.
+     * Start sequence (infinite) type of flash notification. Use {@code Context} to retrieve the
+     * package name as the identifier of this flash notification.
      * The notification can be cancelled later by calling {@link #stopFlashNotificationSequence}
-     * with same {@code Context.getOpPackageName()}.
+     * with same {@code Context}.
      * If the binder associated with this {@link AccessibilityManager} instance dies then the
      * sequence will stop automatically. It is strongly recommended to call
      * {@link #stopFlashNotificationSequence} within a reasonable amount of time after calling
@@ -2104,8 +2104,8 @@
     }
 
     /**
-     * Stop sequence (infinite) type of flash notification. The flash notification with
-     * {@code Context.getOpPackageName()} as identifier will be stopped if exist.
+     * Stop sequence (infinite) type of flash notification. The flash notification with the
+     * package name retrieved from {@code Context} as identifier will be stopped if exist.
      * It is strongly recommended to call this method within a reasonable amount of time after
      * calling {@link #startFlashNotificationSequence} method.
      *
diff --git a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
index 0cc19fb..c7355c1 100644
--- a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
+++ b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
@@ -24,6 +24,13 @@
 }
 
 flag {
+    namespace: "accessibility"
+    name: "braille_display_hid"
+    description: "Enables new APIs for an AccessibilityService to communicate with a HID Braille display"
+    bug: "303522222"
+}
+
+flag {
     name: "cleanup_accessibility_warning_dialog"
     namespace: "accessibility"
     description: "Cleans up duplicated or broken logic surrounding the accessibility warning dialog."
@@ -92,3 +99,10 @@
     description: "Feature flag for system pinch zoom gesture detector and related opt-out apis"
     bug: "283323770"
 }
+
+flag {
+    name: "support_system_pinch_zoom_opt_out_apis"
+    namespace: "accessibility"
+    description: "Feature flag for declaring system pinch zoom opt-out apis"
+    bug: "315089687"
+}
diff --git a/core/java/android/view/animation/AnimationUtils.java b/core/java/android/view/animation/AnimationUtils.java
index 70d8abe..57a3b76 100644
--- a/core/java/android/view/animation/AnimationUtils.java
+++ b/core/java/android/view/animation/AnimationUtils.java
@@ -109,6 +109,40 @@
     }
 
     /**
+     * Locks AnimationUtils{@link #currentAnimationTimeMillis()} to a fixed value for the current
+     * thread. This is used by {@link android.view.Choreographer} to ensure that all accesses
+     * during a vsync update are synchronized to the timestamp of the vsync.
+     *
+     * It is also exposed to tests to allow for rapid, flake-free headless testing.
+     *
+     * Must be followed by a call to {@link #unlockAnimationClock()} to allow time to
+     * progress. Failing to do this will result in stuck animations, scrolls, and flings.
+     *
+     * Note that time is not allowed to "rewind" and must perpetually flow forward. So the
+     * lock may fail if the time is in the past from a previously returned value, however
+     * time will be frozen for the duration of the lock. The clock is a thread-local, so
+     * ensure that {@link #lockAnimationClock(long)}, {@link #unlockAnimationClock()}, and
+     * {@link #currentAnimationTimeMillis()} are all called on the same thread.
+     *
+     * This is also not reference counted in any way. Any call to {@link #unlockAnimationClock()}
+     * will unlock the clock for everyone on the same thread. It is therefore recommended
+     * for tests to use their own thread to ensure that there is no collision with any existing
+     * {@link android.view.Choreographer} instance.
+     *
+     * Have to add the method back because of b/307888459.
+     * Remove this method once the lockAnimationClock(long, long) change
+     * is landed to aosp/android14-tests-dev branch.
+     *
+     * @hide
+     */
+    @TestApi
+    public static void lockAnimationClock(long vsyncMillis) {
+        AnimationState state = sAnimationState.get();
+        state.animationClockLocked = true;
+        state.currentVsyncTimeMillis = vsyncMillis;
+    }
+
+    /**
      * Frees the time lock set in place by {@link #lockAnimationClock(long)}. Must be called
      * to allow the animation clock to self-update.
      *
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index bb49679..dbeffc8 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -1469,7 +1469,8 @@
         if (infos.size() == 0) {
             throw new IllegalArgumentException("No VirtualViewInfo found");
         }
-        if (isCredmanRequested(view) && mIsFillAndSaveDialogDisabledForCredentialManager) {
+        if (shouldSuppressDialogsForCredman(view)
+                && mIsFillAndSaveDialogDisabledForCredentialManager) {
             if (sDebug) {
                 Log.d(TAG, "Ignoring Fill Dialog request since important for credMan:"
                         + view.getAutofillId().toString());
@@ -1493,7 +1494,7 @@
      * @hide
      */
     public void notifyViewEnteredForFillDialog(View v) {
-        if (isCredmanRequested(v)
+        if (shouldSuppressDialogsForCredman(v)
                 && mIsFillAndSaveDialogDisabledForCredentialManager) {
             if (sDebug) {
                 Log.d(TAG, "Ignoring Fill Dialog request since important for credMan:"
@@ -3390,19 +3391,39 @@
         }
     }
 
-    private boolean isCredmanRequested(View view) {
+    private boolean shouldSuppressDialogsForCredman(View view) {
         if (view == null) {
             return false;
         }
+        // isCredential field indicates that the developer might be calling Credman, and we should
+        // suppress autofill dialogs. But it is not a good enough indicator that there is a valid
+        // credman option.
         if (view.isCredential()) {
             return true;
         }
+        return containsAutofillHintPrefix(view, View.AUTOFILL_HINT_CREDENTIAL_MANAGER);
+    }
+
+    private boolean isCredmanRequested(View view) {
+        if (view == null) {
+            return false;
+        }
         String[] hints = view.getAutofillHints();
         if (hints == null) {
             return false;
         }
+        // if hint starts with 'credential=', then we assume that there is a valid
+        // credential option set by the client.
+        return containsAutofillHintPrefix(view, View.AUTOFILL_HINT_CREDENTIAL_MANAGER + "=");
+    }
+
+    private boolean containsAutofillHintPrefix(View view, String prefix) {
+        String[] hints = view.getAutofillHints();
+        if (hints == null) {
+            return false;
+        }
         for (String hint : hints) {
-            if (hint != null && hint.startsWith(View.AUTOFILL_HINT_CREDENTIAL_MANAGER)) {
+            if (hint != null && hint.startsWith(prefix)) {
                 return true;
             }
         }
diff --git a/core/java/android/view/contentcapture/ChildContentCaptureSession.java b/core/java/android/view/contentcapture/ChildContentCaptureSession.java
index 44b4353..70c899f 100644
--- a/core/java/android/view/contentcapture/ChildContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/ChildContentCaptureSession.java
@@ -17,10 +17,16 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.content.ComponentName;
 import android.graphics.Insets;
+import android.graphics.Rect;
+import android.os.IBinder;
+import android.util.SparseArray;
 import android.view.autofill.AutofillId;
 import android.view.contentcapture.ViewNode.ViewStructureImpl;
 
+import java.util.ArrayList;
+
 /**
  * A session that is explicitly created by the app (and hence is a descendant of
  * {@link MainContentCaptureSession}).
@@ -40,17 +46,30 @@
     }
 
     @Override
-    MainContentCaptureSession getMainCaptureSession() {
-        if (mParent instanceof MainContentCaptureSession) {
-            return (MainContentCaptureSession) mParent;
-        }
+    ContentCaptureSession getMainCaptureSession() {
         return mParent.getMainCaptureSession();
     }
 
     @Override
+    void start(@NonNull IBinder token, @NonNull IBinder shareableActivityToken,
+            @NonNull ComponentName component, int flags) {
+        getMainCaptureSession().start(token, shareableActivityToken, component, flags);
+    }
+
+    @Override
+    boolean isDisabled() {
+        return getMainCaptureSession().isDisabled();
+    }
+
+    @Override
+    boolean setDisabled(boolean disabled) {
+        return getMainCaptureSession().setDisabled(disabled);
+    }
+
+    @Override
     ContentCaptureSession newChild(@NonNull ContentCaptureContext clientContext) {
         final ContentCaptureSession child = new ChildContentCaptureSession(this, clientContext);
-        getMainCaptureSession().notifyChildSessionStarted(mId, child.mId, clientContext);
+        internalNotifyChildSessionStarted(mId, child.mId, clientContext);
         return child;
     }
 
@@ -61,51 +80,80 @@
 
     @Override
     public void updateContentCaptureContext(@Nullable ContentCaptureContext context) {
-        getMainCaptureSession().notifyContextUpdated(mId, context);
+        internalNotifyContextUpdated(mId, context);
     }
 
     @Override
     void onDestroy() {
-        getMainCaptureSession().notifyChildSessionFinished(mParent.mId, mId);
+        internalNotifyChildSessionFinished(mParent.mId, mId);
     }
 
     @Override
-    void internalNotifyViewAppeared(@NonNull ViewStructureImpl node) {
-        getMainCaptureSession().notifyViewAppeared(mId, node);
+    void internalNotifyChildSessionStarted(int parentSessionId, int childSessionId,
+            @NonNull ContentCaptureContext clientContext) {
+        getMainCaptureSession()
+                .internalNotifyChildSessionStarted(parentSessionId, childSessionId, clientContext);
     }
 
     @Override
-    void internalNotifyViewDisappeared(@NonNull AutofillId id) {
-        getMainCaptureSession().notifyViewDisappeared(mId, id);
+    void internalNotifyChildSessionFinished(int parentSessionId, int childSessionId) {
+        getMainCaptureSession().internalNotifyChildSessionFinished(parentSessionId, childSessionId);
     }
 
     @Override
-    void internalNotifyViewTextChanged(@NonNull AutofillId id, @Nullable CharSequence text) {
-        getMainCaptureSession().notifyViewTextChanged(mId, id, text);
+    void internalNotifyContextUpdated(int sessionId, @Nullable ContentCaptureContext context) {
+        getMainCaptureSession().internalNotifyContextUpdated(sessionId, context);
     }
 
     @Override
-    void internalNotifyViewInsetsChanged(@NonNull Insets viewInsets) {
-        getMainCaptureSession().notifyViewInsetsChanged(mId, viewInsets);
+    void internalNotifyViewAppeared(int sessionId, @NonNull ViewStructureImpl node) {
+        getMainCaptureSession().internalNotifyViewAppeared(sessionId, node);
     }
 
     @Override
-    public void internalNotifyViewTreeEvent(boolean started) {
-        getMainCaptureSession().notifyViewTreeEvent(mId, started);
+    void internalNotifyViewDisappeared(int sessionId, @NonNull AutofillId id) {
+        getMainCaptureSession().internalNotifyViewDisappeared(sessionId, id);
+    }
+
+    @Override
+    void internalNotifyViewTextChanged(
+            int sessionId, @NonNull AutofillId id, @Nullable CharSequence text) {
+        getMainCaptureSession().internalNotifyViewTextChanged(sessionId, id, text);
+    }
+
+    @Override
+    void internalNotifyViewInsetsChanged(int sessionId, @NonNull Insets viewInsets) {
+        getMainCaptureSession().internalNotifyViewInsetsChanged(mId, viewInsets);
+    }
+
+    @Override
+    public void internalNotifyViewTreeEvent(int sessionId, boolean started) {
+        getMainCaptureSession().internalNotifyViewTreeEvent(sessionId, started);
     }
 
     @Override
     void internalNotifySessionResumed() {
-        getMainCaptureSession().notifySessionResumed();
+        getMainCaptureSession().internalNotifySessionResumed();
     }
 
     @Override
     void internalNotifySessionPaused() {
-        getMainCaptureSession().notifySessionPaused();
+        getMainCaptureSession().internalNotifySessionPaused();
     }
 
     @Override
     boolean isContentCaptureEnabled() {
         return getMainCaptureSession().isContentCaptureEnabled();
     }
+
+    @Override
+    public void notifyWindowBoundsChanged(int sessionId, @NonNull Rect bounds) {
+        getMainCaptureSession().notifyWindowBoundsChanged(sessionId, bounds);
+    }
+
+    @Override
+    public void notifyContentCaptureEvents(
+            @NonNull SparseArray<ArrayList<Object>> contentCaptureEvents) {
+        getMainCaptureSession().notifyContentCaptureEvents(contentCaptureEvents);
+    }
 }
diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java
index a829747..bcef37f 100644
--- a/core/java/android/view/contentcapture/ContentCaptureManager.java
+++ b/core/java/android/view/contentcapture/ContentCaptureManager.java
@@ -499,10 +499,14 @@
 
     @Nullable
     @GuardedBy("mLock")
-    private Handler mHandler;
+    private Handler mUiHandler;
+
+    @Nullable
+    @GuardedBy("mLock")
+    private Handler mContentCaptureHandler;
 
     @GuardedBy("mLock")
-    private MainContentCaptureSession mMainSession;
+    private ContentCaptureSession mMainSession;
 
     @Nullable // set on-demand by addDumpable()
     private Dumper mDumpable;
@@ -587,11 +591,10 @@
      */
     @NonNull
     @UiThread
-    public MainContentCaptureSession getMainContentCaptureSession() {
+    public ContentCaptureSession getMainContentCaptureSession() {
         synchronized (mLock) {
             if (mMainSession == null) {
-                mMainSession = new MainContentCaptureSession(
-                        mContext, this, prepareContentCaptureHandler(), mService);
+                mMainSession = prepareMainSession();
                 if (sVerbose) Log.v(TAG, "getMainContentCaptureSession(): created " + mMainSession);
             }
             return mMainSession;
@@ -600,15 +603,36 @@
 
     @NonNull
     @GuardedBy("mLock")
-    private Handler prepareContentCaptureHandler() {
-        if (mHandler == null) {
-            if (runOnBackgroundThreadEnabled()) {
-                mHandler = BackgroundThread.getHandler();
-            } else {
-                mHandler = Handler.createAsync(Looper.getMainLooper());
-            }
+    private ContentCaptureSession prepareMainSession() {
+        if (runOnBackgroundThreadEnabled()) {
+            return new MainContentCaptureSessionV2(
+                    mContext,
+                    this,
+                    prepareUiHandler(),
+                    prepareContentCaptureHandler(),
+                    mService
+            );
+        } else {
+            return new MainContentCaptureSession(mContext, this, prepareUiHandler(), mService);
         }
-        return mHandler;
+    }
+
+    @NonNull
+    @GuardedBy("mLock")
+    private Handler prepareContentCaptureHandler() {
+        if (mContentCaptureHandler == null) {
+            mContentCaptureHandler = BackgroundThread.getHandler();
+        }
+        return mContentCaptureHandler;
+    }
+
+    @NonNull
+    @GuardedBy("mLock")
+    private Handler prepareUiHandler() {
+        if (mUiHandler == null) {
+            mUiHandler = Handler.createAsync(Looper.getMainLooper());
+        }
+        return mUiHandler;
     }
 
     /** @hide */
@@ -726,7 +750,7 @@
     public boolean isContentCaptureEnabled() {
         if (mOptions.lite) return false;
 
-        final MainContentCaptureSession mainSession;
+        final ContentCaptureSession mainSession;
         synchronized (mLock) {
             mainSession = mMainSession;
         }
@@ -777,7 +801,7 @@
             Log.d(TAG, "setContentCaptureEnabled(): setting to " + enabled + " for " + mContext);
         }
 
-        MainContentCaptureSession mainSession;
+        ContentCaptureSession mainSession;
         synchronized (mLock) {
             if (enabled) {
                 mFlags &= ~ContentCaptureContext.FLAG_DISABLED_BY_APP;
@@ -803,7 +827,7 @@
         final boolean flagSecureEnabled =
                 (params.flags & WindowManager.LayoutParams.FLAG_SECURE) != 0;
 
-        MainContentCaptureSession mainSession;
+        ContentCaptureSession mainSession;
         boolean alreadyDisabledByApp;
         synchronized (mLock) {
             alreadyDisabledByApp = (mFlags & ContentCaptureContext.FLAG_DISABLED_BY_APP) != 0;
diff --git a/core/java/android/view/contentcapture/ContentCaptureSession.java b/core/java/android/view/contentcapture/ContentCaptureSession.java
index bb815c0..0ca36ba2 100644
--- a/core/java/android/view/contentcapture/ContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/ContentCaptureSession.java
@@ -27,9 +27,13 @@
 import android.app.compat.CompatChanges;
 import android.compat.annotation.ChangeId;
 import android.compat.annotation.EnabledSince;
+import android.content.ComponentName;
 import android.graphics.Insets;
+import android.graphics.Rect;
+import android.os.IBinder;
 import android.util.DebugUtils;
 import android.util.Log;
+import android.util.SparseArray;
 import android.view.View;
 import android.view.ViewStructure;
 import android.view.autofill.AutofillId;
@@ -37,6 +41,7 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.IResultReceiver;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.Preconditions;
 
@@ -60,6 +65,18 @@
     private static final SecureRandom ID_GENERATOR = new SecureRandom();
 
     /**
+     * Name of the {@link IResultReceiver} extra used to pass the binder interface to the service.
+     * @hide
+     */
+    public static final String EXTRA_BINDER = "binder";
+
+    /**
+     * Name of the {@link IResultReceiver} extra used to pass the content capture enabled state.
+     * @hide
+     */
+    public static final String EXTRA_ENABLED_STATE = "enabled";
+
+    /**
      * Initial state, when there is no session.
      *
      * @hide
@@ -262,7 +279,19 @@
 
     /** @hide */
     @NonNull
-    abstract MainContentCaptureSession getMainCaptureSession();
+    abstract ContentCaptureSession getMainCaptureSession();
+
+    abstract void start(@NonNull IBinder token, @NonNull IBinder shareableActivityToken,
+            @NonNull ComponentName component, int flags);
+
+    abstract boolean isDisabled();
+
+    /**
+     * Sets the disabled state of content capture.
+     *
+     * @return whether disabled state was changed.
+     */
+    abstract boolean setDisabled(boolean disabled);
 
     /**
      * Gets the id used to identify this session.
@@ -400,10 +429,11 @@
             throw new IllegalArgumentException("Invalid node class: " + node.getClass());
         }
 
-        internalNotifyViewAppeared((ViewStructureImpl) node);
+        internalNotifyViewAppeared(mId, (ViewStructureImpl) node);
     }
 
-    abstract void internalNotifyViewAppeared(@NonNull ViewNode.ViewStructureImpl node);
+    abstract void internalNotifyViewAppeared(
+            int sessionId, @NonNull ViewNode.ViewStructureImpl node);
 
     /**
      * Notifies the Content Capture Service that a node has been removed from the view structure.
@@ -420,10 +450,10 @@
         Objects.requireNonNull(id);
         if (!isContentCaptureEnabled()) return;
 
-        internalNotifyViewDisappeared(id);
+        internalNotifyViewDisappeared(mId, id);
     }
 
-    abstract void internalNotifyViewDisappeared(@NonNull AutofillId id);
+    abstract void internalNotifyViewDisappeared(int sessionId, @NonNull AutofillId id);
 
     /**
      * Notifies the Content Capture Service that a list of nodes has appeared in the view structure.
@@ -445,12 +475,12 @@
             }
         }
 
-        internalNotifyViewTreeEvent(/* started= */ true);
+        internalNotifyViewTreeEvent(mId, /* started= */ true);
         for (int i = 0; i < appearedNodes.size(); i++) {
             ViewStructure v = appearedNodes.get(i);
-            internalNotifyViewAppeared((ViewStructureImpl) v);
+            internalNotifyViewAppeared(mId, (ViewStructureImpl) v);
         }
-        internalNotifyViewTreeEvent(/* started= */ false);
+        internalNotifyViewTreeEvent(mId, /* started= */ false);
     }
 
     /**
@@ -476,15 +506,15 @@
         if (!isContentCaptureEnabled()) return;
 
         if (CompatChanges.isChangeEnabled(NOTIFY_NODES_DISAPPEAR_NOW_SENDS_TREE_EVENTS)) {
-            internalNotifyViewTreeEvent(/* started= */ true);
+            internalNotifyViewTreeEvent(mId, /* started= */ true);
         }
         // TODO(b/123036895): use a internalNotifyViewsDisappeared that optimizes how the event is
         // parcelized
         for (long id : virtualIds) {
-            internalNotifyViewDisappeared(new AutofillId(hostId, id, mId));
+            internalNotifyViewDisappeared(mId, new AutofillId(hostId, id, mId));
         }
         if (CompatChanges.isChangeEnabled(NOTIFY_NODES_DISAPPEAR_NOW_SENDS_TREE_EVENTS)) {
-            internalNotifyViewTreeEvent(/* started= */ false);
+            internalNotifyViewTreeEvent(mId, /* started= */ false);
         }
     }
 
@@ -499,10 +529,10 @@
 
         if (!isContentCaptureEnabled()) return;
 
-        internalNotifyViewTextChanged(id, text);
+        internalNotifyViewTextChanged(mId, id, text);
     }
 
-    abstract void internalNotifyViewTextChanged(@NonNull AutofillId id,
+    abstract void internalNotifyViewTextChanged(int sessionId, @NonNull AutofillId id,
             @Nullable CharSequence text);
 
     /**
@@ -513,13 +543,18 @@
 
         if (!isContentCaptureEnabled()) return;
 
-        internalNotifyViewInsetsChanged(viewInsets);
+        internalNotifyViewInsetsChanged(mId, viewInsets);
     }
 
-    abstract void internalNotifyViewInsetsChanged(@NonNull Insets viewInsets);
+    abstract void internalNotifyViewInsetsChanged(int sessionId, @NonNull Insets viewInsets);
 
     /** @hide */
-    public abstract void internalNotifyViewTreeEvent(boolean started);
+    public void notifyViewTreeEvent(boolean started) {
+        internalNotifyViewTreeEvent(mId, started);
+    }
+
+    /** @hide */
+    abstract void internalNotifyViewTreeEvent(int sessionId, boolean started);
 
     /**
      * Notifies the Content Capture Service that a session has resumed.
@@ -543,6 +578,21 @@
 
     abstract void internalNotifySessionPaused();
 
+    abstract void internalNotifyChildSessionStarted(int parentSessionId, int childSessionId,
+            @NonNull ContentCaptureContext clientContext);
+
+    abstract void internalNotifyChildSessionFinished(int parentSessionId, int childSessionId);
+
+    abstract void internalNotifyContextUpdated(
+            int sessionId, @Nullable ContentCaptureContext context);
+
+    /** @hide */
+    public abstract void notifyWindowBoundsChanged(int sessionId, @NonNull Rect bounds);
+
+    /** @hide */
+    public abstract void notifyContentCaptureEvents(
+            @NonNull SparseArray<ArrayList<Object>> contentCaptureEvents);
+
     /**
      * Creates a {@link ViewStructure} for a "standard" view.
      *
diff --git a/core/java/android/view/contentcapture/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java
index 19ba316..a90c94e 100644
--- a/core/java/android/view/contentcapture/MainContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java
@@ -31,7 +31,6 @@
 import static android.view.contentcapture.ContentCaptureHelper.sDebug;
 import static android.view.contentcapture.ContentCaptureHelper.sVerbose;
 import static android.view.contentcapture.ContentCaptureManager.RESULT_CODE_FALSE;
-import static android.view.contentcapture.flags.Flags.runOnBackgroundThreadEnabled;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -70,10 +69,10 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.NoSuchElementException;
-import java.util.concurrent.ConcurrentLinkedQueue;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
 
+// TODO(b/309411951): Replace V2 as the only main session once the experiment is done.
 /**
  * Main session associated with a context.
  *
@@ -82,6 +81,7 @@
  *
  * @hide
  */
+@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
 public final class MainContentCaptureSession extends ContentCaptureSession {
 
     private static final String TAG = MainContentCaptureSession.class.getSimpleName();
@@ -97,18 +97,6 @@
      */
     private static final int MSG_FLUSH = 1;
 
-    /**
-     * Name of the {@link IResultReceiver} extra used to pass the binder interface to the service.
-     * @hide
-     */
-    public static final String EXTRA_BINDER = "binder";
-
-    /**
-     * Name of the {@link IResultReceiver} extra used to pass the content capture enabled state.
-     * @hide
-     */
-    public static final String EXTRA_ENABLED_STATE = "enabled";
-
     @NonNull
     private final AtomicBoolean mDisabled = new AtomicBoolean(false);
 
@@ -154,15 +142,6 @@
     public ComponentName mComponentName;
 
     /**
-     * Thread-safe queue of events held to be processed as a batch.
-     *
-     * Because it is not guaranteed that the events will be enqueued from a single thread, the
-     * implementation must be thread-safe to prevent unexpected behaviour.
-     */
-    @NonNull
-    private final ConcurrentLinkedQueue<ContentCaptureEvent> mEventProcessQueue;
-
-    /**
      * List of events held to be sent to the {@link ContentCaptureService} as a batch.
      *
      * @hide
@@ -221,14 +200,14 @@
                 binder = resultData.getBinder(EXTRA_BINDER);
                 if (binder == null) {
                     Log.wtf(TAG, "No " + EXTRA_BINDER + " extra result");
-                    mainSession.runOnContentCaptureThread(() -> mainSession.resetSession(
+                    mainSession.mHandler.post(() -> mainSession.resetSession(
                             STATE_DISABLED | STATE_INTERNAL_ERROR));
                     return;
                 }
             } else {
                 binder = null;
             }
-            mainSession.runOnContentCaptureThread(() ->
+            mainSession.mHandler.post(() ->
                     mainSession.onSessionStarted(resultCode, binder));
         }
     }
@@ -249,39 +228,27 @@
         mFlushHistory = logHistorySize > 0 ? new LocalLog(logHistorySize) : null;
 
         mSessionStateReceiver = new SessionStateReceiver(this);
-
-        mEventProcessQueue = new ConcurrentLinkedQueue<>();
     }
 
     @Override
-    MainContentCaptureSession getMainCaptureSession() {
+    ContentCaptureSession getMainCaptureSession() {
         return this;
     }
 
     @Override
     ContentCaptureSession newChild(@NonNull ContentCaptureContext clientContext) {
         final ContentCaptureSession child = new ChildContentCaptureSession(this, clientContext);
-        notifyChildSessionStarted(mId, child.mId, clientContext);
+        internalNotifyChildSessionStarted(mId, child.mId, clientContext);
         return child;
     }
 
     /**
      * Starts this session.
      */
+    @Override
     void start(@NonNull IBinder token, @NonNull IBinder shareableActivityToken,
             @NonNull ComponentName component, int flags) {
-        if (runOnBackgroundThreadEnabled()) {
-            runOnContentCaptureThread(
-                    () -> startImpl(token, shareableActivityToken, component, flags));
-        } else {
-            // Preserve the control arm behaviour.
-            startImpl(token, shareableActivityToken, component, flags);
-        }
-    }
-
-    private void startImpl(@NonNull IBinder token, @NonNull IBinder shareableActivityToken,
-               @NonNull ComponentName component, int flags) {
-        checkOnContentCaptureThread();
+        checkOnUiThread();
         if (!isContentCaptureEnabled()) return;
 
         if (sVerbose) {
@@ -315,15 +282,17 @@
             Log.w(TAG, "Error starting session for " + component.flattenToShortString() + ": " + e);
         }
     }
+
     @Override
     void onDestroy() {
-        clearAndRunOnContentCaptureThread(() -> {
+        mHandler.removeMessages(MSG_FLUSH);
+        mHandler.post(() -> {
             try {
                 flush(FLUSH_REASON_SESSION_FINISHED);
             } finally {
                 destroySession();
             }
-        }, MSG_FLUSH);
+        });
     }
 
     /**
@@ -336,7 +305,7 @@
      */
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
     public void onSessionStarted(int resultCode, @Nullable IBinder binder) {
-        checkOnContentCaptureThread();
+        checkOnUiThread();
         if (binder != null) {
             mDirectServiceInterface = IContentCaptureDirectManager.Stub.asInterface(binder);
             mDirectServiceVulture = () -> {
@@ -385,7 +354,7 @@
     }
 
     private void sendEvent(@NonNull ContentCaptureEvent event, boolean forceFlush) {
-        checkOnContentCaptureThread();
+        checkOnUiThread();
         final int eventType = event.getType();
         if (sVerbose) Log.v(TAG, "handleSendEvent(" + getDebugState() + "): " + event);
         if (!hasStarted() && eventType != ContentCaptureEvent.TYPE_SESSION_STARTED
@@ -429,14 +398,14 @@
     }
 
     private void sendContentProtectionEvent(@NonNull ContentCaptureEvent event) {
-        checkOnContentCaptureThread();
+        checkOnUiThread();
         if (mContentProtectionEventProcessor != null) {
             mContentProtectionEventProcessor.processEvent(event);
         }
     }
 
     private void sendContentCaptureEvent(@NonNull ContentCaptureEvent event, boolean forceFlush) {
-        checkOnContentCaptureThread();
+        checkOnUiThread();
         final int eventType = event.getType();
         final int maxBufferSize = mManager.mOptions.maxBufferSize;
         if (mEvents == null) {
@@ -571,12 +540,12 @@
     }
 
     private boolean hasStarted() {
-        checkOnContentCaptureThread();
+        checkOnUiThread();
         return mState != UNKNOWN_STATE;
     }
 
     private void scheduleFlush(@FlushReason int reason, boolean checkExisting) {
-        checkOnContentCaptureThread();
+        checkOnUiThread();
         if (sVerbose) {
             Log.v(TAG, "handleScheduleFlush(" + getDebugState(reason)
                     + ", checkExisting=" + checkExisting);
@@ -617,11 +586,12 @@
                     + flushFrequencyMs + "ms: " + TimeUtils.logTimeOfDay(mNextFlush));
         }
         // Post using a Runnable directly to trim a few μs from PooledLambda.obtainMessage()
-        mHandler.postDelayed(() -> flushIfNeeded(reason), MSG_FLUSH, flushFrequencyMs);
+        mHandler.postDelayed(() ->
+                flushIfNeeded(reason), MSG_FLUSH, flushFrequencyMs);
     }
 
     private void flushIfNeeded(@FlushReason int reason) {
-        checkOnContentCaptureThread();
+        checkOnUiThread();
         if (mEvents == null || mEvents.isEmpty()) {
             if (sVerbose) Log.v(TAG, "Nothing to flush");
             return;
@@ -633,16 +603,7 @@
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
     @Override
     public void flush(@FlushReason int reason) {
-        if (runOnBackgroundThreadEnabled()) {
-            runOnContentCaptureThread(() -> flushImpl(reason));
-        } else {
-            // Preserve the control arm behaviour.
-            flushImpl(reason);
-        }
-    }
-
-    private void flushImpl(@FlushReason int reason) {
-        checkOnContentCaptureThread();
+        checkOnUiThread();
         if (mEvents == null || mEvents.size() == 0) {
             if (sVerbose) {
                 Log.v(TAG, "Don't flush for empty event buffer.");
@@ -703,7 +664,7 @@
 
     @Override
     public void updateContentCaptureContext(@Nullable ContentCaptureContext context) {
-        notifyContextUpdated(mId, context);
+        internalNotifyContextUpdated(mId, context);
     }
 
     /**
@@ -711,7 +672,7 @@
      */
     @NonNull
     private ParceledListSlice<ContentCaptureEvent> clearEvents() {
-        checkOnContentCaptureThread();
+        checkOnUiThread();
         // NOTE: we must save a reference to the current mEvents and then set it to to null,
         // otherwise clearing it would clear it in the receiving side if the service is also local.
         if (mEvents == null) {
@@ -726,7 +687,7 @@
     /** hide */
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
     public void destroySession() {
-        checkOnContentCaptureThread();
+        checkOnUiThread();
         if (sDebug) {
             Log.d(TAG, "Destroying session (ctx=" + mContext + ", id=" + mId + ") with "
                     + (mEvents == null ? 0 : mEvents.size()) + " event(s) for "
@@ -746,9 +707,6 @@
         }
         mDirectServiceInterface = null;
         mContentProtectionEventProcessor = null;
-        if (runOnBackgroundThreadEnabled()) {
-            mEventProcessQueue.clear();
-        }
     }
 
     // TODO(b/122454205): once we support multiple sessions, we might need to move some of these
@@ -756,7 +714,7 @@
     /** @hide */
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
     public void resetSession(int newState) {
-        checkOnContentCaptureThread();
+        checkOnUiThread();
         if (sVerbose) {
             Log.v(TAG, "handleResetSession(" + getActivityName() + "): from "
                     + getStateAsString(mState) + " to " + getStateAsString(newState));
@@ -781,91 +739,22 @@
     }
 
     @Override
-    void internalNotifyViewAppeared(@NonNull ViewStructureImpl node) {
-        notifyViewAppeared(mId, node);
-    }
-
-    @Override
-    void internalNotifyViewDisappeared(@NonNull AutofillId id) {
-        notifyViewDisappeared(mId, id);
-    }
-
-    @Override
-    void internalNotifyViewTextChanged(@NonNull AutofillId id, @Nullable CharSequence text) {
-        notifyViewTextChanged(mId, id, text);
-    }
-
-    @Override
-    void internalNotifyViewInsetsChanged(@NonNull Insets viewInsets) {
-        notifyViewInsetsChanged(mId, viewInsets);
-    }
-
-    @Override
-    public void internalNotifyViewTreeEvent(boolean started) {
-        notifyViewTreeEvent(mId, started);
-    }
-
-    @Override
-    public void internalNotifySessionResumed() {
-        notifySessionResumed(mId);
-    }
-
-    @Override
-    public void internalNotifySessionPaused() {
-        notifySessionPaused(mId);
-    }
-
-    @Override
-    boolean isContentCaptureEnabled() {
-        return super.isContentCaptureEnabled() && mManager.isContentCaptureEnabled();
-    }
-
-    // Called by ContentCaptureManager.isContentCaptureEnabled
-    boolean isDisabled() {
-        return mDisabled.get();
-    }
-
-    /**
-     * Sets the disabled state of content capture.
-     *
-     * @return whether disabled state was changed.
-     */
-    boolean setDisabled(boolean disabled) {
-        return mDisabled.compareAndSet(!disabled, disabled);
-    }
-
-    // TODO(b/122454205): refactor "notifyXXXX" methods below to a common "Buffer" object that is
-    // shared between ActivityContentCaptureSession and ChildContentCaptureSession objects. Such
-    // change should also get get rid of the "internalNotifyXXXX" methods above
-    void notifyChildSessionStarted(int parentSessionId, int childSessionId,
-            @NonNull ContentCaptureContext clientContext) {
-        final ContentCaptureEvent event =
-                new ContentCaptureEvent(childSessionId, TYPE_SESSION_STARTED)
-                        .setParentSessionId(parentSessionId)
-                        .setClientContext(clientContext);
-        enqueueEvent(event, FORCE_FLUSH);
-    }
-
-    void notifyChildSessionFinished(int parentSessionId, int childSessionId) {
-        final ContentCaptureEvent event =
-                new ContentCaptureEvent(childSessionId, TYPE_SESSION_FINISHED)
-                        .setParentSessionId(parentSessionId);
-        enqueueEvent(event, FORCE_FLUSH);
-    }
-
-    void notifyViewAppeared(int sessionId, @NonNull ViewStructureImpl node) {
+    void internalNotifyViewAppeared(int sessionId, @NonNull ViewStructureImpl node) {
         final ContentCaptureEvent event = new ContentCaptureEvent(sessionId, TYPE_VIEW_APPEARED)
                 .setViewNode(node.mNode);
-        enqueueEvent(event);
+        mHandler.post(() -> sendEvent(event));
     }
 
-    void notifyViewDisappeared(int sessionId, @NonNull AutofillId id) {
+    @Override
+    void internalNotifyViewDisappeared(int sessionId, @NonNull AutofillId id) {
         final ContentCaptureEvent event = new ContentCaptureEvent(sessionId, TYPE_VIEW_DISAPPEARED)
                 .setAutofillId(id);
-        enqueueEvent(event);
+        mHandler.post(() -> sendEvent(event));
     }
 
-    void notifyViewTextChanged(int sessionId, @NonNull AutofillId id, @Nullable CharSequence text) {
+    @Override
+    void internalNotifyViewTextChanged(
+            int sessionId, @NonNull AutofillId id, @Nullable CharSequence text) {
         // Since the same CharSequence instance may be reused in the TextView, we need to make
         // a copy of its content so that its value will not be changed by subsequent updates
         // in the TextView.
@@ -891,113 +780,108 @@
                 .setAutofillId(id).setText(eventText)
                 .setComposingIndex(composingStart, composingEnd)
                 .setSelectionIndex(startIndex, endIndex);
-        enqueueEvent(event);
+        mHandler.post(() -> sendEvent(event));
     }
 
-    void notifyViewInsetsChanged(int sessionId, @NonNull Insets viewInsets) {
+    @Override
+    void internalNotifyViewInsetsChanged(int sessionId, @NonNull Insets viewInsets) {
         final ContentCaptureEvent event =
                 new ContentCaptureEvent(sessionId, TYPE_VIEW_INSETS_CHANGED)
                         .setInsets(viewInsets);
-        enqueueEvent(event);
+        mHandler.post(() -> sendEvent(event));
     }
 
-    void notifyViewTreeEvent(int sessionId, boolean started) {
+    @Override
+    public void internalNotifyViewTreeEvent(int sessionId, boolean started) {
         final int type = started ? TYPE_VIEW_TREE_APPEARING : TYPE_VIEW_TREE_APPEARED;
         final boolean disableFlush = mManager.getFlushViewTreeAppearingEventDisabled();
         final boolean forceFlush = disableFlush ? !started : FORCE_FLUSH;
 
         final ContentCaptureEvent event = new ContentCaptureEvent(sessionId, type);
-        enqueueEvent(event, forceFlush);
+        mHandler.post(() -> sendEvent(event, FORCE_FLUSH));
     }
 
-    void notifySessionResumed(int sessionId) {
-        final ContentCaptureEvent event = new ContentCaptureEvent(sessionId, TYPE_SESSION_RESUMED);
-        enqueueEvent(event, FORCE_FLUSH);
+    @Override
+    public void internalNotifySessionResumed() {
+        final ContentCaptureEvent event = new ContentCaptureEvent(mId, TYPE_SESSION_RESUMED);
+        mHandler.post(() -> sendEvent(event, FORCE_FLUSH));
     }
 
-    void notifySessionPaused(int sessionId) {
-        final ContentCaptureEvent event = new ContentCaptureEvent(sessionId, TYPE_SESSION_PAUSED);
-        enqueueEvent(event, FORCE_FLUSH);
+    @Override
+    public void internalNotifySessionPaused() {
+        final ContentCaptureEvent event = new ContentCaptureEvent(mId, TYPE_SESSION_PAUSED);
+        mHandler.post(() -> sendEvent(event, FORCE_FLUSH));
     }
 
-    void notifyContextUpdated(int sessionId, @Nullable ContentCaptureContext context) {
+    @Override
+    boolean isContentCaptureEnabled() {
+        return super.isContentCaptureEnabled() && mManager.isContentCaptureEnabled();
+    }
+
+    @Override
+    boolean isDisabled() {
+        return mDisabled.get();
+    }
+
+    @Override
+    boolean setDisabled(boolean disabled) {
+        return mDisabled.compareAndSet(!disabled, disabled);
+    }
+
+    @Override
+    void internalNotifyChildSessionStarted(int parentSessionId, int childSessionId,
+            @NonNull ContentCaptureContext clientContext) {
+        final ContentCaptureEvent event =
+                new ContentCaptureEvent(childSessionId, TYPE_SESSION_STARTED)
+                        .setParentSessionId(parentSessionId)
+                        .setClientContext(clientContext);
+        mHandler.post(() -> sendEvent(event, FORCE_FLUSH));
+    }
+
+    @Override
+    void internalNotifyChildSessionFinished(int parentSessionId, int childSessionId) {
+        final ContentCaptureEvent event =
+                new ContentCaptureEvent(childSessionId, TYPE_SESSION_FINISHED)
+                        .setParentSessionId(parentSessionId);
+        mHandler.post(() -> sendEvent(event, FORCE_FLUSH));
+    }
+
+    @Override
+    void internalNotifyContextUpdated(int sessionId, @Nullable ContentCaptureContext context) {
         final ContentCaptureEvent event = new ContentCaptureEvent(sessionId, TYPE_CONTEXT_UPDATED)
                 .setClientContext(context);
-        enqueueEvent(event, FORCE_FLUSH);
+        mHandler.post(() -> sendEvent(event, FORCE_FLUSH));
     }
 
-    /** public because is also used by ViewRootImpl */
+    @Override
     public void notifyWindowBoundsChanged(int sessionId, @NonNull Rect bounds) {
         final ContentCaptureEvent event =
                 new ContentCaptureEvent(sessionId, TYPE_WINDOW_BOUNDS_CHANGED)
                         .setBounds(bounds);
-        enqueueEvent(event);
+        mHandler.post(() -> sendEvent(event));
     }
 
-    private List<ContentCaptureEvent> clearBufferEvents() {
-        final ArrayList<ContentCaptureEvent> bufferEvents = new ArrayList<>();
-        ContentCaptureEvent event;
-        while ((event = mEventProcessQueue.poll()) != null) {
-            bufferEvents.add(event);
-        }
-        return bufferEvents;
-    }
-
-    private void enqueueEvent(@NonNull final ContentCaptureEvent event) {
-        enqueueEvent(event, /* forceFlush */ false);
-    }
-
-    /**
-     * Enqueue the event into {@code mEventProcessBuffer} if it is not an urgent request. Otherwise,
-     * clear the buffer events then starting sending out current event.
-     */
-    private void enqueueEvent(@NonNull final ContentCaptureEvent event, boolean forceFlush) {
-        if (runOnBackgroundThreadEnabled()) {
-            if (forceFlush) {
-                // The buffer events are cleared in the same thread first to prevent new events
-                // being added during the time of context switch. This would disrupt the sequence
-                // of events.
-                final List<ContentCaptureEvent> batchEvents = clearBufferEvents();
-                runOnContentCaptureThread(() -> {
-                    for (int i = 0; i < batchEvents.size(); i++) {
-                        sendEvent(batchEvents.get(i));
-                    }
-                    sendEvent(event, /* forceFlush= */ true);
-                });
-            } else {
-                mEventProcessQueue.offer(event);
-            }
-        } else {
-            mHandler.post(() -> sendEvent(event, forceFlush));
-        }
-    }
-
-    /** public because is also used by ViewRootImpl */
+    @Override
     public void notifyContentCaptureEvents(
             @NonNull SparseArray<ArrayList<Object>> contentCaptureEvents) {
-        if (runOnBackgroundThreadEnabled()) {
-            runOnContentCaptureThread(() -> notifyContentCaptureEventsImpl(contentCaptureEvents));
-        } else {
-            // Preserve the control arm behaviour.
-            notifyContentCaptureEventsImpl(contentCaptureEvents);
-        }
+        notifyContentCaptureEventsImpl(contentCaptureEvents);
     }
 
     private void notifyContentCaptureEventsImpl(
             @NonNull SparseArray<ArrayList<Object>> contentCaptureEvents) {
-        checkOnContentCaptureThread();
+        checkOnUiThread();
         try {
             if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
                 Trace.traceBegin(Trace.TRACE_TAG_VIEW, "notifyContentCaptureEvents");
             }
             for (int i = 0; i < contentCaptureEvents.size(); i++) {
                 int sessionId = contentCaptureEvents.keyAt(i);
-                notifyViewTreeEvent(sessionId, /* started= */ true);
+                internalNotifyViewTreeEvent(sessionId, /* started= */ true);
                 ArrayList<Object> events = contentCaptureEvents.valueAt(i);
                 for_each_event: for (int j = 0; j < events.size(); j++) {
                     Object event = events.get(j);
                     if (event instanceof AutofillId) {
-                        notifyViewDisappeared(sessionId, (AutofillId) event);
+                        internalNotifyViewDisappeared(sessionId, (AutofillId) event);
                     } else if (event instanceof View) {
                         View view = (View) event;
                         ContentCaptureSession session = view.getContentCaptureSession();
@@ -1015,12 +899,12 @@
                         view.onProvideContentCaptureStructure(structure, /* flags= */ 0);
                         session.notifyViewAppeared(structure);
                     } else if (event instanceof Insets) {
-                        notifyViewInsetsChanged(sessionId, (Insets) event);
+                        internalNotifyViewInsetsChanged(sessionId, (Insets) event);
                     } else {
                         Log.w(TAG, "invalid content capture event: " + event);
                     }
                 }
-                notifyViewTreeEvent(sessionId, /* started= */ false);
+                internalNotifyViewTreeEvent(sessionId, /* started= */ false);
             }
         } finally {
             Trace.traceEnd(Trace.TRACE_TAG_VIEW);
@@ -1131,9 +1015,9 @@
      * Therefore, accessing internal properties in {@link MainContentCaptureSession} should
      * always delegate to the assigned thread from {@code mHandler} for synchronization.</p>
      */
-    private void checkOnContentCaptureThread() {
-        final boolean onContentCaptureThread = mHandler.getLooper().isCurrentThread();
-        if (!onContentCaptureThread) {
+    private void checkOnUiThread() {
+        final boolean onUiThread = mHandler.getLooper().isCurrentThread();
+        if (!onUiThread) {
             mWrongThreadCount.incrementAndGet();
             Log.e(TAG, "MainContentCaptureSession running on " + Thread.currentThread());
         }
@@ -1144,38 +1028,4 @@
         Counter.logIncrement(
                 CONTENT_CAPTURE_WRONG_THREAD_METRIC_ID, mWrongThreadCount.getAndSet(0));
     }
-
-    /**
-     * Ensures that {@code r} will be running on the assigned thread.
-     *
-     * <p>This is to prevent unnecessary delegation to Handler that results in fragmented runnable.
-     * </p>
-     */
-    private void runOnContentCaptureThread(@NonNull Runnable r) {
-        if (runOnBackgroundThreadEnabled()) {
-            if (!mHandler.getLooper().isCurrentThread()) {
-                mHandler.post(r);
-            } else {
-                r.run();
-            }
-        } else {
-            // Preserve the control arm behaviour to always post to the handler.
-            mHandler.post(r);
-        }
-    }
-
-    private void clearAndRunOnContentCaptureThread(@NonNull Runnable r, int what) {
-        if (runOnBackgroundThreadEnabled()) {
-            if (!mHandler.getLooper().isCurrentThread()) {
-                mHandler.removeMessages(what);
-                mHandler.post(r);
-            } else {
-                r.run();
-            }
-        } else {
-            // Preserve the control arm behaviour to always post to the handler.
-            mHandler.removeMessages(what);
-            mHandler.post(r);
-        }
-    }
 }
diff --git a/core/java/android/view/contentcapture/MainContentCaptureSessionV2.java b/core/java/android/view/contentcapture/MainContentCaptureSessionV2.java
new file mode 100644
index 0000000..bf1d31c
--- /dev/null
+++ b/core/java/android/view/contentcapture/MainContentCaptureSessionV2.java
@@ -0,0 +1,1184 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.view.contentcapture;
+
+import static android.view.contentcapture.ContentCaptureEvent.TYPE_CONTEXT_UPDATED;
+import static android.view.contentcapture.ContentCaptureEvent.TYPE_SESSION_FINISHED;
+import static android.view.contentcapture.ContentCaptureEvent.TYPE_SESSION_PAUSED;
+import static android.view.contentcapture.ContentCaptureEvent.TYPE_SESSION_RESUMED;
+import static android.view.contentcapture.ContentCaptureEvent.TYPE_SESSION_STARTED;
+import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_APPEARED;
+import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_DISAPPEARED;
+import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_INSETS_CHANGED;
+import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_TEXT_CHANGED;
+import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_TREE_APPEARED;
+import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_TREE_APPEARING;
+import static android.view.contentcapture.ContentCaptureEvent.TYPE_WINDOW_BOUNDS_CHANGED;
+import static android.view.contentcapture.ContentCaptureHelper.getSanitizedString;
+import static android.view.contentcapture.ContentCaptureHelper.sDebug;
+import static android.view.contentcapture.ContentCaptureHelper.sVerbose;
+import static android.view.contentcapture.ContentCaptureManager.RESULT_CODE_FALSE;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.pm.ParceledListSlice;
+import android.graphics.Insets;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.IBinder.DeathRecipient;
+import android.os.RemoteException;
+import android.os.Trace;
+import android.service.contentcapture.ContentCaptureService;
+import android.text.Selection;
+import android.text.Spannable;
+import android.text.TextUtils;
+import android.util.LocalLog;
+import android.util.Log;
+import android.util.SparseArray;
+import android.util.TimeUtils;
+import android.view.View;
+import android.view.ViewStructure;
+import android.view.autofill.AutofillId;
+import android.view.contentcapture.ViewNode.ViewStructureImpl;
+import android.view.contentprotection.ContentProtectionEventProcessor;
+import android.view.inputmethod.BaseInputConnection;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.IResultReceiver;
+import com.android.modules.expresslog.Counter;
+
+import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.NoSuchElementException;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * Main session associated with a context.
+ *
+ * <p>This is forked from {@link MainContentCaptureSession} to hold the logic of running operations
+ * in the background thread.</p>
+ *
+ * @hide
+ */
+@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+public final class MainContentCaptureSessionV2 extends ContentCaptureSession {
+
+    private static final String TAG = MainContentCaptureSession.class.getSimpleName();
+
+    private static final String CONTENT_CAPTURE_WRONG_THREAD_METRIC_ID =
+            "content_capture.value_content_capture_wrong_thread_count";
+
+    // For readability purposes...
+    private static final boolean FORCE_FLUSH = true;
+
+    /**
+     * Handler message used to flush the buffer.
+     */
+    private static final int MSG_FLUSH = 1;
+
+    @NonNull
+    private final AtomicBoolean mDisabled = new AtomicBoolean(false);
+
+    @NonNull
+    private final ContentCaptureManager.StrippedContext mContext;
+
+    @NonNull
+    private final ContentCaptureManager mManager;
+
+    @NonNull
+    private final Handler mUiHandler;
+
+    @NonNull
+    private final Handler mContentCaptureHandler;
+
+    /**
+     * Interface to the system_server binder object - it's only used to start the session (and
+     * notify when the session is finished).
+     */
+    @NonNull
+    private final IContentCaptureManager mSystemServerInterface;
+
+    /**
+     * Direct interface to the service binder object - it's used to send the events, including the
+     * last ones (when the session is finished)
+     *
+     * @hide
+     */
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+    @Nullable
+    public IContentCaptureDirectManager mDirectServiceInterface;
+
+    @Nullable
+    private DeathRecipient mDirectServiceVulture;
+
+    private int mState = UNKNOWN_STATE;
+
+    @Nullable
+    private IBinder mApplicationToken;
+    @Nullable
+    private IBinder mShareableActivityToken;
+
+    /** @hide */
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+    @Nullable
+    public ComponentName mComponentName;
+
+    /**
+     * Thread-safe queue of events held to be processed as a batch.
+     *
+     * Because it is not guaranteed that the events will be enqueued from a single thread, the
+     * implementation must be thread-safe to prevent unexpected behaviour.
+     */
+    @NonNull
+    private final ConcurrentLinkedQueue<ContentCaptureEvent> mEventProcessQueue;
+
+    /**
+     * List of events held to be sent to the {@link ContentCaptureService} as a batch.
+     *
+     * @hide
+     */
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+    @Nullable
+    public ArrayList<ContentCaptureEvent> mEvents;
+
+    // Used just for debugging purposes (on dump)
+    private long mNextFlush;
+
+    /**
+     * Whether the next buffer flush is queued by a text changed event.
+     */
+    private boolean mNextFlushForTextChanged = false;
+
+    @Nullable
+    private final LocalLog mFlushHistory;
+
+    private final AtomicInteger mWrongThreadCount = new AtomicInteger(0);
+
+    /**
+     * Binder object used to update the session state.
+     */
+    @NonNull
+    private final SessionStateReceiver mSessionStateReceiver;
+
+    /** @hide */
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+    @Nullable
+    public ContentProtectionEventProcessor mContentProtectionEventProcessor;
+
+    private static class SessionStateReceiver extends IResultReceiver.Stub {
+        private final WeakReference<MainContentCaptureSessionV2> mMainSession;
+
+        SessionStateReceiver(MainContentCaptureSessionV2 session) {
+            mMainSession = new WeakReference<>(session);
+        }
+
+        @Override
+        public void send(int resultCode, Bundle resultData) {
+            final MainContentCaptureSessionV2 mainSession = mMainSession.get();
+            if (mainSession == null) {
+                Log.w(TAG, "received result after mina session released");
+                return;
+            }
+            final IBinder binder;
+            if (resultData != null) {
+                // Change in content capture enabled.
+                final boolean hasEnabled = resultData.getBoolean(EXTRA_ENABLED_STATE);
+                if (hasEnabled) {
+                    final boolean disabled = (resultCode == RESULT_CODE_FALSE);
+                    mainSession.mDisabled.set(disabled);
+                    return;
+                }
+                binder = resultData.getBinder(EXTRA_BINDER);
+                if (binder == null) {
+                    Log.wtf(TAG, "No " + EXTRA_BINDER + " extra result");
+                    mainSession.runOnContentCaptureThread(() -> mainSession.resetSession(
+                            STATE_DISABLED | STATE_INTERNAL_ERROR));
+                    return;
+                }
+            } else {
+                binder = null;
+            }
+            mainSession.runOnContentCaptureThread(() ->
+                    mainSession.onSessionStarted(resultCode, binder));
+        }
+    }
+
+    /** @hide */
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PROTECTED)
+    public MainContentCaptureSessionV2(
+            @NonNull ContentCaptureManager.StrippedContext context,
+            @NonNull ContentCaptureManager manager,
+            @NonNull Handler uiHandler,
+            @NonNull Handler contentCaptureHandler,
+            @NonNull IContentCaptureManager systemServerInterface) {
+        mContext = context;
+        mManager = manager;
+        mUiHandler = uiHandler;
+        mContentCaptureHandler = contentCaptureHandler;
+        mSystemServerInterface = systemServerInterface;
+
+        final int logHistorySize = mManager.mOptions.logHistorySize;
+        mFlushHistory = logHistorySize > 0 ? new LocalLog(logHistorySize) : null;
+
+        mSessionStateReceiver = new SessionStateReceiver(this);
+
+        mEventProcessQueue = new ConcurrentLinkedQueue<>();
+    }
+
+    @Override
+    ContentCaptureSession getMainCaptureSession() {
+        return this;
+    }
+
+    @Override
+    ContentCaptureSession newChild(@NonNull ContentCaptureContext clientContext) {
+        final ContentCaptureSession child = new ChildContentCaptureSession(this, clientContext);
+        internalNotifyChildSessionStarted(mId, child.mId, clientContext);
+        return child;
+    }
+
+    /**
+     * Starts this session.
+     */
+    @Override
+    void start(@NonNull IBinder token, @NonNull IBinder shareableActivityToken,
+            @NonNull ComponentName component, int flags) {
+        runOnContentCaptureThread(
+                () -> startImpl(token, shareableActivityToken, component, flags));
+    }
+
+    private void startImpl(@NonNull IBinder token, @NonNull IBinder shareableActivityToken,
+               @NonNull ComponentName component, int flags) {
+        checkOnContentCaptureThread();
+        if (!isContentCaptureEnabled()) return;
+
+        if (sVerbose) {
+            Log.v(TAG, "start(): token=" + token + ", comp="
+                    + ComponentName.flattenToShortString(component));
+        }
+
+        if (hasStarted()) {
+            // TODO(b/122959591): make sure this is expected (and when), or use Log.w
+            if (sDebug) {
+                Log.d(TAG, "ignoring handleStartSession(" + token + "/"
+                        + ComponentName.flattenToShortString(component) + " while on state "
+                        + getStateAsString(mState));
+            }
+            return;
+        }
+        mState = STATE_WAITING_FOR_SERVER;
+        mApplicationToken = token;
+        mShareableActivityToken = shareableActivityToken;
+        mComponentName = component;
+
+        if (sVerbose) {
+            Log.v(TAG, "handleStartSession(): token=" + token + ", act="
+                    + getDebugState() + ", id=" + mId);
+        }
+
+        try {
+            mSystemServerInterface.startSession(mApplicationToken, mShareableActivityToken,
+                    component, mId, flags, mSessionStateReceiver);
+        } catch (RemoteException e) {
+            Log.w(TAG, "Error starting session for " + component.flattenToShortString() + ": " + e);
+        }
+    }
+    @Override
+    void onDestroy() {
+        clearAndRunOnContentCaptureThread(() -> {
+            try {
+                flush(FLUSH_REASON_SESSION_FINISHED);
+            } finally {
+                destroySession();
+            }
+        }, MSG_FLUSH);
+    }
+
+    /**
+     * Callback from {@code system_server} after call to {@link
+     * IContentCaptureManager#startSession(IBinder, ComponentName, String, int, IResultReceiver)}.
+     *
+     * @param resultCode session state
+     * @param binder handle to {@code IContentCaptureDirectManager}
+     * @hide
+     */
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+    public void onSessionStarted(int resultCode, @Nullable IBinder binder) {
+        checkOnContentCaptureThread();
+        if (binder != null) {
+            mDirectServiceInterface = IContentCaptureDirectManager.Stub.asInterface(binder);
+            mDirectServiceVulture = () -> {
+                Log.w(TAG, "Keeping session " + mId + " when service died");
+                mState = STATE_SERVICE_DIED;
+                mDisabled.set(true);
+            };
+            try {
+                binder.linkToDeath(mDirectServiceVulture, 0);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed to link to death on " + binder + ": " + e);
+            }
+        }
+
+        if (isContentProtectionEnabled()) {
+            mContentProtectionEventProcessor =
+                    new ContentProtectionEventProcessor(
+                            mManager.getContentProtectionEventBuffer(),
+                            mContentCaptureHandler,
+                            mSystemServerInterface,
+                            mComponentName.getPackageName(),
+                            mManager.mOptions.contentProtectionOptions);
+        } else {
+            mContentProtectionEventProcessor = null;
+        }
+
+        if ((resultCode & STATE_DISABLED) != 0) {
+            resetSession(resultCode);
+        } else {
+            mState = resultCode;
+            mDisabled.set(false);
+            // Flush any pending data immediately as buffering forced until now.
+            flushIfNeeded(FLUSH_REASON_SESSION_CONNECTED);
+        }
+        if (sVerbose) {
+            Log.v(TAG, "handleSessionStarted() result: id=" + mId + " resultCode=" + resultCode
+                    + ", state=" + getStateAsString(mState) + ", disabled=" + mDisabled.get()
+                    + ", binder=" + binder + ", events=" + (mEvents == null ? 0 : mEvents.size()));
+        }
+    }
+
+    /** @hide */
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+    public void sendEvent(@NonNull ContentCaptureEvent event) {
+        sendEvent(event, /* forceFlush= */ false);
+    }
+
+    private void sendEvent(@NonNull ContentCaptureEvent event, boolean forceFlush) {
+        checkOnContentCaptureThread();
+        final int eventType = event.getType();
+        if (sVerbose) Log.v(TAG, "handleSendEvent(" + getDebugState() + "): " + event);
+        if (!hasStarted() && eventType != ContentCaptureEvent.TYPE_SESSION_STARTED
+                && eventType != ContentCaptureEvent.TYPE_CONTEXT_UPDATED) {
+            // TODO(b/120494182): comment when this could happen (dialogs?)
+            if (sVerbose) {
+                Log.v(TAG, "handleSendEvent(" + getDebugState() + ", "
+                        + ContentCaptureEvent.getTypeAsString(eventType)
+                        + "): dropping because session not started yet");
+            }
+            return;
+        }
+        if (mDisabled.get()) {
+            // This happens when the event was queued in the handler before the sesison was ready,
+            // then handleSessionStarted() returned and set it as disabled - we need to drop it,
+            // otherwise it will keep triggering handleScheduleFlush()
+            if (sVerbose) Log.v(TAG, "handleSendEvent(): ignoring when disabled");
+            return;
+        }
+
+        if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
+            if (eventType == TYPE_VIEW_TREE_APPEARING) {
+                Trace.asyncTraceBegin(
+                        Trace.TRACE_TAG_VIEW, /* methodName= */ "sendEventAsync", /* cookie= */ 0);
+            }
+        }
+
+        if (isContentProtectionReceiverEnabled()) {
+            sendContentProtectionEvent(event);
+        }
+        if (isContentCaptureReceiverEnabled()) {
+            sendContentCaptureEvent(event, forceFlush);
+        }
+
+        if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
+            if (eventType == TYPE_VIEW_TREE_APPEARED) {
+                Trace.asyncTraceEnd(
+                        Trace.TRACE_TAG_VIEW, /* methodName= */ "sendEventAsync", /* cookie= */ 0);
+            }
+        }
+    }
+
+    private void sendContentProtectionEvent(@NonNull ContentCaptureEvent event) {
+        checkOnContentCaptureThread();
+        if (mContentProtectionEventProcessor != null) {
+            mContentProtectionEventProcessor.processEvent(event);
+        }
+    }
+
+    private void sendContentCaptureEvent(@NonNull ContentCaptureEvent event, boolean forceFlush) {
+        checkOnContentCaptureThread();
+        final int eventType = event.getType();
+        final int maxBufferSize = mManager.mOptions.maxBufferSize;
+        if (mEvents == null) {
+            if (sVerbose) {
+                Log.v(TAG, "handleSendEvent(): creating buffer for " + maxBufferSize + " events");
+            }
+            mEvents = new ArrayList<>(maxBufferSize);
+        }
+
+        // Some type of events can be merged together
+        boolean addEvent = true;
+
+        if (eventType == TYPE_VIEW_TEXT_CHANGED) {
+            // We determine whether to add or merge the current event by following criteria:
+            // 1. Don't have composing span: always add.
+            // 2. Have composing span:
+            //    2.1 either last or current text is empty: add.
+            //    2.2 last event doesn't have composing span: add.
+            // Otherwise, merge.
+            final CharSequence text = event.getText();
+            final boolean hasComposingSpan = event.hasComposingSpan();
+            if (hasComposingSpan) {
+                ContentCaptureEvent lastEvent = null;
+                for (int index = mEvents.size() - 1; index >= 0; index--) {
+                    final ContentCaptureEvent tmpEvent = mEvents.get(index);
+                    if (event.getId().equals(tmpEvent.getId())) {
+                        lastEvent = tmpEvent;
+                        break;
+                    }
+                }
+                if (lastEvent != null && lastEvent.hasComposingSpan()) {
+                    final CharSequence lastText = lastEvent.getText();
+                    final boolean bothNonEmpty = !TextUtils.isEmpty(lastText)
+                            && !TextUtils.isEmpty(text);
+                    boolean equalContent =
+                            TextUtils.equals(lastText, text)
+                            && lastEvent.hasSameComposingSpan(event)
+                            && lastEvent.hasSameSelectionSpan(event);
+                    if (equalContent) {
+                        addEvent = false;
+                    } else if (bothNonEmpty) {
+                        lastEvent.mergeEvent(event);
+                        addEvent = false;
+                    }
+                    if (!addEvent && sVerbose) {
+                        Log.v(TAG, "Buffering VIEW_TEXT_CHANGED event, updated text="
+                                + getSanitizedString(text));
+                    }
+                }
+            }
+        }
+
+        if (!mEvents.isEmpty() && eventType == TYPE_VIEW_DISAPPEARED) {
+            final ContentCaptureEvent lastEvent = mEvents.get(mEvents.size() - 1);
+            if (lastEvent.getType() == TYPE_VIEW_DISAPPEARED
+                    && event.getSessionId() == lastEvent.getSessionId()) {
+                if (sVerbose) {
+                    Log.v(TAG, "Buffering TYPE_VIEW_DISAPPEARED events for session "
+                            + lastEvent.getSessionId());
+                }
+                lastEvent.mergeEvent(event);
+                addEvent = false;
+            }
+        }
+
+        if (addEvent) {
+            mEvents.add(event);
+        }
+
+        // TODO: we need to change when the flush happens so that we don't flush while the
+        //  composing span hasn't changed. But we might need to keep flushing the events for the
+        //  non-editable views and views that don't have the composing state; otherwise some other
+        //  Content Capture features may be delayed.
+
+        final int numberEvents = mEvents.size();
+
+        final boolean bufferEvent = numberEvents < maxBufferSize;
+
+        if (bufferEvent && !forceFlush) {
+            final int flushReason;
+            if (eventType == TYPE_VIEW_TEXT_CHANGED) {
+                mNextFlushForTextChanged = true;
+                flushReason = FLUSH_REASON_TEXT_CHANGE_TIMEOUT;
+            } else {
+                if (mNextFlushForTextChanged) {
+                    if (sVerbose) {
+                        Log.i(TAG, "Not scheduling flush because next flush is for text changed");
+                    }
+                    return;
+                }
+
+                flushReason = FLUSH_REASON_IDLE_TIMEOUT;
+            }
+            scheduleFlush(flushReason, /* checkExisting= */ true);
+            return;
+        }
+
+        if (mState != STATE_ACTIVE && numberEvents >= maxBufferSize) {
+            // Callback from startSession hasn't been called yet - typically happens on system
+            // apps that are started before the system service
+            // TODO(b/122959591): try to ignore session while system is not ready / boot
+            // not complete instead. Similarly, the manager service should return right away
+            // when the user does not have a service set
+            if (sDebug) {
+                Log.d(TAG, "Closing session for " + getDebugState()
+                        + " after " + numberEvents + " delayed events");
+            }
+            resetSession(STATE_DISABLED | STATE_NO_RESPONSE);
+            // TODO(b/111276913): denylist activity / use special flag to indicate that
+            // when it's launched again
+            return;
+        }
+        final int flushReason;
+        switch (eventType) {
+            case ContentCaptureEvent.TYPE_SESSION_STARTED:
+                flushReason = FLUSH_REASON_SESSION_STARTED;
+                break;
+            case ContentCaptureEvent.TYPE_SESSION_FINISHED:
+                flushReason = FLUSH_REASON_SESSION_FINISHED;
+                break;
+            case ContentCaptureEvent.TYPE_VIEW_TREE_APPEARING:
+                flushReason = FLUSH_REASON_VIEW_TREE_APPEARING;
+                break;
+            case ContentCaptureEvent.TYPE_VIEW_TREE_APPEARED:
+                flushReason = FLUSH_REASON_VIEW_TREE_APPEARED;
+                break;
+            default:
+                flushReason = forceFlush ? FLUSH_REASON_FORCE_FLUSH : FLUSH_REASON_FULL;
+        }
+
+        flush(flushReason);
+    }
+
+    private boolean hasStarted() {
+        checkOnContentCaptureThread();
+        return mState != UNKNOWN_STATE;
+    }
+
+    private void scheduleFlush(@FlushReason int reason, boolean checkExisting) {
+        checkOnContentCaptureThread();
+        if (sVerbose) {
+            Log.v(TAG, "handleScheduleFlush(" + getDebugState(reason)
+                    + ", checkExisting=" + checkExisting);
+        }
+        if (!hasStarted()) {
+            if (sVerbose) Log.v(TAG, "handleScheduleFlush(): session not started yet");
+            return;
+        }
+
+        if (mDisabled.get()) {
+            // Should not be called on this state, as handleSendEvent checks.
+            // But we rather add one if check and log than re-schedule and keep the session alive...
+            Log.e(TAG, "handleScheduleFlush(" + getDebugState(reason) + "): should not be called "
+                    + "when disabled. events=" + (mEvents == null ? null : mEvents.size()));
+            return;
+        }
+        if (checkExisting && mContentCaptureHandler.hasMessages(MSG_FLUSH)) {
+            // "Renew" the flush message by removing the previous one
+            mContentCaptureHandler.removeMessages(MSG_FLUSH);
+        }
+
+        final int flushFrequencyMs;
+        if (reason == FLUSH_REASON_TEXT_CHANGE_TIMEOUT) {
+            flushFrequencyMs = mManager.mOptions.textChangeFlushingFrequencyMs;
+        } else {
+            if (reason != FLUSH_REASON_IDLE_TIMEOUT) {
+                if (sDebug) {
+                    Log.d(TAG, "handleScheduleFlush(" + getDebugState(reason) + "): not a timeout "
+                            + "reason because mDirectServiceInterface is not ready yet");
+                }
+            }
+            flushFrequencyMs = mManager.mOptions.idleFlushingFrequencyMs;
+        }
+
+        mNextFlush = System.currentTimeMillis() + flushFrequencyMs;
+        if (sVerbose) {
+            Log.v(TAG, "handleScheduleFlush(): scheduled to flush in "
+                    + flushFrequencyMs + "ms: " + TimeUtils.logTimeOfDay(mNextFlush));
+        }
+        // Post using a Runnable directly to trim a few μs from PooledLambda.obtainMessage()
+        mContentCaptureHandler.postDelayed(() ->
+                flushIfNeeded(reason), MSG_FLUSH, flushFrequencyMs);
+    }
+
+    private void flushIfNeeded(@FlushReason int reason) {
+        checkOnContentCaptureThread();
+        if (mEvents == null || mEvents.isEmpty()) {
+            if (sVerbose) Log.v(TAG, "Nothing to flush");
+            return;
+        }
+        flush(reason);
+    }
+
+    /** @hide */
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    @Override
+    public void flush(@FlushReason int reason) {
+        runOnContentCaptureThread(() -> flushImpl(reason));
+    }
+
+    private void flushImpl(@FlushReason int reason) {
+        checkOnContentCaptureThread();
+        if (mEvents == null || mEvents.size() == 0) {
+            if (sVerbose) {
+                Log.v(TAG, "Don't flush for empty event buffer.");
+            }
+            return;
+        }
+
+        if (mDisabled.get()) {
+            Log.e(TAG, "handleForceFlush(" + getDebugState(reason) + "): should not be when "
+                    + "disabled");
+            return;
+        }
+
+        if (!isContentCaptureReceiverEnabled()) {
+            return;
+        }
+
+        if (mDirectServiceInterface == null) {
+            if (sVerbose) {
+                Log.v(TAG, "handleForceFlush(" + getDebugState(reason) + "): hold your horses, "
+                        + "client not ready: " + mEvents);
+            }
+            if (!mContentCaptureHandler.hasMessages(MSG_FLUSH)) {
+                scheduleFlush(reason, /* checkExisting= */ false);
+            }
+            return;
+        }
+
+        mNextFlushForTextChanged = false;
+
+        final int numberEvents = mEvents.size();
+        final String reasonString = getFlushReasonAsString(reason);
+
+        if (sVerbose) {
+            ContentCaptureEvent event = mEvents.get(numberEvents - 1);
+            String forceString = (reason == FLUSH_REASON_FORCE_FLUSH) ? ". The force flush event "
+                    + ContentCaptureEvent.getTypeAsString(event.getType()) : "";
+            Log.v(TAG, "Flushing " + numberEvents + " event(s) for " + getDebugState(reason)
+                    + forceString);
+        }
+        if (mFlushHistory != null) {
+            // Logs reason, size, max size, idle timeout
+            final String logRecord = "r=" + reasonString + " s=" + numberEvents
+                    + " m=" + mManager.mOptions.maxBufferSize
+                    + " i=" + mManager.mOptions.idleFlushingFrequencyMs;
+            mFlushHistory.log(logRecord);
+        }
+        try {
+            mContentCaptureHandler.removeMessages(MSG_FLUSH);
+
+            final ParceledListSlice<ContentCaptureEvent> events = clearEvents();
+            mDirectServiceInterface.sendEvents(events, reason, mManager.mOptions);
+        } catch (RemoteException e) {
+            Log.w(TAG, "Error sending " + numberEvents + " for " + getDebugState()
+                    + ": " + e);
+        }
+    }
+
+    @Override
+    public void updateContentCaptureContext(@Nullable ContentCaptureContext context) {
+        internalNotifyContextUpdated(mId, context);
+    }
+
+    /**
+     * Resets the buffer and return a {@link ParceledListSlice} with the previous events.
+     */
+    @NonNull
+    private ParceledListSlice<ContentCaptureEvent> clearEvents() {
+        checkOnContentCaptureThread();
+        // NOTE: we must save a reference to the current mEvents and then set it to to null,
+        // otherwise clearing it would clear it in the receiving side if the service is also local.
+        if (mEvents == null) {
+            return new ParceledListSlice<>(Collections.EMPTY_LIST);
+        }
+
+        final List<ContentCaptureEvent> events = new ArrayList<>(mEvents);
+        mEvents.clear();
+        return new ParceledListSlice<>(events);
+    }
+
+    /** hide */
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+    public void destroySession() {
+        checkOnContentCaptureThread();
+        if (sDebug) {
+            Log.d(TAG, "Destroying session (ctx=" + mContext + ", id=" + mId + ") with "
+                    + (mEvents == null ? 0 : mEvents.size()) + " event(s) for "
+                    + getDebugState());
+        }
+
+        reportWrongThreadMetric();
+        try {
+            mSystemServerInterface.finishSession(mId);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error destroying system-service session " + mId + " for "
+                    + getDebugState() + ": " + e);
+        }
+
+        if (mDirectServiceInterface != null) {
+            mDirectServiceInterface.asBinder().unlinkToDeath(mDirectServiceVulture, 0);
+        }
+        mDirectServiceInterface = null;
+        mContentProtectionEventProcessor = null;
+        mEventProcessQueue.clear();
+    }
+
+    // TODO(b/122454205): once we support multiple sessions, we might need to move some of these
+    // clearings out.
+    /** @hide */
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+    public void resetSession(int newState) {
+        checkOnContentCaptureThread();
+        if (sVerbose) {
+            Log.v(TAG, "handleResetSession(" + getActivityName() + "): from "
+                    + getStateAsString(mState) + " to " + getStateAsString(newState));
+        }
+        mState = newState;
+        mDisabled.set((newState & STATE_DISABLED) != 0);
+        // TODO(b/122454205): must reset children (which currently is owned by superclass)
+        mApplicationToken = null;
+        mShareableActivityToken = null;
+        mComponentName = null;
+        mEvents = null;
+        if (mDirectServiceInterface != null) {
+            try {
+                mDirectServiceInterface.asBinder().unlinkToDeath(mDirectServiceVulture, 0);
+            } catch (NoSuchElementException e) {
+                Log.w(TAG, "IContentCaptureDirectManager does not exist");
+            }
+        }
+        mDirectServiceInterface = null;
+        mContentProtectionEventProcessor = null;
+        mContentCaptureHandler.removeMessages(MSG_FLUSH);
+    }
+
+    @Override
+    void internalNotifyViewAppeared(int sessionId, @NonNull ViewStructureImpl node) {
+        final ContentCaptureEvent event = new ContentCaptureEvent(sessionId, TYPE_VIEW_APPEARED)
+                .setViewNode(node.mNode);
+        enqueueEvent(event);
+    }
+
+    @Override
+    void internalNotifyViewDisappeared(int sessionId, @NonNull AutofillId id) {
+        final ContentCaptureEvent event = new ContentCaptureEvent(sessionId, TYPE_VIEW_DISAPPEARED)
+                .setAutofillId(id);
+        enqueueEvent(event);
+    }
+
+    @Override
+    void internalNotifyViewTextChanged(
+            int sessionId, @NonNull AutofillId id, @Nullable CharSequence text) {
+        // Since the same CharSequence instance may be reused in the TextView, we need to make
+        // a copy of its content so that its value will not be changed by subsequent updates
+        // in the TextView.
+        CharSequence trimmed = TextUtils.trimToParcelableSize(text);
+        final CharSequence eventText = trimmed != null && trimmed == text
+                ? trimmed.toString()
+                : trimmed;
+
+        final int composingStart;
+        final int composingEnd;
+        if (text instanceof Spannable) {
+            composingStart = BaseInputConnection.getComposingSpanStart((Spannable) text);
+            composingEnd = BaseInputConnection.getComposingSpanEnd((Spannable) text);
+        } else {
+            composingStart = ContentCaptureEvent.MAX_INVALID_VALUE;
+            composingEnd = ContentCaptureEvent.MAX_INVALID_VALUE;
+        }
+
+        final int startIndex = Selection.getSelectionStart(text);
+        final int endIndex = Selection.getSelectionEnd(text);
+
+        final ContentCaptureEvent event = new ContentCaptureEvent(sessionId, TYPE_VIEW_TEXT_CHANGED)
+                .setAutofillId(id).setText(eventText)
+                .setComposingIndex(composingStart, composingEnd)
+                .setSelectionIndex(startIndex, endIndex);
+        enqueueEvent(event);
+    }
+
+    @Override
+    void internalNotifyViewInsetsChanged(int sessionId, @NonNull Insets viewInsets) {
+        final ContentCaptureEvent event =
+                new ContentCaptureEvent(sessionId, TYPE_VIEW_INSETS_CHANGED)
+                        .setInsets(viewInsets);
+        enqueueEvent(event);
+    }
+
+    @Override
+    public void internalNotifyViewTreeEvent(int sessionId, boolean started) {
+        final int type = started ? TYPE_VIEW_TREE_APPEARING : TYPE_VIEW_TREE_APPEARED;
+        final boolean disableFlush = mManager.getFlushViewTreeAppearingEventDisabled();
+        final boolean forceFlush = disableFlush ? !started : FORCE_FLUSH;
+
+        final ContentCaptureEvent event = new ContentCaptureEvent(sessionId, type);
+        enqueueEvent(event, forceFlush);
+    }
+
+    @Override
+    public void internalNotifySessionResumed() {
+        final ContentCaptureEvent event = new ContentCaptureEvent(mId, TYPE_SESSION_RESUMED);
+        enqueueEvent(event, FORCE_FLUSH);
+    }
+
+    @Override
+    public void internalNotifySessionPaused() {
+        final ContentCaptureEvent event = new ContentCaptureEvent(mId, TYPE_SESSION_PAUSED);
+        enqueueEvent(event, FORCE_FLUSH);
+    }
+
+    @Override
+    boolean isContentCaptureEnabled() {
+        return super.isContentCaptureEnabled() && mManager.isContentCaptureEnabled();
+    }
+
+    // Called by ContentCaptureManager.isContentCaptureEnabled
+    boolean isDisabled() {
+        return mDisabled.get();
+    }
+
+    /**
+     * Sets the disabled state of content capture.
+     *
+     * @return whether disabled state was changed.
+     */
+    boolean setDisabled(boolean disabled) {
+        return mDisabled.compareAndSet(!disabled, disabled);
+    }
+
+    @Override
+    void internalNotifyChildSessionStarted(int parentSessionId, int childSessionId,
+            @NonNull ContentCaptureContext clientContext) {
+        final ContentCaptureEvent event =
+                new ContentCaptureEvent(childSessionId, TYPE_SESSION_STARTED)
+                        .setParentSessionId(parentSessionId)
+                        .setClientContext(clientContext);
+        enqueueEvent(event, FORCE_FLUSH);
+    }
+
+    @Override
+    void internalNotifyChildSessionFinished(int parentSessionId, int childSessionId) {
+        final ContentCaptureEvent event =
+                new ContentCaptureEvent(childSessionId, TYPE_SESSION_FINISHED)
+                        .setParentSessionId(parentSessionId);
+        enqueueEvent(event, FORCE_FLUSH);
+    }
+
+    @Override
+    void internalNotifyContextUpdated(int sessionId, @Nullable ContentCaptureContext context) {
+        final ContentCaptureEvent event = new ContentCaptureEvent(sessionId, TYPE_CONTEXT_UPDATED)
+                .setClientContext(context);
+        enqueueEvent(event, FORCE_FLUSH);
+    }
+
+    @Override
+    public void notifyWindowBoundsChanged(int sessionId, @NonNull Rect bounds) {
+        final ContentCaptureEvent event =
+                new ContentCaptureEvent(sessionId, TYPE_WINDOW_BOUNDS_CHANGED)
+                        .setBounds(bounds);
+        enqueueEvent(event);
+    }
+
+    private List<ContentCaptureEvent> clearBufferEvents() {
+        final ArrayList<ContentCaptureEvent> bufferEvents = new ArrayList<>();
+        ContentCaptureEvent event;
+        while ((event = mEventProcessQueue.poll()) != null) {
+            bufferEvents.add(event);
+        }
+        return bufferEvents;
+    }
+
+    private void enqueueEvent(@NonNull final ContentCaptureEvent event) {
+        enqueueEvent(event, /* forceFlush */ false);
+    }
+
+    /**
+     * Enqueue the event into {@code mEventProcessBuffer} if it is not an urgent request. Otherwise,
+     * clear the buffer events then starting sending out current event.
+     */
+    private void enqueueEvent(@NonNull final ContentCaptureEvent event, boolean forceFlush) {
+        if (forceFlush) {
+            // The buffer events are cleared in the same thread first to prevent new events
+            // being added during the time of context switch. This would disrupt the sequence
+            // of events.
+            final List<ContentCaptureEvent> batchEvents = clearBufferEvents();
+            runOnContentCaptureThread(() -> {
+                for (int i = 0; i < batchEvents.size(); i++) {
+                    sendEvent(batchEvents.get(i));
+                }
+                sendEvent(event, /* forceFlush= */ true);
+            });
+        } else {
+            mEventProcessQueue.offer(event);
+        }
+    }
+
+    @Override
+    public void notifyContentCaptureEvents(
+            @NonNull SparseArray<ArrayList<Object>> contentCaptureEvents) {
+        runOnUiThread(() -> {
+            prepareViewStructures(contentCaptureEvents);
+            runOnContentCaptureThread(() ->
+                    notifyContentCaptureEventsImpl(contentCaptureEvents));
+        });
+    }
+
+    /**
+     * Traverse events and pre-process {@link View} events to {@link ViewStructureSession} events.
+     * If a {@link View} event is invalid, an empty {@link ViewStructureSession} will still be
+     * provided.
+     */
+    private void prepareViewStructures(
+            @NonNull SparseArray<ArrayList<Object>> contentCaptureEvents) {
+        for (int i = 0; i < contentCaptureEvents.size(); i++) {
+            int sessionId = contentCaptureEvents.keyAt(i);
+            ArrayList<Object> events = contentCaptureEvents.valueAt(i);
+            for_each_event: for (int j = 0; j < events.size(); j++) {
+                Object event = events.get(j);
+                if (event instanceof View) {
+                    View view = (View) event;
+                    ContentCaptureSession session = view.getContentCaptureSession();
+                    ViewStructureSession structureSession = new ViewStructureSession();
+
+                    // Replace the View event with ViewStructureSession no matter the data is
+                    // available or not. This is to ensure the sequence of the events are still
+                    // the same. Calls to notifyViewAppeared will check the availability later.
+                    events.set(j, structureSession);
+                    if (session == null) {
+                        Log.w(TAG, "no content capture session on view: " + view);
+                        continue for_each_event;
+                    }
+                    int actualId = session.getId();
+                    if (actualId != sessionId) {
+                        Log.w(TAG, "content capture session mismatch for view (" + view
+                                + "): was " + sessionId + " before, it's " + actualId + " now");
+                        continue for_each_event;
+                    }
+                    ViewStructure structure = session.newViewStructure(view);
+                    view.onProvideContentCaptureStructure(structure, /* flags= */ 0);
+
+                    structureSession.setSession(session);
+                    structureSession.setStructure(structure);
+                }
+            }
+        }
+    }
+
+    private void notifyContentCaptureEventsImpl(
+            @NonNull SparseArray<ArrayList<Object>> contentCaptureEvents) {
+        checkOnContentCaptureThread();
+        try {
+            if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
+                Trace.traceBegin(Trace.TRACE_TAG_VIEW, "notifyContentCaptureEvents");
+            }
+            for (int i = 0; i < contentCaptureEvents.size(); i++) {
+                int sessionId = contentCaptureEvents.keyAt(i);
+                internalNotifyViewTreeEvent(sessionId, /* started= */ true);
+                ArrayList<Object> events = contentCaptureEvents.valueAt(i);
+                for_each_event: for (int j = 0; j < events.size(); j++) {
+                    Object event = events.get(j);
+                    if (event instanceof AutofillId) {
+                        internalNotifyViewDisappeared(sessionId, (AutofillId) event);
+                    } else if (event instanceof ViewStructureSession viewStructureSession) {
+                        viewStructureSession.notifyViewAppeared();
+                    } else if (event instanceof Insets) {
+                        internalNotifyViewInsetsChanged(sessionId, (Insets) event);
+                    } else {
+                        Log.w(TAG, "invalid content capture event: " + event);
+                    }
+                }
+                internalNotifyViewTreeEvent(sessionId, /* started= */ false);
+            }
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
+        }
+    }
+
+    @Override
+    void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
+        super.dump(prefix, pw);
+
+        pw.print(prefix); pw.print("mContext: "); pw.println(mContext);
+        pw.print(prefix); pw.print("user: "); pw.println(mContext.getUserId());
+        if (mDirectServiceInterface != null) {
+            pw.print(prefix); pw.print("mDirectServiceInterface: ");
+            pw.println(mDirectServiceInterface);
+        }
+        pw.print(prefix); pw.print("mDisabled: "); pw.println(mDisabled.get());
+        pw.print(prefix); pw.print("isEnabled(): "); pw.println(isContentCaptureEnabled());
+        pw.print(prefix); pw.print("state: "); pw.println(getStateAsString(mState));
+        if (mApplicationToken != null) {
+            pw.print(prefix); pw.print("app token: "); pw.println(mApplicationToken);
+        }
+        if (mShareableActivityToken != null) {
+            pw.print(prefix); pw.print("sharable activity token: ");
+            pw.println(mShareableActivityToken);
+        }
+        if (mComponentName != null) {
+            pw.print(prefix); pw.print("component name: ");
+            pw.println(mComponentName.flattenToShortString());
+        }
+        if (mEvents != null && !mEvents.isEmpty()) {
+            final int numberEvents = mEvents.size();
+            pw.print(prefix); pw.print("buffered events: "); pw.print(numberEvents);
+            pw.print('/'); pw.println(mManager.mOptions.maxBufferSize);
+            if (sVerbose && numberEvents > 0) {
+                final String prefix3 = prefix + "  ";
+                for (int i = 0; i < numberEvents; i++) {
+                    final ContentCaptureEvent event = mEvents.get(i);
+                    pw.print(prefix3); pw.print(i); pw.print(": "); event.dump(pw);
+                    pw.println();
+                }
+            }
+            pw.print(prefix); pw.print("mNextFlushForTextChanged: ");
+            pw.println(mNextFlushForTextChanged);
+            pw.print(prefix); pw.print("flush frequency: ");
+            if (mNextFlushForTextChanged) {
+                pw.println(mManager.mOptions.textChangeFlushingFrequencyMs);
+            } else {
+                pw.println(mManager.mOptions.idleFlushingFrequencyMs);
+            }
+            pw.print(prefix); pw.print("next flush: ");
+            TimeUtils.formatDuration(mNextFlush - System.currentTimeMillis(), pw);
+            pw.print(" ("); pw.print(TimeUtils.logTimeOfDay(mNextFlush)); pw.println(")");
+        }
+        if (mFlushHistory != null) {
+            pw.print(prefix); pw.println("flush history:");
+            mFlushHistory.reverseDump(/* fd= */ null, pw, /* args= */ null); pw.println();
+        } else {
+            pw.print(prefix); pw.println("not logging flush history");
+        }
+
+        super.dump(prefix, pw);
+    }
+
+    /**
+     * Gets a string that can be used to identify the activity on logging statements.
+     */
+    private String getActivityName() {
+        return mComponentName == null
+                ? "pkg:" + mContext.getPackageName()
+                : "act:" + mComponentName.flattenToShortString();
+    }
+
+    @NonNull
+    private String getDebugState() {
+        return getActivityName() + " [state=" + getStateAsString(mState) + ", disabled="
+                + mDisabled.get() + "]";
+    }
+
+    @NonNull
+    private String getDebugState(@FlushReason int reason) {
+        return getDebugState() + ", reason=" + getFlushReasonAsString(reason);
+    }
+
+    private boolean isContentProtectionReceiverEnabled() {
+        return mManager.mOptions.contentProtectionOptions.enableReceiver;
+    }
+
+    private boolean isContentCaptureReceiverEnabled() {
+        return mManager.mOptions.enableReceiver;
+    }
+
+    private boolean isContentProtectionEnabled() {
+        // Should not be possible for mComponentName to be null here but check anyway
+        // Should not be possible for groups to be empty if receiver is enabled but check anyway
+        return mManager.mOptions.contentProtectionOptions.enableReceiver
+                && mManager.getContentProtectionEventBuffer() != null
+                && mComponentName != null
+                && (!mManager.mOptions.contentProtectionOptions.requiredGroups.isEmpty()
+                        || !mManager.mOptions.contentProtectionOptions.optionalGroups.isEmpty());
+    }
+
+    /**
+     * Checks that the current work is running on the assigned thread from {@code mHandler} and
+     * count the number of times running on the wrong thread.
+     *
+     * <p>It is not guaranteed that the callers always invoke function from a single thread.
+     * Therefore, accessing internal properties in {@link MainContentCaptureSession} should
+     * always delegate to the assigned thread from {@code mHandler} for synchronization.</p>
+     */
+    private void checkOnContentCaptureThread() {
+        final boolean onContentCaptureThread = mContentCaptureHandler.getLooper().isCurrentThread();
+        if (!onContentCaptureThread) {
+            mWrongThreadCount.incrementAndGet();
+            Log.e(TAG, "MainContentCaptureSession running on " + Thread.currentThread());
+        }
+    }
+
+    /** Reports number of times running on the wrong thread. */
+    private void reportWrongThreadMetric() {
+        Counter.logIncrement(
+                CONTENT_CAPTURE_WRONG_THREAD_METRIC_ID, mWrongThreadCount.getAndSet(0));
+    }
+
+    /**
+     * Ensures that {@code r} will be running on the assigned thread.
+     *
+     * <p>This is to prevent unnecessary delegation to Handler that results in fragmented runnable.
+     * </p>
+     */
+    private void runOnContentCaptureThread(@NonNull Runnable r) {
+        if (!mContentCaptureHandler.getLooper().isCurrentThread()) {
+            mContentCaptureHandler.post(r);
+        } else {
+            r.run();
+        }
+    }
+
+    private void clearAndRunOnContentCaptureThread(@NonNull Runnable r, int what) {
+        if (!mContentCaptureHandler.getLooper().isCurrentThread()) {
+            mContentCaptureHandler.removeMessages(what);
+            mContentCaptureHandler.post(r);
+        } else {
+            r.run();
+        }
+    }
+
+    private void runOnUiThread(@NonNull Runnable r) {
+        if (mUiHandler.getLooper().isCurrentThread()) {
+            r.run();
+        } else {
+            mUiHandler.post(r);
+        }
+    }
+
+    /**
+     * Holds {@link ContentCaptureSession} and related {@link ViewStructure} for processing.
+     */
+    private static final class ViewStructureSession {
+        @Nullable private ContentCaptureSession mSession;
+        @Nullable private ViewStructure mStructure;
+
+        ViewStructureSession() {}
+
+        void setSession(@Nullable ContentCaptureSession session) {
+            this.mSession = session;
+        }
+
+        void setStructure(@Nullable ViewStructure struct) {
+            this.mStructure = struct;
+        }
+
+        /**
+         * Calls {@link ContentCaptureSession#notifyViewAppeared(ViewStructure)} if the session and
+         * the view structure are available.
+         */
+        void notifyViewAppeared() {
+            if (mSession != null && mStructure != null) {
+                mSession.notifyViewAppeared(mStructure);
+            }
+        }
+    }
+}
diff --git a/core/java/android/view/flags/view_flags.aconfig b/core/java/android/view/flags/view_flags.aconfig
new file mode 100644
index 0000000..9f9b7b4
--- /dev/null
+++ b/core/java/android/view/flags/view_flags.aconfig
@@ -0,0 +1,17 @@
+package: "android.view.flags"
+
+flag {
+     name: "enable_surface_native_alloc_registration"
+     namespace: "toolkit"
+     description: "Feature flag for registering surfaces with the VM for faster cleanup"
+     bug: "306193257"
+}
+
+flag {
+    name: "enable_use_measure_cache_during_force_layout"
+    namespace: "toolkit"
+    description: "Enables using the measure cache during a view force layout from the second "
+      "onMeasure call onwards during the same traversal."
+    bug: "316170253"
+    is_fixed_read_only: true
+}
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index ac9ad2d..3bc02a6 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -354,7 +354,11 @@
      * @hide
      */
     public static void ensureDefaultInstanceForDefaultDisplayIfNecessary() {
-        forContextInternal(Display.DEFAULT_DISPLAY, Looper.getMainLooper());
+        // Skip this call if we are in system_server, as the system code should not use this
+        // deprecated instance.
+        if (!ActivityThread.isSystem()) {
+            forContextInternal(Display.DEFAULT_DISPLAY, Looper.getMainLooper());
+        }
     }
 
     private static final Object sLock = new Object();
@@ -2320,6 +2324,15 @@
      * @hide
      */
     public boolean hideSoftInputFromView(@NonNull View view, @HideFlags int flags) {
+        final boolean isFocusedAndWindowFocused = view.hasWindowFocus() && view.isFocused();
+        synchronized (mH) {
+            if (!isFocusedAndWindowFocused && !hasServedByInputMethodLocked(view)) {
+                // Fail early if the view is not focused and not served
+                // to avoid logging many erroneous calls.
+                return false;
+            }
+        }
+
         final var reason = SoftInputShowHideReason.HIDE_SOFT_INPUT_FROM_VIEW;
         final ImeTracker.Token statsToken = ImeTracker.forLogging().onRequestHide(
                 null /* component */, Process.myUid(),
diff --git a/core/java/android/view/inputmethod/flags.aconfig b/core/java/android/view/inputmethod/flags.aconfig
index dc6aa6c..bb7677d6 100644
--- a/core/java/android/view/inputmethod/flags.aconfig
+++ b/core/java/android/view/inputmethod/flags.aconfig
@@ -38,3 +38,12 @@
     description: "Feature flag for supporting stylus handwriting delegation from RemoteViews on the home screen"
     bug: "279959705"
 }
+
+flag {
+    name: "use_handwriting_listener_for_tooltype"
+    namespace: "input_method"
+    description: "Feature flag for using handwriting spy for determining pointer toolType."
+    bug: "309554999"
+    is_fixed_read_only: true
+}
+
diff --git a/core/java/android/view/textclassifier/TextClassifier.java b/core/java/android/view/textclassifier/TextClassifier.java
index ef50045..1d2f653 100644
--- a/core/java/android/view/textclassifier/TextClassifier.java
+++ b/core/java/android/view/textclassifier/TextClassifier.java
@@ -16,6 +16,9 @@
 
 package android.view.textclassifier;
 
+import static android.service.notification.Flags.FLAG_REDACT_SENSITIVE_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS;
+
+import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
@@ -108,6 +111,9 @@
     String TYPE_DATE_TIME = "datetime";
     /** Flight number in IATA format. */
     String TYPE_FLIGHT_NUMBER = "flight";
+    /** One-time login codes */
+    @FlaggedApi(FLAG_REDACT_SENSITIVE_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS)
+    String TYPE_OTP_CODE = "otp_code";
     /**
      * Word that users may be interested to look up for meaning.
      * @hide
@@ -126,7 +132,8 @@
             TYPE_DATE,
             TYPE_DATE_TIME,
             TYPE_FLIGHT_NUMBER,
-            TYPE_DICTIONARY
+            TYPE_DICTIONARY,
+            TYPE_OTP_CODE
     })
     @interface EntityType {}
 
diff --git a/core/java/android/webkit/URLUtil.java b/core/java/android/webkit/URLUtil.java
index c7609a6..828ec26 100644
--- a/core/java/android/webkit/URLUtil.java
+++ b/core/java/android/webkit/URLUtil.java
@@ -44,9 +44,7 @@
     static final String PROXY_BASE = "file:///cookieless_proxy/";
     static final String CONTENT_BASE = "content:";
 
-    /**
-     * Cleans up (if possible) user-entered web addresses
-     */
+    /** Cleans up (if possible) user-entered web addresses */
     public static String guessUrl(String inUrl) {
 
         String retVal = inUrl;
@@ -86,8 +84,12 @@
         return webAddress.toString();
     }
 
-    public static String composeSearchUrl(String inQuery, String template,
-                                          String queryPlaceHolder) {
+    /**
+     * Inserts the {@code inQuery} in the {@code template} after URL-encoding it. The encoded query
+     * will replace the {@code queryPlaceHolder}.
+     */
+    public static String composeSearchUrl(
+            String inQuery, String template, String queryPlaceHolder) {
         int placeHolderIndex = template.indexOf(queryPlaceHolder);
         if (placeHolderIndex < 0) {
             return null;
@@ -104,8 +106,7 @@
             return null;
         }
 
-        buffer.append(template.substring(
-                placeHolderIndex + queryPlaceHolder.length()));
+        buffer.append(template.substring(placeHolderIndex + queryPlaceHolder.length()));
 
         return buffer.toString();
     }
@@ -123,8 +124,7 @@
             byte b = url[i];
             if (b == '%') {
                 if (url.length - i > 2) {
-                    b = (byte) (parseHex(url[i + 1]) * 16
-                            + parseHex(url[i + 2]));
+                    b = (byte) (parseHex(url[i + 1]) * 16 + parseHex(url[i + 2]));
                     i += 2;
                 } else {
                     throw new IllegalArgumentException("Invalid format");
@@ -189,8 +189,8 @@
     }
 
     /**
-     * @return {@code true} if the url is a proxy url to allow cookieless network
-     * requests from a file url.
+     * @return {@code true} if the url is a proxy url to allow cookieless network requests from a
+     *     file url.
      * @deprecated Cookieless proxy is no longer supported.
      */
     @Deprecated
@@ -202,9 +202,10 @@
      * @return {@code true} if the url is a local file.
      */
     public static boolean isFileUrl(String url) {
-        return (null != url) && (url.startsWith(FILE_BASE) &&
-                                 !url.startsWith(ASSET_BASE) &&
-                                 !url.startsWith(PROXY_BASE));
+        return (null != url)
+                && (url.startsWith(FILE_BASE)
+                        && !url.startsWith(ASSET_BASE)
+                        && !url.startsWith(PROXY_BASE));
     }
 
     /**
@@ -232,18 +233,18 @@
      * @return {@code true} if the url is an http: url.
      */
     public static boolean isHttpUrl(String url) {
-        return (null != url) &&
-               (url.length() > 6) &&
-               url.substring(0, 7).equalsIgnoreCase("http://");
+        return (null != url)
+                && (url.length() > 6)
+                && url.substring(0, 7).equalsIgnoreCase("http://");
     }
 
     /**
      * @return {@code true} if the url is an https: url.
      */
     public static boolean isHttpsUrl(String url) {
-        return (null != url) &&
-               (url.length() > 7) &&
-               url.substring(0, 8).equalsIgnoreCase("https://");
+        return (null != url)
+                && (url.length() > 7)
+                && url.substring(0, 8).equalsIgnoreCase("https://");
     }
 
     /**
@@ -271,19 +272,17 @@
             return false;
         }
 
-        return (isAssetUrl(url) ||
-                isResourceUrl(url) ||
-                isFileUrl(url) ||
-                isAboutUrl(url) ||
-                isHttpUrl(url) ||
-                isHttpsUrl(url) ||
-                isJavaScriptUrl(url) ||
-                isContentUrl(url));
+        return (isAssetUrl(url)
+                || isResourceUrl(url)
+                || isFileUrl(url)
+                || isAboutUrl(url)
+                || isHttpUrl(url)
+                || isHttpsUrl(url)
+                || isJavaScriptUrl(url)
+                || isContentUrl(url));
     }
 
-    /**
-     * Strips the url of the anchor.
-     */
+    /** Strips the url of the anchor. */
     public static String stripAnchor(String url) {
         int anchorIndex = url.indexOf('#');
         if (anchorIndex != -1) {
@@ -293,19 +292,16 @@
     }
 
     /**
-     * Guesses canonical filename that a download would have, using
-     * the URL and contentDisposition. File extension, if not defined,
-     * is added based on the mimetype
+     * Guesses canonical filename that a download would have, using the URL and contentDisposition.
+     * File extension, if not defined, is added based on the mimetype
+     *
      * @param url Url to the content
      * @param contentDisposition Content-Disposition HTTP header or {@code null}
      * @param mimeType Mime-type of the content or {@code null}
-     *
      * @return suggested filename
      */
     public static final String guessFileName(
-            String url,
-            @Nullable String contentDisposition,
-            @Nullable String mimeType) {
+            String url, @Nullable String contentDisposition, @Nullable String mimeType) {
         String filename = null;
         String extension = null;
 
@@ -369,8 +365,9 @@
                 // Compare the last segment of the extension against the mime type.
                 // If there's a mismatch, discard the entire extension.
                 int lastDotIndex = filename.lastIndexOf('.');
-                String typeFromExt = MimeTypeMap.getSingleton().getMimeTypeFromExtension(
-                        filename.substring(lastDotIndex + 1));
+                String typeFromExt =
+                        MimeTypeMap.getSingleton()
+                                .getMimeTypeFromExtension(filename.substring(lastDotIndex + 1));
                 if (typeFromExt != null && !typeFromExt.equalsIgnoreCase(mimeType)) {
                     extension = MimeTypeMap.getSingleton().getExtensionFromMimeType(mimeType);
                     if (extension != null) {
@@ -389,17 +386,17 @@
 
     /** Regex used to parse content-disposition headers */
     private static final Pattern CONTENT_DISPOSITION_PATTERN =
-            Pattern.compile("attachment;\\s*filename\\s*=\\s*(\"?)([^\"]*)\\1\\s*$",
-            Pattern.CASE_INSENSITIVE);
+            Pattern.compile(
+                    "attachment;\\s*filename\\s*=\\s*(\"?)([^\"]*)\\1\\s*$",
+                    Pattern.CASE_INSENSITIVE);
 
     /**
-     * Parse the Content-Disposition HTTP Header. The format of the header
-     * is defined here: http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html
-     * This header provides a filename for content that is going to be
-     * downloaded to the file system. We only support the attachment type.
-     * Note that RFC 2616 specifies the filename value must be double-quoted.
-     * Unfortunately some servers do not quote the value so to maintain
-     * consistent behaviour with other browsers, we allow unquoted values too.
+     * Parse the Content-Disposition HTTP Header. The format of the header is defined here:
+     * http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html This header provides a filename for
+     * content that is going to be downloaded to the file system. We only support the attachment
+     * type. Note that RFC 2616 specifies the filename value must be double-quoted. Unfortunately
+     * some servers do not quote the value so to maintain consistent behaviour with other browsers,
+     * we allow unquoted values too.
      */
     @UnsupportedAppUsage
     static String parseContentDisposition(String contentDisposition) {
@@ -409,7 +406,7 @@
                 return m.group(2);
             }
         } catch (IllegalStateException ex) {
-             // This function is defined as returning null when it can't parse the header
+            // This function is defined as returning null when it can't parse the header
         }
         return null;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileUser.kt b/core/java/android/window/ActivityWindowInfo.aidl
similarity index 64%
copy from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileUser.kt
copy to core/java/android/window/ActivityWindowInfo.aidl
index efc7431..d0526bc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileUser.kt
+++ b/core/java/android/window/ActivityWindowInfo.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,12 +14,10 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.custom.di.bound
+package android.window;
 
-import javax.inject.Qualifier
-
-/** User associated with current custom tile binding. */
-@Qualifier
-@MustBeDocumented
-@Retention(AnnotationRetention.RUNTIME)
-annotation class CustomTileUser
+/**
+ * Stores information about a particular Activity Window.
+ * @hide
+ */
+parcelable ActivityWindowInfo;
diff --git a/core/java/android/window/ActivityWindowInfo.java b/core/java/android/window/ActivityWindowInfo.java
new file mode 100644
index 0000000..946bb82
--- /dev/null
+++ b/core/java/android/window/ActivityWindowInfo.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.window;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.graphics.Rect;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Stores the window information about a particular Activity.
+ * It contains the info that is not part of {@link android.content.res.Configuration}.
+ * @hide
+ */
+public final class ActivityWindowInfo implements Parcelable {
+
+    private boolean mIsEmbedded;
+
+    @NonNull
+    private final Rect mTaskBounds = new Rect();
+
+    @NonNull
+    private final Rect mTaskFragmentBounds = new Rect();
+
+    public ActivityWindowInfo() {}
+
+    public ActivityWindowInfo(@NonNull ActivityWindowInfo info) {
+        set(info);
+    }
+
+    /** Copies fields from {@code info}. */
+    public void set(@NonNull ActivityWindowInfo info) {
+        set(info.mIsEmbedded, info.mTaskBounds, info.mTaskFragmentBounds);
+    }
+
+    /** Sets to the given values. */
+    public void set(boolean isEmbedded, @NonNull Rect taskBounds,
+            @NonNull Rect taskFragmentBounds) {
+        mIsEmbedded = isEmbedded;
+        mTaskBounds.set(taskBounds);
+        mTaskFragmentBounds.set(taskFragmentBounds);
+    }
+
+    /**
+     * Whether this activity is embedded, which means it is a TaskFragment that doesn't fill the
+     * leaf Task.
+     */
+    public boolean isEmbedded() {
+        return mIsEmbedded;
+    }
+
+    /**
+     * The bounds of the leaf Task window in display space.
+     */
+    @NonNull
+    public Rect getTaskBounds() {
+        return mTaskBounds;
+    }
+
+    /**
+     * The bounds of the leaf TaskFragment window in display space.
+     * This can be referring to the bounds of the same window as {@link #getTaskBounds()} when
+     * the activity is not embedded.
+     */
+    @NonNull
+    public Rect getTaskFragmentBounds() {
+        return mTaskFragmentBounds;
+    }
+
+    private ActivityWindowInfo(@NonNull Parcel in) {
+        mIsEmbedded = in.readBoolean();
+        mTaskBounds.readFromParcel(in);
+        mTaskFragmentBounds.readFromParcel(in);
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeBoolean(mIsEmbedded);
+        mTaskBounds.writeToParcel(dest, flags);
+        mTaskFragmentBounds.writeToParcel(dest, flags);
+    }
+
+    @NonNull
+    public static final Creator<ActivityWindowInfo> CREATOR =
+            new Creator<>() {
+                @Override
+                public ActivityWindowInfo createFromParcel(@NonNull Parcel in) {
+                    return new ActivityWindowInfo(in);
+                }
+
+                @Override
+                public ActivityWindowInfo[] newArray(int size) {
+                    return new ActivityWindowInfo[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public boolean equals(@Nullable Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        final ActivityWindowInfo other = (ActivityWindowInfo) o;
+        return mIsEmbedded == other.mIsEmbedded
+                && mTaskBounds.equals(other.mTaskBounds)
+                && mTaskFragmentBounds.equals(other.mTaskFragmentBounds);
+    }
+
+    @Override
+    public int hashCode() {
+        int result = 17;
+        result = 31 * result + (mIsEmbedded ? 1 : 0);
+        result = 31 * result + mTaskBounds.hashCode();
+        result = 31 * result + mTaskFragmentBounds.hashCode();
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        return "ActivityWindowInfo{isEmbedded=" + mIsEmbedded
+                + ", taskBounds=" + mTaskBounds
+                + ", taskFragmentBounds=" + mTaskFragmentBounds
+                + "}";
+    }
+}
diff --git a/core/java/android/window/SplashScreenView.java b/core/java/android/window/SplashScreenView.java
index bdaad2b..473b814 100644
--- a/core/java/android/window/SplashScreenView.java
+++ b/core/java/android/window/SplashScreenView.java
@@ -47,6 +47,7 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.Window;
+import android.view.WindowManager;
 import android.widget.FrameLayout;
 import android.widget.ImageView;
 
@@ -337,7 +338,14 @@
                         "SplashScreenView");
                 ImageView imageView = new ImageView(viewContext);
                 imageView.setBackground(mIconDrawable);
-                viewHost.setView(imageView, mIconSize, mIconSize);
+                final int windowFlag = WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
+                        | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+                        | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
+                final WindowManager.LayoutParams lp =
+                        new WindowManager.LayoutParams(mIconSize, mIconSize,
+                                WindowManager.LayoutParams.TYPE_APPLICATION, windowFlag,
+                                PixelFormat.TRANSPARENT);
+                viewHost.setView(imageView, lp);
                 SurfaceControlViewHost.SurfacePackage surfacePackage = viewHost.getSurfacePackage();
                 surfaceView.setChildSurfacePackage(surfacePackage);
                 view.mSurfacePackage = surfacePackage;
diff --git a/core/java/android/window/TaskFragmentOperation.java b/core/java/android/window/TaskFragmentOperation.java
index 0ec9ffe..acc6a74 100644
--- a/core/java/android/window/TaskFragmentOperation.java
+++ b/core/java/android/window/TaskFragmentOperation.java
@@ -120,6 +120,11 @@
      */
     public static final int OP_TYPE_REMOVE_TASK_FRAGMENT_DECOR_SURFACE = 15;
 
+    /**
+     * Applies dimming on the parent Task which could cross two TaskFragments.
+     */
+    public static final int OP_TYPE_SET_DIM_ON_TASK = 16;
+
     @IntDef(prefix = { "OP_TYPE_" }, value = {
             OP_TYPE_UNKNOWN,
             OP_TYPE_CREATE_TASK_FRAGMENT,
@@ -138,6 +143,7 @@
             OP_TYPE_REORDER_TO_TOP_OF_TASK,
             OP_TYPE_CREATE_TASK_FRAGMENT_DECOR_SURFACE,
             OP_TYPE_REMOVE_TASK_FRAGMENT_DECOR_SURFACE,
+            OP_TYPE_SET_DIM_ON_TASK,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface OperationType {}
@@ -165,12 +171,14 @@
 
     private final boolean mIsolatedNav;
 
+    private final boolean mDimOnTask;
+
     private TaskFragmentOperation(@OperationType int opType,
             @Nullable TaskFragmentCreationParams taskFragmentCreationParams,
             @Nullable IBinder activityToken, @Nullable Intent activityIntent,
             @Nullable Bundle bundle, @Nullable IBinder secondaryFragmentToken,
             @Nullable TaskFragmentAnimationParams animationParams,
-            boolean isolatedNav) {
+            boolean isolatedNav, boolean dimOnTask) {
         mOpType = opType;
         mTaskFragmentCreationParams = taskFragmentCreationParams;
         mActivityToken = activityToken;
@@ -179,6 +187,7 @@
         mSecondaryFragmentToken = secondaryFragmentToken;
         mAnimationParams = animationParams;
         mIsolatedNav = isolatedNav;
+        mDimOnTask = dimOnTask;
     }
 
     private TaskFragmentOperation(Parcel in) {
@@ -190,6 +199,7 @@
         mSecondaryFragmentToken = in.readStrongBinder();
         mAnimationParams = in.readTypedObject(TaskFragmentAnimationParams.CREATOR);
         mIsolatedNav = in.readBoolean();
+        mDimOnTask = in.readBoolean();
     }
 
     @Override
@@ -202,6 +212,7 @@
         dest.writeStrongBinder(mSecondaryFragmentToken);
         dest.writeTypedObject(mAnimationParams, flags);
         dest.writeBoolean(mIsolatedNav);
+        dest.writeBoolean(mDimOnTask);
     }
 
     @NonNull
@@ -282,6 +293,13 @@
         return mIsolatedNav;
     }
 
+    /**
+     * Returns whether the dim layer should apply on the parent Task.
+     */
+    public boolean isDimOnTask() {
+        return mDimOnTask;
+    }
+
     @Override
     public String toString() {
         final StringBuilder sb = new StringBuilder();
@@ -305,6 +323,7 @@
             sb.append(", animationParams=").append(mAnimationParams);
         }
         sb.append(", isolatedNav=").append(mIsolatedNav);
+        sb.append(", dimOnTask=").append(mDimOnTask);
 
         sb.append('}');
         return sb.toString();
@@ -313,7 +332,7 @@
     @Override
     public int hashCode() {
         return Objects.hash(mOpType, mTaskFragmentCreationParams, mActivityToken, mActivityIntent,
-                mBundle, mSecondaryFragmentToken, mAnimationParams, mIsolatedNav);
+                mBundle, mSecondaryFragmentToken, mAnimationParams, mIsolatedNav, mDimOnTask);
     }
 
     @Override
@@ -329,7 +348,8 @@
                 && Objects.equals(mBundle, other.mBundle)
                 && Objects.equals(mSecondaryFragmentToken, other.mSecondaryFragmentToken)
                 && Objects.equals(mAnimationParams, other.mAnimationParams)
-                && mIsolatedNav == other.mIsolatedNav;
+                && mIsolatedNav == other.mIsolatedNav
+                && mDimOnTask == other.mDimOnTask;
     }
 
     @Override
@@ -363,6 +383,8 @@
 
         private boolean mIsolatedNav;
 
+        private boolean mDimOnTask;
+
         /**
          * @param opType the {@link OperationType} of this {@link TaskFragmentOperation}.
          */
@@ -435,13 +457,22 @@
         }
 
         /**
+         * Sets the dimming to apply on the parent Task if any.
+         */
+        @NonNull
+        public Builder setDimOnTask(boolean dimOnTask) {
+            mDimOnTask = dimOnTask;
+            return this;
+        }
+
+        /**
          * Constructs the {@link TaskFragmentOperation}.
          */
         @NonNull
         public TaskFragmentOperation build() {
             return new TaskFragmentOperation(mOpType, mTaskFragmentCreationParams, mActivityToken,
                     mActivityIntent, mBundle, mSecondaryFragmentToken, mAnimationParams,
-                    mIsolatedNav);
+                    mIsolatedNav, mDimOnTask);
         }
     }
 }
diff --git a/core/java/android/window/TransitionFilter.java b/core/java/android/window/TransitionFilter.java
index e62d5c9..64fe66e 100644
--- a/core/java/android/window/TransitionFilter.java
+++ b/core/java/android/window/TransitionFilter.java
@@ -212,7 +212,9 @@
                         continue;
                     }
                 }
-                if (!matchesTopActivity(change.getTaskInfo())) continue;
+                if (!matchesTopActivity(change.getTaskInfo(), change.getActivityComponent())) {
+                    continue;
+                }
                 if (mModes != null) {
                     boolean pass = false;
                     for (int m = 0; m < mModes.length; ++m) {
@@ -234,11 +236,15 @@
             return false;
         }
 
-        private boolean matchesTopActivity(ActivityManager.RunningTaskInfo info) {
+        private boolean matchesTopActivity(ActivityManager.RunningTaskInfo taskInfo,
+                @Nullable ComponentName activityComponent) {
             if (mTopActivity == null) return true;
-            if (info == null) return false;
-            final ComponentName component = info.topActivity;
-            return mTopActivity.equals(component);
+            if (activityComponent != null) {
+                return mTopActivity.equals(activityComponent);
+            } else if (taskInfo != null) {
+                return mTopActivity.equals(taskInfo.topActivity);
+            }
+            return false;
         }
 
         /** Check if the request matches this filter. It may generate false positives */
@@ -247,7 +253,7 @@
             if (mActivityType == ACTIVITY_TYPE_UNDEFINED) return true;
             return request.getTriggerTask() != null
                     && request.getTriggerTask().getActivityType() == mActivityType
-                    && matchesTopActivity(request.getTriggerTask());
+                    && matchesTopActivity(request.getTriggerTask(), null /* activityCmp */);
         }
 
         @Override
diff --git a/core/java/android/window/TransitionInfo.java b/core/java/android/window/TransitionInfo.java
index 7c9340e..bceb872 100644
--- a/core/java/android/window/TransitionInfo.java
+++ b/core/java/android/window/TransitionInfo.java
@@ -44,6 +44,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
+import android.content.ComponentName;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.hardware.HardwareBuffer;
@@ -635,6 +636,7 @@
         private @ColorInt int mBackgroundColor;
         private SurfaceControl mSnapshot = null;
         private float mSnapshotLuma;
+        private ComponentName mActivityComponent = null;
 
         public Change(@Nullable WindowContainerToken container, @NonNull SurfaceControl leash) {
             mContainer = container;
@@ -663,6 +665,7 @@
             mBackgroundColor = in.readInt();
             mSnapshot = in.readTypedObject(SurfaceControl.CREATOR);
             mSnapshotLuma = in.readFloat();
+            mActivityComponent = in.readTypedObject(ComponentName.CREATOR);
         }
 
         private Change localRemoteCopy() {
@@ -685,6 +688,7 @@
             out.mBackgroundColor = mBackgroundColor;
             out.mSnapshot = mSnapshot != null ? new SurfaceControl(mSnapshot, "localRemote") : null;
             out.mSnapshotLuma = mSnapshotLuma;
+            out.mActivityComponent = mActivityComponent;
             return out;
         }
 
@@ -780,6 +784,11 @@
             mSnapshotLuma = luma;
         }
 
+        /** Sets the component-name of the container. Container must be an Activity. */
+        public void setActivityComponent(@Nullable ComponentName component) {
+            mActivityComponent = component;
+        }
+
         /** @return the container that is changing. May be null if non-remotable (eg. activity) */
         @Nullable
         public WindowContainerToken getContainer() {
@@ -913,6 +922,12 @@
             return mSnapshotLuma;
         }
 
+        /** @return the component-name of this container (if it is an activity). */
+        @Nullable
+        public ComponentName getActivityComponent() {
+            return mActivityComponent;
+        }
+
         /** @hide */
         @Override
         public void writeToParcel(@NonNull Parcel dest, int flags) {
@@ -936,6 +951,7 @@
             dest.writeInt(mBackgroundColor);
             dest.writeTypedObject(mSnapshot, flags);
             dest.writeFloat(mSnapshotLuma);
+            dest.writeTypedObject(mActivityComponent, flags);
         }
 
         @NonNull
@@ -994,6 +1010,10 @@
             if (mLastParent != null) {
                 sb.append(" lastParent="); sb.append(mLastParent);
             }
+            if (mActivityComponent != null) {
+                sb.append(" component=");
+                sb.append(mActivityComponent.flattenToShortString());
+            }
             sb.append('}');
             return sb.toString();
         }
diff --git a/core/java/android/window/WindowOnBackInvokedDispatcher.java b/core/java/android/window/WindowOnBackInvokedDispatcher.java
index 6a8ca33..86804c6 100644
--- a/core/java/android/window/WindowOnBackInvokedDispatcher.java
+++ b/core/java/android/window/WindowOnBackInvokedDispatcher.java
@@ -174,6 +174,21 @@
         }
     }
 
+    /**
+     * Indicates if the dispatcher is actively dispatching to a callback.
+     */
+    public boolean isDispatching() {
+        return mIsDispatching;
+    }
+
+    private void onStartDispatching() {
+        mIsDispatching = true;
+    }
+
+    private void onStopDispatching() {
+        mIsDispatching = false;
+    }
+
     private void sendCancelledIfInProgress(@NonNull OnBackInvokedCallback callback) {
         boolean isInProgress = mProgressAnimator.isBackAnimationInProgress();
         if (isInProgress && callback instanceof OnBackAnimationCallback) {
@@ -231,7 +246,7 @@
                                     .ImeOnBackInvokedCallback
                                 ? ((ImeOnBackInvokedDispatcher.ImeOnBackInvokedCallback)
                                         callback).getIOnBackInvokedCallback()
-                                : new OnBackInvokedCallbackWrapper(callback);
+                                : new OnBackInvokedCallbackWrapper(callback, this);
                 callbackInfo = new OnBackInvokedCallbackInfo(
                         iCallback,
                         priority,
@@ -258,6 +273,7 @@
 
     @NonNull
     private static final BackProgressAnimator mProgressAnimator = new BackProgressAnimator();
+    private boolean mIsDispatching = false;
 
     /**
      * The {@link Context} in ViewRootImp and Activity could be different, this will make sure it
@@ -317,18 +333,33 @@
             }
         }
         final CallbackRef mCallbackRef;
+        /**
+         * The dispatcher this callback is registered with.
+         * This can be null for callbacks on {@link ImeOnBackInvokedDispatcher} because they are
+         * forwarded and registered on the app's {@link WindowOnBackInvokedDispatcher}. */
+        @Nullable
+        private final WindowOnBackInvokedDispatcher mDispatcher;
 
-        OnBackInvokedCallbackWrapper(@NonNull OnBackInvokedCallback callback) {
+        OnBackInvokedCallbackWrapper(
+                @NonNull OnBackInvokedCallback callback,
+                WindowOnBackInvokedDispatcher dispatcher) {
             mCallbackRef = new CallbackRef(callback, true /* useWeakRef */);
+            mDispatcher = dispatcher;
         }
 
-        OnBackInvokedCallbackWrapper(@NonNull OnBackInvokedCallback callback, boolean useWeakRef) {
+        OnBackInvokedCallbackWrapper(
+                @NonNull OnBackInvokedCallback callback,
+                boolean useWeakRef) {
             mCallbackRef = new CallbackRef(callback, useWeakRef);
+            mDispatcher = null;
         }
 
         @Override
         public void onBackStarted(BackMotionEvent backEvent) {
             Handler.getMain().post(() -> {
+                if (mDispatcher != null) {
+                    mDispatcher.onStartDispatching();
+                }
                 final OnBackAnimationCallback callback = getBackAnimationCallback();
                 if (callback != null) {
                     mProgressAnimator.onBackStarted(backEvent, event ->
@@ -353,6 +384,9 @@
         @Override
         public void onBackCancelled() {
             Handler.getMain().post(() -> {
+                if (mDispatcher != null) {
+                    mDispatcher.onStopDispatching();
+                }
                 mProgressAnimator.onBackCancelled(() -> {
                     final OnBackAnimationCallback callback = getBackAnimationCallback();
                     if (callback != null) {
@@ -365,6 +399,9 @@
         @Override
         public void onBackInvoked() throws RemoteException {
             Handler.getMain().post(() -> {
+                if (mDispatcher != null) {
+                    mDispatcher.onStopDispatching();
+                }
                 boolean isInProgress = mProgressAnimator.isBackAnimationInProgress();
                 mProgressAnimator.reset();
                 final OnBackInvokedCallback callback = mCallbackRef.get();
diff --git a/core/java/android/window/flags/wallpaper_manager.aconfig b/core/java/android/window/flags/wallpaper_manager.aconfig
index f03c993..ea9da96 100644
--- a/core/java/android/window/flags/wallpaper_manager.aconfig
+++ b/core/java/android/window/flags/wallpaper_manager.aconfig
@@ -13,3 +13,10 @@
   description: "Support storing different wallpaper crops for different display dimensions. Only effective after rebooting."
   bug: "281648899"
 }
+
+flag {
+  name: "no_consecutive_visibility_events"
+  namespace: "systemui"
+  description: "Prevent the system from sending consecutive onVisibilityChanged(false) events."
+  bug: "285631818"
+}
\ No newline at end of file
diff --git a/core/java/android/window/flags/window_surfaces.aconfig b/core/java/android/window/flags/window_surfaces.aconfig
index 56df493..3794c5d 100644
--- a/core/java/android/window/flags/window_surfaces.aconfig
+++ b/core/java/android/window/flags/window_surfaces.aconfig
@@ -63,4 +63,12 @@
     description: "Enable trustedPresentationListener on windows public API"
     is_fixed_read_only: true
     bug: "278027319"
-}
\ No newline at end of file
+}
+
+flag {
+    namespace: "window_surfaces"
+    name: "sdk_desired_present_time"
+    description: "Feature flag for the new SDK API to set desired present time"
+    is_fixed_read_only: true
+    bug: "295038072"
+}
diff --git a/core/java/android/window/flags/windowing_frontend.aconfig b/core/java/android/window/flags/windowing_frontend.aconfig
index 216acdc..bb16ad2 100644
--- a/core/java/android/window/flags/windowing_frontend.aconfig
+++ b/core/java/android/window/flags/windowing_frontend.aconfig
@@ -38,6 +38,14 @@
 }
 
 flag {
+  name: "draw_magnifier_border_outside_wmlock"
+  namespace: "windowing_frontend"
+  description: "Avoid holding WM locks for a long time when executing lockCanvas"
+  bug: "316075123"
+  is_fixed_read_only: true
+}
+
+flag {
   name: "introduce_smoother_dimmer"
   namespace: "windowing_frontend"
   description: "Refactor dim to fix flickers"
@@ -64,7 +72,7 @@
     name: "predictive_back_system_animations"
     namespace: "systemui"
     description: "Predictive back for system animations"
-    bug: "309545085"
+    bug: "319421778"
     is_fixed_read_only: true
 }
 
@@ -74,4 +82,12 @@
     description: "Enable record activity snapshot by default"
     bug: "259497289"
     is_fixed_read_only: true
+}
+
+flag {
+    name: "supports_multi_instance_system_ui"
+    namespace: "multitasking"
+    description: "Feature flag to enable a multi-instance system ui component property."
+    bug: "262864589"
+    is_fixed_read_only: true
 }
\ No newline at end of file
diff --git a/core/java/android/window/flags/windowing_sdk.aconfig b/core/java/android/window/flags/windowing_sdk.aconfig
index 933cc49..f743ab7 100644
--- a/core/java/android/window/flags/windowing_sdk.aconfig
+++ b/core/java/android/window/flags/windowing_sdk.aconfig
@@ -2,13 +2,6 @@
 
 # Project link: https://gantry.corp.google.com/projects/android_platform_windowing_sdk/changes
 
-flag {
-    namespace: "windowing_sdk"
-    name: "sync_window_config_update_flag"
-    description: "Whether the feature to sync different window-related config updates is enabled"
-    bug: "260873529"
-}
-
 # Using a fixed read only flag because there are ClientTransaction scheduling before
 # WindowManagerService creation.
 flag {
@@ -35,13 +28,6 @@
 
 flag {
     namespace: "windowing_sdk"
-    name: "window_state_resize_item_flag"
-    description: "Whether to dispatch window resize through ClientTransaction is enabled"
-    bug: "301870955"
-}
-
-flag {
-    namespace: "windowing_sdk"
     name: "fullscreen_dim_flag"
     description: "Whether to allow showing fullscreen dim on ActivityEmbedding split"
     bug: "253533308"
@@ -52,4 +38,12 @@
     name: "activity_embedding_interactive_divider_flag"
     description: "Whether the interactive divider feature is enabled"
     bug: "293654166"
+}
+
+flag {
+    namespace: "windowing_sdk"
+    name: "activity_window_info_flag"
+    description: "To dispatch ActivityWindowInfo through ClientTransaction"
+    bug: "287582673"
+    is_fixed_read_only: true
 }
\ No newline at end of file
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 7534d29..7dcbbea 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -249,6 +249,7 @@
 
     private UserHandle mCloneProfileUserHandle;
     private UserHandle mTabOwnerUserHandleForLaunch;
+    private UserHandle mPrivateProfileUserHandle;
 
     protected final LatencyTracker mLatencyTracker = getLatencyTracker();
 
@@ -441,6 +442,7 @@
         mPersonalProfileUserHandle = fetchPersonalProfileUserHandle();
         mWorkProfileUserHandle = fetchWorkProfileUserProfile();
         mCloneProfileUserHandle = fetchCloneProfileUserHandle();
+        mPrivateProfileUserHandle = fetchPrivateProfileUserHandle();
         mTabOwnerUserHandleForLaunch = fetchTabOwnerUserHandleForLaunch();
 
         // The last argument of createResolverListAdapter is whether to do special handling
@@ -648,7 +650,8 @@
                 initialIntents,
                 rList,
                 filterLastUsed,
-                /* userHandle */ getPersonalProfileUserHandle());
+                getPersonalProfileUserHandle());
+
         QuietModeManager quietModeManager = createQuietModeManager();
         return new ResolverMultiProfilePagerAdapter(
                 /* context */ this,
@@ -747,6 +750,9 @@
     }
 
     protected UserHandle getPersonalProfileUserHandle() {
+        if (privateSpaceEnabled() && isLaunchedAsPrivateProfile()){
+            return mPrivateProfileUserHandle;
+        }
         return mPersonalProfileUserHandle;
     }
     protected @Nullable UserHandle getWorkProfileUserHandle() {
@@ -761,6 +767,10 @@
         return mTabOwnerUserHandleForLaunch;
     }
 
+    protected UserHandle getPrivateProfileUserHandle() {
+        return mPrivateProfileUserHandle;
+    }
+
     protected UserHandle fetchPersonalProfileUserHandle() {
         // ActivityManager.getCurrentUser() refers to the current Foreground user. When clone/work
         // profile is active, we always make the personal tab from the foreground user.
@@ -795,12 +805,28 @@
         return mCloneProfileUserHandle;
     }
 
+    protected @Nullable UserHandle fetchPrivateProfileUserHandle() {
+        mPrivateProfileUserHandle = null;
+        UserManager userManager = getSystemService(UserManager.class);
+        for (final UserInfo userInfo :
+                userManager.getProfiles(mPersonalProfileUserHandle.getIdentifier())) {
+            if (userInfo.isPrivateProfile()) {
+                mPrivateProfileUserHandle = userInfo.getUserHandle();
+                break;
+            }
+        }
+        return mPrivateProfileUserHandle;
+    }
+
     private UserHandle fetchTabOwnerUserHandleForLaunch() {
-        // If we are in work profile's process, return WorkProfile user as owner, otherwise we
-        // always return PersonalProfile user as owner
-        return UserHandle.of(UserHandle.myUserId()).equals(getWorkProfileUserHandle())
-                ? getWorkProfileUserHandle()
-                : getPersonalProfileUserHandle();
+        // If we are in work or private profile's process, return WorkProfile/PrivateProfile user
+        // as owner, otherwise we always return PersonalProfile user as owner
+        if (UserHandle.of(UserHandle.myUserId()).equals(getWorkProfileUserHandle())) {
+            return getWorkProfileUserHandle();
+        } else if (privateSpaceEnabled() && isLaunchedAsPrivateProfile()) {
+            return getPrivateProfileUserHandle();
+        }
+        return getPersonalProfileUserHandle();
     }
 
     private boolean hasWorkProfile() {
@@ -816,7 +842,15 @@
                 && (UserHandle.myUserId() == getCloneProfileUserHandle().getIdentifier());
     }
 
+    protected final boolean isLaunchedAsPrivateProfile() {
+        return getPrivateProfileUserHandle() != null
+                && (UserHandle.myUserId() == getPrivateProfileUserHandle().getIdentifier());
+    }
+
     protected boolean shouldShowTabs() {
+        if (privateSpaceEnabled() && isLaunchedAsPrivateProfile()) {
+            return false;
+        }
         return hasWorkProfile() && ENABLE_TABBED_VIEW;
     }
 
@@ -2619,6 +2653,11 @@
         return resolveInfo.userHandle;
     }
 
+    private boolean privateSpaceEnabled() {
+        return mIsIntentPicker && android.os.Flags.allowPrivateProfile()
+                && android.multiuser.Flags.allowResolverSheetForPrivateSpace();
+    }
+
     /**
      * An a11y delegate that expands resolver drawer when gesture navigation reaches a partially
      * invisible target in the list.
diff --git a/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java b/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java
index 1bd0982..eeea17b 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java
@@ -68,10 +68,10 @@
         // TODO b/291899544: for released flags, use resource config values
         /** Value used by polite notif. feature */
         public static final Flag NOTIF_COOLDOWN_T1 = devFlag(
-                "persist.debug.sysui.notification.notif_cooldown_t1", 5000);
+                "persist.debug.sysui.notification.notif_cooldown_t1", 60000);
         /** Value used by polite notif. feature */
         public static final Flag NOTIF_COOLDOWN_T2 = devFlag(
-                "persist.debug.sysui.notification.notif_cooldown_t2", 3000);
+                "persist.debug.sysui.notification.notif_cooldown_t2", 5000);
         /** Value used by polite notif. feature */
         public static final Flag NOTIF_VOLUME1 = devFlag(
                 "persist.debug.sysui.notification.notif_volume1", 30);
@@ -80,12 +80,6 @@
         /** Value used by polite notif. feature. -1 to ignore the counter */
         public static final Flag NOTIF_COOLDOWN_COUNTER_RESET = devFlag(
                 "persist.debug.sysui.notification.notif_cooldown_counter_reset", 10);
-        /**
-         * Value used by polite notif. feature: cooldown behavior/strategy. Valid values: rule1,
-         * rule2
-         */
-        public static final Flag NOTIF_COOLDOWN_RULE = devFlag(
-                "persist.debug.sysui.notification.notif_cooldown_rule", "rule1");
 
         /** b/303716154: For debugging only: use short bitmap duration. */
         public static final Flag DEBUG_SHORT_BITMAP_DURATION = devFlag(
diff --git a/core/java/com/android/internal/content/PackageMonitor.java b/core/java/com/android/internal/content/PackageMonitor.java
index c89cfc4..5705b7e 100644
--- a/core/java/com/android/internal/content/PackageMonitor.java
+++ b/core/java/com/android/internal/content/PackageMonitor.java
@@ -37,6 +37,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.os.BackgroundThread;
 
+import java.lang.ref.WeakReference;
 import java.util.Objects;
 import java.util.concurrent.Executor;
 
@@ -63,6 +64,8 @@
 
     PackageMonitorCallback mPackageMonitorCallback;
 
+    private Executor mExecutor;
+
     @UnsupportedAppUsage
     public PackageMonitor() {
         final boolean isCore = UserHandle.isCore(android.os.Process.myUid());
@@ -106,8 +109,8 @@
         if (mPackageMonitorCallback == null) {
             PackageManager pm = mRegisteredContext.getPackageManager();
             if (pm != null) {
-                mPackageMonitorCallback = new PackageMonitorCallback(this,
-                        new HandlerExecutor(mRegisteredHandler));
+                mExecutor = new HandlerExecutor(mRegisteredHandler);
+                mPackageMonitorCallback = new PackageMonitorCallback(this);
                 int userId = user != null ? user.getIdentifier() : mRegisteredContext.getUserId();
                 pm.registerPackageMonitorCallback(mPackageMonitorCallback, userId);
             }
@@ -131,6 +134,7 @@
         }
         mPackageMonitorCallback = null;
         mRegisteredContext = null;
+        mExecutor = null;
     }
 
     public void onBeginPackageChanges() {
@@ -362,6 +366,13 @@
         doHandlePackageEvent(intent);
     }
 
+
+    private void postHandlePackageEvent(Intent intent) {
+        if (mExecutor != null) {
+            mExecutor.execute(() -> doHandlePackageEvent(intent));
+        }
+    }
+
     /**
      * Handle the package related event
      * @param intent the intent that contains package related event information
@@ -516,13 +527,10 @@
     }
 
     private static final class PackageMonitorCallback extends IRemoteCallback.Stub {
+        private final WeakReference<PackageMonitor> mMonitorWeakReference;
 
-        private final PackageMonitor mPackageMonitor;
-        private final Executor mExecutor;
-
-        PackageMonitorCallback(PackageMonitor monitor, Executor executor) {
-            mPackageMonitor = monitor;
-            mExecutor = executor;
+        PackageMonitorCallback(PackageMonitor monitor) {
+            mMonitorWeakReference = new WeakReference<>(monitor);
         }
 
         @Override
@@ -537,7 +545,10 @@
                 Log.w(TAG, "No intent is set for PackageMonitorCallback");
                 return;
             }
-            mExecutor.execute(() -> mPackageMonitor.doHandlePackageEvent(intent));
+            PackageMonitor monitor = mMonitorWeakReference.get();
+            if (monitor != null) {
+                monitor.postHandlePackageEvent(intent);
+            }
         }
     }
 }
diff --git a/core/java/com/android/internal/foldables/FoldGracePeriodProvider.java b/core/java/com/android/internal/foldables/FoldGracePeriodProvider.java
new file mode 100644
index 0000000..53164f3
--- /dev/null
+++ b/core/java/com/android/internal/foldables/FoldGracePeriodProvider.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.foldables;
+
+import android.os.Build;
+import android.sysprop.FoldLockBehaviorProperties;
+import android.util.Slog;
+
+import com.android.internal.foldables.flags.Flags;
+
+import java.util.function.Supplier;
+
+/**
+ * Wrapper class to access {@link FoldLockBehaviorProperties}.
+ */
+public class FoldGracePeriodProvider {
+
+    private static final String TAG = "FoldGracePeriodProvider";
+    private final Supplier<Boolean> mFoldGracePeriodEnabled = Flags::foldGracePeriodEnabled;
+
+    /**
+     * Whether the fold grace period feature is enabled.
+     */
+    public boolean isEnabled() {
+        if ((Build.IS_ENG || Build.IS_USERDEBUG)
+                && FoldLockBehaviorProperties.fold_grace_period_enabled().orElse(false)) {
+            return true;
+        }
+        try {
+            return mFoldGracePeriodEnabled.get();
+        } catch (Throwable ex) {
+            Slog.i(TAG,
+                    "Flags not ready yet. Return false for "
+                            + Flags.FLAG_FOLD_GRACE_PERIOD_ENABLED,
+                    ex);
+            return false;
+        }
+    }
+}
diff --git a/core/java/com/android/internal/foldables/fold_lock_setting_flags.aconfig b/core/java/com/android/internal/foldables/fold_lock_setting_flags.aconfig
index 44f436ea..d73e623 100644
--- a/core/java/com/android/internal/foldables/fold_lock_setting_flags.aconfig
+++ b/core/java/com/android/internal/foldables/fold_lock_setting_flags.aconfig
@@ -7,3 +7,11 @@
     bug: "274447767"
     is_fixed_read_only: true
 }
+
+flag {
+    name: "fold_grace_period_enabled"
+    namespace: "display_manager"
+    description: "Feature flag for Folding Grace Period"
+    bug: "308417021"
+    is_fixed_read_only: true
+}
diff --git a/core/java/com/android/internal/os/BatteryStatsHistory.java b/core/java/com/android/internal/os/BatteryStatsHistory.java
index 1330e16..7a79e0f 100644
--- a/core/java/com/android/internal/os/BatteryStatsHistory.java
+++ b/core/java/com/android/internal/os/BatteryStatsHistory.java
@@ -977,11 +977,16 @@
     /**
      * @return true if there is more than 100MB free disk space left.
      */
+    @android.ravenwood.annotation.RavenwoodReplace
     private boolean hasFreeDiskSpace() {
         final StatFs stats = new StatFs(mHistoryDir.getAbsolutePath());
         return stats.getAvailableBytes() > MIN_FREE_SPACE;
     }
 
+    private boolean hasFreeDiskSpace$ravenwood() {
+        return true;
+    }
+
     @VisibleForTesting
     public List<String> getFilesNames() {
         List<String> names = new ArrayList<>();
diff --git a/core/java/com/android/internal/os/PowerProfile.java b/core/java/com/android/internal/os/PowerProfile.java
index 2298cbd..ab982f5 100644
--- a/core/java/com/android/internal/os/PowerProfile.java
+++ b/core/java/com/android/internal/os/PowerProfile.java
@@ -50,6 +50,7 @@
  * Customize the XML file for different devices.
  * [hidden]
  */
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
 public class PowerProfile {
 
     public static final String TAG = "PowerProfile";
@@ -321,6 +322,13 @@
     private int mCpuPowerBracketCount;
 
     @VisibleForTesting
+    public PowerProfile() {
+        synchronized (sLock) {
+            initLocked();
+        }
+    }
+
+    @VisibleForTesting
     @UnsupportedAppUsage
     public PowerProfile(Context context) {
         this(context, false);
@@ -358,6 +366,10 @@
         if (sPowerItemMap.size() == 0 && sPowerArrayMap.size() == 0) {
             readPowerValuesFromXml(context, xmlId);
         }
+        initLocked();
+    }
+
+    private void initLocked() {
         initCpuClusters();
         initCpuScalingPolicies();
         initCpuPowerBrackets();
diff --git a/core/java/com/android/internal/pm/parsing/PackageInfoCommonUtils.java b/core/java/com/android/internal/pm/parsing/PackageInfoCommonUtils.java
new file mode 100644
index 0000000..983658a
--- /dev/null
+++ b/core/java/com/android/internal/pm/parsing/PackageInfoCommonUtils.java
@@ -0,0 +1,654 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.pm.parsing;
+
+import static com.android.internal.pm.pkg.SEInfoUtil.COMPLETE_STR;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.Attribution;
+import android.content.pm.ComponentInfo;
+import android.content.pm.ConfigurationInfo;
+import android.content.pm.FallbackCategoryProvider;
+import android.content.pm.FeatureGroupInfo;
+import android.content.pm.FeatureInfo;
+import android.content.pm.InstrumentationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageItemInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PathPermission;
+import android.content.pm.PermissionInfo;
+import android.content.pm.ProviderInfo;
+import android.content.pm.ServiceInfo;
+import android.content.pm.Signature;
+import android.content.pm.SigningDetails;
+import android.content.pm.SigningInfo;
+import android.os.Debug;
+import android.os.PatternMatcher;
+import android.os.UserHandle;
+import android.util.DebugUtils;
+import android.util.Slog;
+
+import com.android.internal.pm.parsing.pkg.AndroidPackageHidden;
+import com.android.internal.pm.parsing.pkg.AndroidPackageLegacyUtils;
+import com.android.internal.pm.parsing.pkg.PackageImpl;
+import com.android.internal.pm.pkg.component.ComponentParseUtils;
+import com.android.internal.pm.pkg.component.ParsedActivity;
+import com.android.internal.pm.pkg.component.ParsedAttribution;
+import com.android.internal.pm.pkg.component.ParsedComponent;
+import com.android.internal.pm.pkg.component.ParsedInstrumentation;
+import com.android.internal.pm.pkg.component.ParsedMainComponent;
+import com.android.internal.pm.pkg.component.ParsedPermission;
+import com.android.internal.pm.pkg.component.ParsedProvider;
+import com.android.internal.pm.pkg.component.ParsedService;
+import com.android.internal.pm.pkg.component.ParsedUsesPermission;
+import com.android.internal.pm.pkg.parsing.ParsingPackageHidden;
+import com.android.internal.pm.pkg.parsing.ParsingPackageUtils;
+import com.android.internal.pm.pkg.parsing.ParsingUtils;
+import com.android.internal.util.ArrayUtils;
+import com.android.server.pm.pkg.AndroidPackage;
+
+import java.util.List;
+
+/**
+ * Method that use a {@link AndroidPackage} to generate a {@link PackageInfo} though
+ * the given {@link PackageManager.PackageInfoFlags}
+ * @hide
+ **/
+// TODO(b/317215254): refactor coped code from PackageInfoUtils
+public class PackageInfoCommonUtils {
+
+    private static final String TAG = ParsingUtils.TAG;
+    private static final boolean DEBUG = false;
+
+    /**
+     * Generates a {@link PackageInfo} from the given {@link AndroidPackage}
+     */
+    @Nullable
+    public static PackageInfo generate(@Nullable AndroidPackage pkg,
+            @PackageManager.PackageInfoFlagsBits long flags, int userId) {
+        if (pkg == null) {
+            return null;
+        }
+        ApplicationInfo applicationInfo = generateApplicationInfo(pkg, flags, userId);
+
+        PackageInfo info = new PackageInfo();
+        info.packageName = pkg.getPackageName();
+        info.splitNames = pkg.getSplitNames();
+        info.versionCode = ((ParsingPackageHidden) pkg).getVersionCode();
+        info.versionCodeMajor = ((ParsingPackageHidden) pkg).getVersionCodeMajor();
+        info.baseRevisionCode = pkg.getBaseRevisionCode();
+        info.splitRevisionCodes = pkg.getSplitRevisionCodes();
+        info.versionName = pkg.getVersionName();
+        if (!pkg.isLeavingSharedUser()) {
+            info.sharedUserId = pkg.getSharedUserId();
+            info.sharedUserLabel = pkg.getSharedUserLabelResourceId();
+        }
+        info.applicationInfo = applicationInfo;
+        info.installLocation = pkg.getInstallLocation();
+        if ((info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0
+                || (info.applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
+            info.requiredForAllUsers = pkg.isRequiredForAllUsers();
+        }
+        info.restrictedAccountType = pkg.getRestrictedAccountType();
+        info.requiredAccountType = pkg.getRequiredAccountType();
+        info.overlayTarget = pkg.getOverlayTarget();
+        info.targetOverlayableName = pkg.getOverlayTargetOverlayableName();
+        info.overlayCategory = pkg.getOverlayCategory();
+        info.overlayPriority = pkg.getOverlayPriority();
+        info.mOverlayIsStatic = pkg.isOverlayIsStatic();
+        info.compileSdkVersion = pkg.getCompileSdkVersion();
+        info.compileSdkVersionCodename = pkg.getCompileSdkVersionCodeName();
+        info.isStub = pkg.isStub();
+        info.coreApp = pkg.isCoreApp();
+        info.isApex = pkg.isApex();
+
+        if ((flags & PackageManager.GET_CONFIGURATIONS) != 0) {
+            int size = pkg.getConfigPreferences().size();
+            if (size > 0) {
+                info.configPreferences = new ConfigurationInfo[size];
+                pkg.getConfigPreferences().toArray(info.configPreferences);
+            }
+            size = pkg.getRequestedFeatures().size();
+            if (size > 0) {
+                info.reqFeatures = new FeatureInfo[size];
+                pkg.getRequestedFeatures().toArray(info.reqFeatures);
+            }
+            size = pkg.getFeatureGroups().size();
+            if (size > 0) {
+                info.featureGroups = new FeatureGroupInfo[size];
+                pkg.getFeatureGroups().toArray(info.featureGroups);
+            }
+        }
+        if ((flags & PackageManager.GET_PERMISSIONS) != 0) {
+            int size = ArrayUtils.size(pkg.getPermissions());
+            if (size > 0) {
+                info.permissions = new PermissionInfo[size];
+                for (int i = 0; i < size; i++) {
+                    final var permission = pkg.getPermissions().get(i);
+                    final var permissionInfo = generatePermissionInfo(permission, flags);
+                    info.permissions[i] = permissionInfo;
+                }
+            }
+            final List<ParsedUsesPermission> usesPermissions = pkg.getUsesPermissions();
+            size = usesPermissions.size();
+            if (size > 0) {
+                info.requestedPermissions = new String[size];
+                info.requestedPermissionsFlags = new int[size];
+                for (int i = 0; i < size; i++) {
+                    final ParsedUsesPermission usesPermission = usesPermissions.get(i);
+                    info.requestedPermissions[i] = usesPermission.getName();
+                    // The notion of required permissions is deprecated but for compatibility.
+                    info.requestedPermissionsFlags[i] |=
+                            PackageInfo.REQUESTED_PERMISSION_REQUIRED;
+                    if ((usesPermission.getUsesPermissionFlags()
+                            & ParsedUsesPermission.FLAG_NEVER_FOR_LOCATION) != 0) {
+                        info.requestedPermissionsFlags[i] |=
+                                PackageInfo.REQUESTED_PERMISSION_NEVER_FOR_LOCATION;
+                    }
+                    if (pkg.getImplicitPermissions().contains(info.requestedPermissions[i])) {
+                        info.requestedPermissionsFlags[i] |=
+                                PackageInfo.REQUESTED_PERMISSION_IMPLICIT;
+                    }
+                }
+            }
+        }
+        if ((flags & PackageManager.GET_ATTRIBUTIONS_LONG) != 0) {
+            int size = ArrayUtils.size(pkg.getAttributions());
+            if (size > 0) {
+                info.attributions = new Attribution[size];
+                for (int i = 0; i < size; i++) {
+                    ParsedAttribution parsedAttribution = pkg.getAttributions().get(i);
+                    if (parsedAttribution != null) {
+                        info.attributions[i] = new Attribution(parsedAttribution.getTag(),
+                                parsedAttribution.getLabel());
+                    }
+                }
+            }
+            if (pkg.isAttributionsUserVisible()) {
+                info.applicationInfo.privateFlagsExt
+                        |= ApplicationInfo.PRIVATE_FLAG_EXT_ATTRIBUTIONS_ARE_USER_VISIBLE;
+            } else {
+                info.applicationInfo.privateFlagsExt
+                        &= ~ApplicationInfo.PRIVATE_FLAG_EXT_ATTRIBUTIONS_ARE_USER_VISIBLE;
+            }
+        } else {
+            info.applicationInfo.privateFlagsExt
+                    &= ~ApplicationInfo.PRIVATE_FLAG_EXT_ATTRIBUTIONS_ARE_USER_VISIBLE;
+        }
+
+        final SigningDetails signingDetails = pkg.getSigningDetails();
+        // deprecated method of getting signing certificates
+        if ((flags & PackageManager.GET_SIGNATURES) != 0) {
+            if (signingDetails.hasPastSigningCertificates()) {
+                // Package has included signing certificate rotation information.  Return the oldest
+                // cert so that programmatic checks keep working even if unaware of key rotation.
+                info.signatures = new Signature[1];
+                info.signatures[0] = signingDetails.getPastSigningCertificates()[0];
+            } else if (signingDetails.hasSignatures()) {
+                // otherwise keep old behavior
+                int numberOfSigs = signingDetails.getSignatures().length;
+                info.signatures = new Signature[numberOfSigs];
+                System.arraycopy(signingDetails.getSignatures(), 0, info.signatures, 0,
+                        numberOfSigs);
+            }
+        }
+
+        // replacement for GET_SIGNATURES
+        if ((flags & PackageManager.GET_SIGNING_CERTIFICATES) != 0) {
+            if (signingDetails != SigningDetails.UNKNOWN) {
+                // only return a valid SigningInfo if there is signing information to report
+                info.signingInfo = new SigningInfo(signingDetails);
+            } else {
+                info.signingInfo = null;
+            }
+        }
+
+        if ((flags & PackageManager.GET_ACTIVITIES) != 0) {
+            final int size = pkg.getActivities().size();
+            if (size > 0) {
+                int num = 0;
+                final ActivityInfo[] res = new ActivityInfo[size];
+                for (int i = 0; i < size; i++) {
+                    final ParsedActivity a = pkg.getActivities().get(i);
+                    if (isMatch(pkg, a.isDirectBootAware(), flags)) {
+                        if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(
+                                a.getName())) {
+                            continue;
+                        }
+                        res[num++] = generateActivityInfo(a, flags, applicationInfo);
+                    }
+                }
+                info.activities = ArrayUtils.trimToSize(res, num);
+            }
+        }
+        if ((flags & PackageManager.GET_RECEIVERS) != 0) {
+            final int size = pkg.getReceivers().size();
+            if (size > 0) {
+                int num = 0;
+                final ActivityInfo[] res = new ActivityInfo[size];
+                for (int i = 0; i < size; i++) {
+                    final ParsedActivity a = pkg.getReceivers().get(i);
+                    if (isMatch(pkg, a.isDirectBootAware(), flags)) {
+                        res[num++] = generateActivityInfo(a, flags, applicationInfo);
+                    }
+                }
+                info.receivers = ArrayUtils.trimToSize(res, num);
+            }
+        }
+        if ((flags & PackageManager.GET_SERVICES) != 0) {
+            final int size = pkg.getServices().size();
+            if (size > 0) {
+                int num = 0;
+                final ServiceInfo[] res = new ServiceInfo[size];
+                for (int i = 0; i < size; i++) {
+                    final ParsedService s = pkg.getServices().get(i);
+                    if (isMatch(pkg, s.isDirectBootAware(), flags)) {
+                        res[num++] = generateServiceInfo(s, flags, applicationInfo);
+                    }
+                }
+                info.services = ArrayUtils.trimToSize(res, num);
+            }
+        }
+        if ((flags & PackageManager.GET_PROVIDERS) != 0) {
+            final int size = pkg.getProviders().size();
+            if (size > 0) {
+                int num = 0;
+                final ProviderInfo[] res = new ProviderInfo[size];
+                for (int i = 0; i < size; i++) {
+                    final ParsedProvider pr = pkg.getProviders().get(i);
+                    if (isMatch(pkg, pr.isDirectBootAware(), flags)) {
+                        res[num++] = generateProviderInfo(pkg, pr, flags, applicationInfo, userId);
+                    }
+                }
+                info.providers = ArrayUtils.trimToSize(res, num);
+            }
+        }
+        if ((flags & PackageManager.GET_INSTRUMENTATION) != 0) {
+            final int size = pkg.getInstrumentations().size();
+            if (size > 0) {
+                info.instrumentation = new InstrumentationInfo[size];
+                for (int i = 0; i < size; i++) {
+                    info.instrumentation[i] = generateInstrumentationInfo(
+                            pkg.getInstrumentations().get(i), pkg, flags, userId);
+                }
+            }
+        }
+
+        return info;
+    }
+
+    private static void updateApplicationInfo(ApplicationInfo ai, long flags) {
+        if ((flags & PackageManager.GET_META_DATA) == 0) {
+            ai.metaData = null;
+        }
+        if ((flags & PackageManager.GET_SHARED_LIBRARY_FILES) == 0) {
+            ai.sharedLibraryFiles = null;
+            ai.sharedLibraryInfos = null;
+        }
+
+        // CompatibilityMode is global state.
+        if (!ParsingPackageUtils.sCompatibilityModeEnabled) {
+            ai.disableCompatibilityMode();
+        }
+
+        if (ai.category == ApplicationInfo.CATEGORY_UNDEFINED) {
+            ai.category = FallbackCategoryProvider.getFallbackCategory(ai.packageName);
+        }
+        ai.seInfoUser = COMPLETE_STR;
+    }
+
+    @Nullable
+    private static ApplicationInfo generateApplicationInfo(@NonNull AndroidPackage pkg,
+            @PackageManager.ApplicationInfoFlagsBits long flags, @UserIdInt int userId) {
+
+        // Make shallow copy so we can store the metadata/libraries safely
+        ApplicationInfo info = ((AndroidPackageHidden) pkg).toAppInfoWithoutState();
+
+        updateApplicationInfo(info, flags);
+
+        initForUser(info, pkg, userId);
+
+        info.primaryCpuAbi = AndroidPackageLegacyUtils.getRawPrimaryCpuAbi(pkg);
+        info.secondaryCpuAbi = AndroidPackageLegacyUtils.getRawSecondaryCpuAbi(pkg);
+
+        if ((flags & PackageManager.GET_META_DATA) != 0) {
+            info.metaData = pkg.getMetaData();
+        }
+        if ((flags & PackageManager.GET_SHARED_LIBRARY_FILES) != 0) {
+            List<String> usesLibraryFiles = pkg.getUsesLibraries();
+
+            info.sharedLibraryFiles = usesLibraryFiles.isEmpty()
+                    ? null : usesLibraryFiles.toArray(new String[0]);
+        }
+
+        return info;
+    }
+
+    @Nullable
+    private static ActivityInfo generateActivityInfo(ParsedActivity a,
+            @PackageManager.ComponentInfoFlagsBits long flags,
+            @NonNull ApplicationInfo applicationInfo) {
+        if (a == null) return null;
+
+        // Make shallow copies so we can store the metadata safely
+        ActivityInfo ai = new ActivityInfo();
+        ai.targetActivity = a.getTargetActivity();
+        ai.processName = a.getProcessName();
+        ai.exported = a.isExported();
+        ai.theme = a.getTheme();
+        ai.uiOptions = a.getUiOptions();
+        ai.parentActivityName = a.getParentActivityName();
+        ai.permission = a.getPermission();
+        ai.taskAffinity = a.getTaskAffinity();
+        ai.flags = a.getFlags();
+        ai.privateFlags = a.getPrivateFlags();
+        ai.launchMode = a.getLaunchMode();
+        ai.documentLaunchMode = a.getDocumentLaunchMode();
+        ai.maxRecents = a.getMaxRecents();
+        ai.configChanges = a.getConfigChanges();
+        ai.softInputMode = a.getSoftInputMode();
+        ai.persistableMode = a.getPersistableMode();
+        ai.lockTaskLaunchMode = a.getLockTaskLaunchMode();
+        ai.screenOrientation = a.getScreenOrientation();
+        ai.resizeMode = a.getResizeMode();
+        ai.setMaxAspectRatio(a.getMaxAspectRatio());
+        ai.setMinAspectRatio(a.getMinAspectRatio());
+        ai.supportsSizeChanges = a.isSupportsSizeChanges();
+        ai.requestedVrComponent = a.getRequestedVrComponent();
+        ai.rotationAnimation = a.getRotationAnimation();
+        ai.colorMode = a.getColorMode();
+        ai.windowLayout = a.getWindowLayout();
+        ai.attributionTags = a.getAttributionTags();
+        if ((flags & PackageManager.GET_META_DATA) != 0) {
+            var metaData = a.getMetaData();
+            // Backwards compatibility, coerce to null if empty
+            ai.metaData = metaData.isEmpty() ? null : metaData;
+        } else {
+            ai.metaData = null;
+        }
+        ai.applicationInfo = applicationInfo;
+        ai.requiredDisplayCategory = a.getRequiredDisplayCategory();
+        ai.setKnownActivityEmbeddingCerts(a.getKnownActivityEmbeddingCerts());
+        assignFieldsComponentInfoParsedMainComponent(ai, a);
+        return ai;
+    }
+
+    @Nullable
+    private static ServiceInfo generateServiceInfo(ParsedService s,
+            @PackageManager.ComponentInfoFlagsBits long flags,
+            @NonNull ApplicationInfo applicationInfo) {
+        if (s == null) return null;
+
+        // Make shallow copies so we can store the metadata safely
+        ServiceInfo si = new ServiceInfo();
+        si.exported = s.isExported();
+        si.flags = s.getFlags();
+        si.permission = s.getPermission();
+        si.processName = s.getProcessName();
+        si.mForegroundServiceType = s.getForegroundServiceType();
+        si.applicationInfo = applicationInfo;
+        if ((flags & PackageManager.GET_META_DATA) != 0) {
+            var metaData = s.getMetaData();
+            // Backwards compatibility, coerce to null if empty
+            si.metaData = metaData.isEmpty() ? null : metaData;
+        }
+        assignFieldsComponentInfoParsedMainComponent(si, s);
+        return si;
+    }
+
+    @Nullable
+    private static ProviderInfo generateProviderInfo(AndroidPackage pkg, ParsedProvider p,
+            @PackageManager.ComponentInfoFlagsBits long flags,
+            @NonNull ApplicationInfo applicationInfo, int userId) {
+        if (p == null) return null;
+
+        if (!pkg.getPackageName().equals(applicationInfo.packageName)) {
+            Slog.wtf(TAG, "AppInfo's package name is different. Expected=" + pkg.getPackageName()
+                    + " actual=" + applicationInfo.packageName);
+            applicationInfo = generateApplicationInfo(pkg, flags, userId);
+        }
+
+        // Make shallow copies so we can store the metadata safely
+        ProviderInfo pi = new ProviderInfo();
+        pi.exported = p.isExported();
+        pi.flags = p.getFlags();
+        pi.processName = p.getProcessName();
+        pi.authority = p.getAuthority();
+        pi.isSyncable = p.isSyncable();
+        pi.readPermission = p.getReadPermission();
+        pi.writePermission = p.getWritePermission();
+        pi.grantUriPermissions = p.isGrantUriPermissions();
+        pi.forceUriPermissions = p.isForceUriPermissions();
+        pi.multiprocess = p.isMultiProcess();
+        pi.initOrder = p.getInitOrder();
+        pi.uriPermissionPatterns = p.getUriPermissionPatterns().toArray(new PatternMatcher[0]);
+        pi.pathPermissions = p.getPathPermissions().toArray(new PathPermission[0]);
+        if ((flags & PackageManager.GET_URI_PERMISSION_PATTERNS) == 0) {
+            pi.uriPermissionPatterns = null;
+        }
+        if ((flags & PackageManager.GET_META_DATA) != 0) {
+            var metaData = p.getMetaData();
+            // Backwards compatibility, coerce to null if empty
+            pi.metaData = metaData.isEmpty() ? null : metaData;
+        }
+        pi.applicationInfo = applicationInfo;
+        assignFieldsComponentInfoParsedMainComponent(pi, p);
+        return pi;
+    }
+
+    @Nullable
+    private static InstrumentationInfo generateInstrumentationInfo(ParsedInstrumentation i,
+            AndroidPackage pkg, @PackageManager.ComponentInfoFlagsBits long flags, int userId) {
+        if (i == null) return null;
+
+        InstrumentationInfo info = new InstrumentationInfo();
+        info.targetPackage = i.getTargetPackage();
+        info.targetProcesses = i.getTargetProcesses();
+        info.handleProfiling = i.isHandleProfiling();
+        info.functionalTest = i.isFunctionalTest();
+
+        info.sourceDir = pkg.getBaseApkPath();
+        info.publicSourceDir = pkg.getBaseApkPath();
+        info.splitNames = pkg.getSplitNames();
+        info.splitSourceDirs = pkg.getSplitCodePaths().length == 0 ? null : pkg.getSplitCodePaths();
+        info.splitPublicSourceDirs = pkg.getSplitCodePaths().length == 0
+                ? null : pkg.getSplitCodePaths();
+        info.splitDependencies = pkg.getSplitDependencies().size() == 0
+                ? null : pkg.getSplitDependencies();
+
+        initForUser(info, pkg, userId);
+
+        info.primaryCpuAbi = AndroidPackageLegacyUtils.getRawPrimaryCpuAbi(pkg);
+        info.secondaryCpuAbi = AndroidPackageLegacyUtils.getRawSecondaryCpuAbi(pkg);
+        info.nativeLibraryDir = pkg.getNativeLibraryDir();
+        info.secondaryNativeLibraryDir = pkg.getSecondaryNativeLibraryDir();
+
+        assignFieldsPackageItemInfoParsedComponent(info, i);
+
+        if ((flags & PackageManager.GET_META_DATA) == 0) {
+            info.metaData = null;
+        } else {
+            var metaData = i.getMetaData();
+            // Backwards compatibility, coerce to null if empty
+            info.metaData = metaData.isEmpty() ? null : metaData;
+        }
+
+        return info;
+    }
+
+    @Nullable
+    private static PermissionInfo generatePermissionInfo(ParsedPermission p,
+            @PackageManager.ComponentInfoFlagsBits long flags) {
+        // TODO(b/135203078): Remove null checks and make all usages @NonNull
+        if (p == null) return null;
+
+        PermissionInfo pi = new PermissionInfo(p.getBackgroundPermission());
+
+        assignFieldsPackageItemInfoParsedComponent(pi, p);
+
+        pi.group = p.getGroup();
+        pi.requestRes = p.getRequestRes();
+        pi.protectionLevel = p.getProtectionLevel();
+        pi.descriptionRes = p.getDescriptionRes();
+        pi.flags = p.getFlags();
+        pi.knownCerts = p.getKnownCerts();
+
+        if ((flags & PackageManager.GET_META_DATA) == 0) {
+            pi.metaData = null;
+        } else {
+            var metaData = p.getMetaData();
+            // Backwards compatibility, coerce to null if empty
+            pi.metaData = metaData.isEmpty() ? null : metaData;
+        }
+        return pi;
+    }
+
+    private static void assignFieldsComponentInfoParsedMainComponent(
+            @NonNull ComponentInfo info, @NonNull ParsedMainComponent component) {
+        assignFieldsPackageItemInfoParsedComponent(info, component);
+        info.descriptionRes = component.getDescriptionRes();
+        info.directBootAware = component.isDirectBootAware();
+        info.enabled = component.isEnabled();
+        info.splitName = component.getSplitName();
+        info.attributionTags = component.getAttributionTags();
+        info.nonLocalizedLabel = component.getNonLocalizedLabel();
+        info.icon = component.getIcon();
+    }
+
+    private static void assignFieldsPackageItemInfoParsedComponent(
+            @NonNull PackageItemInfo packageItemInfo, @NonNull ParsedComponent component) {
+        packageItemInfo.nonLocalizedLabel = ComponentParseUtils.getNonLocalizedLabel(component);
+        packageItemInfo.icon = ComponentParseUtils.getIcon(component);
+        packageItemInfo.banner = component.getBanner();
+        packageItemInfo.labelRes = component.getLabelRes();
+        packageItemInfo.logo = component.getLogo();
+        packageItemInfo.name = component.getName();
+        packageItemInfo.packageName = component.getPackageName();
+    }
+
+    private static void initForUser(ApplicationInfo output, AndroidPackage input,
+            @UserIdInt int userId) {
+        PackageImpl pkg = ((PackageImpl) input);
+        String packageName = input.getPackageName();
+        output.uid = UserHandle.getUid(userId, UserHandle.getAppId(input.getUid()));
+
+        // For performance reasons, all these paths are built as strings
+        final String credentialDir = pkg.getBaseAppDataCredentialProtectedDirForSystemUser();
+        final String deviceDir = pkg.getBaseAppDataDeviceProtectedDirForSystemUser();
+        if (credentialDir !=  null && deviceDir != null) {
+            if (userId == UserHandle.USER_SYSTEM) {
+                output.credentialProtectedDataDir = credentialDir + packageName;
+                output.deviceProtectedDataDir = deviceDir + packageName;
+            } else {
+                // Convert /data/user/0/ -> /data/user/1/com.example.app
+                String userIdString = String.valueOf(userId);
+                int credentialLength = credentialDir.length();
+                output.credentialProtectedDataDir = new StringBuilder(credentialDir)
+                        .replace(credentialLength - 2, credentialLength - 1, userIdString)
+                        .append(packageName)
+                        .toString();
+                int deviceLength = deviceDir.length();
+                output.deviceProtectedDataDir = new StringBuilder(deviceDir)
+                        .replace(deviceLength - 2, deviceLength - 1, userIdString)
+                        .append(packageName)
+                        .toString();
+            }
+        }
+
+        if (input.isDefaultToDeviceProtectedStorage()
+                && PackageManager.APPLY_DEFAULT_TO_DEVICE_PROTECTED_STORAGE) {
+            output.dataDir = output.deviceProtectedDataDir;
+        } else {
+            output.dataDir = output.credentialProtectedDataDir;
+        }
+    }
+
+    // This duplicates the ApplicationInfo variant because it uses field assignment and the classes
+    // don't inherit from each other, unfortunately. Consolidating logic would introduce overhead.
+    private static void initForUser(InstrumentationInfo output, AndroidPackage input,
+            @UserIdInt int userId) {
+        PackageImpl pkg = ((PackageImpl) input);
+        String packageName = input.getPackageName();
+
+        // For performance reasons, all these paths are built as strings
+        final String credentialDir = pkg.getBaseAppDataCredentialProtectedDirForSystemUser();
+        final String deviceDir = pkg.getBaseAppDataDeviceProtectedDirForSystemUser();
+        if (credentialDir !=  null && deviceDir != null) {
+            if (userId == UserHandle.USER_SYSTEM) {
+                output.credentialProtectedDataDir = credentialDir + packageName;
+                output.deviceProtectedDataDir = deviceDir + packageName;
+            } else {
+                // Convert /data/user/0/ -> /data/user/1/com.example.app
+                String userIdString = String.valueOf(userId);
+                int credentialLength = credentialDir.length();
+                output.credentialProtectedDataDir = new StringBuilder(credentialDir)
+                        .replace(credentialLength - 2, credentialLength - 1, userIdString)
+                        .append(packageName)
+                        .toString();
+                int deviceLength = deviceDir.length();
+                output.deviceProtectedDataDir = new StringBuilder(deviceDir)
+                        .replace(deviceLength - 2, deviceLength - 1, userIdString)
+                        .append(packageName)
+                        .toString();
+            }
+        }
+
+        if (input.isDefaultToDeviceProtectedStorage()
+                && PackageManager.APPLY_DEFAULT_TO_DEVICE_PROTECTED_STORAGE) {
+            output.dataDir = output.deviceProtectedDataDir;
+        } else {
+            output.dataDir = output.credentialProtectedDataDir;
+        }
+    }
+
+    /**
+     * Test if the given component is considered system, enabled and a match for the given
+     * flags.
+     *
+     * <p>
+     * Expects at least one of {@link PackageManager#MATCH_DIRECT_BOOT_AWARE} and {@link
+     * PackageManager#MATCH_DIRECT_BOOT_UNAWARE} are specified in {@code flags}.
+     * </p>
+     */
+    private static boolean isMatch(AndroidPackage pkg,
+            boolean isComponentDirectBootAware, long flags) {
+        final boolean isSystem = ((AndroidPackageHidden) pkg).isSystem();
+        if ((flags & PackageManager.MATCH_SYSTEM_ONLY) != 0) {
+            if (!isSystem) {
+                return reportIfDebug(false, flags);
+            }
+        }
+
+        final boolean matchesUnaware = ((flags & PackageManager.MATCH_DIRECT_BOOT_UNAWARE) != 0)
+                && !isComponentDirectBootAware;
+        final boolean matchesAware = ((flags & PackageManager.MATCH_DIRECT_BOOT_AWARE) != 0)
+                && isComponentDirectBootAware;
+        return reportIfDebug(matchesUnaware || matchesAware, flags);
+    }
+
+    private static boolean reportIfDebug(boolean result, long flags) {
+        if (DEBUG && !result) {
+            Slog.i(TAG, "No match!; flags: "
+                    + DebugUtils.flagsToString(PackageManager.class, "MATCH_", flags) + " "
+                    + Debug.getCaller());
+        }
+        return result;
+    }
+}
diff --git a/core/java/com/android/internal/pm/parsing/PackageParser2.java b/core/java/com/android/internal/pm/parsing/PackageParser2.java
index e413293..2c54672 100644
--- a/core/java/com/android/internal/pm/parsing/PackageParser2.java
+++ b/core/java/com/android/internal/pm/parsing/PackageParser2.java
@@ -20,6 +20,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityThread;
+import android.app.Application;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.parsing.PackageLite;
 import android.content.pm.parsing.result.ParseInput;
@@ -40,6 +41,7 @@
 import com.android.internal.util.ArrayUtils;
 
 import java.io.File;
+import java.util.ArrayList;
 import java.util.List;
 
 /**
@@ -78,10 +80,19 @@
             displayMetrics.setToDefaults();
         }
 
-        PermissionManager permissionManager = ActivityThread.currentApplication()
-                .getSystemService(PermissionManager.class);
-        List<PermissionManager.SplitPermissionInfo> splitPermissions = permissionManager
-                .getSplitPermissions();
+        List<PermissionManager.SplitPermissionInfo> splitPermissions = null;
+
+        final Application application = ActivityThread.currentApplication();
+        if (application != null) {
+            final PermissionManager permissionManager =
+                    application.getSystemService(PermissionManager.class);
+            if (permissionManager != null) {
+                splitPermissions = permissionManager.getSplitPermissions();
+            }
+        }
+        if (splitPermissions == null) {
+            splitPermissions = new ArrayList<>();
+        }
 
         mCacher = cacher;
 
diff --git a/core/java/com/android/internal/pm/pkg/component/ParsedProviderUtils.java b/core/java/com/android/internal/pm/pkg/component/ParsedProviderUtils.java
index 5d82d04..12aff1c 100644
--- a/core/java/com/android/internal/pm/pkg/component/ParsedProviderUtils.java
+++ b/core/java/com/android/internal/pm/pkg/component/ParsedProviderUtils.java
@@ -29,6 +29,7 @@
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
+import android.multiuser.Flags;
 import android.os.Build;
 import android.os.PatternMatcher;
 import android.util.Slog;
@@ -126,6 +127,10 @@
                     .setFlags(provider.getFlags() | flag(ProviderInfo.FLAG_SINGLE_USER,
                             R.styleable.AndroidManifestProvider_singleUser, sa));
 
+            if (Flags.enableSystemUserOnlyForServicesAndProviders()) {
+                provider.setFlags(provider.getFlags() | flag(ProviderInfo.FLAG_SYSTEM_USER_ONLY,
+                        R.styleable.AndroidManifestProvider_systemUserOnly, sa));
+            }
             visibleToEphemeral = sa.getBoolean(
                     R.styleable.AndroidManifestProvider_visibleToInstantApps, false);
             if (visibleToEphemeral) {
diff --git a/core/java/com/android/internal/pm/pkg/component/ParsedServiceUtils.java b/core/java/com/android/internal/pm/pkg/component/ParsedServiceUtils.java
index a1dd19a3..4ac542f8 100644
--- a/core/java/com/android/internal/pm/pkg/component/ParsedServiceUtils.java
+++ b/core/java/com/android/internal/pm/pkg/component/ParsedServiceUtils.java
@@ -29,6 +29,7 @@
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
+import android.multiuser.Flags;
 import android.os.Build;
 
 import com.android.internal.R;
@@ -105,6 +106,11 @@
                             | flag(ServiceInfo.FLAG_SINGLE_USER,
                             R.styleable.AndroidManifestService_singleUser, sa)));
 
+            if (Flags.enableSystemUserOnlyForServicesAndProviders()) {
+                service.setFlags(service.getFlags() | flag(ServiceInfo.FLAG_SYSTEM_USER_ONLY,
+                        R.styleable.AndroidManifestService_systemUserOnly, sa));
+            }
+
             visibleToEphemeral = sa.getBoolean(
                     R.styleable.AndroidManifestService_visibleToInstantApps, false);
             if (visibleToEphemeral) {
diff --git a/core/java/com/android/internal/policy/IKeyguardService.aidl b/core/java/com/android/internal/policy/IKeyguardService.aidl
index 36b7ee5..d62c8f3 100644
--- a/core/java/com/android/internal/policy/IKeyguardService.aidl
+++ b/core/java/com/android/internal/policy/IKeyguardService.aidl
@@ -130,4 +130,10 @@
      * Note that it's called only if the device is interactive.
      */
     void onSystemKeyPressed(int keycode);
+
+    /**
+     * Requests to show the keyguard immediately without locking the device. Keyguard will show
+     * whether a screen lock was configured or not (including if screen lock is SWIPE or NONE).
+     */
+    void showDismissibleKeyguard();
 }
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index 31910ac..4e3b64c 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -31,6 +31,7 @@
 import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_EDGE_TO_EDGE_ENFORCED;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
 
@@ -46,6 +47,7 @@
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.res.Configuration;
 import android.content.res.Resources.Theme;
@@ -294,9 +296,9 @@
     private int mFrameResource = 0;
 
     private int mTextColor = 0;
-    int mStatusBarColor = 0;
-    int mNavigationBarColor = 0;
-    int mNavigationBarDividerColor = 0;
+    int mStatusBarColor = Color.TRANSPARENT;
+    int mNavigationBarColor = Color.TRANSPARENT;
+    int mNavigationBarDividerColor = Color.TRANSPARENT;
     private boolean mForcedStatusBarColor = false;
     private boolean mForcedNavigationBarColor = false;
 
@@ -388,11 +390,9 @@
         mProxyOnBackInvokedDispatcher = new ProxyOnBackInvokedDispatcher(context);
         mAllowFloatingWindowsFillScreen = context.getResources().getBoolean(
                 com.android.internal.R.bool.config_allowFloatingWindowsFillScreen);
-        mEdgeToEdgeEnforced =
-                context.getApplicationInfo().targetSdkVersion >= ENFORCE_EDGE_TO_EDGE_SDK_VERSION
-                        || (CompatChanges.isChangeEnabled(ENFORCE_EDGE_TO_EDGE)
-                                && Flags.enforceEdgeToEdge());
+        mEdgeToEdgeEnforced = isEdgeToEdgeEnforced(context.getApplicationInfo(), true /* local */);
         if (mEdgeToEdgeEnforced) {
+            getAttributes().privateFlags |= PRIVATE_FLAG_EDGE_TO_EDGE_ENFORCED;
             mDecorFitsSystemWindows = false;
         }
     }
@@ -431,6 +431,22 @@
         mActivityConfigCallback = activityConfigCallback;
     }
 
+    /**
+     * Returns whether the given application is enforced to go edge-to-edge.
+     *
+     * @param info The application to query.
+     * @param local Whether this is called from the process of the given application.
+     * @return {@code true} if edge-to-edge is enforced. Otherwise, {@code false}.
+     */
+    public static boolean isEdgeToEdgeEnforced(ApplicationInfo info, boolean local) {
+        return info.targetSdkVersion >= ENFORCE_EDGE_TO_EDGE_SDK_VERSION
+                || (Flags.enforceEdgeToEdge() && (local
+                        // Calling this doesn't require a permission.
+                        ? CompatChanges.isChangeEnabled(ENFORCE_EDGE_TO_EDGE)
+                        // Calling this requires permissions.
+                        : info.isChangeEnabled(ENFORCE_EDGE_TO_EDGE)));
+    }
+
     @Override
     public final void setContainer(Window container) {
         super.setContainer(container);
@@ -2548,17 +2564,10 @@
         final boolean targetPreL = targetSdk < android.os.Build.VERSION_CODES.LOLLIPOP;
         final boolean targetPreQ = targetSdk < Build.VERSION_CODES.Q;
 
-        if (!mForcedStatusBarColor) {
-            final int statusBarCompatibleColor = context.getColor(R.color.status_bar_compatible);
-            final int statusBarDefaultColor = context.getColor(R.color.status_bar_default);
-            final int statusBarColor = a.getColor(R.styleable.Window_statusBarColor,
-                    statusBarDefaultColor);
-
-            mStatusBarColor = statusBarColor == statusBarDefaultColor && !mEdgeToEdgeEnforced
-                    ? statusBarCompatibleColor
-                    : statusBarColor;
+        if (!mForcedStatusBarColor && !mEdgeToEdgeEnforced) {
+            mStatusBarColor = a.getColor(R.styleable.Window_statusBarColor, Color.BLACK);
         }
-        if (!mForcedNavigationBarColor) {
+        if (!mForcedNavigationBarColor && !mEdgeToEdgeEnforced) {
             final int navBarCompatibleColor = context.getColor(R.color.navigation_bar_compatible);
             final int navBarDefaultColor = context.getColor(R.color.navigation_bar_default);
             final int navBarColor = a.getColor(R.styleable.Window_navigationBarColor,
@@ -2566,7 +2575,6 @@
 
             mNavigationBarColor =
                     navBarColor == navBarDefaultColor
-                            && !mEdgeToEdgeEnforced
                             && !context.getResources().getBoolean(
                                     R.bool.config_navBarDefaultTransparent)
                     ? navBarCompatibleColor
@@ -2575,7 +2583,7 @@
             mNavigationBarDividerColor = a.getColor(R.styleable.Window_navigationBarDividerColor,
                     Color.TRANSPARENT);
         }
-        if (!targetPreQ) {
+        if (!targetPreQ && !mEdgeToEdgeEnforced) {
             mEnsureStatusBarContrastWhenTransparent = a.getBoolean(
                     R.styleable.Window_enforceStatusBarContrast, false);
             mEnsureNavigationBarContrastWhenTransparent = a.getBoolean(
@@ -3899,6 +3907,9 @@
 
     @Override
     public void setStatusBarColor(int color) {
+        if (mEdgeToEdgeEnforced) {
+            return;
+        }
         if (mStatusBarColor == color && mForcedStatusBarColor) {
             return;
         }
@@ -3920,6 +3931,9 @@
 
     @Override
     public void setNavigationBarColor(int color) {
+        if (mEdgeToEdgeEnforced) {
+            return;
+        }
         if (mNavigationBarColor == color && mForcedNavigationBarColor) {
             return;
         }
@@ -3936,6 +3950,9 @@
 
     @Override
     public void setNavigationBarDividerColor(int navigationBarDividerColor) {
+        if (mEdgeToEdgeEnforced) {
+            return;
+        }
         mNavigationBarDividerColor = navigationBarDividerColor;
         if (mDecor != null) {
             mDecor.updateColorViews(null, false /* animate */);
@@ -3949,6 +3966,9 @@
 
     @Override
     public void setStatusBarContrastEnforced(boolean ensureContrast) {
+        if (mEdgeToEdgeEnforced) {
+            return;
+        }
         mEnsureStatusBarContrastWhenTransparent = ensureContrast;
         if (mDecor != null) {
             mDecor.updateColorViews(null, false /* animate */);
@@ -3962,6 +3982,9 @@
 
     @Override
     public void setNavigationBarContrastEnforced(boolean enforceContrast) {
+        if (mEdgeToEdgeEnforced) {
+            return;
+        }
         mEnsureNavigationBarContrastWhenTransparent = enforceContrast;
         if (mDecor != null) {
             mDecor.updateColorViews(null, false /* animate */);
@@ -4031,6 +4054,9 @@
 
     @Override
     public void setDecorFitsSystemWindows(boolean decorFitsSystemWindows) {
+        if (mEdgeToEdgeEnforced) {
+            return;
+        }
         mDecorFitsSystemWindows = decorFitsSystemWindows;
         applyDecorFitsSystemWindows();
     }
diff --git a/core/java/com/android/internal/power/ModemPowerProfile.java b/core/java/com/android/internal/power/ModemPowerProfile.java
index a555ae3..b15c10e 100644
--- a/core/java/com/android/internal/power/ModemPowerProfile.java
+++ b/core/java/com/android/internal/power/ModemPowerProfile.java
@@ -39,6 +39,7 @@
 /**
  * ModemPowerProfile for handling the modem element in the power_profile.xml
  */
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
 public class ModemPowerProfile {
     private static final String TAG = "ModemPowerProfile";
 
diff --git a/core/java/com/android/internal/util/function/pooled/PooledLambda.java b/core/java/com/android/internal/util/function/pooled/PooledLambda.java
index bdc8a66..1d6d69c 100755
--- a/core/java/com/android/internal/util/function/pooled/PooledLambda.java
+++ b/core/java/com/android/internal/util/function/pooled/PooledLambda.java
@@ -1009,7 +1009,7 @@
             K arg11, L arg12) {
         synchronized (Message.sPoolSync) {
             PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
-                    function, 11, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7,
+                    function, 12, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7,
                     arg8, arg9, arg10, arg11, arg12);
             return Message.obtain().setCallback(callback.recycleOnUse());
         }
diff --git a/core/java/com/android/server/OWNERS b/core/java/com/android/server/OWNERS
deleted file mode 100644
index 1c2d19d..0000000
--- a/core/java/com/android/server/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-per-file SystemConfig.java = file:/PACKAGE_MANAGER_OWNERS
diff --git a/core/jni/android_database_SQLiteConnection.cpp b/core/jni/android_database_SQLiteConnection.cpp
index 893cc98..6f1c763 100644
--- a/core/jni/android_database_SQLiteConnection.cpp
+++ b/core/jni/android_database_SQLiteConnection.cpp
@@ -82,10 +82,16 @@
     const String8 path;
     const String8 label;
 
+    // The prepared statement used to determine which tables are updated by a statement.  This
+    // is is initially null.  It is set non-null on first use.
+    sqlite3_stmt* tableQuery;
+
     volatile bool canceled;
 
     SQLiteConnection(sqlite3* db, int openFlags, const String8& path, const String8& label) :
-        db(db), openFlags(openFlags), path(path), label(label), canceled(false) { }
+            db(db), openFlags(openFlags), path(path), label(label), tableQuery(nullptr),
+            canceled(false) { }
+
 };
 
 // Called each time a statement begins execution, when tracing is enabled.
@@ -188,6 +194,9 @@
 
     if (connection) {
         ALOGV("Closing connection %p", connection->db);
+        if (connection->tableQuery != nullptr) {
+            sqlite3_finalize(connection->tableQuery);
+        }
         int err = sqlite3_close(connection->db);
         if (err != SQLITE_OK) {
             // This can happen if sub-objects aren't closed first.  Make sure the caller knows.
@@ -419,6 +428,46 @@
     return sqlite3_stmt_readonly(statement) != 0;
 }
 
+static jboolean nativeUpdatesTempOnly(JNIEnv* env, jclass,
+        jlong connectionPtr, jlong statementPtr) {
+    sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
+    SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
+
+    int result = SQLITE_OK;
+    if (connection->tableQuery == nullptr) {
+        static char const* sql =
+                "SELECT COUNT(*) FROM tables_used(?) WHERE schema != 'temp' AND wr != 0";
+        result = sqlite3_prepare_v2(connection->db, sql, -1, &connection->tableQuery, nullptr);
+        if (result != SQLITE_OK) {
+            ALOGE("failed to compile query table: %s",
+                  sqlite3_errstr(sqlite3_extended_errcode(connection->db)));
+            return false;
+        }
+    }
+
+    // A temporary, to simplify the code.
+    sqlite3_stmt* query = connection->tableQuery;
+    sqlite3_reset(query);
+    sqlite3_clear_bindings(query);
+    result = sqlite3_bind_text(query, 1, sqlite3_sql(statement), -1, SQLITE_STATIC);
+    if (result != SQLITE_OK) {
+        ALOGE("tables bind pointer returns %s", sqlite3_errstr(result));
+        return false;
+    }
+    result = sqlite3_step(query);
+    if (result != SQLITE_ROW) {
+        ALOGE("tables query error: %d/%s", result, sqlite3_errstr(result));
+        // Make sure the query is no longer bound to the statement.
+        sqlite3_clear_bindings(query);
+        return false;
+    }
+
+    int count = sqlite3_column_int(query, 0);
+    // Make sure the query is no longer bound to the statement SQL string.
+    sqlite3_clear_bindings(query);
+    return count == 0;
+}
+
 static jint nativeGetColumnCount(JNIEnv* env, jclass clazz, jlong connectionPtr,
         jlong statementPtr) {
     sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
@@ -915,6 +964,8 @@
             (void*)nativeGetParameterCount },
     { "nativeIsReadOnly", "(JJ)Z",
             (void*)nativeIsReadOnly },
+    { "nativeUpdatesTempOnly", "(JJ)Z",
+            (void*)nativeUpdatesTempOnly },
     { "nativeGetColumnCount", "(JJ)I",
             (void*)nativeGetColumnCount },
     { "nativeGetColumnName", "(JJI)Ljava/lang/String;",
diff --git a/core/jni/android_graphics_BLASTBufferQueue.cpp b/core/jni/android_graphics_BLASTBufferQueue.cpp
index 4474d4ca..70505a4 100644
--- a/core/jni/android_graphics_BLASTBufferQueue.cpp
+++ b/core/jni/android_graphics_BLASTBufferQueue.cpp
@@ -139,10 +139,13 @@
     return queue->syncNextTransaction(
             [globalCallbackRef](SurfaceComposerClient::Transaction* t) {
                 JNIEnv* env = getenv(globalCallbackRef->vm());
+                ScopedLocalRef<jobject>
+                        transactionObject(env,
+                                          env->NewObject(gTransactionClassInfo.clazz,
+                                                         gTransactionClassInfo.ctor,
+                                                         reinterpret_cast<jlong>(t)));
                 env->CallVoidMethod(globalCallbackRef->object(), gTransactionConsumer.accept,
-                                    env->NewObject(gTransactionClassInfo.clazz,
-                                                   gTransactionClassInfo.ctor,
-                                                   reinterpret_cast<jlong>(t)));
+                                    transactionObject.get());
             },
             acquireSingleBuffer);
 }
diff --git a/core/jni/android_hardware_SensorManager.cpp b/core/jni/android_hardware_SensorManager.cpp
index 9c883d1..56ea52d 100644
--- a/core/jni/android_hardware_SensorManager.cpp
+++ b/core/jni/android_hardware_SensorManager.cpp
@@ -225,6 +225,19 @@
     return translateNativeSensorToJavaSensor(env, sensor, *sensorList[index]) != NULL;
 }
 
+static jboolean nativeGetDefaultDeviceSensorAtIndex(JNIEnv *env, jclass clazz, jlong sensorManager,
+                                                    jobject sensor, jint index) {
+    SensorManager *mgr = reinterpret_cast<SensorManager *>(sensorManager);
+
+    Vector<Sensor> sensorList;
+    ssize_t count = mgr->getDefaultDeviceSensorList(sensorList);
+    if (ssize_t(index) >= count) {
+        return false;
+    }
+
+    return translateNativeSensorToJavaSensor(env, sensor, sensorList[index]) != NULL;
+}
+
 static void
 nativeGetDynamicSensors(JNIEnv *env, jclass clazz, jlong sensorManager, jobject sensorList) {
 
@@ -539,6 +552,9 @@
         {"nativeGetSensorAtIndex", "(JLandroid/hardware/Sensor;I)Z",
          (void *)nativeGetSensorAtIndex},
 
+        {"nativeGetDefaultDeviceSensorAtIndex", "(JLandroid/hardware/Sensor;I)Z",
+         (void *)nativeGetDefaultDeviceSensorAtIndex},
+
         {"nativeGetDynamicSensors", "(JLjava/util/List;)V", (void *)nativeGetDynamicSensors},
 
         {"nativeGetRuntimeSensors", "(JILjava/util/List;)V", (void *)nativeGetRuntimeSensors},
diff --git a/core/jni/android_hardware_SyncFence.cpp b/core/jni/android_hardware_SyncFence.cpp
index b996653..6e94616 100644
--- a/core/jni/android_hardware_SyncFence.cpp
+++ b/core/jni/android_hardware_SyncFence.cpp
@@ -66,6 +66,10 @@
     return fromJlong<Fence>(jPtr)->getSignalTime();
 }
 
+static void SyncFence_incRef(JNIEnv*, jobject, jlong jPtr) {
+    fromJlong<Fence>(jPtr)->incStrong((void*)SyncFence_incRef);
+}
+
 // ----------------------------------------------------------------------------
 // JNI Glue
 // ----------------------------------------------------------------------------
@@ -80,6 +84,7 @@
         { "nGetFd", "(J)I", (void*) SyncFence_getFd },
         { "nWait",  "(JJ)Z", (void*) SyncFence_wait },
         { "nGetSignalTime", "(J)J", (void*) SyncFence_getSignalTime },
+        { "nIncRef", "(J)V", (void*) SyncFence_incRef },
 };
 // clang-format on
 
diff --git a/core/jni/android_os_HwBinder.cpp b/core/jni/android_os_HwBinder.cpp
index 781895e..477bd09 100644
--- a/core/jni/android_os_HwBinder.cpp
+++ b/core/jni/android_os_HwBinder.cpp
@@ -258,14 +258,59 @@
     JHwBinder::SetNativeContext(env, thiz, context);
 }
 
-static void JHwBinder_native_transact(
-        JNIEnv * /* env */,
-        jobject /* thiz */,
-        jint /* code */,
-        jobject /* requestObj */,
-        jobject /* replyObj */,
-        jint /* flags */) {
-    CHECK(!"Should not be here");
+static void JHwBinder_native_transact(JNIEnv *env, jobject thiz, jint code, jobject requestObj,
+                                      jobject replyObj, jint flags) {
+    if (requestObj == NULL) {
+        jniThrowException(env, "java/lang/NullPointerException", NULL);
+        return;
+    }
+    sp<hardware::IBinder> binder = JHwBinder::GetNativeBinder(env, thiz);
+    sp<android::hidl::base::V1_0::IBase> base = new android::hidl::base::V1_0::BpHwBase(binder);
+    hidl_string desc;
+    auto ret = base->interfaceDescriptor(
+            [&desc](const hidl_string &descriptor) { desc = descriptor; });
+    ret.assertOk();
+    // Only the fake hwservicemanager is allowed to be used locally like this.
+    if (desc != "android.hidl.manager@1.2::IServiceManager" &&
+        desc != "android.hidl.manager@1.1::IServiceManager" &&
+        desc != "android.hidl.manager@1.0::IServiceManager") {
+        LOG(FATAL) << "Local binders are not supported!";
+    }
+    if (replyObj == nullptr) {
+        LOG(FATAL) << "Unexpected null replyObj. code: " << code;
+        return;
+    }
+    const hardware::Parcel *request = JHwParcel::GetNativeContext(env, requestObj)->getParcel();
+    sp<JHwParcel> replyContext = JHwParcel::GetNativeContext(env, replyObj);
+    hardware::Parcel *reply = replyContext->getParcel();
+
+    request->setDataPosition(0);
+
+    bool isOneway = (flags & IBinder::FLAG_ONEWAY) != 0;
+    if (!isOneway) {
+        replyContext->setTransactCallback([](auto &replyParcel) {});
+    }
+
+    env->CallVoidMethod(thiz, gFields.onTransactID, code, requestObj, replyObj, flags);
+
+    if (env->ExceptionCheck()) {
+        jthrowable excep = env->ExceptionOccurred();
+        env->ExceptionDescribe();
+        env->ExceptionClear();
+
+        binder_report_exception(env, excep, "Uncaught error or exception in hwbinder!");
+
+        env->DeleteLocalRef(excep);
+    }
+
+    if (!isOneway) {
+        if (!replyContext->wasSent()) {
+            // The implementation never finished the transaction.
+            LOG(ERROR) << "The reply failed to send!";
+        }
+    }
+
+    reply->setDataPosition(0);
 }
 
 static void JHwBinder_native_registerService(
diff --git a/core/jni/android_os_VintfObject.cpp b/core/jni/android_os_VintfObject.cpp
index 1baea2a..a5b2f65 100644
--- a/core/jni/android_os_VintfObject.cpp
+++ b/core/jni/android_os_VintfObject.cpp
@@ -46,6 +46,7 @@
 using vintf::Version;
 using vintf::VintfObject;
 using vintf::Vndk;
+using vintf::CheckFlags::ENABLE_ALL_CHECKS;
 
 template<typename V>
 static inline jobjectArray toJavaStringArray(JNIEnv* env, const V& v) {
@@ -93,12 +94,16 @@
     return toJavaStringArray(env, cStrings);
 }
 
-static jint android_os_VintfObject_verifyWithoutAvb(JNIEnv* env, jclass) {
+static jint android_os_VintfObject_verifyBuildAtBoot(JNIEnv* env, jclass) {
     std::string error;
-    int32_t status = VintfObject::GetInstance()->checkCompatibility(&error,
-            ::android::vintf::CheckFlags::DISABLE_AVB_CHECK);
+    // Use temporary VintfObject, not the shared instance, to release memory
+    // after check.
+    int32_t status =
+            VintfObject::Builder()
+                    .build()
+                    ->checkCompatibility(&error, ENABLE_ALL_CHECKS.disableAvb().disableKernel());
     if (status)
-        LOG(WARNING) << "VintfObject.verifyWithoutAvb() returns " << status << ": " << error;
+        LOG(WARNING) << "VintfObject.verifyBuildAtBoot() returns " << status << ": " << error;
     return status;
 }
 
@@ -170,7 +175,7 @@
 
 static const JNINativeMethod gVintfObjectMethods[] = {
         {"report", "()[Ljava/lang/String;", (void*)android_os_VintfObject_report},
-        {"verifyWithoutAvb", "()I", (void*)android_os_VintfObject_verifyWithoutAvb},
+        {"verifyBuildAtBoot", "()I", (void*)android_os_VintfObject_verifyBuildAtBoot},
         {"getHalNamesAndVersions", "()[Ljava/lang/String;",
          (void*)android_os_VintfObject_getHalNamesAndVersions},
         {"getSepolicyVersion", "()Ljava/lang/String;",
diff --git a/core/jni/android_view_InputEventReceiver.cpp b/core/jni/android_view_InputEventReceiver.cpp
index 5b68e8e..f7d8152 100644
--- a/core/jni/android_view_InputEventReceiver.cpp
+++ b/core/jni/android_view_InputEventReceiver.cpp
@@ -82,6 +82,7 @@
     status_t initialize();
     void dispose();
     status_t finishInputEvent(uint32_t seq, bool handled);
+    bool probablyHasInput();
     status_t reportTimeline(int32_t inputEventId, nsecs_t gpuCompletedTime, nsecs_t presentTime);
     status_t consumeEvents(JNIEnv* env, bool consumeBatches, nsecs_t frameTime,
             bool* outConsumedBatch);
@@ -165,6 +166,10 @@
     return processOutboundEvents();
 }
 
+bool NativeInputEventReceiver::probablyHasInput() {
+    return mInputConsumer.probablyHasInput();
+}
+
 status_t NativeInputEventReceiver::reportTimeline(int32_t inputEventId, nsecs_t gpuCompletedTime,
                                                   nsecs_t presentTime) {
     if (kDebugDispatchCycle) {
@@ -547,6 +552,12 @@
     }
 }
 
+static bool nativeProbablyHasInput(JNIEnv* env, jclass clazz, jlong receiverPtr) {
+    sp<NativeInputEventReceiver> receiver =
+            reinterpret_cast<NativeInputEventReceiver*>(receiverPtr);
+    return receiver->probablyHasInput();
+}
+
 static void nativeReportTimeline(JNIEnv* env, jclass clazz, jlong receiverPtr, jint inputEventId,
                                  jlong gpuCompletedTime, jlong presentTime) {
     if (IdGenerator::getSource(inputEventId) != IdGenerator::Source::INPUT_READER) {
@@ -597,6 +608,7 @@
          (void*)nativeInit},
         {"nativeDispose", "(J)V", (void*)nativeDispose},
         {"nativeFinishInputEvent", "(JIZ)V", (void*)nativeFinishInputEvent},
+        {"nativeProbablyHasInput", "(J)Z", (void*)nativeProbablyHasInput},
         {"nativeReportTimeline", "(JIJJ)V", (void*)nativeReportTimeline},
         {"nativeConsumeBatchedInputEvents", "(JJ)Z", (void*)nativeConsumeBatchedInputEvents},
         {"nativeDump", "(JLjava/lang/String;)Ljava/lang/String;", (void*)nativeDump},
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index db42246..55326da 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -231,6 +231,16 @@
 
 static struct {
     jclass clazz;
+    jmethodID accept;
+} gConsumerClassInfo;
+
+static struct {
+    jclass clazz;
+    jmethodID ctor;
+} gTransactionStatsClassInfo;
+
+static struct {
+    jclass clazz;
     jmethodID ctor;
     jfieldID format;
     jfieldID alphaInterpretation;
@@ -317,6 +327,52 @@
     }
 };
 
+class TransactionCompletedListenerWrapper {
+public:
+    explicit TransactionCompletedListenerWrapper(JNIEnv* env, jobject object) {
+        env->GetJavaVM(&mVm);
+        mTransactionCompletedListenerObject = env->NewGlobalRef(object);
+        LOG_ALWAYS_FATAL_IF(!mTransactionCompletedListenerObject, "Failed to make global ref");
+    }
+
+    ~TransactionCompletedListenerWrapper() {
+        getenv()->DeleteGlobalRef(mTransactionCompletedListenerObject);
+    }
+
+    void callback(nsecs_t latchTime, const sp<Fence>& presentFence,
+                  const std::vector<SurfaceControlStats>& /*stats*/) {
+        JNIEnv* env = getenv();
+        // Adding a strong reference for java SyncFence
+        presentFence->incStrong(0);
+
+        jobject stats =
+                env->NewObject(gTransactionStatsClassInfo.clazz, gTransactionStatsClassInfo.ctor,
+                               latchTime, presentFence.get());
+        env->CallVoidMethod(mTransactionCompletedListenerObject, gConsumerClassInfo.accept, stats);
+        env->DeleteLocalRef(stats);
+        DieIfException(env, "Uncaught exception in TransactionCompletedListener.");
+    }
+
+    static void transactionCallbackThunk(void* context, nsecs_t latchTime,
+                                         const sp<Fence>& presentFence,
+                                         const std::vector<SurfaceControlStats>& stats) {
+        TransactionCompletedListenerWrapper* listener =
+                reinterpret_cast<TransactionCompletedListenerWrapper*>(context);
+        listener->callback(latchTime, presentFence, stats);
+        delete listener;
+    }
+
+private:
+    jobject mTransactionCompletedListenerObject;
+    JavaVM* mVm;
+
+    JNIEnv* getenv() {
+        JNIEnv* env;
+        mVm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6);
+        return env;
+    }
+};
+
 class WindowInfosReportedListenerWrapper : public gui::BnWindowInfosReportedListener {
 public:
     explicit WindowInfosReportedListenerWrapper(JNIEnv* env, jobject listener) {
@@ -1879,10 +1935,16 @@
 
     FrameTimelineInfo ftInfo;
     ftInfo.vsyncId = frameTimelineVsyncId;
-    ftInfo.inputEventId = android::os::IInputConstants::INVALID_INPUT_EVENT_ID;
     transaction->setFrameTimelineInfo(ftInfo);
 }
 
+static void nativeSetDesiredPresentTime(JNIEnv* env, jclass clazz, jlong transactionObj,
+                                        jlong desiredPresentTime) {
+    auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
+
+    transaction->setDesiredPresentTime(desiredPresentTime);
+}
+
 static void nativeAddTransactionCommittedListener(JNIEnv* env, jclass clazz, jlong transactionObj,
                                                   jobject transactionCommittedListenerObject) {
     auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
@@ -1894,6 +1956,17 @@
                                                  context);
 }
 
+static void nativeAddTransactionCompletedListener(JNIEnv* env, jclass clazz, jlong transactionObj,
+                                                  jobject transactionCompletedListenerObject) {
+    auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
+
+    void* context =
+            new TransactionCompletedListenerWrapper(env, transactionCompletedListenerObject);
+    transaction->addTransactionCompletedCallback(TransactionCompletedListenerWrapper::
+                                                         transactionCallbackThunk,
+                                                 context);
+}
+
 static void nativeSetTrustedPresentationCallback(JNIEnv* env, jclass clazz, jlong transactionObj,
                                                  jlong nativeObject,
                                                  jlong trustedPresentationCallbackObject,
@@ -2318,6 +2391,8 @@
             (void*)nativeSurfaceFlushJankData },
     {"nativeAddTransactionCommittedListener", "(JLandroid/view/SurfaceControl$TransactionCommittedListener;)V",
             (void*) nativeAddTransactionCommittedListener },
+    {"nativeAddTransactionCompletedListener", "(JLjava/util/function/Consumer;)V",
+            (void*) nativeAddTransactionCompletedListener },
     {"nativeSetTrustedPresentationCallback", "(JJJLandroid/view/SurfaceControl$TrustedPresentationThresholds;)V",
             (void*) nativeSetTrustedPresentationCallback },
     {"nativeClearTrustedPresentationCallback", "(JJ)V",
@@ -2337,6 +2412,8 @@
     {"getNativeTrustedPresentationCallbackFinalizer", "()J", (void*)getNativeTrustedPresentationCallbackFinalizer },
     {"nativeGetStalledTransactionInfo", "(I)Landroid/gui/StalledTransactionInfo;",
             (void*) nativeGetStalledTransactionInfo },
+    {"nativeSetDesiredPresentTime", "(JJ)V",
+            (void*) nativeSetDesiredPresentTime },
         // clang-format on
 };
 
@@ -2539,6 +2616,16 @@
     gTransactionCommittedListenerClassInfo.onTransactionCommitted =
             GetMethodIDOrDie(env, transactionCommittedListenerClazz, "onTransactionCommitted",
                              "()V");
+    jclass consumerClazz = FindClassOrDie(env, "java/util/function/Consumer");
+    gConsumerClassInfo.clazz = MakeGlobalRefOrDie(env, consumerClazz);
+    gConsumerClassInfo.accept =
+            GetMethodIDOrDie(env, consumerClazz, "accept", "(Ljava/lang/Object;)V");
+
+    jclass transactionStatsClazz =
+            FindClassOrDie(env, "android/view/SurfaceControl$TransactionStats");
+    gTransactionStatsClassInfo.clazz = MakeGlobalRefOrDie(env, transactionStatsClazz);
+    gTransactionStatsClassInfo.ctor =
+            GetMethodIDOrDie(env, gTransactionStatsClassInfo.clazz, "<init>", "(JJ)V");
 
     jclass displayDecorationSupportClazz =
             FindClassOrDie(env, "android/hardware/graphics/common/DisplayDecorationSupport");
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 7c5885a..7e325a5 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -2187,6 +2187,7 @@
   }
 
   if (multiuser_get_app_id(uid) == AID_NETWORK_STACK) {
+    capabilities |= (1LL << CAP_WAKE_ALARM);
     capabilities |= (1LL << CAP_NET_ADMIN);
     capabilities |= (1LL << CAP_NET_BROADCAST);
     capabilities |= (1LL << CAP_NET_BIND_SERVICE);
diff --git a/core/jni/hwbinder/EphemeralStorage.cpp b/core/jni/hwbinder/EphemeralStorage.cpp
index 95bb42e..ef0750c 100644
--- a/core/jni/hwbinder/EphemeralStorage.cpp
+++ b/core/jni/hwbinder/EphemeralStorage.cpp
@@ -164,7 +164,7 @@
             }
 
             default:
-                CHECK(!"Should not be here");
+                CHECK(!"Should not be here") << "Item type: " << item.mType;
         }
     }
 
diff --git a/core/proto/OWNERS b/core/proto/OWNERS
index db391f7..a854e36 100644
--- a/core/proto/OWNERS
+++ b/core/proto/OWNERS
@@ -18,6 +18,7 @@
 per-file apphibernationservice.proto = file:/core/java/android/apphibernation/OWNERS
 per-file android/hardware/sensorprivacy.proto = ntmyren@google.com,evanseverson@google.com
 per-file background_install_control.proto = wenhaowang@google.com,georgechan@google.com,billylau@google.com
+per-file android/content/intent.proto = file:/PACKAGE_MANAGER_OWNERS
 
 # Biometrics
 jaggies@google.com
@@ -31,5 +32,3 @@
 
 # Accessibility
 pweaver@google.com
-hongmingjin@google.com
-cbrower@google.com
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index 382a82c..52e0124 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -395,6 +395,8 @@
     optional bool should_refresh_activity_for_camera_compat = 40;
     optional bool should_refresh_activity_via_pause_for_camera_compat = 41;
     optional bool should_override_min_aspect_ratio = 42;
+    optional bool should_ignore_orientation_request_loop = 43;
+    optional bool should_override_force_resize_app = 44;
 }
 
 /* represents WindowToken */
@@ -404,7 +406,7 @@
     optional WindowContainerProto window_container = 1;
     optional int32 hash_code = 2;
     repeated WindowStateProto windows = 3 [deprecated=true];
-    optional bool waiting_to_show = 5;
+    optional bool waiting_to_show = 5 [deprecated=true];
     optional bool paused = 6;
 }
 
diff --git a/core/proto/android/view/displaycutout.proto b/core/proto/android/view/displaycutout.proto
index 72d5303..53155d2 100644
--- a/core/proto/android/view/displaycutout.proto
+++ b/core/proto/android/view/displaycutout.proto
@@ -32,4 +32,5 @@
     optional .android.graphics.RectProto bound_right = 5;
     optional .android.graphics.RectProto bound_bottom = 6;
     optional .android.graphics.RectProto waterfall_insets = 7;
+    repeated int32 side_overrides = 8;
 }
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index c6209dd..5f3f641 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -578,6 +578,7 @@
     <protected-broadcast android:name="com.android.settings.network.SWITCH_TO_SUBSCRIPTION" />
     <protected-broadcast android:name="com.android.settings.wifi.action.NETWORK_REQUEST" />
 
+    <protected-broadcast android:name="android.app.action.KEYGUARD_PRIVATE_NOTIFICATIONS_CHANGED" />
     <protected-broadcast android:name="NotificationManagerService.TIMEOUT" />
     <protected-broadcast android:name="NotificationHistoryDatabase.CLEANUP" />
     <protected-broadcast android:name="ScheduleConditionProvider.EVALUATE" />
@@ -1708,6 +1709,7 @@
 
     <!-- @SystemApi Allows camera access by Headless System User 0 when device is running in
             HSUM Mode.
+         @FlaggedApi("com.android.internal.camera.flags.camera_hsum_permission")
     @hide -->
     <permission android:name="android.permission.CAMERA_HEADLESS_SYSTEM_USER"
         android:permissionGroup="android.permission-group.UNDEFINED"
@@ -5212,6 +5214,14 @@
     <permission android:name="android.permission.BIND_REMOTE_DISPLAY"
         android:protectionLevel="signature" />
 
+    <!-- Must be required by a android.media.tv.ad.TvAdService to ensure that only the system can
+         bind to it.
+         <p>Protection level: signature|privileged
+         @FlaggedApi("android.media.tv.flags.enable_ad_service_fw")
+    -->
+    <permission android:name="android.permission.BIND_TV_AD_SERVICE"
+                android:protectionLevel="signature|privileged" />
+
     <!-- Must be required by a {@link android.media.tv.TvInputService}
          to ensure that only the system can bind to it.
          <p>Protection level: signature|privileged
@@ -7145,6 +7155,16 @@
         android:label="@string/permlab_foregroundServiceFileManagement"
         android:protectionLevel="normal|instant" />
 
+    <!-- @FlaggedApi("android.content.pm.introduce_media_processing_type")
+         Allows a regular application to use {@link android.app.Service#startForeground
+         Service.startForeground} with the type "mediaProcessing".
+         <p>Protection level: normal|instant
+    -->
+    <permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PROCESSING"
+                android:description="@string/permdesc_foregroundServiceMediaProcessing"
+                android:label="@string/permlab_foregroundServiceMediaProcessing"
+                android:protectionLevel="normal|instant" />
+
     <!-- Allows a regular application to use {@link android.app.Service#startForeground
          Service.startForeground} with the type "specialUse".
          <p>Protection level: normal|appop|instant
@@ -7691,6 +7711,13 @@
     <permission android:name="android.permission.MONITOR_KEYBOARD_BACKLIGHT"
                 android:protectionLevel="signature" />
 
+    <!-- Allows low-level access to monitor sticky modifier state changes when A11Y Sticky keys
+         feature is enabled.
+         <p>Not for use by third-party applications.
+         @hide -->
+    <permission android:name="android.permission.MONITOR_STICKY_MODIFIER_STATE"
+                android:protectionLevel="signature" />
+
     <uses-permission android:name="android.permission.HANDLE_QUERY_PACKAGE_RESTART" />
 
     <!-- Allows financed device kiosk apps to perform actions on the Device Lock service
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 8e89075..8281462 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -1906,8 +1906,7 @@
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Naweek"</string>
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"Geleentheid"</string>
     <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Slaap"</string>
-    <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
-    <skip />
+    <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Bestuur deur <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Aan"</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Af"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> demp sekere klanke"</string>
@@ -2366,4 +2365,5 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Werk 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Toets"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Gemeenskaplik"</string>
+    <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
 </resources>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index b38257c..ff7dee8 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -1906,8 +1906,7 @@
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"á‹šáˆłáˆáŠ•á‰” ኄሚፍቔ ቀናቔ"</string>
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"ክሔተቔ"</string>
     <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"መተኛቔ"</string>
-    <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
-    <skip />
+    <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"በ<xliff:g id="APP_NAME">%1$s</xliff:g> ዹሚተዳደር"</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"በርቷል"</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"ጠፍቷል"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> አንዳንዔ ዔምጟቜን ኄዚዘጋ ነው"</string>
@@ -2366,4 +2365,5 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"ሄራ 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"ሙኚራ"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"ዚጋራ"</string>
+    <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
 </resources>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index a77fc65..0cc49a5 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -1910,8 +1910,7 @@
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Ù†Ù‡Ű§ÙŠŰ© Ű§Ù„ŰŁŰłŰšÙˆŰč"</string>
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"Ű­ŰŻŰ«"</string>
     <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Ű§Ù„Ù†ÙˆÙ…"</string>
-    <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
-    <skip />
+    <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"ŰȘŰ­ŰȘ ۄۯۧ۱۩ \"<xliff:g id="APP_NAME">%1$s</xliff:g>\""</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"مفŰčَّل"</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"ŰșÙŠŰ± مفŰčَّل"</string>
     <string name="muted_by" msgid="91464083490094950">"يŰčمل <xliff:g id="THIRD_PARTY">%1$s</xliff:g> Űčلى كŰȘم ŰšŰč۶ Ű§Ù„ŰŁŰ”ÙˆŰ§ŰȘ."</string>
@@ -2317,7 +2316,7 @@
     <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7089318886628390827">"Ű„ŰČŰ§Ù„Ű© Ű§Ù„Ű­ŰžŰ±"</string>
     <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Ű§Ù„ŰźŰ”ÙˆŰ”ÙŠŰ© في ŰŹÙ‡Ű§ŰČ Ű§Ù„Ű§ŰłŰȘŰŽŰčۧ۱"</string>
     <string name="splash_screen_view_icon_description" msgid="180638751260598187">"Ű±Ù…ŰČ Ű§Ù„ŰȘŰ·ŰšÙŠÙ‚"</string>
-    <string name="splash_screen_view_branding_description" msgid="7911129347402728216">"Ű§Ù„Ű”ÙˆŰ±Ű© Ű§Ù„Ű°Ù‡Ù†ÙŠŰ© للŰčÙ„Ű§Ù…Ű© Ű§Ù„ŰȘŰŹŰ§Ű±ÙŠŰ© للŰȘŰ·ŰšÙŠÙ‚"</string>
+    <string name="splash_screen_view_branding_description" msgid="7911129347402728216">"Ù‡ÙˆÙŠŰ© Ű§Ù„ŰčÙ„Ű§Ù…Ű© Ű§Ù„ŰȘŰŹŰ§Ű±ÙŠŰ© للŰȘŰ·ŰšÙŠÙ‚"</string>
     <string name="view_and_control_notification_title" msgid="4300765399209912240">"Ű§Ù„ŰȘŰ­Ù‚Ù‘Ù‚ من Ű„ŰčۯۧۯۧŰȘ Ű§Ù„ÙˆŰ”ÙˆÙ„"</string>
     <string name="view_and_control_notification_content" msgid="8003766498562604034">"يمكن Ù„ŰźŰŻÙ…Ű© <xliff:g id="SERVICE_NAME">%s</xliff:g> Ű§Ù„Ű§Ű·Ù‘Ù„Ű§Űč Űčلى ێۧێŰȘك ÙˆŰ§Ù„ŰȘŰ­ÙƒÙ‘Ù… ÙÙŠÙ‡Ű§. Ű§Ù†Ù‚Ű± Ù„Ù…Ű±Ű§ŰŹŰčŰ© Ű§Ù„Ű„ŰčۯۧۯۧŰȘ."</string>
     <string name="ui_translation_accessibility_translated_text" msgid="3197547218178944544">"<xliff:g id="MESSAGE">%1$s</xliff:g> (مُŰȘŰ±ŰŹÙŽÙ…)."</string>
@@ -2344,9 +2343,9 @@
     <string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"ÙŠŰłÙ…Ű­ Ù‡Ű°Ű§ Ű§Ù„Ű„Ű°Ù† للŰȘŰ·ŰšÙŠÙ‚ Ű§Ù„Ù…Ű”Ű§Ű­Űš ۚۚۯۥ Ű§Ù„ŰźŰŻÙ…Ű§ŰȘ Ű§Ù„ŰȘي ŰȘŰčمل في Ű§Ù„Ù…Ù‚ŰŻÙ‘Ù…Ű© من Ű§Ù„ŰźÙ„ÙÙŠŰ©."</string>
     <string name="mic_access_on_toast" msgid="2666925317663845156">"Ű§Ù„Ù…ÙŠÙƒŰ±ÙˆÙÙˆÙ† مŰȘۭۧ."</string>
     <string name="mic_access_off_toast" msgid="8111040892954242437">"ŰȘم ۭ۞۱ Ű§Ù„Ù…ÙŠÙƒŰ±ÙˆÙÙˆÙ†."</string>
-    <string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"يŰȘŰčŰ°Ù‘Ű± ۄۏ۱ۧۥ Ù†ŰłŰź Ù…Ű·Ű§ŰšÙ‚ Ù„Ù…Ű­ŰȘوى ŰŹÙ‡Ű§ŰČك Ű„Ù„Ù‰ Ű§Ù„ŰŽŰ§ŰŽŰ©"</string>
+    <string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"يŰȘŰčŰ°Ù‘Ű± Ű§Ù„ŰšŰ«Ù‘ Űčلى Ű§Ù„ŰŽŰ§ŰŽŰ©"</string>
     <string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"ÙŠÙŰ±ŰŹÙ‰ ۧ۳ŰȘŰźŰŻŰ§Ù… ÙƒŰ§ŰšÙ„ ۹۟۱ ÙˆŰ„Űčۧۯ۩ Ű§Ù„Ù…Ű­Ű§ÙˆÙ„Ű©."</string>
-    <string name="connected_display_thermally_unavailable_notification_content" msgid="9205758199439955949">"ŰŹÙ‡Ű§ŰČك ŰłŰ§ŰźÙ† للŰșŰ§ÙŠŰ© ÙˆÙ„Ű§ يمكنه ۄۏ۱ۧۥ Ù†ŰłŰź Ù…Ű·Ű§ŰšÙ‚ Ù„Ù„Ù…Ű­ŰȘوى Ű„Ù„Ù‰ Ű§Ù„ŰŽŰ§ŰŽŰ© Ű„Ù„Ù‰ ŰŁÙ† ŰȘÙ†ŰźÙŰ¶ ۭ۱ۧ۱ŰȘه."</string>
+    <string name="connected_display_thermally_unavailable_notification_content" msgid="9205758199439955949">"ŰŹÙ‡Ű§ŰČك ŰłŰ§ŰźÙ† للŰșŰ§ÙŠŰ© ÙˆÙ„Ű§ يمكنه Ű§Ù„ŰšŰ« Űčلى Ű§Ù„ŰŽŰ§ŰŽŰ© Ű„Ù„Ù‰ ŰŁÙ† ŰȘÙ†ŰźÙŰ¶ ۭ۱ۧ۱ŰȘه."</string>
     <string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual Screen"</string>
     <string name="concurrent_display_notification_active_title" msgid="4892473462327943673">"‏ميŰČŰ© Dual Screen مفŰčÙ‘Ù„Ű©"</string>
     <string name="concurrent_display_notification_active_content" msgid="5889355473710601270">"ÙŠŰłŰȘŰźŰŻÙ… \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" كلŰȘۧ Ű§Ù„ŰŽŰ§ŰŽŰȘين لŰč۱۶ Ű§Ù„Ù…Ű­ŰȘوى."</string>
@@ -2370,4 +2369,5 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"ملف Ű§Ù„Űčمل 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"ملف ŰŽŰźŰ”ÙŠ ŰȘŰŹŰ±ÙŠŰšÙŠ"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"ملف ŰŽŰźŰ”ÙŠ Ù…ŰŽŰȘŰ±Ùƒ"</string>
+    <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
 </resources>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index f85f61c..ddc2367 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -1177,7 +1177,7 @@
     <string name="yes" msgid="9069828999585032361">"àŠ àŠżàŠ• àŠ†àŠ›à§‡"</string>
     <string name="no" msgid="5122037903299899715">"àŠŹàŠŸàŠ€àŠżàŠČ àŠ•à§°àŠ•"</string>
     <string name="dialog_alert_title" msgid="651856561974090712">"àŠźàŠšà§‹àŠŻà§‹àŠ— àŠŠàŠżàŠŹ"</string>
-    <string name="loading" msgid="3138021523725055037">"àŠČ\'àŠĄ àŠ•à§°àŠż àŠ„àŠ•àŠŸ àŠčà§ˆàŠ›à§‡…"</string>
+    <string name="loading" msgid="3138021523725055037">"àŠČ’àŠĄ àŠčৈ àŠ†àŠ›à§‡…"</string>
     <string name="capital_on" msgid="2770685323900821829">"àŠ…àŠš àŠ•à§°àŠ•"</string>
     <string name="capital_off" msgid="7443704171014626777">"àŠ…àŠ« àŠ•à§°àŠ•"</string>
     <string name="checked" msgid="9179896827054513119">"àŠŸàŠżàŠ• àŠšàŠżàŠčà§àŠš àŠŠàŠżàŠŻàŠŒàŠŸ àŠčà§ˆàŠ›à§‡"</string>
@@ -1906,8 +1906,7 @@
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"àŠžàŠȘà§àŠ€àŠŸàŠč àŠ…àŠšà§àŠ€"</string>
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"àŠ•àŠŸàŠ°à§àŠŻàŠ•à§à§°àŠź"</string>
     <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"àŠšàŠżàŠŠà§à§°àŠŸà§°àŠ€"</string>
-    <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
-    <skip />
+    <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"<xliff:g id="APP_NAME">%1$s</xliff:g>àŠ àŠȘà§°àŠżàŠšàŠŸàŠČàŠšàŠŸ àŠ•à§°àŠŸ"</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"àŠ…àŠš àŠ†àŠ›à§‡"</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"àŠ…àŠ« àŠ†àŠ›à§‡"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g>àŠ àŠ•àŠżàŠ›à§àŠźàŠŸàŠš àŠ§à§àŠŹàŠšàŠż àŠźàŠżàŠ‰àŠŸ àŠ•à§°àŠż àŠ†àŠ›à§‡"</string>
@@ -2366,4 +2365,5 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"àŠ•à§°à§àŠźàŠžà§àŠ„àŠŸàŠš à§©"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"àŠȘà§°à§€àŠ•à§àŠ·àŠŸ"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"àŠ¶à§àŠŹà§‡àŠŻàŠŒàŠŸà§° àŠ•à§°àŠŸ"</string>
+    <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
 </resources>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index ed1e340..794e26a 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -1906,8 +1906,7 @@
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Həftə sonu"</string>
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"Tədbir"</string>
     <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Yuxu vaxtı"</string>
-    <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
-    <skip />
+    <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"<xliff:g id="APP_NAME">%1$s</xliff:g> idarə edir"</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Aktiv"</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Deaktiv"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> bəzi səsləri səssiz rejimə salır"</string>
@@ -2366,4 +2365,5 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"İß 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Kommunal"</string>
+    <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
 </resources>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index ea15cee..8df8b87 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -1907,8 +1907,7 @@
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Vikend"</string>
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"Događaj"</string>
     <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Spavanje"</string>
-    <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
-    <skip />
+    <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Upravlja: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Uključeno"</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Isključeno"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> isključuje neke zvuke"</string>
@@ -2367,4 +2366,5 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Posao 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Zajedničko"</string>
+    <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
 </resources>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index 8da0d1a..13801d0 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -1908,8 +1908,7 @@
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Đ’Ń‹Ń…Đ°ĐŽĐœŃ‹Ń"</string>
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"ĐŸĐ°ĐŽĐ·Đ”Ń"</string>
     <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Đ ŃĐ¶Ń‹ĐŒ ŃĐœŃƒ"</string>
-    <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
-    <skip />
+    <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"ПаЮ ĐșіраĐČĐ°ĐœĐœĐ”ĐŒ ĐżŃ€Đ°ĐłŃ€Đ°ĐŒŃ‹ \"<xliff:g id="APP_NAME">%1$s</xliff:g>\""</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"ĐŁĐșĐ»ŃŽŃ‡Đ°ĐœĐ°"</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"ВыĐșĐ»ŃŽŃ‡Đ°ĐœĐ°"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> ĐČыĐșлючаД ĐœĐ”ĐșĐ°Ń‚ĐŸŃ€Ń‹Ń гуĐșі"</string>
@@ -2368,4 +2367,5 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"ĐŸŃ€Đ°Ń†ĐŸŃžĐœŃ‹ 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"бэстаĐČы"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"ĐĄŃƒĐżĐŸĐ»ŃŒĐœŃ‹"</string>
+    <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
 </resources>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index e97d9dc..e0e9b09 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -1906,8 +1906,7 @@
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"ĐĄŃŠĐ±ĐŸŃ‚Đ° Đž ĐœĐ”ĐŽĐ”Đ»Ń"</string>
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"ХъбОтОД"</string>
     <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Đ’Ń€Đ”ĐŒĐ” за ŃŃŠĐœ"</string>
-    <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
-    <skip />
+    <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"УпраĐČĐ»ŃĐČа сД ĐŸŃ‚ <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"ВĐșĐ»."</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"ИзĐșĐ»."</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> заглушаĐČа ĐœŃĐșĐŸĐž Đ·ĐČуцо"</string>
@@ -2340,9 +2339,9 @@
     <string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"Đ Đ°Đ·Ń€Đ”ŃˆĐ°ĐČа ĐœĐ° ĐŽĐ°ĐŽĐ”ĐœĐŸ ĐżŃ€ĐžĐŽŃ€ŃƒĐ¶Đ°ĐČĐ°Ń‰ĐŸ ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžĐ” Ўа стартора услугО ĐœĐ° ĐżŃ€Đ”ĐŽĐ”Đœ ĐżĐ»Đ°Đœ, ĐŽĐŸĐșĐ°Ń‚ĐŸ сД ĐžĐ·ĐżŃŠĐ»ĐœŃĐČа ĐœĐ° Đ·Đ°ĐŽĐ”Đœ ĐżĐ»Đ°Đœ."</string>
     <string name="mic_access_on_toast" msgid="2666925317663845156">"МоĐșŃ€ĐŸŃ„ĐŸĐœŃŠŃ‚ Đ” ĐœĐ°Đ»ĐžŃ†Đ”"</string>
     <string name="mic_access_off_toast" msgid="8111040892954242437">"МоĐșŃ€ĐŸŃ„ĐŸĐœŃŠŃ‚ Đ” Đ±Đ»ĐŸĐșĐžŃ€Đ°Đœ"</string>
-    <string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"ĐĐ” ĐŒĐŸĐ¶Đ” Ўа сД ĐșĐŸĐżĐžŃ€Đ° ĐŸĐłĐ»Đ”ĐŽĐ°Đ»ĐœĐŸ ĐœĐ° ĐŽĐžŃĐżĐ»Đ”Ń"</string>
+    <string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"ĐĐ” ĐŒĐŸĐ¶Đ” Ўа сД ĐŽŃƒĐ±Đ»ĐžŃ€Đ° ĐœĐ° ĐŽĐžŃĐżĐ»Đ”Ń"</string>
     <string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"Đ˜Đ·ĐżĐŸĐ»Đ·ĐČаĐčтД Юруг ĐșабДл Đž ĐŸĐżĐžŃ‚Đ°ĐčтД ĐŸŃ‚ĐœĐŸĐČĐŸ"</string>
-    <string name="connected_display_thermally_unavailable_notification_content" msgid="9205758199439955949">"ĐŁŃŃ‚Ń€ĐŸĐčстĐČĐŸŃ‚ĐŸ ĐČĐž Đ” тĐČърЎД Ń‚ĐŸĐżĐ»ĐŸ Đž ĐœŃĐŒĐ° Ўа ĐŒĐŸĐ¶Đ” Ўа ĐŽŃƒĐ±Đ»ĐžŃ€Đ° ĐœĐ° Đ”ĐșŃ€Đ°ĐœĐ°, прДЎО Ўа сД ĐŸŃ…Đ»Đ°ĐŽĐž"</string>
+    <string name="connected_display_thermally_unavailable_notification_content" msgid="9205758199439955949">"ĐŁŃŃ‚Ń€ĐŸĐčстĐČĐŸŃ‚ĐŸ ĐČĐž Đ” тĐČърЎД Ń‚ĐŸĐżĐ»ĐŸ. Đ”ŃƒĐ±Đ»ĐžŃ€Đ°ĐœĐ”Ń‚ĐŸ щД Đ” ĐČŃŠĐ·ĐŒĐŸĐ¶ĐœĐŸ, слДЎ ĐșĐ°Ń‚ĐŸ сД ĐŸŃ…Đ»Đ°ĐŽĐž"</string>
     <string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual screen"</string>
     <string name="concurrent_display_notification_active_title" msgid="4892473462327943673">"Đ€ŃƒĐœĐșцоята Dual Screen Đ” ĐČĐșĐ»ŃŽŃ‡Đ”ĐœĐ°"</string>
     <string name="concurrent_display_notification_active_content" msgid="5889355473710601270">"<xliff:g id="APP_NAME">%1$s</xliff:g> ĐžĐ·ĐżĐŸĐ»Đ·ĐČа Đž ĐŽĐČата Đ”ĐșŃ€Đ°ĐœĐ°, за Ўа ĐżĐŸĐșазĐČа ŃŃŠĐŽŃŠŃ€Đ¶Đ°ĐœĐžĐ”"</string>
@@ -2366,4 +2365,5 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"ĐĄĐ»ŃƒĐ¶Đ”Đ±ĐœĐž 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"йДстĐČĐ°ĐœĐ”"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"ОбщО"</string>
+    <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
 </resources>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index fa2c283..cf8c64d 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -1906,8 +1906,7 @@
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"àŠžàŠȘà§àŠ€àŠŸàŠčàŠŸàŠšà§àŠ€"</string>
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"àŠ‡àŠ­à§‡àŠšà§àŠŸ"</string>
     <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"àŠ˜à§àŠźàŠŸàŠšà§‹àŠ° àŠžàŠźàŠŻàŠŒ"</string>
-    <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
-    <skip />
+    <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"<xliff:g id="APP_NAME">%1$s</xliff:g> àŠźà§àŠŻàŠŸàŠšà§‡àŠœ àŠ•àŠ°à§‡"</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"àŠšàŠŸàŠČু àŠ†àŠ›à§‡"</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"àŠŹàŠšà§àŠ§ àŠ†àŠ›à§‡"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> àŠ•àŠżàŠ›à§ àŠžàŠŸàŠ‰àŠšà§àŠĄàŠ•à§‡ àŠźàŠżàŠ‰àŠŸ àŠ•àŠ°à§‡ àŠŠàŠżàŠšà§àŠ›à§‡"</string>
@@ -2366,4 +2365,5 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"à§©àŠŻàŠŒ àŠ…àŠ«àŠżàŠž"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"àŠȘàŠ°à§€àŠ•à§àŠ·àŠŸ"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"àŠ•àŠźàŠżàŠ‰àŠšàŠŸàŠČ"</string>
+    <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
 </resources>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index cd941ce..ef941e19f 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -1902,13 +1902,12 @@
     <string name="zen_mode_rule_name_combination" msgid="7174598364351313725">"<xliff:g id="FIRST">%1$s</xliff:g>/<xliff:g id="REST">%2$s</xliff:g>"</string>
     <string name="toolbar_collapse_description" msgid="8009920446193610996">"Suzi"</string>
     <string name="zen_mode_feature_name" msgid="3785547207263754500">"Ne ometaj"</string>
-    <string name="zen_mode_downtime_feature_name" msgid="5886005761431427128">"Neaktivnost"</string>
+    <string name="zen_mode_downtime_feature_name" msgid="5886005761431427128">"Vrijeme mirovanja"</string>
     <string name="zen_mode_default_weeknights_name" msgid="7902108149994062847">"Radni dan uvečer"</string>
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Vikend"</string>
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"Događaj"</string>
     <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Spavanje"</string>
-    <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
-    <skip />
+    <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Upravlja <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Uključeno"</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Isključeno"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> isključuje neke zvukove"</string>
@@ -2367,4 +2366,5 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"3. poslovno"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Testno"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Opće"</string>
+    <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
 </resources>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index fbdcf56..ec14677 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -1907,8 +1907,7 @@
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Cap de setmana"</string>
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"Esdeveniment"</string>
     <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Mentre dormo"</string>
-    <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
-    <skip />
+    <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Gestionat per <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Activat"</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Desactivat"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> està silenciant alguns sons"</string>
@@ -2341,9 +2340,9 @@
     <string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"Permet que una aplicació complementària iniciï serveis en primer pla des d\'un segon pla."</string>
     <string name="mic_access_on_toast" msgid="2666925317663845156">"El micròfon està disponible"</string>
     <string name="mic_access_off_toast" msgid="8111040892954242437">"El micròfon està bloquejat"</string>
-    <string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"No es pot projectar a la pantalla"</string>
+    <string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"No es pot duplicar a la pantalla"</string>
     <string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"Utilitza un altre cable i torna-ho a provar"</string>
-    <string name="connected_display_thermally_unavailable_notification_content" msgid="9205758199439955949">"El dispositiu està massa calent i no pot projectar a la pantalla fins que es refredi"</string>
+    <string name="connected_display_thermally_unavailable_notification_content" msgid="9205758199439955949">"El dispositiu està massa calent i no pot duplicar a la pantalla fins que es refredi"</string>
     <string name="concurrent_display_notification_name" msgid="1526911253558311131">"Pantalla dual"</string>
     <string name="concurrent_display_notification_active_title" msgid="4892473462327943673">"La pantalla dual està activada"</string>
     <string name="concurrent_display_notification_active_content" msgid="5889355473710601270">"<xliff:g id="APP_NAME">%1$s</xliff:g> està utilitzant les dues pantalles per mostrar contingut"</string>
@@ -2367,4 +2366,5 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Treball 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Prova"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Compartit"</string>
+    <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
 </resources>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 156f18d..e518de4 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -1908,8 +1908,7 @@
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Víkend"</string>
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"Událost"</string>
     <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Spánek"</string>
-    <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
-    <skip />
+    <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Spravováno aplikací <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Zapnuto"</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Vypnuto"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> vypíná určité zvuky"</string>
@@ -2368,4 +2367,5 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Práce 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Komunální"</string>
+    <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
 </resources>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 9f89293..d3f8550 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -367,11 +367,11 @@
     <string name="permlab_receiveMms" msgid="4000650116674380275">"modtage tekstbeskeder (mms)"</string>
     <string name="permdesc_receiveMms" msgid="958102423732219710">"Tillader, at appen kan modtage og behandle mms-beskeder. Det betyder, at appen kan overvåge eller slette de beskeder, der sendes til din enhed, uden at vise dem til dig."</string>
     <string name="permlab_bindCellBroadcastService" msgid="586746677002040651">"Videresend Cell Broadcast-meddelelser"</string>
-    <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"Tillader, at appen bindes til Cell Broadcast-modulet, så Cell Broadcast-meddelelser kan videresendes, når de modtages. I nogle områder sendes der Cell Broadcast-underretninger for at advare dig om nødsituationer. Ondsindede apps kan forstyrre effektiviteten eller driften af din enhed, når den modtager en Cell Broadcast-meddelelse om en nødsituation."</string>
+    <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"Tillader, at appen bindes til Cell Broadcast-modulet, så Cell Broadcast-meddelelser kan videresendes, når de modtages. I regioner sendes der Cell Broadcast-underretninger for at advare dig om nødsituationer. Ondsindede apps kan forstyrre effektiviteten eller driften af din enhed, når den modtager en Cell Broadcast-meddelelse om en nødsituation."</string>
     <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"Administrere igangværende opkald"</string>
     <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"Giver appen tilladelse til at se oplysninger om igangværende opkald på din enhed og styre disse opkald."</string>
     <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"læse Cell Broadcast-meddelelser"</string>
-    <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"Tillader, at appen læser Cell Broadcast-underretninger, der modtages af din enhed. I nogle områder sendes der Cell Broadcast-underretninger for at advare om nødsituationer. Ondsindede apps kan forstyrre ydelsen eller driften af ​din ​enhed, når der modtages en Cell Broadcast-meddelelse om en nødsituation."</string>
+    <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"Tillader, at appen læser Cell Broadcast-underretninger, der modtages af din enhed. I regioner sendes der Cell Broadcast-underretninger for at advare om nødsituationer. Ondsindede apps kan forstyrre ydelsen eller driften af ​din ​enhed, når der modtages en Cell Broadcast-meddelelse om en nødsituation."</string>
     <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"læse feeds, jeg abonnerer på"</string>
     <string name="permdesc_subscribedFeedsRead" msgid="6911349196661811865">"Tillader, at appen kan hente oplysninger om de feeds, der synkroniseres."</string>
     <string name="permlab_sendSms" msgid="7757368721742014252">"Send og se sms-beskeder"</string>
@@ -1906,8 +1906,7 @@
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Weekend"</string>
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"Begivenhed"</string>
     <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Sover"</string>
-    <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
-    <skip />
+    <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Administreres af <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Til"</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Fra"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> slår nogle lyde fra"</string>
@@ -1951,14 +1950,14 @@
     <string name="user_creation_adding" msgid="7305185499667958364">"Vil du give <xliff:g id="APP">%1$s</xliff:g> tilladelse til at oprette en nye bruger med <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
     <string name="supervised_user_creation_label" msgid="6884904353827427515">"Tilføj en administreret bruger"</string>
     <string name="language_selection_title" msgid="52674936078683285">"Tilføj et sprog"</string>
-    <string name="country_selection_title" msgid="5221495687299014379">"Områdeindstilling"</string>
+    <string name="country_selection_title" msgid="5221495687299014379">"Regionpræferencer"</string>
     <string name="search_language_hint" msgid="7004225294308793583">"Angiv sprog"</string>
     <string name="language_picker_section_suggested" msgid="6556199184638990447">"Foreslået"</string>
     <string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"Forslag"</string>
     <string name="language_picker_section_suggested_bilingual" msgid="5932198319583556613">"Foreslåede sprog"</string>
-    <string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"Foreslåede områder"</string>
+    <string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"Foreslåede regioner"</string>
     <string name="language_picker_section_all" msgid="1985809075777564284">"Alle sprog"</string>
-    <string name="region_picker_section_all" msgid="756441309928774155">"Alle områder"</string>
+    <string name="region_picker_section_all" msgid="756441309928774155">"Alle regioner"</string>
     <string name="locale_search_menu" msgid="6258090710176422934">"Søg"</string>
     <string name="app_suspended_title" msgid="888873445010322650">"Appen er ikke tilgængelig"</string>
     <string name="app_suspended_default_message" msgid="6451215678552004172">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> er ikke tilgængelig lige nu. Dette administreres af <xliff:g id="APP_NAME_1">%2$s</xliff:g>."</string>
@@ -1998,7 +1997,7 @@
     <string name="profile_encrypted_title" msgid="9001208667521266472">"Nogle funktioner er begrænsede"</string>
     <string name="profile_encrypted_detail" msgid="5279730442756849055">"Arbejdsprofilen er låst"</string>
     <string name="profile_encrypted_message" msgid="1128512616293157802">"Tryk for at låse profilen op"</string>
-    <string name="usb_mtp_launch_notification_title" msgid="774319638256707227">"Tilsluttet <xliff:g id="PRODUCT_NAME">%1$s</xliff:g>"</string>
+    <string name="usb_mtp_launch_notification_title" msgid="774319638256707227">"Forbundet <xliff:g id="PRODUCT_NAME">%1$s</xliff:g>"</string>
     <string name="usb_mtp_launch_notification_description" msgid="6942535713629852684">"Tryk for at se filer"</string>
     <string name="pin_target" msgid="8036028973110156895">"Fastgør"</string>
     <string name="pin_specific_target" msgid="7824671240625957415">"Fastgør <xliff:g id="LABEL">%1$s</xliff:g>"</string>
@@ -2051,7 +2050,7 @@
     <string name="autofill_save_type_password" msgid="5624528786144539944">"adgangskode"</string>
     <string name="autofill_save_type_address" msgid="3111006395818252885">"adresse"</string>
     <string name="autofill_save_type_credit_card" msgid="3583795235862046693">"kreditkort"</string>
-    <string name="autofill_save_type_debit_card" msgid="3169397504133097468">"betalingskort"</string>
+    <string name="autofill_save_type_debit_card" msgid="3169397504133097468">"debetkort"</string>
     <string name="autofill_save_type_payment_card" msgid="6555012156728690856">"betalingskort"</string>
     <string name="autofill_save_type_generic_card" msgid="1019367283921448608">"kort"</string>
     <string name="autofill_save_type_username" msgid="1018816929884640882">"brugernavn"</string>
@@ -2366,4 +2365,5 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Arbejde 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Fælles"</string>
+    <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
 </resources>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 182b974..514d695 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -355,7 +355,7 @@
     <string name="permlab_fullScreenIntent" msgid="4310888199502509104">"Benachrichtigungen auf einem gesperrten Gerät als Vollbildaktivitäten anzeigen"</string>
     <string name="permdesc_fullScreenIntent" msgid="1100721419406643997">"Ermöglicht der App, Benachrichtigungen auf einem gesperrten Gerät als Vollbildaktivitäten anzuzeigen"</string>
     <string name="permlab_install_shortcut" msgid="7451554307502256221">"Verknüpfungen installieren"</string>
-    <string name="permdesc_install_shortcut" msgid="4476328467240212503">"ohne Zutun des Nutzers Verknüpfungen zum Startbildschirm hinzufügen."</string>
+    <string name="permdesc_install_shortcut" msgid="4476328467240212503">"Ermöglicht einer App, dem Startbildschirm ohne Zutun des Nutzers Verknüpfungen hinzuzufügen."</string>
     <string name="permlab_uninstall_shortcut" msgid="295263654781900390">"Verknüpfungen deinstallieren"</string>
     <string name="permdesc_uninstall_shortcut" msgid="1924735350988629188">"Ermöglicht einer App das Entfernen von Verknüpfungen vom Startbildschirm ohne Eingriff des Nutzers"</string>
     <string name="permlab_processOutgoingCalls" msgid="4075056020714266558">"Ausgehende Anrufe umleiten"</string>
@@ -1906,8 +1906,7 @@
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Wochenende"</string>
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"Termin"</string>
     <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Schlafen"</string>
-    <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
-    <skip />
+    <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Verwaltet von <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"An"</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Aus"</string>
     <string name="muted_by" msgid="91464083490094950">"Einige Töne werden von <xliff:g id="THIRD_PARTY">%1$s</xliff:g> stummgeschaltet"</string>
@@ -2340,8 +2339,8 @@
     <string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"Ermöglicht einer Companion-App, Dienste im Vordergrund aus dem Hintergrund zu starten."</string>
     <string name="mic_access_on_toast" msgid="2666925317663845156">"Mikrofon ist verfügbar"</string>
     <string name="mic_access_off_toast" msgid="8111040892954242437">"Mikrofon ist blockiert"</string>
-    <string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"Kann nicht auf das Display gespiegelt werden"</string>
-    <string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"Verwende ein anderes Kabel und versuch es noch einmal"</string>
+    <string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"Bildschirm kann nicht gespiegelt werden"</string>
+    <string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"Versuch es mit einem anderen Kabel"</string>
     <string name="connected_display_thermally_unavailable_notification_content" msgid="9205758199439955949">"Dein Gerät ist zu heiß und kann den Bildschirm erst spiegeln, wenn es abgekühlt ist"</string>
     <string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual Screen"</string>
     <string name="concurrent_display_notification_active_title" msgid="4892473462327943673">"Dual Screen ist aktiviert"</string>
@@ -2366,4 +2365,5 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Geschäftlich 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Gemeinsam genutzt"</string>
+    <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
 </resources>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index d33a2f3..a392c0f 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -1906,8 +1906,7 @@
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Σαββατοκύριακο"</string>
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"ΣυμβÎŹν"</string>
     <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Ύπνος"</string>
-    <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
-    <skip />
+    <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"ΔιαχεÎŻριση από <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Ενεργός"</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Ανενεργός"</string>
     <string name="muted_by" msgid="91464083490094950">"Το τρÎŻτο μέρος <xliff:g id="THIRD_PARTY">%1$s</xliff:g> θέτει ορισμένους Îźχους σε σÎŻγαση"</string>
@@ -2366,4 +2365,5 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"ΕργασÎŻα 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"ΔοκιμÎź"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Κοινόχρηστο"</string>
+    <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
 </resources>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index a8d807e..7498488 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -1906,8 +1906,7 @@
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Weekend"</string>
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"Event"</string>
     <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Sleeping"</string>
-    <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
-    <skip />
+    <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Managed by <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"On"</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Off"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> is muting some sounds"</string>
@@ -2366,4 +2365,5 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Work 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Communal"</string>
+    <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
 </resources>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index 2885e4d..fd76ce5 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -1906,8 +1906,7 @@
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Weekend"</string>
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"Event"</string>
     <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Sleeping"</string>
-    <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
-    <skip />
+    <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Managed by <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"On"</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Off"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> is muting some sounds"</string>
@@ -2366,4 +2365,5 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Work 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Communal"</string>
+    <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
 </resources>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index ed4e953..1f2ccce 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -1906,8 +1906,7 @@
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Weekend"</string>
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"Event"</string>
     <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Sleeping"</string>
-    <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
-    <skip />
+    <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Managed by <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"On"</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Off"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> is muting some sounds"</string>
@@ -2366,4 +2365,5 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Work 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Communal"</string>
+    <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
 </resources>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index c7e4252..d4fd01e 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -1906,8 +1906,7 @@
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Weekend"</string>
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"Event"</string>
     <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Sleeping"</string>
-    <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
-    <skip />
+    <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Managed by <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"On"</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Off"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> is muting some sounds"</string>
@@ -2366,4 +2365,5 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Work 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Communal"</string>
+    <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
 </resources>
diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml
index 8efab74..8a89ebd 100644
--- a/core/res/res/values-en-rXC/strings.xml
+++ b/core/res/res/values-en-rXC/strings.xml
@@ -1906,8 +1906,7 @@
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‎‏‎‏‎‏‎‎‏‏‎‏‎‏‎‏‎‏‏‎‏‏‎‏‎‏‎‏‎‎‏‏‎‏‎‏‏‏‎‏‏‎‏‏‎‏‏‏‏‏‏‎‎‎‏‎‏‎‎Weekend‎‏‎‎‏‎"</string>
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‏‏‎‏‎‎‏‏‎‏‎‎‏‏‏‎‏‎‎‎‏‎‎‎‏‏‎‎‏‎‎‏‏‏‎‏‏‏‏‏‏‎‎‎‏‎‎‎‏‎‎‎‎‎‎‎‏‎Event‎‏‎‎‏‎"</string>
     <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‎‎‎‏‎‏‏‏‏‎‏‎‎‎‏‏‎‎‏‏‎‎‎‏‏‎‎‏‎‏‏‏‎‏‏‏‎‎‏‎‎‏‏‎‏‏‏‏‎‎‏‏‏‎‏‏‏‏‎Sleeping‎‏‎‎‏‎"</string>
-    <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
-    <skip />
+    <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‏‏‎‏‎‎‏‏‏‏‏‎‎‏‏‎‏‎‏‏‎‏‎‏‎‏‎‏‎‎‏‏‎‏‎‏‎‏‏‎‏‏‏‎‎‏‎‎‏‎‎‎‎‎‎‏‏‏‎Managed by ‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‎‏‎‎‎‏‏‏‎‏‏‎‏‏‏‎‎‏‏‎‏‏‏‏‎‏‎‎‏‏‏‏‏‏‎‎‏‎‏‏‏‎‏‏‏‎‏‏‎‏‏‏‎‎‎‏‎‎On‎‏‎‎‏‎"</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‎‎‏‎‎‏‎‎‏‏‏‎‎‎‏‎‏‏‏‏‎‎‎‎‎‏‎‏‎‎‎‏‏‏‎‏‏‏‎‎‏‏‎‎‏‏‏‎‏‎‏‎‏‏‎‏‏‎‎Off‎‏‎‎‏‎"</string>
     <string name="muted_by" msgid="91464083490094950">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‎‏‏‎‏‎‎‎‏‎‎‏‏‏‏‎‎‏‎‎‎‎‏‏‎‎‏‎‏‏‎‏‏‏‎‎‎‏‎‎‎‏‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‏‎‎‎‏‎‎‏‏‎<xliff:g id="THIRD_PARTY">%1$s</xliff:g>‎‏‎‎‏‏‏‎ is muting some sounds‎‏‎‎‏‎"</string>
@@ -2366,4 +2365,5 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‏‏‎‎‎‏‎‏‏‏‏‏‎‏‏‎‎‏‏‏‎‏‏‎‎‏‏‎‏‎‏‏‏‎‎‎‎‎‏‏‎‏‏‏‏‏‏‎‎‏‏‏‎‎‎‏‎‏‎Work 3‎‏‎‎‏‎"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‏‎‎‏‏‏‏‎‏‏‎‎‎‏‏‏‏‎‏‎‎‎‎‎‏‎‎‎‏‏‎‏‏‎‎‎‏‎‎‏‎‏‏‏‏‎‏‏‏‏‎‎‎‏‎‏‏‎Test‎‏‎‎‏‎"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‎‏‎‏‎‏‏‎‎‎‏‎‏‎‎‏‏‎‎‏‎‎‎‏‏‎‏‏‏‎‎‏‎‎‎‎‎‏‏‏‎‎‎‏‎‏‎‎‎‎‏‎‏‎‏‎‏‏‎Communal‎‏‎‎‏‎"</string>
+    <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
 </resources>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 9cea763..f6117cd 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -1907,8 +1907,7 @@
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Fin de semana"</string>
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"Evento"</string>
     <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Dormir"</string>
-    <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
-    <skip />
+    <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Administradas por <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Activado"</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Desactivado"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> silencia algunos sonidos"</string>
@@ -2367,4 +2366,5 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Trabajo 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Probar"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Compartido"</string>
+    <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
 </resources>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 3acb71f..406f879 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -1907,8 +1907,7 @@
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Fin de semana"</string>
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"Evento"</string>
     <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Durmiendo"</string>
-    <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
-    <skip />
+    <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Gestionado por <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Activado"</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Desactivado"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> silencia algunos sonidos"</string>
@@ -2367,4 +2366,5 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Trabajo 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Prueba"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Común"</string>
+    <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
 </resources>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 5a18823..afcc618 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -1906,8 +1906,7 @@
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Nädalavahetus"</string>
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"Sündmus"</string>
     <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Magamine"</string>
-    <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
-    <skip />
+    <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Haldab <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Sees"</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Väljas"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> vaigistab teatud helid"</string>
@@ -2366,4 +2365,5 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Töö 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Jagatud"</string>
+    <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
 </resources>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index 625bb00..2b9c555 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -558,7 +558,7 @@
     <string name="permlab_changeTetherState" msgid="9079611809931863861">"aldatu telefono bidezko konektagarritasuna"</string>
     <string name="permdesc_changeTetherState" msgid="3025129606422533085">"Partekatutako Interneterako konexioaren egoera aldatzeko baimena ematen die aplikazioei."</string>
     <string name="permlab_accessWifiState" msgid="5552488500317911052">"ikusi wifi-konexioak"</string>
-    <string name="permdesc_accessWifiState" msgid="6913641669259483363">"Wi-Fi sareei buruzko informazioa ikusteko baimena ematen die aplikazioei, adibidez, Wi-Fi konexioa aktibatuta dagoen eta konektatutako Wi-Fi gailuen izenak zein diren."</string>
+    <string name="permdesc_accessWifiState" msgid="6913641669259483363">"Wi-Fi sareei buruzko informazioa ikusteko baimena ematen die aplikazioei, adibidez, wifi-konexioa aktibatuta dagoen eta konektatutako Wi-Fi gailuen izenak zein diren."</string>
     <string name="permlab_changeWifiState" msgid="7947824109713181554">"konektatu wifira edo deskonektatu bertatik"</string>
     <string name="permdesc_changeWifiState" msgid="7170350070554505384">"Wi-Fi sarbide-puntuetara konektatzeko edo haietatik deskonektatzeko baimena ematen die aplikazioei, baita Wi-Fi sareen gailu-konfigurazioari aldaketak egitekoa ere."</string>
     <string name="permlab_changeWifiMulticastState" msgid="285626875870754696">"onartu Wi-Fi Multicast harrera"</string>
@@ -1906,8 +1906,7 @@
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Asteburua"</string>
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"Gertaera"</string>
     <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Lo egiteko"</string>
-    <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
-    <skip />
+    <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Kudeatzailea: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Aktibatuta"</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Desaktibatuta"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> soinu batzuk isilarazten ari da"</string>
@@ -2332,7 +2331,7 @@
     <string name="default_card_name" msgid="9198284935962911468">"<xliff:g id="CARDNUMBER">%d</xliff:g> TXARTELA"</string>
     <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Aplikazio osagarrien erloju-profilaren baimena erlojuak kudeatzeko"</string>
     <string name="permdesc_companionProfileWatch" msgid="5655698581110449397">"Erlojuak kudeatzeko baimena ematen die aplikazio osagarriei."</string>
-    <string name="permlab_observeCompanionDevicePresence" msgid="9008994909653990465">"Begiratu gailu osagarrien presentzia"</string>
+    <string name="permlab_observeCompanionDevicePresence" msgid="9008994909653990465">"Begiratu gailu osagarrien presentziari"</string>
     <string name="permdesc_observeCompanionDevicePresence" msgid="3011699826788697852">"Gailu osagarrien presentzia begiratzeko baimena ematen die aplikazio osagarriei gailuak inguruan edo urrun daudenean."</string>
     <string name="permlab_deliverCompanionMessages" msgid="3931552294842980887">"Entregatu aplikazio osagarrien mezuak"</string>
     <string name="permdesc_deliverCompanionMessages" msgid="2170847384281412850">"Beste gailuetan mezuak entregatzeko baimena ematen die aplikazio osagarriei."</string>
@@ -2366,4 +2365,5 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Lanekoa 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Probakoa"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Partekatua"</string>
+    <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
 </resources>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index be9c114..eb71a7d 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -338,7 +338,7 @@
     <string name="capability_title_canRequestFilterKeyEvents" msgid="2772371671541753254">"Ù†ÙˆŰŽŰȘŰ§Ű±ÛŒ ۱ۧ که ŰȘŰ§ÛŒÙŸ می‌Ú©Ù†ÛŒŰŻ Ù…ŰŽŰ§Ù‡ŰŻÙ‡ Ú©Ù†ŰŻ"</string>
     <string name="capability_desc_canRequestFilterKeyEvents" msgid="2381315802405773092">"Ű§Ű·Ù„Ű§ŰčۧŰȘ ŰŽŰźŰ”ÛŒ Ù…Ű§Ù†Ù†ŰŻ ŰŽÙ…Ű§Ű±Ù‡ کۧ۱ŰȘ ۧŰčŰȘŰšŰ§Ű±ÛŒ و ÚŻŰ°Ű±ÙˆŰ§Ú˜Ù‡‌Ù‡Ű§ ۱ۧ Ù„Ű­Ű§Űž می‌Ú©Ù†ŰŻ."</string>
     <string name="capability_title_canControlMagnification" msgid="7701572187333415795">"کنŰȘŰ±Ù„ ۯ۱ێŰȘ‌Ù†Ù…Ű§ÛŒÛŒ Ù†Ù…Ű§ÛŒŰŽÚŻŰ±"</string>
-    <string name="capability_desc_canControlMagnification" msgid="2206586716709254805">"۳۷ۭ و موقŰčیŰȘ ŰšŰČ۱گ‌Ù†Ù…Ű§ÛŒÛŒ Ù†Ù…Ű§ÛŒŰŽÚŻŰ± ۱ۧ کنŰȘŰ±Ù„ Ú©Ù†ÛŒŰŻ."</string>
+    <string name="capability_desc_canControlMagnification" msgid="2206586716709254805">"موقŰčیŰȘ و ۳۷ۭ ŰšŰČ۱گ‌Ù†Ù…Ű§ÛŒÛŒ Ù†Ù…Ű§ÛŒŰŽÚŻŰ± ۱ۧ کنŰȘŰ±Ù„ Ú©Ù†ÛŒŰŻ."</string>
     <string name="capability_title_canPerformGestures" msgid="9106545062106728987">"Ű§ŰŹŰ±Ű§ÛŒ Ű§ŰŽŰ§Ű±Ù‡‌Ù‡Ű§"</string>
     <string name="capability_desc_canPerformGestures" msgid="6619457251067929726">"می‌ŰȘÙˆŰ§Ù†ÛŒŰŻ Ű¶Ű±ŰšÙ‡ ŰšŰČÙ†ÛŒŰŻŰŒ Ű§Ù†ÚŻŰŽŰȘŰȘŰ§Ù† ۱ۧ ŰȘÙ†ŰŻ ŰšÚ©ŰŽÛŒŰŻŰŒ Ű§Ù†ÚŻŰŽŰȘŰ§Ù†ŰȘŰ§Ù† ۱ۧ ŰšÙ‡ هم نŰČŰŻÛŒÚ© ÛŒŰ§ ۧŰČ Ù‡Ù… ŰŻÙˆŰ± Ú©Ù†ÛŒŰŻ و Ű§ŰŽŰ§Ű±Ù‡‌Ù‡Ű§ÛŒ ŰŻÛŒÚŻŰ±ÛŒ ۧۏ۱ۧ Ú©Ù†ÛŒŰŻ."</string>
     <string name="capability_title_canCaptureFingerprintGestures" msgid="1189053104594608091">"Ű§ŰŽŰ§Ű±Ù‡‌Ù‡Ű§ÛŒ ۧ۫۱ Ű§Ù†ÚŻŰŽŰȘ"</string>
@@ -1906,8 +1906,7 @@
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"۹۟۱ هفŰȘه"</string>
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"Ű±ÙˆÛŒŰŻŰ§ŰŻ"</string>
     <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"ŰźÙˆŰ§ŰšÛŒŰŻÙ†"</string>
-    <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
-    <skip />
+    <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"ŰȘŰ­ŰȘ‌Ù…ŰŻÛŒŰ±ÛŒŰȘ <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Ű±ÙˆŰŽÙ†"</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"ŰźŰ§Ù…ÙˆŰŽ"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> ŰŻŰ±Ű­Ű§Ù„ Ù‚Ű·Űč Ú©Ű±ŰŻÙ† ŰšŰčŰ¶ÛŒ ۧŰČ Ű”ŰŻŰ§Ù‡Ű§ŰłŰȘ"</string>
@@ -2340,9 +2339,9 @@
     <string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"ŰšÙ‡ ŰšŰ±Ù†Ű§Ù…Ù‡ Ù‡Ù…Ű±Ű§Ù‡ ۧۏۧŰČه می‌ŰŻÙ‡ŰŻ ŰłŰ±ÙˆÛŒŰł‌Ù‡Ű§ÛŒ ÙŸÛŒŰŽ‌Ù†Ù…Ű§ ۱ۧ ۧŰČ ÙŸŰł‌ŰČمینه Ű±Ű§Ù‡‌Ű§Ù†ŰŻŰ§ŰČی Ú©Ù†ŰŻ."</string>
     <string name="mic_access_on_toast" msgid="2666925317663845156">"Ù…ÛŒÚ©Ű±ÙˆÙÙˆÙ† ۯ۱ۯ۳ŰȘ۱۳ ۧ۳ŰȘ"</string>
     <string name="mic_access_off_toast" msgid="8111040892954242437">"Ù…ÛŒÚ©Ű±ÙˆÙÙˆÙ† Ù…ŰłŰŻÙˆŰŻ ŰŽŰŻ"</string>
-    <string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"ۚۧŰČŰȘۧۚ ŰŻŰ§ŰŻÙ† ŰšÙ‡ Ù†Ù…Ű§ÛŒŰŽÚŻŰ± ممکن Ù†ŰšÙˆŰŻ"</string>
+    <string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"Ù‚Ű±ÛŒÙ†Ù‡‌۳ۧŰČی Ű±ÙˆÛŒ Ù†Ù…Ű§ÛŒŰŽÚŻŰ± ممکن Ù†ŰšÙˆŰŻ"</string>
     <string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"ۧŰČ Ú©Ű§ŰšÙ„ ŰŻÛŒÚŻŰ±ÛŒ ۧ۳ŰȘÙŰ§ŰŻÙ‡ Ú©Ù†ÛŒŰŻ و ŰŻÙˆŰšŰ§Ű±Ù‡ Ű§Ù…ŰȘŰ­Ű§Ù† Ú©Ù†ÛŒŰŻ"</string>
-    <string name="connected_display_thermally_unavailable_notification_content" msgid="9205758199439955949">"ŰŻŰłŰȘÚŻŰ§Ù‡ ŰšŰłÛŒŰ§Ű± ÚŻŰ±Ù… ۧ۳ŰȘ و ŰȘۧ ŰČÙ…Ű§Ù†ÛŒ‌که ŰźÙ†Ú© Ù†ŰŽÙˆŰŻ نمی‌ŰȘÙˆŰ§Ù†ŰŻ Ù…Ű­ŰȘÙˆŰ§ ۱ۧ ۯ۱ Ù†Ù…Ű§ÛŒŰŽÚŻŰ± ۚۧŰČŰȘۧۚ ŰŻÙ‡ŰŻ."</string>
+    <string name="connected_display_thermally_unavailable_notification_content" msgid="9205758199439955949">"ŰŻŰłŰȘÚŻŰ§Ù‡ ŰšŰłÛŒŰ§Ű± ÚŻŰ±Ù… ۧ۳ŰȘ و ŰȘۧ ŰČÙ…Ű§Ù†ÛŒ‌که ŰźÙ†Ú© Ù†ŰŽÙˆŰŻ نمی‌ŰȘÙˆŰ§Ù†ŰŻ Ù…Ű­ŰȘÙˆŰ§ ۱ۧ Ű±ÙˆÛŒ Ù†Ù…Ű§ÛŒŰŽÚŻŰ± Ù‚Ű±ÛŒÙ†Ù‡‌۳ۧŰČی Ú©Ù†ŰŻ."</string>
     <string name="concurrent_display_notification_name" msgid="1526911253558311131">"‫Dual screen"</string>
     <string name="concurrent_display_notification_active_title" msgid="4892473462327943673">"‏‫Dual Screen Ű±ÙˆŰŽÙ† ۧ۳ŰȘ"</string>
     <string name="concurrent_display_notification_active_content" msgid="5889355473710601270">"‫<xliff:g id="APP_NAME">%1$s</xliff:g> ۧŰČ Ù‡Ű± ŰŻÙˆ Ù†Ù…Ű§ÛŒŰŽÚŻŰ± ŰšŰ±Ű§ÛŒ Ù†Ù…Ű§ÛŒŰŽ Ù…Ű­ŰȘÙˆŰ§ ۧ۳ŰȘÙŰ§ŰŻÙ‡ می‌Ú©Ù†ŰŻ"</string>
@@ -2366,4 +2365,5 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"کۧ۱ Ûł"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"ŰąŰČÙ…Ű§ÛŒŰŽ"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Űčمومی"</string>
+    <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
 </resources>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index d34817e..8359bf6 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -1906,8 +1906,7 @@
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Viikonloppuna"</string>
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"Tapahtuma"</string>
     <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Nukkuminen"</string>
-    <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
-    <skip />
+    <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Ylläpitäjä: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Päällä"</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Pois päältä"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> mykistää joitakin ääniä"</string>
@@ -2342,7 +2341,7 @@
     <string name="mic_access_off_toast" msgid="8111040892954242437">"Mikrofoni on estetty"</string>
     <string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"Näytön peilaaminen ei onnistu"</string>
     <string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"Käytä eri johtoa ja yritä uudelleen"</string>
-    <string name="connected_display_thermally_unavailable_notification_content" msgid="9205758199439955949">"Laite on liian kuuma eikä voi peilata näyttöön, kunnes se viilenee"</string>
+    <string name="connected_display_thermally_unavailable_notification_content" msgid="9205758199439955949">"Laite on liian kuuma eikä voi peilata näyttöön, ennen kuin se viilenee"</string>
     <string name="concurrent_display_notification_name" msgid="1526911253558311131">"Kaksoisnäyttö"</string>
     <string name="concurrent_display_notification_active_title" msgid="4892473462327943673">"Kaksoisnäyttö on päällä"</string>
     <string name="concurrent_display_notification_active_content" msgid="5889355473710601270">"<xliff:g id="APP_NAME">%1$s</xliff:g> käyttää molempia näyttöjä sisällön näyttämiseen"</string>
@@ -2366,4 +2365,5 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Työ 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Testi"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Jaettu"</string>
+    <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
 </resources>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index ba7fd52..8842ae8 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -1907,8 +1907,7 @@
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Fin de semaine"</string>
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"Événement"</string>
     <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Sommeil"</string>
-    <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
-    <skip />
+    <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Géré par <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Activé"</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Désactivé"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> désactive certains sons"</string>
@@ -2367,4 +2366,5 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Professionnel 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Commun"</string>
+    <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
 </resources>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 06b9ec8..3b24230 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -1724,7 +1724,7 @@
     <string name="color_inversion_feature_name" msgid="2672824491933264951">"Inversion des couleurs"</string>
     <string name="color_correction_feature_name" msgid="7975133554160979214">"Correction des couleurs"</string>
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Mode une main"</string>
-    <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Encore moins lumineux"</string>
+    <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Luminosité ultra-réduite"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Appareils auditifs"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Touches de volume appuyées de manière prolongée. Service <xliff:g id="SERVICE_NAME">%1$s</xliff:g> activé."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Touches de volume appuyées de manière prolongée. Service <xliff:g id="SERVICE_NAME">%1$s</xliff:g> désactivé."</string>
@@ -1907,8 +1907,7 @@
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Week-end"</string>
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"Événement"</string>
     <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Sommeil"</string>
-    <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
-    <skip />
+    <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Géré par <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Activé"</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Désactivé"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> coupe certains sons"</string>
@@ -2367,4 +2366,5 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Professionnel 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Commun"</string>
+    <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
 </resources>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index f599631..703ed7c 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -1906,8 +1906,7 @@
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Fin de semana"</string>
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"Evento"</string>
     <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Durmindo"</string>
-    <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
-    <skip />
+    <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Xestionada por <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Activada"</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Desactivada"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> está silenciando algúns sons"</string>
@@ -2342,7 +2341,7 @@
     <string name="mic_access_off_toast" msgid="8111040892954242437">"O micrófono está bloqueado"</string>
     <string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"Non se pode proxectar contido na pantalla"</string>
     <string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"Cambia de cable e téntao de novo"</string>
-    <string name="connected_display_thermally_unavailable_notification_content" msgid="9205758199439955949">"O teu dispositivo está demasiado quente; mentres non arrefríe, non se poderá proxectar contido na pantalla"</string>
+    <string name="connected_display_thermally_unavailable_notification_content" msgid="9205758199439955949">"O dispositivo está moi quente; mentres non arrefríe, non se poderá proxectar contido na pantalla"</string>
     <string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual Screen"</string>
     <string name="concurrent_display_notification_active_title" msgid="4892473462327943673">"Dual Screen está activada"</string>
     <string name="concurrent_display_notification_active_content" msgid="5889355473710601270">"<xliff:g id="APP_NAME">%1$s</xliff:g> está usando ambas as pantallas para mostrar contido"</string>
@@ -2366,4 +2365,5 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Traballo 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Proba"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Compartido"</string>
+    <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
 </resources>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index 5e12c7d..2e1f0e6 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -1906,8 +1906,7 @@
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"àȘžàȘȘ્àȘ€àȘŸàȘčàȘŸàȘ‚àȘ€"</string>
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"àȘ‡àȘ”ેàȘšà«àȘŸ"</string>
     <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"àȘšàȘżàȘ·à«àȘ•્àȘ°àȘżàȘŻ"</string>
-    <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
-    <skip />
+    <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"<xliff:g id="APP_NAME">%1$s</xliff:g> àȘŠà«àȘ”àȘŸàȘ°àȘŸ àȘźà«‡àȘšà«‡àȘœ àȘ•àȘ°àȘ”àȘŸàȘźàȘŸàȘ‚ àȘ†àȘ”ે àȘ›à«‡"</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"àȘšàȘŸàȘČુ àȘ›à«‡"</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"àȘŹàȘ‚àȘ§ àȘ›à«‡"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> àȘ…àȘźà«àȘ• àȘ…àȘ”àȘŸàȘœà«‹àȘšà«‡ àȘźà«àȘŻà«‚àȘŸ àȘ•àȘ°à«‡ àȘ›à«‡"</string>
@@ -2366,4 +2365,5 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"àȘ‘àȘ«àȘżàȘž 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"àȘȘàȘ°à«€àȘ•્àȘ·àȘŁ àȘ•àȘ°à«‹"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"àȘ•ૉàȘźà«àȘŻà«àȘšàȘČ"</string>
+    <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
 </resources>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index a6d70dd..336d998 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -1226,7 +1226,7 @@
     <string name="aerr_report" msgid="3095644466849299308">"à€«à€Œà„€à€Ąà€Źà„ˆà€• à€­à„‡à€œà„‡à€‚"</string>
     <string name="aerr_close" msgid="3398336821267021852">"à€Źà€‚à€Š à€•à€°à„‡à€‚"</string>
     <string name="aerr_mute" msgid="2304972923480211376">"à€Ąà€żà€”à€Ÿà€‡à€ž à€«à€żà€° à€žà„‡ à€Șà„à€°à€Ÿà€°à€‚à€­ à€čà„‹à€šà„‡ à€€à€• à€źà„à€Żà„‚à€Ÿ à€•à€°à„‡à€‚"</string>
-    <string name="aerr_wait" msgid="3198677780474548217">"à€Șà„à€°à€€à„€à€•à„à€·à€Ÿ à€•à€°à„‡à€‚"</string>
+    <string name="aerr_wait" msgid="3198677780474548217">"à€‡à€‚à€€à€œà€Œà€Ÿà€° à€•à€°à„‡à€‚"</string>
     <string name="aerr_close_app" msgid="8318883106083050970">"à€à€Ș à€Źà€‚à€Š à€•à€°à„‡à€‚"</string>
     <string name="anr_title" msgid="7290329487067300120"></string>
     <string name="anr_activity_application" msgid="8121716632960340680">"<xliff:g id="APPLICATION">%2$s</xliff:g> à€Șà„à€°à€€à€żà€žà€Ÿà€Š à€šà€čà„€à€‚ à€Šà„‡ à€°à€čà€Ÿ à€čà„ˆ"</string>
@@ -1235,7 +1235,7 @@
     <string name="anr_process" msgid="1664277165911816067">"<xliff:g id="PROCESS">%1$s</xliff:g> à€Șà„à€°à€•à„à€°à€żà€Żà€Ÿ à€Șà„à€°à€€à€żà€žà€Ÿà€Š à€šà€čà„€à€‚ à€Šà„‡ à€°à€čà„€ à€čà„ˆ"</string>
     <string name="force_close" msgid="9035203496368973803">"à€ à„€à€• à€čà„ˆ"</string>
     <string name="report" msgid="2149194372340349521">"à€°à€żà€Șà„‹à€°à„à€Ÿ à€•à€°à„‡à€‚"</string>
-    <string name="wait" msgid="7765985809494033348">"à€Șà„à€°à€€à„€à€•à„à€·à€Ÿ à€•à€°à„‡à€‚"</string>
+    <string name="wait" msgid="7765985809494033348">"à€‡à€‚à€€à€œà€Œà€Ÿà€° à€•à€°à„‡à€‚"</string>
     <string name="webpage_unresponsive" msgid="7850879412195273433">"à€Șà„‡à€œ à€Șà„à€°à€€à€żà€žà€Ÿà€Š à€šà€čà„€à€‚ à€Šà„‡ à€°à€čà€Ÿ à€čà„ˆ.\n\nà€•à„‍à€Żà€Ÿ à€†à€Ș à€‡à€žà„‡ à€Źà€‚à€Š à€•à€°à€šà€Ÿ à€šà€Ÿà€čà€€à„‡ à€čà„ˆà€‚?"</string>
     <string name="launch_warning_title" msgid="6725456009564953595">"à€à€Ș à€•à„‹ à€Šà„‚à€žà€°à„‡ à€”à„‡à€Źà€Čà€żà€‚à€• à€Șà€° à€Čà„‰à€šà„à€š à€•à€żà€Żà€Ÿ à€—à€Żà€Ÿ"</string>
     <string name="launch_warning_replace" msgid="3073392976283203402">"<xliff:g id="APP_NAME">%1$s</xliff:g> à€…à€­à„€ à€šà€Č à€°à€čà€Ÿ à€čà„ˆ."</string>
@@ -1906,8 +1906,7 @@
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"à€žà€Șà„à€€à€Ÿà€čà€Ÿà€‚à€€"</string>
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"à€‡à€”à„‡à€‚à€Ÿ"</string>
     <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"à€žà„‹à€€à„‡ à€žà€źà€Ż"</string>
-    <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
-    <skip />
+    <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"à€źà„ˆà€šà„‡à€œ à€•à€°à€šà„‡ à€”à€Ÿà€Čà€Ÿ à€à€Șà„à€Čà€żà€•à„‡à€¶à€š: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"à€šà€Ÿà€Čà„‚ à€čà„ˆ"</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"à€Źà€‚à€Š à€čà„ˆ"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> à€•à„à€› à€†à€”à€Ÿà€œà€Œà„‡à€‚ à€źà„‍à€Żà„‚à€Ÿ à€•à€° à€°à€čà€Ÿ à€čà„ˆ"</string>
@@ -2340,9 +2339,9 @@
     <string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"à€‡à€žà€žà„‡ à€žà€Ÿà€„à„€ à€à€Șà„à€Čà€żà€•à„‡à€¶à€š à€•à„‹ à€Źà„ˆà€•à€—à„à€°à€Ÿà€‰à€‚à€Ą à€źà„‡à€‚ à€«à€Œà„‹à€°à€—à„à€°à€Ÿà€‰à€‚à€Ą à€žà„‡à€”à€Ÿà€à€‚ à€šà€Čà€Ÿà€šà„‡ à€•à„€ à€…à€šà„à€źà€€à€ż à€źà€żà€Čà€€à„€ à€čà„ˆ."</string>
     <string name="mic_access_on_toast" msgid="2666925317663845156">"à€źà€Ÿà€‡à€•à„à€°à„‹à€«à€Œà„‹à€š à€‡à€žà„à€€à„‡à€źà€Ÿà€Č à€•à€żà€Żà€Ÿ à€œà€Ÿ à€žà€•à€€à€Ÿ à€čà„ˆ"</string>
     <string name="mic_access_off_toast" msgid="8111040892954242437">"à€źà€Ÿà€‡à€•à„à€°à„‹à€«à€Œà„‹à€š à€•à„‹ à€Źà„à€Čà„‰à€• à€•à€żà€Żà€Ÿ à€—à€Żà€Ÿ à€čà„ˆ"</string>
-    <string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"à€Ąà€żà€žà€Șà„à€Čà„‡ à€•à€Ÿ à€•à„‰à€šà„à€Ÿà„‡à€‚à€Ÿ à€šà€čà„€à€‚ à€Šà€żà€–à€Ÿà€Żà€Ÿ à€œà€Ÿ à€žà€•à€€à€Ÿ"</string>
+    <string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"à€•à„‰à€šà„à€Ÿà„‡à€‚à€Ÿ à€Ąà€żà€žà€Șà„à€Čà„‡ à€šà€čà„€à€‚ à€•à€żà€Żà€Ÿ à€œà€Ÿ à€žà€•à€€à€Ÿ"</string>
     <string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"à€•à„‹à€ˆ à€Šà„‚à€žà€°à€Ÿ à€•à„‡à€Źà€Č à€‡à€žà„à€€à„‡à€źà€Ÿà€Č à€•à€°à€•à„‡ à€«à€żà€° à€žà„‡ à€•à„‹à€¶à€żà€¶ à€•à€°à„‡à€‚"</string>
-    <string name="connected_display_thermally_unavailable_notification_content" msgid="9205758199439955949">"à€†à€Șà€•à€Ÿ à€Ąà€żà€”à€Ÿà€‡à€ž à€Źà€čà„à€€ à€—à€°à„à€ź à€čà„ˆ. à€‡à€žà€Čà€żà€, à€‡à€žà€•à„‡ à€ à€‚à€Ąà€Ÿ à€čà„‹à€šà„‡ à€€à€• à€Šà„‚à€žà€°à„‡ à€Ąà€żà€žà€Șà„à€Čà„‡ à€Șà€° à€‡à€žà€•à„€ à€žà„à€•à„à€°à„€à€š à€¶à„‡à€Żà€° à€šà€čà„€à€‚ à€•à„€ à€œà€Ÿ à€žà€•à€€à„€"</string>
+    <string name="connected_display_thermally_unavailable_notification_content" msgid="9205758199439955949">"à€†à€Șà€•à€Ÿ à€Ąà€żà€”à€Ÿà€‡à€ž à€Źà€čà„à€€ à€—à€°à„à€ź à€čà„‹ à€—à€Żà€Ÿ à€čà„ˆ. à€œà€Ź à€€à€• à€Żà€č à€ à€‚à€Ąà€Ÿ à€šà€čà„€à€‚ à€čà„‹ à€œà€Ÿà€€à€Ÿ, à€€à€Ź à€€à€• à€Šà„‚à€žà€°à„‡ à€Ąà€żà€”à€Ÿà€‡à€ž à€Șà€° à€‡à€žà€•à„€ à€žà„à€•à„à€°à„€à€š à€Ąà€żà€žà€Șà„à€Čà„‡ à€šà€čà„€à€‚ à€•à„€ à€œà€Ÿ à€žà€•à€€à„€"</string>
     <string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual screen"</string>
     <string name="concurrent_display_notification_active_title" msgid="4892473462327943673">"Dual screen à€•à„€ à€žà„à€”à€żà€§à€Ÿ à€šà€Ÿà€Čà„‚ à€čà„ˆ"</string>
     <string name="concurrent_display_notification_active_content" msgid="5889355473710601270">"<xliff:g id="APP_NAME">%1$s</xliff:g>, à€•à„‰à€šà„à€Ÿà„‡à€‚à€Ÿ à€Šà€żà€–à€Ÿà€šà„‡ à€•à„‡ à€Čà€żà€ à€Šà„‹à€šà„‹à€‚ à€žà„à€•à„à€°à„€à€š à€•à€Ÿ à€‡à€žà„à€€à„‡à€źà€Ÿà€Č à€•à€° à€°à€čà€Ÿ à€čà„ˆ"</string>
@@ -2366,4 +2365,5 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"à€‘à€«à€Œà€żà€ž 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"à€Ÿà„‡à€žà„à€Ÿ"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"à€•à€źà„à€Żà„‚à€šà€żà€Ÿà„€"</string>
+    <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
 </resources>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index e574b9a..ec5a77e 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -1907,8 +1907,7 @@
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Vikend"</string>
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"Događaj"</string>
     <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Spavanje"</string>
-    <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
-    <skip />
+    <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Upravlja <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Uključeno"</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Isključeno"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> isključuje neke zvukove"</string>
@@ -2367,4 +2366,5 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Posao 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Zajedničko"</string>
+    <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
 </resources>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index a7e67f6..3bcd44b 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -1906,8 +1906,7 @@
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Hétvége"</string>
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"Esemény"</string>
     <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Alvás"</string>
-    <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
-    <skip />
+    <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"KezelƑ: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Bekapcsolva"</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Kikapcsolva"</string>
     <string name="muted_by" msgid="91464083490094950">"A(z) <xliff:g id="THIRD_PARTY">%1$s</xliff:g> lenémít néhány hangot"</string>
@@ -2366,4 +2365,5 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"3. munkahelyi"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Teszt"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Közös"</string>
+    <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
 </resources>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index 6e517fb..603d7c6 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -1906,8 +1906,7 @@
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Ő‡ŐĄŐąŐĄŐ©-ŐŻŐ«Ö€ŐĄŐŻŐ«"</string>
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"Ő„Ő«Ő»ŐžÖŐĄŐŒŐžÖ‚ŐŽ"</string>
     <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Ő”Ő¶Ő« ŐȘŐĄŐŽŐĄŐ¶ŐĄŐŻ"</string>
-    <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
-    <skip />
+    <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"ÔżŐĄŐŒŐĄŐŸŐĄÖ€ŐŸŐžÖ‚ŐŽ Ő§ <xliff:g id="APP_NAME">%1$s</xliff:g> Ő°ŐĄŐŸŐ„ŐŹŐŸŐĄŐźŐ« ŐŻŐžŐČՎից"</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Ő„Ő«ŐĄÖŐŸŐĄŐź Ő§"</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Ô±Ő¶Ő»ŐĄŐżŐŸŐĄŐź Ő§"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g>-Ő¶ ŐĄŐ¶Ő»ŐĄŐżŐžÖ‚ŐŽ Ő§ ŐžÖ€ŐžŐ· Ő±ŐĄŐ”Ő¶Ő„Ö€"</string>
@@ -2366,4 +2365,5 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Ô±Ő·Ő­ŐĄŐżŐĄŐ¶Ö„ŐĄŐ”Ő«Ő¶ 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Ő“ŐžÖ€Ő±Ő¶ŐĄŐŻŐĄŐ¶"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"ÔžŐ¶Ő€Ő°ŐĄŐ¶ŐžÖ‚Ö€"</string>
+    <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
 </resources>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index f263f47..7886280 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -1906,8 +1906,7 @@
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Akhir pekan"</string>
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"Acara"</string>
     <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Tidur"</string>
-    <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
-    <skip />
+    <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Dikelola oleh <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Aktif"</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Nonaktif"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> mematikan beberapa suara"</string>
@@ -2342,7 +2341,7 @@
     <string name="mic_access_off_toast" msgid="8111040892954242437">"Mikrofon diblokir"</string>
     <string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"Tidak dapat mencerminkan ke layar"</string>
     <string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"Gunakan kabel lain dan coba lagi"</string>
-    <string name="connected_display_thermally_unavailable_notification_content" msgid="9205758199439955949">"Perangkat terlalu panas dan tidak dapat mencerminkan ke layar sampai cukup dingin"</string>
+    <string name="connected_display_thermally_unavailable_notification_content" msgid="9205758199439955949">"Perangkat terlalu panas dan tidak dapat mencerminkan ke layar sebelum suhunya cukup dingin"</string>
     <string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual Screen"</string>
     <string name="concurrent_display_notification_active_title" msgid="4892473462327943673">"Dual screen aktif"</string>
     <string name="concurrent_display_notification_active_content" msgid="5889355473710601270">"<xliff:g id="APP_NAME">%1$s</xliff:g> menggunakan kedua layar untuk menampilkan konten"</string>
@@ -2366,4 +2365,5 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Kerja 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Pengujian"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Umum"</string>
+    <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
 </resources>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index 25608cf..ddf60c1 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -1906,8 +1906,7 @@
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Helgi"</string>
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"Viðburður"</string>
     <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Svefn"</string>
-    <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
-    <skip />
+    <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Stýrt af <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Kveikt"</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Slökkt"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> þaggar í einhverjum hljóðum"</string>
@@ -2366,4 +2365,5 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Vinna 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Prófun"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Sameiginlegt"</string>
+    <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
 </resources>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index a99fbe6..3877ac4 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -1907,8 +1907,7 @@
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Fine settimana"</string>
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"Evento"</string>
     <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Notte"</string>
-    <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
-    <skip />
+    <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Gestione: app <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"On"</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Off"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> sta disattivando alcuni suoni"</string>
@@ -2367,4 +2366,5 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Lavoro 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Condiviso"</string>
+    <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
 </resources>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 66250b0..eb2f3b2 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -1907,8 +1907,7 @@
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"ŚĄŚ•ŚŁ Ś”Ś©Ś‘Ś•Śą"</string>
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"ŚŚ™ŚšŚ•Śą"</string>
     <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"کڙڠڔ"</string>
-    <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
-    <skip />
+    <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Ś‘Ś Ś™Ś”Ś•Śœ کڜ <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"ŚžŚŠŚ‘ Ś€ŚąŚ™Śœ"</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"ŚžŚŠŚ‘ ŚžŚ•Ś©Ś‘ŚȘ"</string>
     <string name="muted_by" msgid="91464083490094950">"Ś—ŚœŚ§ ŚžŚ”ŚŠŚœŚ™ŚœŚ™Ś ŚžŚ•Ś©ŚȘŚ§Ś™Ś ŚąŚœ ڙړڙ <xliff:g id="THIRD_PARTY">%1$s</xliff:g>"</string>
@@ -2367,4 +2366,5 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Ś€ŚšŚ•Ś€Ś™Śœ ŚąŚ‘Ś•Ś“Ś” 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"ڑړڙڧڔ"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"کڙŚȘڕڀڙ"</string>
+    <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
 </resources>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 8b87228..a2d40af 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -322,7 +322,7 @@
     <string name="permgrouplab_camera" msgid="9090413408963547706">"ă‚«ăƒĄăƒ©"</string>
     <string name="permgroupdesc_camera" msgid="7585150538459320326">"ć†™çœŸăšć‹•ç”»ăźæ’źćœ±"</string>
     <string name="permgrouplab_nearby_devices" msgid="5529147543651181991">"ä»˜èż‘ăźăƒ‡ăƒă‚€ă‚č"</string>
-    <string name="permgroupdesc_nearby_devices" msgid="3213561597116913508">"ä»˜èż‘ăźăƒ‡ăƒă‚€ă‚čた\\n怜ć‡șăšæŽ„ç¶š"</string>
+    <string name="permgroupdesc_nearby_devices" msgid="3213561597116913508">"ä»˜èż‘ăźăƒ‡ăƒă‚€ă‚čăźæ€œć‡șăšæŽ„ç¶š"</string>
     <string name="permgrouplab_calllog" msgid="7926834372073550288">"é€šè©±ć±„æ­Ž"</string>
     <string name="permgroupdesc_calllog" msgid="2026996642917801803">"é€šè©±ć±„æ­ŽăźèȘ­ăżć–ă‚Šăšæ›žăèŸŒăż"</string>
     <string name="permgrouplab_phone" msgid="570318944091926620">"電話"</string>
@@ -1872,7 +1872,7 @@
     <string name="managed_profile_label_badge_2" msgid="5673187309555352550">"2 ç•Ș盟た仕äș‹ç”š<xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="managed_profile_label_badge_3" msgid="6882151970556391957">"3 ç•Ș盟た仕äș‹ç”š<xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="clone_profile_label_badge" msgid="1871997694718793964">"<xliff:g id="LABEL">%1$s</xliff:g> ăźă‚Żăƒ­ăƒŒăƒł"</string>
-    <string name="private_profile_label_badge" msgid="1712086003787839183">"怋äșș甹<xliff:g id="LABEL">%1$s</xliff:g>"</string>
+    <string name="private_profile_label_badge" msgid="1712086003787839183">"ăƒ—ăƒ©ă‚€ăƒ™ăƒŒăƒˆăź<xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="lock_to_app_unlock_pin" msgid="3890940811866290782">"ă‚Șăƒ•ăƒ©ă‚€ăƒłć†ç”Ÿă‚’è§Łé™€ă™ă‚‹ć‰ă«PINăźć…„ćŠ›ă‚’æ±‚ă‚ă‚‹"</string>
     <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"ç”»éąć›șćźšă‚’è§Łé™€ă™ă‚‹ć‰ă«ăƒ­ăƒƒă‚Żè§Łé™€ăƒ‘ă‚żăƒŒăƒłăźć…„ćŠ›ă‚’æ±‚ă‚ă‚‹"</string>
     <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"ă‚Șăƒ•ăƒ©ă‚€ăƒłć†ç”Ÿă‚’è§Łé™€ă™ă‚‹ć‰ă«ăƒ‘ă‚čăƒŻăƒŒăƒ‰ăźć…„ćŠ›ă‚’æ±‚ă‚ă‚‹"</string>
@@ -1906,8 +1906,7 @@
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"週末"</string>
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"äșˆćźš"</string>
     <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"睡眠侭"</string>
-    <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
-    <skip />
+    <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"<xliff:g id="APP_NAME">%1$s</xliff:g> ă«ă‚ˆăŁăŠçźĄç†ă•ă‚ŒăŠă„ăŸă™"</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"ON"</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"OFF"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> ă«ă‚ˆă‚Šäž€éƒšăźéŸłăŻăƒŸăƒ„ăƒŒăƒˆă«èš­ćźš"</string>
@@ -2366,4 +2365,5 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"仕äș‹ç”š 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"テă‚čト"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"慱甚"</string>
+    <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
 </resources>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index 839a817..dee1f1f 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -1906,8 +1906,7 @@
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"ჹაბათ-კვირა"</string>
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"მოვლენა"</string>
     <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"áƒ«áƒ˜áƒšáƒ˜áƒĄáƒáƒĄ"</string>
-    <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
-    <skip />
+    <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"მართავს <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"áƒ©áƒáƒ áƒ—áƒŁáƒšáƒ˜"</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"გამორთული"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> ზოგიერთ ჼმას ადუმებს"</string>
@@ -2342,7 +2341,7 @@
     <string name="mic_access_off_toast" msgid="8111040892954242437">"მიკროჀონი დაბლოკილია"</string>
     <string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"ეკრანზე არეკვლა áƒšáƒ”áƒŁáƒ«áƒšáƒ”áƒ‘áƒ”áƒšáƒ˜áƒ"</string>
     <string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"გამოიყენეთ სჼვა კაბელი და áƒȘადეთ ჼელაჼლა"</string>
-    <string name="connected_display_thermally_unavailable_notification_content" msgid="9205758199439955949">"თჄვენი მოწყობილობა ძალიან თბილია და ვერ ასაჼავს ეკრანზე სანამ არ გაგრილდება"</string>
+    <string name="connected_display_thermally_unavailable_notification_content" msgid="9205758199439955949">"თჄვენი მოწყობილობა ძალიან თბილია და ეკრანზე არეკვლას ვერ მოაჼერჼებს, სანამ არ გაგრილდება"</string>
     <string name="concurrent_display_notification_name" msgid="1526911253558311131">"ორმაგი ეკრანი"</string>
     <string name="concurrent_display_notification_active_title" msgid="4892473462327943673">"ორმაგი ეკრანი áƒ©áƒáƒ áƒ—áƒŁáƒšáƒ˜áƒ"</string>
     <string name="concurrent_display_notification_active_content" msgid="5889355473710601270">"<xliff:g id="APP_NAME">%1$s</xliff:g> იყენებს ორივე ეკრანს ჹინაარსის áƒĄáƒáƒ©áƒ•áƒ”áƒœáƒ”áƒ‘áƒšáƒáƒ“"</string>
@@ -2366,4 +2365,5 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"სამსაჼური 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"საჱესჱო"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"საერთო"</string>
+    <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
 </resources>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index 45abbef..4cf61ac 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -1906,8 +1906,7 @@
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Đ”Đ”ĐŒĐ°Đ»Ń‹Ń ĐșÒŻĐœĐŽĐ”Ń€Ń–"</string>
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"Іс-шара"</string>
     <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Ò°ĐčÒ›Ń‹ Ń€Đ”Đ¶ĐžĐŒŃ–"</string>
-    <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
-    <skip />
+    <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"<xliff:g id="APP_NAME">%1$s</xliff:g> Đ±Đ°ŃÒ›Đ°Ń€Đ°ĐŽŃ‹."</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"ÒšĐŸŃŃƒĐ»Ń‹"</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"ÓšŃˆŃ–Ń€ŃƒĐ»Ń–"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> ĐșĐ”Đčбір ЎыбыстарЎы Ó©ŃˆŃ–Ń€ŃƒĐŽĐ”"</string>
@@ -2340,8 +2339,8 @@
     <string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"ХДріĐș Ò›ĐŸĐ»ĐŽĐ°ĐœĐ±Đ°Ò“Đ° эĐșŃ€Đ°ĐœĐŽŃ‹Ò› Ń€Đ”Đ¶ĐžĐŒĐŽĐ”ĐłŃ– Ò›Ń‹Đ·ĐŒĐ”Ń‚Ń‚Đ”Ń€ĐŽŃ– Ń„ĐŸĐœĐœĐ°Đœ ісĐșĐ” Ò›ĐŸŃŃƒÒ“Đ° Ń€Ò±Ò›ŃĐ°Ń‚ бДрілДЎі."</string>
     <string name="mic_access_on_toast" msgid="2666925317663845156">"МоĐșŃ€ĐŸŃ„ĐŸĐœ Ò›ĐŸĐ»Đ¶Đ”Ń‚Ń–ĐŒĐŽŃ–."</string>
     <string name="mic_access_off_toast" msgid="8111040892954242437">"МоĐșŃ€ĐŸŃ„ĐŸĐœ Đ±Đ»ĐŸĐșŃ‚Đ°Đ»Ò“Đ°Đœ."</string>
-    <string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"ДОсплДĐčгД ĐșÓ©ŃˆŃ–Ń€ĐŒĐ”ĐœŃ– ĐșÓ©Ń€ŃĐ”Ń‚Ńƒ ĐŒÒŻĐŒĐșŃ–Đœ Đ”ĐŒĐ”Ń"</string>
-    <string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"Đ‘Đ°ŃÒ›Đ° ĐșĐ°Đ±Đ”Đ»ŃŒĐŒĐ”Đœ әрДĐșДтті Ò›Đ°Đčталап ĐșÓ©Ń€Ń–ÒŁŃ–Đ·."</string>
+    <string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"Đ­ĐșŃ€Đ°Đœ ĐșÓ©ŃˆŃ–Ń€ĐŒĐ”ŃŃ–Đœ ĐșÓ©Ń€ŃĐ”Ń‚Ńƒ ĐŒÒŻĐŒĐșŃ–Đœ Đ”ĐŒĐ”Ń"</string>
+    <string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"Đ‘Đ°ŃÒ›Đ° ĐșĐ°Đ±Đ”Đ»ŃŒĐŒĐ”Đœ ĐșÓ©Ń€Ń–ÒŁŃ–Đ·."</string>
     <string name="connected_display_thermally_unavailable_notification_content" msgid="9205758199439955949">"ÒšÒ±Ń€Ń‹Đ»Ò“Ń‹ÒŁŃ‹Đ· Ń‚Ń‹ĐŒ Ò›Ń‹Đ·Ń‹Đż ĐșДтті, ŃĐŸĐœĐŽŃ‹Ò›Ń‚Đ°Đœ ĐŸĐ» ŃŃƒŃ‹ĐŒĐ°ĐčŃ‹ĐœŃˆĐ°, ЎОсплДĐčгД эĐșŃ€Đ°Đœ ĐșÓ©ŃˆŃ–Ń€ĐŒĐ”ŃŃ–Đœ ĐșөрсДтД Đ°Đ»ĐŒĐ°ĐčЮы."</string>
     <string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual Screen"</string>
     <string name="concurrent_display_notification_active_title" msgid="4892473462327943673">"Dual Screen Ń„ŃƒĐœĐșцоясы Ò›ĐŸŃŃƒĐ»Ń‹"</string>
@@ -2366,4 +2365,5 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Đ–Ò±ĐŒŃ‹Ń 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"ĐĄŃ‹ĐœĐ°Ò›"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Жалпы"</string>
+    <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
 </resources>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index 3aec1f6..7973f2a 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -1906,8 +1906,7 @@
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"ចុងសប្ដាហ៍"</string>
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"ព្រážčត្តិការណ៍"</string>
     <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"កំពុងដេក"</string>
-    <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
-    <skip />
+    <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"គ្រប់គ្រងដោយ <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"បសក"</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"បិទ"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> áž€áŸ†áž–áž»áž„â€‹áž”áž·áž‘ážŸáŸ†ážĄáŸáž„â€‹áž˜ážœáž™áž…áŸ†áž“ážœáž“"</string>
@@ -2366,4 +2365,5 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"ការងារទឞ 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"ការធ្វស​តេស្ត"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"ទឌទៅ"</string>
+    <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
 </resources>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index 388fe20..7e5e5b5 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -1906,8 +1906,7 @@
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"àČ”àČŸàȰàČŸàȂàČ€àłàČŻ"</string>
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"àȈàČ”àł†àȂàČŸàł"</string>
     <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"àČšàČżàČŠàłàČ°àł†àČŻ àČžàČźàČŻ"</string>
-    <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
-    <skip />
+    <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"<xliff:g id="APP_NAME">%1$s</xliff:g> àČźàł‚àČČàȕ àČšàČżàČ°àłàČ”àČčàČżàČžàČČàČŸàȗàČżàČŠàł†"</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"àȆàČšàł àȆàȗàČżàČŠàł†"</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"àȆàČ«àł àȆàȗàČżàČŠàł†"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> àČ§àłàČ”àČšàČż àČźàłàČŻàł‚àČŸàł àČźàČŸàČĄàłàČ€àłàČ€àČżàČŠàłàČŠàČŸàČ°àł†"</string>
@@ -2340,7 +2339,7 @@
     <string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"àČźàłàČšàłàČšàł†àČČàł† àČžàł‡àČ”àł†àȗàČłàČšàłàČšàł àČčàČżàČšàłàČšàł†àČČàł†àČŻàČżàȂàČŠ àČȘàłàȰàČŸàȰàȂàČ­àČżàČžàČČàł àȕàȂàČȘàłàČŻàČŸàČšàČżàČŻàČšàł àČ†àłàČŻàČȘàł‌àČ—àł† àȅàČšàłàČźàČ€àČżàČžàłàČ€àłàČ€àČŠàł†."</string>
     <string name="mic_access_on_toast" msgid="2666925317663845156">"àČźàłˆàČ•àłàČ°àłŠàČ«àł‹àČšàł àČČàČ­àłàČŻàČ”àČżàČŠàł†"</string>
     <string name="mic_access_off_toast" msgid="8111040892954242437">"àČźàłˆàČ•àłàČ°àłŠàČ«àł‹àČšàł àȅàČšàłàČšàł àČšàČżàČ°àłàČŹàȂàȧàČżàČžàČČàČŸàȗàČżàČŠàł†"</string>
-    <string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"àČĄàČżàČžàł‌àČȘàłàČČàł‡àČ—àł† àČȘàłàȰàČ€àČżàČŹàČżàȂàČŹàČżàČžàČČàł àČžàČŸàČ§àłàČŻàČ”àČżàČČàłàČČ"</string>
+    <string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"àČĄàČżàČžàł‌àČȘàłàČČàł‡àČ—àł† àČȘàłàȰàČ€àČżàČŹàČżàȂàČŹàČżàČžàČČàł àČžàČŸàČ§àłàČŻàČ”àČŸàČ—àłàČ€àłàČ€àČżàČČàłàČČ"</string>
     <string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"àČŹàł‡àČ°àł† àČ•àł‡àČŹàČČàł àČŹàČłàČžàČż àČčàČŸàČ—àł‚ àČȘàłàČšàȃ àČȘàłàȰàČŻàČ€àłàČšàČżàČžàČż"</string>
     <string name="connected_display_thermally_unavailable_notification_content" msgid="9205758199439955949">"àČšàČżàČźàłàČź àČžàČŸàȧàČšàČ”àł àČ€àłàȂàČŹàČŸ àČŹàČżàČžàČżàČŻàČŸàȗàČżàČŠàł† àČźàČ€àłàČ€àł àȅàČŠàł àČ€àČŁàłàČŁàȗàČŸàČ—àłàČ”àČ”àČ°àł†àČ—àł† àČĄàČżàČžàł‌àČȘàłàČČàł‡àČ—àł† àČȘàłàȰàČ€àČżàČŹàČżàȂàČŹàČżàČžàČČàł àČžàČŸàČ§àłàČŻàČ”àČżàČČàłàČČ"</string>
     <string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual Screen"</string>
@@ -2366,4 +2365,5 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"àČ•àł†àČČàČž 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"àČȘàČ°àł€àČ•àłàČ·àł†"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"àČžàČźàłàČŠàČŸàČŻ"</string>
+    <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
 </resources>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index f002fd5..cfc7730 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -1906,8 +1906,7 @@
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"ìŁŒë§"</string>
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"ìș˜ëŠ°ë” 음정"</string>
     <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"수멎 시간"</string>
-    <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
-    <skip />
+    <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"êŽ€ëŠŹìž: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"ì‚Źìš©"</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"ì‚Źìš© 쀑지"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g>(읎)가 음부 ì†ŒëŠŹë„Œ 음소거핚"</string>
@@ -2366,4 +2365,5 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"직임 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"테슀튞"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"êł”ë™"</string>
+    <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
 </resources>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index 73161e4..dcdfc80 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -1906,8 +1906,7 @@
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Đ”Đ”ĐŒ Đ°Đ»Ń‹Ńˆ"</string>
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"Иш-чара"</string>
     <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"ĐŁĐčĐșу Ń€Đ”Đ¶ĐžĐŒĐž"</string>
-    <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
-    <skip />
+    <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"<xliff:g id="APP_NAME">%1$s</xliff:g> башĐșарат"</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"ĐšÒŻĐčÒŻĐș"</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"ÓšŃ‡ÒŻĐș"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> аĐčŃ€Ń‹ĐŒ ÒŻĐœĐŽÓ©Ń€ĐŽÒŻ Ó©Ń‡ÒŻŃ€ÒŻÒŻĐŽÓ©"</string>
@@ -2340,7 +2339,7 @@
     <string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"ĐšÓ©ĐŒÓ©ĐșŃ‡ÒŻ ĐșĐŸĐ»ĐŽĐŸĐœĐŒĐŸĐłĐŸ аĐșтоĐČĐŽÒŻÒŻ ĐșŃ‹Đ·ĐŒĐ°Ń‚Ń‚Đ°Ń€ĐŽŃ‹ Ń„ĐŸĐœĐŽĐŸ ĐžŃˆŃ‚Đ”Ń‚ÒŻÒŻĐłÓ© уруĐșсат бДрДт."</string>
     <string name="mic_access_on_toast" msgid="2666925317663845156">"МоĐșŃ€ĐŸŃ„ĐŸĐœ жДтĐșОлОĐșŃ‚ÒŻÒŻ"</string>
     <string name="mic_access_off_toast" msgid="8111040892954242437">"МоĐșŃ€ĐŸŃ„ĐŸĐœ Đ±Ó©ĐłÓ©Ń‚Ń‚Ó©Đ»ĐłÓ©Đœ"</string>
-    <string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"Đ­ĐșŃ€Đ°ĐœĐłĐ° ĐșÒŻĐ·ĐłÒŻĐŽÓ©Đč Ń‡Đ°ĐłŃ‹Đ»ĐŽŃ‹Ń€ŃƒŃƒ ĐŒÒŻĐŒĐșÒŻĐœ ŃĐŒĐ”Ń"</string>
+    <string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"БашĐșа эĐșŃ€Đ°ĐœĐłĐ° чыгаруу ĐŒÒŻĐŒĐșÒŻĐœ ŃĐŒĐ”Ń"</string>
     <string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"БашĐșа ĐșабДлЎО ĐșĐŸĐ»ĐŽĐŸĐœŃƒĐż, ĐșаĐčра араĐșДт ĐșŃ‹Đ»Ń‹ÒŁŃ‹Đ·"</string>
     <string name="connected_display_thermally_unavailable_notification_content" msgid="9205758199439955949">"ĐąÒŻĐ·ĐŒÓ©ĐłÒŻÒŁÒŻĐ· өтө ысып ĐșДттО Đ¶Đ°ĐœĐ° ал ĐŒŃƒĐ·ĐŽĐ°ĐŒĐ°ĐčŃ‹ĐœŃ‡Đ° башĐșа эĐșŃ€Đ°ĐœĐłĐ° чыгара албаĐčт"</string>
     <string name="concurrent_display_notification_name" msgid="1526911253558311131">"ĐšĐŸŃˆ эĐșŃ€Đ°Đœ"</string>
@@ -2366,4 +2365,5 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Đ–ŃƒĐŒŃƒŃˆ 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"ĐĄŃ‹ĐœĐŸĐŸ"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Жалпы"</string>
+    <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
 </resources>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index e687436..49266a4 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -1906,8 +1906,7 @@
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"àș—້àșČàșàș­àșČàș—àșŽàș”"</string>
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"àșàșČàș™àș™àș±àș”ໝàșČàș"</string>
     <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"àșàșČàș™àș™àș­àș™"</string>
-    <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
-    <skip />
+    <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"àșˆàș±àș”àșàșČàș™à»‚àș”àș <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"ເàș›àș”àș”àșąàșč່"</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"àș›àșŽàș”àșąàșč່"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> àș›àșŽàș”àșȘàșœàș‡àșšàșČàș‡àșąà»ˆàșČàș‡à»„àș§à»‰"</string>
@@ -2366,4 +2365,5 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"àș§àșœàș 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"àș—àș»àș”àșȘàș­àșš"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"àșȘ່àș§àș™àșàșČàș‡"</string>
+    <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
 </resources>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index b3c9f37..aefda2e 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -1908,8 +1908,7 @@
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"SavaitgalÄŻ"</string>
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"Äźvykis"</string>
     <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Miegas"</string>
-    <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
-    <skip />
+    <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Tvarko „<xliff:g id="APP_NAME">%1$s</xliff:g>“"</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Äźjungti"</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Išjungti"</string>
     <string name="muted_by" msgid="91464083490094950">"„<xliff:g id="THIRD_PARTY">%1$s</xliff:g>“ nutildo kai kuriuos garsus"</string>
@@ -2368,4 +2367,5 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Darbas (3)"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Bandymas"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Bendruomenės"</string>
+    <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
 </resources>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index a14e7c5..f17f02f 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -1907,8 +1907,7 @@
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"NedēČas nogalē"</string>
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"Pasākums"</string>
     <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Gulēšana"</string>
-    <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
-    <skip />
+    <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Pārvalda <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Ieslēgta"</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Izslēgta"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> izslēdz noteiktas skaƆas"</string>
@@ -2367,4 +2366,5 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Darbam (3.)"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Testēšanai"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Kopīgs"</string>
+    <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
 </resources>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index e4ba9fa..dbcf11f 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -1906,8 +1906,7 @@
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"ВоĐșĐ”ĐœĐŽ"</string>
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"ĐĐ°ŃŃ‚Đ°Đœ"</string>
     <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"ХпОДњД"</string>
-    <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
-    <skip />
+    <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"УпраĐČуĐČĐ°ĐœĐŸ ĐŸĐŽ <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"ВĐșĐ»ŃƒŃ‡Đ”ĐœĐŸ"</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"ИсĐșĐ»ŃƒŃ‡Đ”ĐœĐŸ"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> ОсĐșĐ»ŃƒŃ‡Đž ĐœĐ”ĐșĐŸĐž Đ·ĐČуцо"</string>
@@ -2340,7 +2339,7 @@
     <string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"Đ”ĐŸĐ·ĐČĐŸĐ»ŃƒĐČа ĐżŃ€ĐžĐŽŃ€ŃƒĐ¶ĐœĐ° аплОĐșацоја Ўа Đ·Đ°ĐżĐŸŃ‡ĐœĐ” услугО ĐČĐŸ ĐżŃ€Đ”ĐŽĐ”Đœ ĐżĐ»Đ°Đœ ĐŸĐŽ Đ·Đ°ĐŽĐœĐžĐœĐ°Ń‚Đ°."</string>
     <string name="mic_access_on_toast" msgid="2666925317663845156">"МоĐșŃ€ĐŸŃ„ĐŸĐœĐŸŃ‚ Đ” ĐŽĐŸŃŃ‚Đ°ĐżĐ”Đœ"</string>
     <string name="mic_access_off_toast" msgid="8111040892954242437">"МоĐșŃ€ĐŸŃ„ĐŸĐœĐŸŃ‚ Đ” Đ±Đ»ĐŸĐșĐžŃ€Đ°Đœ"</string>
-    <string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"ĐĐ” ĐŒĐŸĐ¶Đ” Ўа сД ĐŸŃ‚ŃĐ»ĐžĐșуĐČа за проĐșажуĐČањД"</string>
+    <string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"ĐĐ” ĐŒĐŸĐ¶Đ” Ўа сД ĐŸŃ‚ŃĐ»ĐžĐșа Đ”ĐșŃ€Đ°ĐœĐŸŃ‚"</string>
     <string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"ĐšĐŸŃ€ĐžŃŃ‚Đ”Ń‚Đ” Юруг ĐșабДл Đž ĐŸĐ±ĐžĐŽĐ”Ń‚Đ” сД ĐżĐŸĐČŃ‚ĐŸŃ€ĐœĐŸ"</string>
     <string name="connected_display_thermally_unavailable_notification_content" msgid="9205758199439955949">"Đ’Đ°ŃˆĐžĐŸŃ‚ ŃƒŃ€Đ”ĐŽ Đ” ĐżŃ€Đ”ĐŒĐœĐŸĐłŃƒ Ń‚ĐŸĐżĐŸĐ» Đž ĐœĐ” ĐŒĐŸĐ¶Đ” Ўа сД ĐŸŃ‚ŃĐ»ĐžĐșуĐČа ĐœĐ° Đ”ĐșŃ€Đ°ĐœĐŸŃ‚ ĐŽĐŸĐŽĐ”Đșа ĐœĐ” сД ОзлаЎО"</string>
     <string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual Screen"</string>
@@ -2366,4 +2365,5 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Đ Đ°Đ±ĐŸŃ‚Đ”Đœ ĐżŃ€ĐŸŃ„ĐžĐ» 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"ĐŸŃ€ĐŸŃ„ĐžĐ» за тДстОрањД"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"ĐŸŃ€ĐŸŃ„ĐžĐ» ĐœĐ° Đ·Đ°Đ”ĐŽĐœĐžŃ†Đ°Ń‚Đ°"</string>
+    <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
 </resources>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index 8aa2c9a..68c0749 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -1906,8 +1906,7 @@
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"àŽ”àŽŸàŽ°àŽŸàŽšà”àŽ€à”àŽŻàŽ‚"</string>
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"àŽ‡àŽ”àŽšà”àŽ±à”"</string>
     <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"àŽ‰àŽ±àŽ•à”àŽ•àŽ‚"</string>
-    <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
-    <skip />
+    <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"<xliff:g id="APP_NAME">%1$s</xliff:g> àŽźàŽŸàŽšà”‡àŽœà” àŽšà”†àŽŻà”àŽŻà”àŽšà”àŽšàŽ€à”"</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"àŽ“àŽŁàŽŸàŽŁà”"</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"àŽ“àŽ«àŽŸàŽŁà”"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> àŽšàŽżàŽČ àŽ¶àŽŹà”‌àŽŠàŽ™à”àŽ™à”Ÿ àŽźà”àŽŻà”‚àŽŸà”àŽŸà” àŽšà”†àŽŻà”àŽŻà”àŽšà”àŽšà”"</string>
@@ -2366,4 +2365,5 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"àŽ”àŽŠà”àŽŻà”‹àŽ—àŽżàŽ•àŽ‚ 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"àŽŸà”†àŽžà”‌àŽ±à”àŽ±à”"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"àŽ•àŽźà”àŽźà”àŽŻà”‚àŽŁà”œ"</string>
+    <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
 </resources>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index 27bcd80..97858ef2 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -1906,8 +1906,7 @@
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"ĐĐŒŃ€Đ°Đ»Ń‚Ń‹Đœ Ó©ĐŽÓ©Ń€"</string>
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"ÒźĐčĐ» яĐČЎал"</string>
     <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"ĐŁĐœŃ‚Đ»Đ°ĐłŃ‹Đœ цаг"</string>
-    <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
-    <skip />
+    <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"<xliff:g id="APP_NAME">%1$s</xliff:g>-с уЮорЮЮаг"</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"АсаалттаĐč"</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"ĐŁĐœŃ‚Ń€Đ°Đ°Đ»Ń‚Ń‚Đ°Đč"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> Đ·Đ°Ń€ĐžĐŒ ĐŽŃƒŃƒĐœŃ‹ Ўууг хааж баĐčĐœĐ°"</string>
@@ -2366,4 +2365,5 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"АжОл 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"ĐąŃƒŃ€ŃˆĐžĐ»Ń‚"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"НоĐčтоĐčĐœ"</string>
+    <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
 </resources>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index 48d4fdb..cd553a4 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -1906,8 +1906,7 @@
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"à€†à€ à€”à€Ąà„à€Żà€Ÿà€šà„à€Żà€Ÿ à€¶à„‡à€”à€Ÿà„€"</string>
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"à€‡à€”à„‍à€čà„‡à€‚à€Ÿ"</string>
     <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"à€à„‹à€Șà€€à€Ÿà€šà€Ÿ"</string>
-    <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
-    <skip />
+    <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"<xliff:g id="APP_NAME">%1$s</xliff:g> à€Šà„à€”à€Ÿà€°à„‡ à€”à„à€Żà€”à€žà„à€„à€Ÿà€Șà€żà€€"</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"à€žà„à€°à„‚ à€†à€čà„‡"</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"à€Źà€‚à€Š à€†à€čà„‡"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> à€•à€Ÿà€čà„€ à€§à„‍à€”à€šà„€ à€źà„‍à€Żà„‚à€Ÿ à€•à€°à€€ à€†à€čà„‡"</string>
@@ -2342,7 +2341,7 @@
     <string name="mic_access_off_toast" msgid="8111040892954242437">"à€źà€Ÿà€Żà€•à„à€°à„‹à€«à„‹à€š à€Źà„à€Čà„‰à€• à€•à„‡à€Čà„‡à€Čà€Ÿ à€†à€čà„‡"</string>
     <string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"à€Ąà€żà€žà„à€Șà„à€Čà„‡à€”à€° à€źà€żà€°à€° à€•à€°à„‚ à€¶à€•à€€ à€šà€Ÿà€čà„€"</string>
     <string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"à€”à„‡à€—à€łà„€ à€•à„‡à€Źà€Č à€”à€Ÿà€Șà€°à„‚à€š à€Șà„à€šà„à€čà€Ÿ à€Șà„à€°à€Żà€€à„à€š à€•à€°à€Ÿ"</string>
-    <string name="connected_display_thermally_unavailable_notification_content" msgid="9205758199439955949">"à€€à„à€źà€šà„‡ à€Ąà€żà€”à„à€čà€Ÿà€‡à€ž à€–à„‚à€Ș à€—à€°à€ź à€†à€čà„‡ à€†à€Łà€ż à€€à„‡ à€„à€‚à€Ą à€čà„‹à€ˆà€Șà€°à„à€Żà€‚à€€ à€Ąà€żà€žà„à€Șà„à€Čà„‡à€źà€§à„à€Żà„‡ à€źà€żà€°à€° à€•à€°à„‚ à€¶à€•à€€ à€šà€Ÿà€čà„€"</string>
+    <string name="connected_display_thermally_unavailable_notification_content" msgid="9205758199439955949">"à€€à„à€źà€šà„‡ à€Ąà€żà€”à„à€čà€Ÿà€‡à€ž à€–à„‚à€Ș à€—à€°à€ź à€†à€čà„‡ à€†à€Łà€ż à€€à„‡ à€„à€‚à€Ą à€čà„‹à€ˆà€Șà€°à„à€Żà€‚à€€ à€Ąà€żà€žà„à€Șà„à€Čà„‡à€”à€° à€źà€żà€°à€° à€•à€°à„‚ à€¶à€•à€€ à€šà€Ÿà€čà„€"</string>
     <string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual screen"</string>
     <string name="concurrent_display_notification_active_title" msgid="4892473462327943673">"Dual screen à€žà„à€°à„‚ à€†à€čà„‡"</string>
     <string name="concurrent_display_notification_active_content" msgid="5889355473710601270">"à€†à€¶à€Ż à€Šà€Ÿà€–à€”à€Łà„à€Żà€Ÿà€žà€Ÿà€ à„€ <xliff:g id="APP_NAME">%1$s</xliff:g> à€Šà„‹à€šà„à€čà„€ à€Ąà€żà€žà„à€Șà„à€Čà„‡ à€”à€Ÿà€Șà€°à€€ à€†à€čà„‡"</string>
@@ -2366,4 +2365,5 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"à€‘à€«à€żà€ž à„©"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"à€šà€Ÿà€šà€Łà„€"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"à€žà€Ÿà€źà„à€Šà€Ÿà€Żà€żà€•"</string>
+    <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
 </resources>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index 5254490..90ffb21 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -1906,8 +1906,7 @@
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Hujung minggu"</string>
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"Acara"</string>
     <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Tidur"</string>
-    <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
-    <skip />
+    <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Diurus oleh <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Hidup"</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Mati"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> meredamkan sesetengah bunyi"</string>
@@ -2366,4 +2365,5 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Kerja 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Ujian"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Umum"</string>
+    <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
 </resources>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index 9e8a4a9..fa5efa6 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -1906,8 +1906,7 @@
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"စနေ၊ တနငá€șá€čဂနလေ"</string>
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"အစြအစဉá€ș"</string>
     <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"အိပá€șနေချိနá€ș"</string>
-    <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
-    <skip />
+    <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"<xliff:g id="APP_NAME">%1$s</xliff:g> က စဟမံသညá€ș"</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"ဖလင့á€ș"</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"ပိတá€ș"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> သညá€ș ဥချို့ဥသံကို ပိတá€șနေသညá€ș"</string>
@@ -2366,4 +2365,5 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"အလုပá€ș ၃"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"စမá€șှသပá€șá€™á€Ÿá€Ż"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"ဥမျဏသသုံသ"</string>
+    <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
 </resources>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 196f98d..f97e437 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -1906,8 +1906,7 @@
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Helg"</string>
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"Aktivitet"</string>
     <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Sover"</string>
-    <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
-    <skip />
+    <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Administreres av <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"På"</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Av"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> slår av noen lyder"</string>
@@ -2366,4 +2365,5 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Jobb 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Felles"</string>
+    <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
 </resources>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index 1411463..99dcae0 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -978,7 +978,7 @@
     <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"à€«à„‡à€°à€ż à€Șà„à€°à€Żà€Ÿà€ž à€—à€°à„à€šà„à€čà„‹à€žà„"</string>
     <string name="lockscreen_password_wrong" msgid="8605355913868947490">"à€«à„‡à€°à€ż à€Șà„à€°à€Żà€Ÿà€ž à€—à€°à„à€šà„à€čà„‹à€žà„"</string>
     <string name="lockscreen_storage_locked" msgid="634993789186443380">"à€žà€Źà„ˆ à€žà„à€”à€żà€§à€Ÿà€čà€°à„‚ à€° à€Ąà„‡à€Ÿà€Ÿà€•à€Ÿ à€Čà€Ÿà€—à€ż à€…à€šà€Čà€• à€—à€°à„à€šà„à€čà„‹à€žà„"</string>
-    <string name="faceunlock_multiple_failures" msgid="681991538434031708">"à€«à„‡à€ž à€…à€šà€Čà€• à€Șà„à€°à€Żà„‹à€— à€—à€°à„€ à€…à€šà€Čà€• à€—à€°à„à€šà„‡ à€Șà„à€°à€Żà€Ÿà€ž à€…à€€à„à€Żà€Ÿà€§à€żà€• à€§à„‡à€°à„ˆ à€­à€Żà„‹"</string>
+    <string name="faceunlock_multiple_failures" msgid="681991538434031708">"à€«à„‡à€ž à€…à€šà€Čà€• à€Șà„à€°à€Żà„‹à€— à€—à€°à„€ à€…à€šà€Čà€• à€—à€°à„à€šà„‡ à€Șà„à€°à€Żà€Ÿà€ž à€…à€€à„à€Żà€§à€żà€• à€§à„‡à€°à„ˆ à€­à€Żà„‹"</string>
     <string name="lockscreen_missing_sim_message_short" msgid="1229301273156907613">"SIM à€•à€Ÿà€°à„à€Ą à€čà€Ÿà€Čà€żà€à€•à„‹ à€›à„ˆà€š"</string>
     <string name="lockscreen_missing_sim_message" product="tablet" msgid="3986843848305639161">"à€Ÿà„à€Żà€Ÿà€Źà„à€Čà„‡à€Ÿà€źà€Ÿ SIM à€•à€Ÿà€°à„à€Ą à€čà€Ÿà€Čà€żà€à€•à„‹ à€›à„ˆà€šà„€"</string>
     <string name="lockscreen_missing_sim_message" product="tv" msgid="3903140876952198273">"à€€à€Șà€Ÿà€ˆà€‚à€•à„‹ Android TV à€Ąà€żà€­à€Ÿà€‡à€žà€źà€Ÿ SIM à€•à€Ÿà€°à„à€Ą à€čà€Ÿà€Čà€żà€à€•à„‹ à€›à„ˆà€šà„€"</string>
@@ -1906,8 +1906,7 @@
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"à€¶à€šà€żà€Źà€Ÿà€°"</string>
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"à€•à€Ÿà€°à„à€Żà€•à„à€°à€ź"</string>
     <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"à€šà€żà€Šà€Ÿà€à€•à€Ÿ à€Źà„‡à€Čà€Ÿ"</string>
-    <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
-    <skip />
+    <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"<xliff:g id="APP_NAME">%1$s</xliff:g> à€Čà„‡ à€”à„à€Żà€”à€žà„à€„à€Ÿà€Șà€š à€—à€°à„‡à€•à„‹"</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"à€…à€š à€›"</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"à€…à€« à€›"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> à€Čà„‡ à€•à„‡à€čà„€ à€§à„à€”à€šà€żà€čà€°à„‚ à€źà„à€Żà„à€Ÿ à€—à€°à„à€Šà„ˆ à€›"</string>
@@ -2366,4 +2365,5 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"à€•à€Ÿà€°à„à€Ż à€Șà„à€°à„‹à€«à€Ÿà€‡à€Č à„©"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"à€Șà€°à„€à€•à„à€·à€Ł"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"à€žà€Ÿà€źà„à€Šà€Ÿà€Żà€żà€•"</string>
+    <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
 </resources>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index c41dae7..f0d34a5 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -1906,8 +1906,7 @@
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Weekend"</string>
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"Afspraken"</string>
     <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Slapen"</string>
-    <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
-    <skip />
+    <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Beheerd door <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Aan"</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Uit"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> zet sommige geluiden uit"</string>
@@ -2366,4 +2365,5 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Werk 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Gemeenschappelijk"</string>
+    <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
 </resources>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index 7a1b7f4..bfde6c9 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -277,7 +277,7 @@
     <string name="notification_channel_account" msgid="6436294521740148173">"àŹ†àŹ•àŹŸàŹ‰àŹŁà­àŹŸàŹ° àŹžà­àŹ„àŹżàŹ€àŹż"</string>
     <string name="notification_channel_developer" msgid="1691059964407549150">"àŹĄà­‡àŹ­àŹČàŹȘàŹ°àŹ™à­àŹ• àŹźà­‡àŹžà­‡àŹœà­‍"</string>
     <string name="notification_channel_developer_important" msgid="7197281908918789589">"àŹ—à­àŹ°à­àŹ€à­àŹ”àŹȘà­‚àŹ°à­àŹŁà­àŹŁ àŹĄà­‡àŹ­à­‡àŹČàŹȘàŹ°à­ àŹźà­‡àŹžà­‡àŹœà­‌àŹ—à­àŹĄàŹŒàŹżàŹ•"</string>
-    <string name="notification_channel_updates" msgid="7907863984825495278">"àŹ…àŹȘàŹĄà­‡àŹŸà­‍"</string>
+    <string name="notification_channel_updates" msgid="7907863984825495278">"àŹ…àŹȘàŹĄà­‡àŹŸ àŹ•àŹ°àŹšà­àŹ€à­"</string>
     <string name="notification_channel_network_status" msgid="2127687368725272809">"àŹšà­‡àŹŸà­±àŹ°à­àŹ• àŹžà­àŹ„àŹżàŹ€àŹż"</string>
     <string name="notification_channel_network_alerts" msgid="6312366315654526528">"àŹšà­‡àŹŸà­±àŹ°à­àŹ• àŹ…àŹČàŹ°à­àŹŸ"</string>
     <string name="notification_channel_network_available" msgid="6083697929214165169">"àŹšà­‡àŹŸà­‌à­±àŹ°à­àŹ• àŹ‰àŹȘàŹČàŹŹà­àŹ§ àŹ…àŹ›àŹż"</string>
@@ -1906,8 +1906,7 @@
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"àŹžàŹȘà­àŹ€àŹŸàŹčàŹŸàŹšà­àŹ€"</string>
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"àŹ‡àŹ­à­‡àŹŁà­àŹŸ"</string>
     <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"àŹ¶à­‹àŹ‡àŹŹàŹŸ"</string>
-    <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
-    <skip />
+    <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"<xliff:g id="APP_NAME">%1$s</xliff:g> àŹŠà­à­±àŹŸàŹ°àŹŸ àŹȘàŹ°àŹżàŹšàŹŸàŹłàŹżàŹ€"</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"àŹšàŹŸàŹČୁ àŹ…àŹ›àŹż"</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"àŹŹàŹšà­àŹŠ àŹ…àŹ›àŹż"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> àŹ•àŹżàŹ›àŹż àŹžàŹŸàŹ‰àŹŁà­àŹĄàŹ•à­ àŹźà­à­Ÿà­àŹŸà­ àŹ•àŹ°à­àŹ›àŹż"</string>
@@ -2366,4 +2365,5 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"à­±àŹŸàŹ°à­àŹ• 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"àŹŸà­‡àŹ·à­àŹŸ"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"àŹ•àŹźà­à­Ÿà­àŹšàŹŸàŹČ"</string>
+    <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
 </resources>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index cccb3dd..d3901f7 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -1906,8 +1906,7 @@
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"àščàš«àšŒàš€à©‡ àšŠàšŸ àš…à©°àš€àšČàšŸ àšŠàšżàšš"</string>
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"àš‡àš”à©ˆàš‚àšŸ"</string>
     <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"àšžà©ŒàšŁ àš”à©‡àšČੇ"</string>
-    <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
-    <skip />
+    <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"<xliff:g id="APP_NAME">%1$s</xliff:g> àš”à©±àšČà©‹àš‚ àšȘà©àš°àšŹà©°àš§àšżàš€"</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"àššàšŸàšČੂ àščੈ"</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"àšŹà©°àšŠ àščੈ"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> àš•à©àš àš§à©àššà©€àš†àš‚ àššà©‚à©° àšźàšżàšŠàšŸ àš•àš° àš°àščੀ àščੈ"</string>
@@ -1921,7 +1920,7 @@
     <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS àšŹà©‡àššàš€à©€ àššà©‚à©° àš”à©€àšĄà©€àš“ àš•àšŸàšČ àš”àšżà©±àšš àšŹàšŠàšČàšżàš† àš—àšżàš†"</string>
     <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS àšŹà©‡àššàš€à©€ àššà©‚à©° USSD àšŹà©‡àššàš€à©€ àš”àšżà©±àšš àšŹàšŠàšČàšżàš† àš—àšżàš†"</string>
     <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"àššàš”à©€àš‚ SS àšŹà©‡àššàš€à©€ àš”àšżà©±àšš àšŹàšŠàšČàšżàš† àš—àšżàš†"</string>
-    <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"àš«àšŒàšżàšžàšŒàšżà©°àš— àšžà©°àšŹà©°àš§à©€ àšžà©àššà©‡àš€àššàšŸ"</string>
+    <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"àš«àšŒàšżàšžàšŒàšżà©°àš— àšžà©°àšŹà©°àš§à©€ àš…àšČàš°àšŸ"</string>
     <string name="notification_work_profile_content_description" msgid="5296477955677725799">"àš•àšŸàš°àšœ àšȘà©àš°à©‹àš«àšŸàšˆàšČ"</string>
     <string name="notification_alerted_content_description" msgid="6139691253611265992">"àšžà©àššà©‡àš€àššàšŸàš”àšŸàš‚"</string>
     <string name="notification_verified_content_description" msgid="6401483602782359391">"àšȘà©àšžàšŒàšŸà©€àš•àšżàš°àš€"</string>
@@ -2366,4 +2365,5 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"àš•à©°àšź àšžà©°àšŹà©°àš§à©€ 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"àšœàšŸàš‚àšš"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"àš­àšŸàšˆàššàšŸàš°àš•"</string>
+    <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
 </resources>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 5143e9e..e03679b 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -1908,8 +1908,7 @@
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Weekend"</string>
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"Wydarzenie"</string>
     <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Sen"</string>
-    <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
-    <skip />
+    <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Zarządzana przez aplikację <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"WƂączono"</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"WyƂączono"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> wycisza niektóre dĆșwięki"</string>
@@ -2344,7 +2343,7 @@
     <string name="mic_access_off_toast" msgid="8111040892954242437">"Mikrofon jest zablokowany"</string>
     <string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"Nie moĆŒna utworzyć odbicia lustrzanego na wyƛwietlaczu"</string>
     <string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"UĆŒyj innego kabla i spróbuj ponownie"</string>
-    <string name="connected_display_thermally_unavailable_notification_content" msgid="9205758199439955949">"Urządzenie ma zbyt wysoką temperaturę i nie moĆŒe utworzyć odbicia lustrzanego zawartoƛci ekranu, dopóki się nie ochƂodzi."</string>
+    <string name="connected_display_thermally_unavailable_notification_content" msgid="9205758199439955949">"Urządzenie jest zbyt ciepƂe i nie moĆŒe utworzyć odbicia lustrzanego zawartoƛci ekranu, dopóki się nie ochƂodzi."</string>
     <string name="concurrent_display_notification_name" msgid="1526911253558311131">"Podwójny ekran"</string>
     <string name="concurrent_display_notification_active_title" msgid="4892473462327943673">"WƂączono podwójny ekran"</string>
     <string name="concurrent_display_notification_active_content" msgid="5889355473710601270">"Aplikacja <xliff:g id="APP_NAME">%1$s</xliff:g> korzysta z obu wyƛwietlaczy, aby pokazać treƛci"</string>
@@ -2368,4 +2367,5 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"SƂuĆŒbowy 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Testowy"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Wspólny"</string>
+    <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
 </resources>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index f7b6b0b..2888c5f 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -1907,8 +1907,7 @@
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Fim de semana"</string>
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"Evento"</string>
     <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Dormir"</string>
-    <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
-    <skip />
+    <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Gerenciada pelo app <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Ativada"</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Desativada"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> está silenciando alguns sons"</string>
@@ -2367,4 +2366,5 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Trabalho 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Teste"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Público"</string>
+    <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
 </resources>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 5e6cc3e..7a3201f 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -1907,8 +1907,7 @@
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Fim de semana"</string>
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"Evento"</string>
     <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"A dormir"</string>
-    <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
-    <skip />
+    <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Gerido por <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Ativada"</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Desativada"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> está a desativar alguns sons."</string>
@@ -2367,4 +2366,5 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Trabalho 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Teste"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Comum"</string>
+    <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
 </resources>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index f7b6b0b..2888c5f 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -1907,8 +1907,7 @@
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Fim de semana"</string>
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"Evento"</string>
     <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Dormir"</string>
-    <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
-    <skip />
+    <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Gerenciada pelo app <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Ativada"</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Desativada"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> está silenciando alguns sons"</string>
@@ -2367,4 +2366,5 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Trabalho 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Teste"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Público"</string>
+    <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
 </resources>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index b5b411e..082fbb2 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -1907,8 +1907,7 @@
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Weekend"</string>
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"Eveniment"</string>
     <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Somn"</string>
-    <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
-    <skip />
+    <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Gestionat de <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Activată"</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Dezactivată"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> dezactivează anumite sunete"</string>
@@ -2367,4 +2366,5 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Serviciu 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Comun"</string>
+    <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
 </resources>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 6948a1a..8a03a6b 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -1908,8 +1908,7 @@
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Đ’Ń‹Ń…ĐŸĐŽĐœŃ‹Đ”"</string>
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"ĐœĐ”Ń€ĐŸĐżŃ€ĐžŃŃ‚ĐžĐ”"</string>
     <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Đ’Ń€Đ”ĐŒŃ ŃĐœĐ°"</string>
-    <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
-    <skip />
+    <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"ĐŸĐŸĐŽ упраĐČĐ»Đ”ĐœĐžĐ”ĐŒ ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžŃ \"<xliff:g id="APP_NAME">%1$s</xliff:g>\""</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"ВĐșĐ»ŃŽŃ‡Đ”ĐœĐŸ"</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"ОтĐșĐ»ŃŽŃ‡Đ”ĐœĐŸ"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> ĐżŃ€ĐžĐłĐ»ŃƒŃˆĐ°Đ”Ń‚ ĐœĐ”ĐșĐŸŃ‚ĐŸŃ€Ń‹Đ” Đ·ĐČуĐșĐž."</string>
@@ -2342,9 +2341,9 @@
     <string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"ĐĄĐŸĐżŃƒŃ‚ŃŃ‚ĐČŃƒŃŽŃ‰Đ”Đ” ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžĐ” ŃĐŒĐŸĐ¶Đ”Ń‚ запусĐșать аĐșтоĐČĐœŃ‹Đ” ŃĐ»ŃƒĐ¶Đ±Ń‹ Оз Ń„ĐŸĐœĐŸĐČĐŸĐłĐŸ Ń€Đ”Đ¶ĐžĐŒĐ°."</string>
     <string name="mic_access_on_toast" msgid="2666925317663845156">"МоĐșŃ€ĐŸŃ„ĐŸĐœ ĐŽĐŸŃŃ‚ŃƒĐżĐ”Đœ."</string>
     <string name="mic_access_off_toast" msgid="8111040892954242437">"МоĐșŃ€ĐŸŃ„ĐŸĐœ Đ·Đ°Đ±Đ»ĐŸĐșĐžŃ€ĐŸĐČĐ°Đœ."</string>
-    <string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"ĐĐ” ŃƒĐŽĐ°Đ”Ń‚ŃŃ ĐŽŃƒĐ±Đ»ĐžŃ€ĐŸĐČать ĐœĐ° эĐșŃ€Đ°Đœ"</string>
+    <string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"ĐĐ” ŃƒĐŽĐ°Đ”Ń‚ŃŃ ĐŽŃƒĐ±Đ»ĐžŃ€ĐŸĐČать эĐșŃ€Đ°Đœ"</string>
     <string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"Đ˜ŃĐżĐŸĐ»ŃŒĐ·ŃƒĐčтД ĐŽŃ€ŃƒĐłĐŸĐč ĐșĐ°Đ±Đ”Đ»ŃŒ ОлО ĐżĐŸĐČŃ‚ĐŸŃ€ĐžŃ‚Đ” ĐżĐŸĐżŃ‹Ń‚Đșу."</string>
-    <string name="connected_display_thermally_unavailable_notification_content" msgid="9205758199439955949">"Đ’Đ°ŃˆĐ” ŃƒŃŃ‚Ń€ĐŸĐčстĐČĐŸ слОшĐșĐŸĐŒ ŃĐžĐ»ŃŒĐœĐŸ ĐœĐ°ĐłŃ€Đ”Đ»ĐŸŃŃŒ. ĐšĐŸĐłĐŽĐ° ĐŸĐœĐŸ ĐŸŃŃ‚Ń‹ĐœĐ”Ń‚, ĐČы ŃĐœĐŸĐČа ŃĐŒĐŸĐ¶Đ”Ń‚Đ” пДрДЎаĐČать ĐžĐ·ĐŸĐ±Ń€Đ°Đ¶Đ”ĐœĐžĐ” ĐœĐ° ĐŽŃ€ŃƒĐłĐŸĐč эĐșŃ€Đ°Đœ."</string>
+    <string name="connected_display_thermally_unavailable_notification_content" msgid="9205758199439955949">"ĐŁŃŃ‚Ń€ĐŸĐčстĐČĐŸ слОшĐșĐŸĐŒ ĐœĐ°ĐłŃ€Đ”Đ»ĐŸŃŃŒ Đž ĐœĐ” ĐŒĐŸĐ¶Đ”Ń‚ ĐŽŃƒĐ±Đ»ĐžŃ€ĐŸĐČать эĐșŃ€Đ°Đœ. ĐŸĐŸĐŽĐŸĐ¶ĐŽĐžŃ‚Đ”, ĐżĐŸĐșа ĐŸĐœĐŸ ĐŸŃŃ‚Ń‹ĐœĐ”Ń‚."</string>
     <string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual Screen"</string>
     <string name="concurrent_display_notification_active_title" msgid="4892473462327943673">"Đ€ŃƒĐœĐșцоя Dual Screen ĐČĐșĐ»ŃŽŃ‡Đ”ĐœĐ°"</string>
     <string name="concurrent_display_notification_active_content" msgid="5889355473710601270">"<xliff:g id="APP_NAME">%1$s</xliff:g> ĐžŃĐżĐŸĐ»ŃŒĐ·ŃƒĐ”Ń‚ ĐŸĐ±Đ° эĐșŃ€Đ°ĐœĐ°."</string>
@@ -2368,4 +2367,5 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Đ Đ°Đ±ĐŸŃ‡ĐžĐč 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"ĐąĐ”ŃŃ‚ĐŸĐČыĐč"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"ĐĄĐŸĐČĐŒĐ”ŃŃ‚ĐœŃ‹Đč"</string>
+    <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
 </resources>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index 7ed085d..89ee9f9 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -1906,8 +1906,7 @@
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"සති අන්තà¶ș"</string>
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"à·ƒà·’à¶Żà·”à·€à·“à¶ž"</string>
     <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"à¶±à·’à¶Żà· ගනිඞින්"</string>
-    <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
-    <skip />
+    <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"<xliff:g id="APP_NAME">%1$s</xliff:g> විසින් කළඞනාකරණà¶ș කරà¶șි"</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"ක්‍රිà¶șාත්ඞකà¶șි"</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"ක්‍රිà¶șාවිරහිතà¶șි"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> සඞහර à·à¶¶à·Šà¶Ż à¶±à·’à·„à¶Ź කරඞින්"</string>
@@ -2366,4 +2365,5 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"කාර්à¶șාගà¶ș 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"ඎරීක්ෂණà¶ș"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"වාර්ගික"</string>
+    <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
 </resources>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index b6dd850..bbfefd1 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -1908,8 +1908,7 @@
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Víkend"</string>
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"UdalosƄ"</string>
     <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Spánok"</string>
-    <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
-    <skip />
+    <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Spravované aplikáciou <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Zapnuté"</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Vypnuté"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> vypína niektoré zvuky"</string>
@@ -2368,4 +2367,5 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"3. pracovný"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Testovací"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Spoločný"</string>
+    <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
 </resources>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index df7e614..e4e9a37 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -1908,8 +1908,7 @@
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Konec tedna"</string>
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"Dogodek"</string>
     <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Spanje"</string>
-    <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
-    <skip />
+    <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Upravlja <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Vklopljeno"</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Izklopljeno"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> izklaplja nekatere zvoke"</string>
@@ -2342,7 +2341,7 @@
     <string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"Spremljevalni aplikaciji dovoljuje, da storitve v ospredju zaĆŸene iz ozadja."</string>
     <string name="mic_access_on_toast" msgid="2666925317663845156">"Mikrofon je na voljo"</string>
     <string name="mic_access_off_toast" msgid="8111040892954242437">"Mikrofon je blokiran"</string>
-    <string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"Ni mogoče zrcaliti zaslona"</string>
+    <string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"Ni mogoče zrcaliti v zaslon"</string>
     <string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"Uporabite drug kabel in poskusite znova"</string>
     <string name="connected_display_thermally_unavailable_notification_content" msgid="9205758199439955949">"Naprava je pretopla in ne more zrcaliti v zaslon, dokler se ne ohladi"</string>
     <string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual Screen"</string>
@@ -2368,4 +2367,5 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Delo 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Preizkus"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Skupno"</string>
+    <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
 </resources>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index f84966b..288d8bf 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -1906,8 +1906,7 @@
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Fundjava"</string>
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"Ngjarje"</string>
     <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Në gjumë"</string>
-    <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
-    <skip />
+    <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Menaxhohet nga <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Aktivizuar"</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Çaktivizuar"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> po çaktivizon disa tinguj"</string>
@@ -2366,4 +2365,5 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Puna 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"I përbashkët"</string>
+    <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
 </resources>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index f37fe05..479a1db 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -1907,8 +1907,7 @@
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"ВоĐșĐ”ĐœĐŽ"</string>
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"Đ”ĐŸĐłĐ°Ń’Đ°Ń˜"</string>
     <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"ХпаĐČањД"</string>
-    <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
-    <skip />
+    <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"УпраĐČља: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"ĐŁĐșŃ™ŃƒŃ‡Đ”ĐœĐŸ"</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"ИсĐșŃ™ŃƒŃ‡Đ”ĐœĐŸ"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> ОсĐșŃ™ŃƒŃ‡ŃƒŃ˜Đ” ĐœĐ”ĐșĐ” Đ·ĐČуĐșĐ”"</string>
@@ -2367,4 +2366,5 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"ĐŸĐŸŃĐ°ĐŸ 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"йДст"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Đ—Đ°Ń˜Đ”ĐŽĐœĐžŃ‡ĐșĐŸ"</string>
+    <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
 </resources>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index cbba6d44..a30aa0a 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -1614,7 +1614,7 @@
     <string name="fingerprints" msgid="148690767172613723">"Fingeravtryck:"</string>
     <string name="sha256_fingerprint" msgid="7103976380961964600">"SHA-256-fingeravtryck"</string>
     <string name="sha1_fingerprint" msgid="2339915142825390774">"SHA-1-fingeravtryck:"</string>
-    <string name="activity_chooser_view_see_all" msgid="3917045206812726099">"Visa alla"</string>
+    <string name="activity_chooser_view_see_all" msgid="3917045206812726099">"Se alla"</string>
     <string name="activity_chooser_view_dialog_title_default" msgid="8880731437191978314">"Välj aktivitet"</string>
     <string name="share_action_provider_share_with" msgid="1904096863622941880">"Dela med"</string>
     <string name="sending" msgid="206925243621664438">"Skickar ..."</string>
@@ -1906,8 +1906,7 @@
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"I helgen"</string>
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"Händelse"</string>
     <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"När jag sover"</string>
-    <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
-    <skip />
+    <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Hanteras av <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"På"</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Av"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> stänger av vissa ljud"</string>
@@ -2366,4 +2365,5 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Arbete 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Allmän"</string>
+    <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
 </resources>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index ae9c3f8..9a063fb 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -575,7 +575,7 @@
     <string name="permdesc_changeWimaxState" product="tablet" msgid="4011097664859480108">"Inaruhusu programu kuunganisha kompyuta kibao,  na kukata kompyuta kibao kutoka mitandao ya WiMAX."</string>
     <string name="permdesc_changeWimaxState" product="tv" msgid="5373274458799425276">"Huruhusu programu iunganishe na kutenganisha kifaa chako cha Android TV na mitandao ya WiMAX."</string>
     <string name="permdesc_changeWimaxState" product="default" msgid="1551666203780202101">"Inaruhusu programu kuunganisha simu kwenye, na kukata simu kutoka mitandao ya WiMAX."</string>
-    <string name="permlab_bluetooth" msgid="586333280736937209">"oanisha na vifaa vya Bluetooth"</string>
+    <string name="permlab_bluetooth" msgid="586333280736937209">"unganisha na vifaa vya Bluetooth"</string>
     <string name="permdesc_bluetooth" product="tablet" msgid="3053222571491402635">"Huruhusu programu kuona usanidi wa Bluetooth kwenye kompyuta kibao, na kutuma na kukubali miunganisho kwa vifaa vilivyooanishwa."</string>
     <string name="permdesc_bluetooth" product="tv" msgid="8851534496561034998">"Huruhusu programu iangalie mipangilio iliyowekwa ya Bluetooth kwenye kifaa chako cha Android TV na kufanya na kukubali miunganisho na vifaa vilivyooanishwa."</string>
     <string name="permdesc_bluetooth" product="default" msgid="2779606714091276746">"Huruhusu programu kuona usanidi wa Bluetooth kwenye simu, na kutuma na kukubali miunganisho kwa vifaa vilivyooanishwa."</string>
@@ -1906,8 +1906,7 @@
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Wikendi"</string>
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"Tukio"</string>
     <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Kulala"</string>
-    <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
-    <skip />
+    <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Inadhibitiwa na <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Imewashwa"</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Imezimwa"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> inazima baadhi ya sauti"</string>
@@ -2366,4 +2365,5 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Wa 3 wa Kazini"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Jaribio"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Unaoshirikiwa"</string>
+    <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
 </resources>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index cfa5c72..23402f7 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -1906,8 +1906,7 @@
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"àź”àźŸàź° àź‡àź±àŻàź€àźż"</string>
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"àźšàźżàź•àźŽàŻàź”àŻ"</string>
     <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"àź‰àź±àź•àŻàź•àź€àŻàź€àźżàźČàŻ"</string>
-    <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
-    <skip />
+    <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"àźšàźżàź°àŻàź”àź•àźżàźȘàŻàźȘàź€àŻ: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"àź†àź©àŻ"</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"àź†àźƒàźȘàŻ"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> àźšàźżàźČ àź’àźČàźżàź•àźłàŻˆ àźźàŻàźŸàź•àŻàź•àŻàź•àźżàź±àź€àŻ"</string>
@@ -2366,4 +2365,5 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"àźȘàźŁàźż 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"àźȘàź°àźżàźšàŻ‹àź€àź©àŻˆ"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"àźȘàŻŠàź€àŻ"</string>
+    <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
 </resources>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index 52d7539..30697b3 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -1906,8 +1906,7 @@
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"ఔటరటంఀం"</string>
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"ఈఔెంట్"</string>
     <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"à°šà°żà°Šà±à°°à°Ÿà°”à°žà±à°„"</string>
-    <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
-    <skip />
+    <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"<xliff:g id="APP_NAME">%1$s</xliff:g> ఊ్ఔటరట à°źà±‡à°šà±‡à°œà± à°šà±‡à°Żà°Źà°Ąà±à°€à±‹à°‚à°Šà°ż"</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"ఆచ్‌à°Čో à°‰à°‚à°Šà°ż"</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"ఆఫ్‌à°Čో à°‰à°‚à°Šà°ż"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> à°•à±Šà°šà±à°šà°ż ధ్ఔచుà°Čచు à°źà±à°Żà±‚à°Ÿà± à°šà±‡à°žà±à°€à±‹à°‚à°Šà°ż"</string>
@@ -2366,4 +2365,5 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"ఆఫీఞ్ 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"à°Șరీక్ష"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"à°•à°źà±à°Żà±‚à°šà°Č్"</string>
+    <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
 </resources>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 759b6e4..66ed053 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -1906,8 +1906,7 @@
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"àžȘàžžàž”àžȘàž±àž›àž”àžČàž«àčŒ"</string>
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"àžàžŽàžˆàžàžŁàžŁàžĄ"</string>
     <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"àž™àž­àž™àž«àž„àž±àžš"</string>
-    <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
-    <skip />
+    <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"àžˆàž±àž”àžàžČàžŁàč‚àž”àžą <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"àč€àž›àžŽàž”"</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"àž›àžŽàž”"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> àžàžłàž„àž±àž‡àž›àžŽàž”àč€àžȘàž”àžąàž‡àžšàžČàž‡àžŁàžČàžąàžàžČàžŁ"</string>
@@ -2366,4 +2365,5 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"àž‡àžČàž™ 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"àž—àž”àžȘàž­àžš"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"àžȘàčˆàž§àž™àžàž„àžČàž‡"</string>
+    <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
 </resources>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 650b3fe..c83daa3 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -1906,8 +1906,7 @@
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Weekend"</string>
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"Event"</string>
     <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Pag-sleep"</string>
-    <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
-    <skip />
+    <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Pinapamahalaan ng <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Naka-on"</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Naka-off"</string>
     <string name="muted_by" msgid="91464083490094950">"Minu-mute ng <xliff:g id="THIRD_PARTY">%1$s</xliff:g> ang ilang tunog"</string>
@@ -2366,4 +2365,5 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Trabaho 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Communal"</string>
+    <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
 </resources>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 9e84fdd..c2c8ec6 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -1906,8 +1906,7 @@
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Hafta sonu"</string>
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"Etkinlik"</string>
     <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Uyku"</string>
-    <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
-    <skip />
+    <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"<xliff:g id="APP_NAME">%1$s</xliff:g> tarafından yönetiliyor"</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Açık"</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Kapalı"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> bazı sesleri kapatıyor"</string>
@@ -2366,4 +2365,5 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"İß 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Paylaßılan"</string>
+    <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
 </resources>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 2772eb4..00139a6 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -1908,8 +1908,7 @@
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"На ĐČĐžŃ…Ń–ĐŽĐœĐžŃ…"</string>
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"ĐŸĐŸĐŽŃ–Ń"</string>
     <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"ПіЮ час ŃĐœŃƒ"</string>
-    <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
-    <skip />
+    <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"ĐšĐ”Ń€ŃƒŃ” ĐŽĐŸĐŽĐ°Ń‚ĐŸĐș <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"ĐŁĐČŃ–ĐŒĐșĐœĐ”ĐœĐŸ"</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Đ’ĐžĐŒĐșĐœĐ”ĐœĐŸ"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> ĐČĐžĐŒĐžĐșає ĐŽĐ”ŃĐșі Đ·ĐČуĐșĐž"</string>
@@ -2368,4 +2367,5 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Đ ĐŸĐ±ĐŸŃ‡ĐžĐč ĐżŃ€ĐŸŃ„Ń–Đ»ŃŒ 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"ĐąĐ”ŃŃ‚ĐŸĐČĐžĐč ĐżŃ€ĐŸŃ„Ń–Đ»ŃŒ"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"ĐĄĐżŃ–Đ»ŃŒĐœĐžĐč ĐżŃ€ĐŸŃ„Ń–Đ»ŃŒ"</string>
+    <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
 </resources>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index 0324697..dc3ca47 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -1906,8 +1906,7 @@
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"ویک Ű§ÛŒÙ†Úˆ"</string>
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"Ű§ÛŒÙˆÙ†Ùč"</string>
     <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"ŰłÙˆÙ†Ű§"</string>
-    <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
-    <skip />
+    <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"<xliff:g id="APP_NAME">%1$s</xliff:g> کے ŰČÛŒŰ± Ű§Ù†ŰȘŰžŰ§Ù… ہے"</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"ŰąÙ† ہے"</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"ŰąÙ ہے"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> Ú©Ú†ÚŸ ŰąÙˆŰ§ŰČوÚș کو ŰźŰ§Ù…ÙˆŰŽ ک۱ Ű±ÛŰ§ ہے"</string>
@@ -2340,9 +2339,9 @@
     <string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"۳ۧŰȘÚŸÛŒ Ű§ÛŒÙŸ کو ÙŸŰł Ù…Ù†ŰžŰ± ŰłÛ’ ÙŸÛŒŰŽ Ù…Ù†ŰžŰ± کی ŰłŰ±ÙˆŰłŰČ ŰŽŰ±ÙˆŰč Ú©Ű±Ù†Û’ کی ۧۏۧŰČŰȘ ŰŻÛŒŰȘی ہے۔"</string>
     <string name="mic_access_on_toast" msgid="2666925317663845156">"Ù…Ű§ŰŠÛŒÚ©Ű±ÙˆÙÙˆÙ† ŰŻŰłŰȘÛŒŰ§Űš ہے"</string>
     <string name="mic_access_off_toast" msgid="8111040892954242437">"Ù…Ű§ŰŠÛŒÚ©Ű±ÙˆÙÙˆÙ† Ù…ŰłŰŻÙˆŰŻ ہے"</string>
-    <string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"ÚˆŰłÙŸÙ„Û’ ÙŸŰ± ŰŻÙˆ Ű·Ű±ÙÛ Ù…Ű·Ű§ŰšÙ‚ŰȘ ÙŸŰ°ÛŒŰ±ÛŒ ممکن نہیÚș ہے"</string>
+    <string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"ÚˆŰłÙŸÙ„Û’ ÙŸŰ± Ù…Ű±Ű± نہیÚș ہو ŰłÚ©ŰȘۧ"</string>
     <string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"Ù…ŰźŰȘلف Ú©ÛŒŰšÙ„ ۧ۳ŰȘŰčÙ…Ű§Ù„ Ú©Ű±ÛŒÚș Ű§ÙˆŰ± ŰŻÙˆŰšŰ§Ű±Û Ú©ÙˆŰŽŰŽ Ú©Ű±ÛŒÚș"</string>
-    <string name="connected_display_thermally_unavailable_notification_content" msgid="9205758199439955949">"ŰąÙŸ کۧ ŰąÙ„Û ŰšÛŰȘ ÚŻŰ±Ù… ہے Ű§ÙˆŰ± ŰŹŰš ŰȘÚ© یہ ÙčÚŸÙ†ÚˆŰ§ نہیÚș ہو ۏۧŰȘۧ ۧ۳ وقŰȘ ŰȘÚ© ÚˆŰłÙŸÙ„Û’ میÚș ŰčÚ©Űł ŰŻÙˆ Ű·Ű±ÙÛ Ù…Ű·Ű§ŰšÙ‚ŰȘ ÙŸŰ°ÛŒŰ± نہیÚș ہو ŰłÚ©ŰȘۧ"</string>
+    <string name="connected_display_thermally_unavailable_notification_content" msgid="9205758199439955949">"ŰąÙŸ کۧ ŰąÙ„Û ŰšÛŰȘ ÚŻŰ±Ù… ہے Ű§ÙˆŰ± ŰŹŰš ŰȘÚ© یہ ÙčÚŸÙ†ÚˆŰ§ نہیÚș ہو ۏۧŰȘۧ ۧ۳ وقŰȘ ŰȘÚ© ÚˆŰłÙŸÙ„Û’ ÙŸŰ± Ù…Ű±Ű± نہیÚș ہو ŰłÚ©ŰȘۧ"</string>
     <string name="concurrent_display_notification_name" msgid="1526911253558311131">"ŰŻÙˆÛŰ±ÛŒ Ű§ŰłÚ©Ű±ÛŒÙ†"</string>
     <string name="concurrent_display_notification_active_title" msgid="4892473462327943673">"ŰŻÙˆÛŰ±ÛŒ Ű§ŰłÚ©Ű±ÛŒÙ† ŰąÙ† ہے"</string>
     <string name="concurrent_display_notification_active_content" msgid="5889355473710601270">"Ù…ÙˆŰ§ŰŻ ŰŻÚ©ÚŸŰ§Ù†Û’ Ú©ÛŒÙ„ŰŠÛ’ <xliff:g id="APP_NAME">%1$s</xliff:g> ŰŻÙˆÙ†ÙˆÚș ÚˆŰłÙŸÙ„ÛŒŰČ Ű§ŰłŰȘŰčÙ…Ű§Ù„ ک۱ Ű±ÛÛŒ ہے"</string>
@@ -2366,4 +2365,5 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"ŰȘÛŒŰłŰ±ÛŒ ŰŻÙŰȘŰ±ÛŒ ÙŸŰ±ÙˆÙŰ§ŰŠÙ„"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"ÙčÛŒŰłÙč"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"کمیونل"</string>
+    <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
 </resources>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index 1a83288..23ef54c 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -1906,8 +1906,7 @@
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Dam olish kunlari"</string>
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"Tadbir"</string>
     <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Uyquda"</string>
-    <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
-    <skip />
+    <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"<xliff:g id="APP_NAME">%1$s</xliff:g> tomonidan boshqariladi"</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Yoniq"</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Oʻchiq"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> ayrim tovushlarni ovozsiz qilgan"</string>
@@ -2366,4 +2365,5 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Ish 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Umumiy"</string>
+    <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
 </resources>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index a6c64ee..22d9f06 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -1906,8 +1906,7 @@
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Cuối tuáș§n"</string>
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"Sự kiện"</string>
     <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Ngá»§"</string>
-    <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
-    <skip />
+    <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Do <xliff:g id="APP_NAME">%1$s</xliff:g> quáșŁn lý"</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Báș­t"</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"TáșŻt"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> đang táșŻt một số âm thanh"</string>
@@ -2366,4 +2365,5 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Công việc 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Kiểm thử"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Dùng chung"</string>
+    <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
 </resources>
diff --git a/core/res/res/values-watch/config.xml b/core/res/res/values-watch/config.xml
index af30532..31acd9a 100644
--- a/core/res/res/values-watch/config.xml
+++ b/core/res/res/values-watch/config.xml
@@ -90,4 +90,7 @@
          {@link MotionEvent#AXIS_SCROLL} generated by {@link InputDevice#SOURCE_ROTARY_ENCODER}
          devices. -->
     <bool name="config_viewRotaryEncoderHapticScrollFedbackEnabled">true</bool>
+
+    <!-- If this is true, allow wake from theater mode from motion. -->
+    <bool name="config_allowTheaterModeWakeFromMotion">true</bool>
 </resources>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 4b64daf..dec4705 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -1882,8 +1882,8 @@
     <string name="confirm_battery_saver" msgid="5247976246208245754">"硼漚"</string>
     <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"ćœšçœç””æšĄćŒäž‹ïŒŒçł»ç»ŸäŒšćŻç”šæ·±è‰Čäž»éą˜ïŒŒćč¶é™ćˆ¶æˆ–ć…łé—­ćŽć°æŽ»ćŠšă€æŸäș›è§†è§‰æ•ˆæžœă€ç‰čćźšćŠŸèƒœć’Œéƒšćˆ†çœ‘ç»œèżžæŽ„ă€‚"</string>
     <string name="battery_saver_description" msgid="8518809702138617167">"ćœšçœç””æšĄćŒäž‹ïŒŒçł»ç»ŸäŒšćŻç”šæ·±è‰Čäž»éą˜ïŒŒćč¶é™ćˆ¶æˆ–ć…łé—­ćŽć°æŽ»ćŠšă€æŸäș›è§†è§‰æ•ˆæžœă€ç‰čćźšćŠŸèƒœć’Œéƒšćˆ†çœ‘ç»œèżžæŽ„ă€‚"</string>
-    <string name="data_saver_description" msgid="4995164271550590517">"äžșäș†ć‡ć°‘攁量消耗攁量节省皋ćșäŒšé˜»æ­ąæŸäș›ćș”ç”šćœšćŽć°æ”¶ć‘æ•°æźă€‚æ‚šćœ“ć‰äœżç”šçš„ćș”ç”šćŻä»„æ”¶ć‘æ•°æźïŒŒäœ†éą‘çŽ‡ćŻèƒœäŒšé™äœŽă€‚äžŸäŸ‹è€Œèš€ïŒŒèż™ćŻèƒœæ„ć‘łç€ć›Ÿç‰‡ćȘæœ‰ćœšæ‚šç‚č按äč‹ćŽæ‰äŒšæ˜Ÿç€ș。"</string>
-    <string name="data_saver_enable_title" msgid="7080620065745260137">"èŠćŒ€ćŻæ”é‡èŠ‚çœçš‹ćșć—"</string>
+    <string name="data_saver_description" msgid="4995164271550590517">"äžșäș†ć‡ć°‘æ”é‡æ¶ˆè€—ïŒŒçœæ”æšĄćŒäŒšé˜»æ­ąæŸäș›ćș”ç”šćœšćŽć°æ”¶ć‘æ•°æźă€‚æ‚šćœ“ć‰äœżç”šçš„ćș”ç”šćŻä»„æ”¶ć‘æ•°æźïŒŒäœ†éą‘çŽ‡ćŻèƒœäŒšé™äœŽă€‚äžŸäŸ‹è€Œèš€ïŒŒèż™ćŻèƒœæ„ć‘łç€ć›Ÿç‰‡ćȘæœ‰ćœšæ‚šç‚č按äč‹ćŽæ‰äŒšæ˜Ÿç€ș。"</string>
+    <string name="data_saver_enable_title" msgid="7080620065745260137">"èŠćŒ€ćŻçœæ”æšĄćŒć—ïŒŸ"</string>
     <string name="data_saver_enable_button" msgid="4399405762586419726">"ćŒ€ćŻ"</string>
     <string name="zen_mode_duration_minutes_summary" msgid="4555514757230849789">"{count,plural, =1{1 ćˆ†é’ŸïŒˆç›Žćˆ° {formattedTime}}other{# ćˆ†é’ŸïŒˆç›Žćˆ° {formattedTime}}}"</string>
     <string name="zen_mode_duration_minutes_summary_short" msgid="1187553788355486950">"{count,plural, =1{1 ćˆ†é’ŸïŒˆç›Žćˆ° {formattedTime}}other{# ćˆ†é’ŸïŒˆç›Žćˆ° {formattedTime}}}"</string>
@@ -1906,8 +1906,7 @@
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"ć‘šæœ«"</string>
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"æŽ»ćŠš"</string>
     <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"睡眠"</string>
-    <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
-    <skip />
+    <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"由<xliff:g id="APP_NAME">%1$s</xliff:g>缡理"</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"ć·Č搯甹"</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"ć·Č恜甹"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g>æ­Łćœšć°†æŸäș›éŸłæ•ˆèźŸäžș静音"</string>
@@ -2366,4 +2365,5 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"ć·„äœœ 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"æ”‹èŻ•"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"慱甚"</string>
+    <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
 </resources>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index 9610a38..769c274 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -1906,8 +1906,7 @@
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"週末"</string>
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"æŽ»ć‹•"</string>
     <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"睡眠"</string>
-    <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
-    <skip />
+    <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"由<xliff:g id="APP_NAME">%1$s</xliff:g>缡理"</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"ć·Č開敟"</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"ć·Č關閉"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g>æ­Łć°‡æŸäș›éŸłæ•ˆèš­ç‚ș靜音"</string>
@@ -2340,9 +2339,9 @@
     <string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"ć…èš±éššé™„æ‡‰ç”šçš‹ćŒćŸžèƒŒæ™Żć•Ÿć‹•ć‰æ™Żæœć‹™ă€‚"</string>
     <string name="mic_access_on_toast" msgid="2666925317663845156">"ćŻä»„äœżç”šéș„態鱹"</string>
     <string name="mic_access_off_toast" msgid="8111040892954242437">"ć·Č氁鎖éș„態鱹"</string>
-    <string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"ç„Ąæł•ć°‡ç•«éąéĄćƒæŠ•æ”Ÿè‡łèžąćč•"</string>
-    <string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"請æ”čç”šć…¶ä»–é€ŁæŽ„ç·šïŒŒç„¶ćŸŒć†è©Šäž€æŹĄ"</string>
-    <string name="connected_display_thermally_unavailable_notification_content" msgid="9205758199439955949">"èŁçœźéŽç†±ïŒŒè«‹ç­‰ćˆ°é™æș«ćŸŒć†ć°‡ç•«éąéĄćƒæŠ•ć°„è‡łèžąćč•"</string>
+    <string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"ç„Ąæł•éĄćƒæŠ•æ”Ÿ"</string>
+    <string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"請æ”čç”šć…¶ä»–é€ŁæŽ„ç·šć†è©Š"</string>
+    <string name="connected_display_thermally_unavailable_notification_content" msgid="9205758199439955949">"èŁçœźéŽç†±ïŒŒé™æș«ćŸŒæ‰ćŻéĄćƒæŠ•æ”Ÿè‡łèžąćč•"</string>
     <string name="concurrent_display_notification_name" msgid="1526911253558311131">"é›™èžąćč•"</string>
     <string name="concurrent_display_notification_active_title" msgid="4892473462327943673">"ć·Čé–‹ć•Ÿé›™èžąćč•ćŠŸèƒœ"</string>
     <string name="concurrent_display_notification_active_content" msgid="5889355473710601270">"「<xliff:g id="APP_NAME">%1$s</xliff:g>ă€æ­Łćœšäœżç”šé›™èžąćč•饯ç€ș慧ćźč"</string>
@@ -2366,4 +2365,5 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"ć·„äœœ 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"æžŹè©Š"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"慱甚"</string>
+    <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
 </resources>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 6164be5..2c8c046 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -1906,8 +1906,7 @@
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"週末"</string>
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"æŽ»ć‹•"</string>
     <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"睡眠"</string>
-    <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
-    <skip />
+    <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"ç”±ă€Œ<xliff:g id="APP_NAME">%1$s</xliff:g>」缡理"</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"ć·Č敟甹"</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"ć·Č恜甹"</string>
     <string name="muted_by" msgid="91464083490094950">"「<xliff:g id="THIRD_PARTY">%1$s</xliff:g>ă€æ­Łćœšé—œé–‰éƒšćˆ†éŸłæ•ˆ"</string>
@@ -2366,4 +2365,5 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"ć·„äœœ 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"æžŹè©Š"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"通甹"</string>
+    <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
 </resources>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index f2a23bc..42f0b3f 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -1906,8 +1906,7 @@
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Ngempelasonto"</string>
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"Umcimbi"</string>
     <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Ulele"</string>
-    <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
-    <skip />
+    <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Iphethwe yi-<xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Kuvuliwe"</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Kuvaliwe"</string>
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> ithulisa eminye imisindo"</string>
@@ -2366,4 +2365,5 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Umsebenzi 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Hlola"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Okomphakathi"</string>
+    <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
 </resources>
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 596cfe5..6019524 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -301,9 +301,7 @@
             granted to the system companion device manager service -->
         <flag name="companion" value="0x800000" />
         <!-- Additional flag from base permission type: this permission will be granted to the
-             retail demo app, as defined by the OEM.
-             This flag has been replaced by the retail demo role and is a no-op since Android V.
-          -->
+             retail demo app, as defined by the OEM. -->
         <flag name="retailDemo" value="0x1000000" />
         <!-- Additional flag from base permission type: this permission will be granted to the
              recents app. -->
@@ -508,6 +506,12 @@
          receivers, and providers; it can not be used with activities. -->
     <attr name="singleUser" format="boolean" />
 
+    <!-- If set to true, only a single instance of this component will
+    run and be available for the SYSTEM user. Non SYSTEM users will not be
+    allowed to access the component if this flag is enabled.
+    This flag can be used with services, receivers, providers and activities. -->
+    <attr name="systemUserOnly" format="boolean" />
+
     <!-- Specify a specific process that the associated code is to run in.
          Use with the application tag (to supply a default process for all
          application components), or with the activity, receiver, service,
@@ -1746,6 +1750,12 @@
             TODO: b/258855262 mark this field as {@code hide} once this bug is fixed.
             <flag name="fileManagement" value="0x1000" />
         -->
+        <!-- Media processing use cases such as video or photo editing and processing.
+            <p>Requires the app to hold the permission
+            {@link android.Manifest.permission#FOREGROUND_SERVICE_MEDIA_PROCESSING} in order to use
+            this type.
+        -->
+        <flag name="mediaProcessing" value="0x2000" />
         <!-- Use cases that can't be categorized into any other foreground service types, but also
             can't use @link android.app.job.JobInfo.Builder} APIs.
             See {@link android.content.pm.ServiceInfo#FOREGROUND_SERVICE_TYPE_SPECIAL_USE} for the
@@ -2855,6 +2865,7 @@
              Context.createAttributionContext() using the first attribution tag
              contained here. -->
         <attr name="attributionTags" />
+        <attr name="systemUserOnly" format="boolean" />
     </declare-styleable>
 
     <!-- Attributes that can be supplied in an AndroidManifest.xml
@@ -2986,7 +2997,12 @@
              depends on the number of isolated services that an application starts,
              and how much memory those services save by preloading and sharing memory with
              the app zygote. Therefore, it is recommended to measure memory usage under
-             typical workloads to determine whether it makes sense to use this flag. -->
+             typical workloads to determine whether it makes sense to use this flag.
+
+             <p>There is a limit to the number of isolated services that can be spawned from
+                the Application Zygote; the absolute limit is 100, but due to potential
+                delays in service process cleanup, a much safer limit to use in practice is 50.
+             -->
         <attr name="useAppZygote" format="boolean" />
         <!-- If this is a foreground service, specify its category. -->
         <attr name="foregroundServiceType" />
@@ -3008,6 +3024,7 @@
              ignored when the process is bound into a shared isolated process by a client.
         -->
         <attr name="allowSharedIsolatedProcess" format="boolean" />
+        <attr name="systemUserOnly" format="boolean" />
     </declare-styleable>
 
     <!-- @hide The <code>apex-system-service</code> tag declares an apex system service
@@ -3135,7 +3152,7 @@
         <attr name="uiOptions" />
         <attr name="parentActivityName" />
         <attr name="singleUser" />
-        <!-- @hide This broadcast receiver or activity will only receive broadcasts for the
+        <!-- This broadcast receiver or activity will only receive broadcasts for the
              system user-->
         <attr name="systemUserOnly" format="boolean" />
         <attr name="persistableMode" />
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index eddd81e..53a6270 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -568,10 +568,6 @@
     <color name="side_fps_button_color">#00677E</color>
 
     <!-- Color for system bars -->
-    <color name="status_bar_compatible">@android:color/black</color>
-    <!-- This uses non-regular transparent intentionally. It is used to tell if the transparent
-         color is set by the framework or not. -->
-    <color name="status_bar_default">#00808080</color>
     <color name="navigation_bar_compatible">@android:color/black</color>
     <!-- This uses non-regular transparent intentionally. It is used to tell if the transparent
          color is set by the framework or not. -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 804e9ef..5be29a6 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2349,6 +2349,8 @@
     <string name="config_defaultCallRedirection" translatable="false"></string>
     <!-- The name of the package that will hold the call screening role by default. -->
     <string name="config_defaultCallScreening" translatable="false"></string>
+    <!-- The name of the package that will hold the wallet role by default. -->
+    <string name="config_defaultWallet" translatable="false" />
     <!-- The name of the package that will hold the system gallery role. -->
     <string name="config_systemGallery" translatable="false">com.android.gallery3d</string>
     <!-- The names of the packages that will hold the automotive projection role. -->
@@ -4017,6 +4019,18 @@
          by shrinking the display such that it does not overlap the cutout area. -->
     <bool name="config_maskMainBuiltInDisplayCutout">false</bool>
 
+    <!-- This string array provide override side of each rotation of the given insets.
+         Array of "[rotation],[side]".
+         Undefined rotation will apply the default behavior.
+         When there are cutouts on multiple edges of the display, the override won't take any
+         effect. -->
+    <string-array name="config_mainBuiltInDisplayCutoutSideOverride" translatable="false">
+        <!-- Example:
+        <item>90,top</item>
+        <item>270,bottom</item>
+        -->
+    </string-array>
+
     <!-- Ultrasound support for Mic/speaker path -->
     <!-- Whether the default microphone audio source supports near-ultrasound frequencies
          (range of 18 - 21 kHz). -->
@@ -6373,6 +6387,8 @@
     </string>
     <bool name="config_fillSecondaryBuiltInDisplayCutout">false</bool>
     <bool name="config_maskSecondaryBuiltInDisplayCutout">false</bool>
+    <string-array name="config_secondaryBuiltInDisplayCutoutSideOverride" translatable="false">
+    </string-array>
 
     <!-- An array contains unique ids of all built-in displays and the unique id of a display can be
          obtained from {@link Display#getUniqueId}. This array should be set for multi-display
@@ -6418,6 +6434,11 @@
         <item>@string/config_secondaryBuiltInDisplayCutoutRectApproximation</item>
     </string-array>
 
+    <array name="config_displayCutoutSideOverrideArray" translatable="false">
+        <item>@array/config_mainBuiltInDisplayCutoutSideOverride</item>
+        <item>@array/config_secondaryBuiltInDisplayCutoutSideOverride</item>
+    </array>
+
     <!-- The maskBuiltInDisplayCutout config for each display in a multi-display device. -->
     <array name="config_maskBuiltInDisplayCutoutArray" translatable="false">
         <item>@bool/config_maskMainBuiltInDisplayCutout</item>
diff --git a/core/res/res/values/config_telephony.xml b/core/res/res/values/config_telephony.xml
index 8d80af4..5346454 100644
--- a/core/res/res/values/config_telephony.xml
+++ b/core/res/res/values/config_telephony.xml
@@ -212,6 +212,36 @@
     <bool name="config_send_satellite_datagram_to_modem_in_demo_mode">false</bool>
     <java-symbol type="bool" name="config_send_satellite_datagram_to_modem_in_demo_mode" />
 
+    <!-- List of country codes where oem-enabled satellite services are either allowed or disallowed
+         by the device. Each country code is a lowercase 2 character ISO-3166-1 alpha-2.
+         -->
+    <string-array name="config_oem_enabled_satellite_country_codes">
+    </string-array>
+    <java-symbol type="array" name="config_oem_enabled_satellite_country_codes" />
+
+    <!-- The file storing S2-cell-based satellite access restriction of the countries defined by
+         config_oem_enabled_satellite_countries. -->
+    <string name="config_oem_enabled_satellite_s2cell_file"></string>
+    <java-symbol type="string" name="config_oem_enabled_satellite_s2cell_file" />
+
+    <!-- Whether to treat the countries defined by the config_oem_enabled_satellite_countries
+         as satellite-allowed areas. The default true value means the countries defined by
+         config_oem_enabled_satellite_countries will be treated as satellite-allowed areas.
+         -->
+    <bool name="config_oem_enabled_satellite_access_allow">true</bool>
+    <java-symbol type="bool" name="config_oem_enabled_satellite_access_allow" />
+
+    <!-- The time duration in seconds which is used to decide whether the Location returned from
+         LocationManager#getLastKnownLocation is fresh.
+
+         The Location is considered fresh if the duration from the Location's elapsed real time to
+         the current elapsed real time is less than this config. If the Location is considered
+         fresh, it will be used as the current location by Telephony to decide whether satellite
+         services should be allowed.
+         -->
+    <integer name="config_oem_enabled_satellite_location_fresh_duration">600</integer>
+    <java-symbol type="integer" name="config_oem_enabled_satellite_location_fresh_duration" />
+
     <!-- Whether enhanced IWLAN handover check is enabled. If enabled, telephony frameworks
          will not perform handover if the target transport is out of service, or VoPS not
          supported. The network will be torn down on the source transport, and will be
diff --git a/core/res/res/values/public-staging.xml b/core/res/res/values/public-staging.xml
index 540967d..7b5c49c 100644
--- a/core/res/res/values/public-staging.xml
+++ b/core/res/res/values/public-staging.xml
@@ -119,6 +119,8 @@
     <public name="optional"/>
     <!-- @FlaggedApi("android.media.tv.flags.enable_ad_service_fw") -->
     <public name="adServiceTypes" />
+    <!-- @FlaggedApi("android.multiuser.enable_system_user_only_for_services_and_providers") -->
+    <public name="systemUserOnly"/>
   </staging-public-group>
 
   <staging-public-group type="id" first-id="0x01bc0000">
@@ -128,8 +130,10 @@
   </staging-public-group>
 
   <staging-public-group type="string" first-id="0x01ba0000">
-    <!-- @hide @SystemApi -->
+    <!-- @hide @SystemApi @FlaggedApi("android.permission.flags.retail_demo_role_enabled") -->
     <public name="config_defaultRetailDemo" />
+    <!-- @hide @SystemApi @FlaggedApi("android.permission.flags.wallet_role_enabled") -->
+    <public name="config_defaultWallet" />
   </staging-public-group>
 
   <staging-public-group type="dimen" first-id="0x01b90000">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index d2fb9e1..f285806 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1248,6 +1248,11 @@
     <string name="permdesc_foregroundServiceFileManagement">Allows the app to make use of foreground services with the type \"fileManagement\"</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permlab_foregroundServiceMediaProcessing">run foreground service with the type \"mediaProcessing\"</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_foregroundServiceMediaProcessing">Allows the app to make use of foreground services with the type \"mediaProcessing\"</string>
+
+    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_foregroundServiceSpecialUse">run foreground service with the type \"specialUse\"</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permdesc_foregroundServiceSpecialUse">Allows the app to make use of foreground services with the type \"specialUse\"</string>
@@ -1820,7 +1825,7 @@
     <!-- Message shown during fingerprint acquisision when the fingerprint cannot be recognized -->
     <string name="fingerprint_acquired_partial">Press firmly on the sensor</string>
     <!-- Message shown during fingerprint acquisision when the fingerprint cannot be recognized -->
-    <string name="fingerprint_acquired_insufficient">Can\u2019t recognize fingerprint. Try again.</string>
+    <string name="fingerprint_acquired_insufficient">Fingerprint not recognized. Try again.</string>
     <!-- Message shown during fingerprint acquisision when the fingerprint sensor needs cleaning -->
     <string name="fingerprint_acquired_imager_dirty">Clean fingerprint sensor and try again</string>
     <string name="fingerprint_acquired_imager_dirty_alt">Clean sensor and try again</string>
@@ -1954,7 +1959,7 @@
     <!-- Message shown during face acquisition when the sensor needs to be recalibrated [CHAR LIMIT=50] -->
     <string name="face_acquired_recalibrate">Please re-enroll your face.</string>
     <!-- Message shown during face enrollment when a different person's face is detected [CHAR LIMIT=50] -->
-    <string name="face_acquired_too_different">Can\u2019t recognize face. Try again.</string>
+    <string name="face_acquired_too_different">Face not recognized. Try again.</string>
     <!-- Message shown during face enrollment when the face is too similar to a previous acquisition [CHAR LIMIT=50] -->
     <string name="face_acquired_too_similar">Change the position of your head slightly</string>
     <!-- Message shown during acqusition when the user's face is turned too far left or right [CHAR LIMIT=50] -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 9589fb0..d12ef2b 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3095,8 +3095,6 @@
   <java-symbol type="bool" name="config_navBarDefaultTransparent" />
   <java-symbol type="color" name="navigation_bar_default"/>
   <java-symbol type="color" name="navigation_bar_compatible"/>
-  <java-symbol type="color" name="status_bar_default"/>
-  <java-symbol type="color" name="status_bar_compatible"/>
 
   <!-- EditText suggestion popup. -->
   <java-symbol type="id" name="suggestionWindowContainer" />
@@ -4036,6 +4034,7 @@
   <java-symbol type="string" name="global_action_logout" />
   <java-symbol type="string" name="config_mainBuiltInDisplayCutout" />
   <java-symbol type="string" name="config_mainBuiltInDisplayCutoutRectApproximation" />
+  <java-symbol type="array" name="config_mainBuiltInDisplayCutoutSideOverride" />
   <java-symbol type="drawable" name="messaging_user" />
   <java-symbol type="bool" name="config_fillMainBuiltInDisplayCutout" />
   <java-symbol type="drawable" name="ic_logout" />
@@ -5002,9 +5001,11 @@
   <java-symbol type="string" name="config_secondaryBuiltInDisplayCutoutRectApproximation" />
   <java-symbol type="bool" name="config_fillSecondaryBuiltInDisplayCutout" />
   <java-symbol type="bool" name="config_maskSecondaryBuiltInDisplayCutout" />
+  <java-symbol type="array" name="config_secondaryBuiltInDisplayCutoutSideOverride" />
   <java-symbol type="array" name="config_displayUniqueIdArray" />
   <java-symbol type="array" name="config_displayCutoutPathArray" />
   <java-symbol type="array" name="config_displayCutoutApproximationRectArray" />
+  <java-symbol type="array" name="config_displayCutoutSideOverrideArray" />
   <java-symbol type="array" name="config_fillBuiltInDisplayCutoutArray" />
   <java-symbol type="array" name="config_maskBuiltInDisplayCutoutArray" />
   <java-symbol type="dimen" name="secondary_waterfall_display_left_edge_size" />
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index d5d67ab..bdbf96b 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -190,7 +190,7 @@
         <item name="windowTranslucentStatus">false</item>
         <item name="windowTranslucentNavigation">false</item>
         <item name="windowDrawsSystemBarBackgrounds">false</item>
-        <item name="statusBarColor">@color/status_bar_default</item>
+        <item name="statusBarColor">@color/black</item>
         <item name="navigationBarColor">@color/navigation_bar_default</item>
         <item name="windowActionBarFullscreenDecorLayout">@layout/screen_action_bar</item>
         <item name="windowContentTransitions">false</item>
diff --git a/core/sysprop/FoldLockBehaviorProperties.sysprop b/core/sysprop/FoldLockBehaviorProperties.sysprop
index d337954..120e4bb 100644
--- a/core/sysprop/FoldLockBehaviorProperties.sysprop
+++ b/core/sysprop/FoldLockBehaviorProperties.sysprop
@@ -22,3 +22,11 @@
     scope: Internal
     access: Readonly
 }
+
+prop {
+    api_name: "fold_grace_period_enabled"
+    type: Boolean
+    prop_name: "persist.fold_grace_period_enabled"
+    scope: Internal
+    access: Readonly
+}
diff --git a/core/tests/coretests/src/android/animation/AnimatorSetCallsTest.java b/core/tests/coretests/src/android/animation/AnimatorSetCallsTest.java
index 43266a5..cb3f99c 100644
--- a/core/tests/coretests/src/android/animation/AnimatorSetCallsTest.java
+++ b/core/tests/coretests/src/android/animation/AnimatorSetCallsTest.java
@@ -17,6 +17,7 @@
 package android.animation;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
 import android.util.PollingCheck;
@@ -343,6 +344,20 @@
     }
 
     @Test
+    public void childAnimatorCancelsDuringUpdate_animatorSetIsEnded() throws Throwable {
+        mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+            @Override
+            public void onAnimationUpdate(ValueAnimator animation) {
+                animation.cancel();
+            }
+        });
+        mActivity.runOnUiThread(() -> {
+            mSet1.start();
+            assertFalse(mSet1.isRunning());
+        });
+    }
+
+    @Test
     public void reentrantStart() throws Throwable {
         CountDownLatch latch = new CountDownLatch(3);
         AnimatorListenerAdapter listener = new AnimatorListenerAdapter() {
diff --git a/core/tests/coretests/src/android/app/AutomaticZenRuleTest.java b/core/tests/coretests/src/android/app/AutomaticZenRuleTest.java
index 1925588..9d85b65 100644
--- a/core/tests/coretests/src/android/app/AutomaticZenRuleTest.java
+++ b/core/tests/coretests/src/android/app/AutomaticZenRuleTest.java
@@ -16,6 +16,8 @@
 
 package android.app;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.fail;
 
@@ -26,6 +28,8 @@
 import android.os.Parcel;
 import android.platform.test.annotations.EnableFlags;
 import android.platform.test.flag.junit.SetFlagsRule;
+import android.service.notification.ZenDeviceEffects;
+import android.service.notification.ZenPolicy;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
@@ -226,4 +230,66 @@
 
         assertThrows(IllegalArgumentException.class, () -> builder.setType(100));
     }
+
+    @Test
+    @EnableFlags(Flags.FLAG_MODES_API)
+    public void testCanUpdate_nullPolicyAndDeviceEffects() {
+        AutomaticZenRule.Builder builder = new AutomaticZenRule.Builder("name",
+                Uri.parse("uri://short"));
+
+        AutomaticZenRule rule = builder.setUserModifiedFields(0)
+                .setZenPolicy(null)
+                .setDeviceEffects(null)
+                .build();
+
+        assertThat(rule.canUpdate()).isTrue();
+
+        rule = builder.setUserModifiedFields(1).build();
+        assertThat(rule.canUpdate()).isFalse();
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_MODES_API)
+    public void testCanUpdate_policyModified() {
+        ZenPolicy.Builder policyBuilder = new ZenPolicy.Builder().setUserModifiedFields(0);
+        ZenPolicy policy = policyBuilder.build();
+
+        AutomaticZenRule.Builder builder = new AutomaticZenRule.Builder("name",
+                Uri.parse("uri://short"));
+        AutomaticZenRule rule = builder.setUserModifiedFields(0)
+                .setZenPolicy(policy)
+                .setDeviceEffects(null).build();
+
+        // Newly created ZenPolicy is not user modified.
+        assertThat(policy.getUserModifiedFields()).isEqualTo(0);
+        assertThat(rule.canUpdate()).isTrue();
+
+        policy = policyBuilder.setUserModifiedFields(1).build();
+        assertThat(policy.getUserModifiedFields()).isEqualTo(1);
+        rule = builder.setZenPolicy(policy).build();
+        assertThat(rule.canUpdate()).isFalse();
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_MODES_API)
+    public void testCanUpdate_deviceEffectsModified() {
+        ZenDeviceEffects.Builder deviceEffectsBuilder =
+                new ZenDeviceEffects.Builder().setUserModifiedFields(0);
+        ZenDeviceEffects deviceEffects = deviceEffectsBuilder.build();
+
+        AutomaticZenRule.Builder builder = new AutomaticZenRule.Builder("name",
+                Uri.parse("uri://short"));
+        AutomaticZenRule rule = builder.setUserModifiedFields(0)
+                .setZenPolicy(null)
+                .setDeviceEffects(deviceEffects).build();
+
+        // Newly created ZenDeviceEffects is not user modified.
+        assertThat(deviceEffects.getUserModifiedFields()).isEqualTo(0);
+        assertThat(rule.canUpdate()).isTrue();
+
+        deviceEffects = deviceEffectsBuilder.setUserModifiedFields(1).build();
+        assertThat(deviceEffects.getUserModifiedFields()).isEqualTo(1);
+        rule = builder.setDeviceEffects(deviceEffects).build();
+        assertThat(rule.canUpdate()).isFalse();
+    }
 }
diff --git a/core/tests/coretests/src/android/app/servertransaction/ActivityConfigurationChangeItemTest.java b/core/tests/coretests/src/android/app/servertransaction/ActivityConfigurationChangeItemTest.java
deleted file mode 100644
index 785a8a1..0000000
--- a/core/tests/coretests/src/android/app/servertransaction/ActivityConfigurationChangeItemTest.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app.servertransaction;
-
-import static org.junit.Assert.assertEquals;
-import static org.mockito.Mockito.doReturn;
-
-import android.app.Activity;
-import android.app.ClientTransactionHandler;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.os.IBinder;
-import android.platform.test.annotations.Presubmit;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-/**
- * Tests for {@link ActivityConfigurationChangeItem}.
- *
- * Build/Install/Run:
- *  atest FrameworksCoreTests:ActivityConfigurationChangeItemTest
- */
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-@Presubmit
-public class ActivityConfigurationChangeItemTest {
-
-    @Mock
-    private ClientTransactionHandler mHandler;
-    @Mock
-    private IBinder mToken;
-    @Mock
-    private Activity mActivity;
-    // Can't mock final class.
-    private final Configuration mConfiguration = new Configuration();
-
-    @Before
-    public void setup() {
-        MockitoAnnotations.initMocks(this);
-    }
-
-    @Test
-    public void testGetContextToUpdate() {
-        doReturn(mActivity).when(mHandler).getActivity(mToken);
-
-        final ActivityConfigurationChangeItem item = ActivityConfigurationChangeItem
-                .obtain(mToken, mConfiguration);
-        final Context context = item.getContextToUpdate(mHandler);
-
-        assertEquals(mActivity, context);
-    }
-}
diff --git a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionItemTest.java b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionItemTest.java
new file mode 100644
index 0000000..b5e8203
--- /dev/null
+++ b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionItemTest.java
@@ -0,0 +1,239 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.servertransaction;
+
+import static android.content.Context.DEVICE_ID_DEFAULT;
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.verify;
+
+import android.app.Activity;
+import android.app.ActivityThread;
+import android.app.ClientTransactionHandler;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+import android.util.ArrayMap;
+import android.util.MergedConfiguration;
+import android.view.IWindow;
+import android.view.InsetsState;
+import android.window.ClientWindowFrames;
+import android.window.WindowContext;
+import android.window.WindowContextInfo;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for subtypes of {@link ClientTransactionItem}.
+ *
+ * Build/Install/Run:
+ *  atest FrameworksCoreTests:ClientTransactionItemTest
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@Presubmit
+public class ClientTransactionItemTest {
+
+    @Mock
+    private ClientTransactionHandler mHandler;
+    @Mock
+    private IBinder mActivityToken;
+    @Mock
+    private Activity mActivity;
+    @Mock
+    private PendingTransactionActions mPendingActions;
+    @Mock
+    private IBinder mWindowClientToken;
+    @Mock
+    private WindowContext mWindowContext;
+    @Mock
+    private IWindow mWindow;
+
+    // Can't mock final class.
+    private Configuration mGlobalConfig;
+    private Configuration mConfiguration;
+    private ActivityThread.ActivityClientRecord mActivityClientRecord;
+    private ArrayMap<IBinder, DestroyActivityItem> mActivitiesToBeDestroyed;
+    private InsetsState mInsetsState;
+    private ClientWindowFrames mFrames;
+    private MergedConfiguration mMergedConfiguration;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        mGlobalConfig = new Configuration();
+        mConfiguration = new Configuration();
+        mActivitiesToBeDestroyed = new ArrayMap<>();
+        mActivityClientRecord = new ActivityThread.ActivityClientRecord();
+        mInsetsState = new InsetsState();
+        mFrames = new ClientWindowFrames();
+        mMergedConfiguration = new MergedConfiguration(mGlobalConfig, mConfiguration);
+
+        doReturn(mActivity).when(mHandler).getActivity(mActivityToken);
+        doReturn(mActivitiesToBeDestroyed).when(mHandler).getActivitiesToBeDestroyed();
+    }
+
+    @Test
+    public void testActivityConfigurationChangeItem_getContextToUpdate() {
+        final ActivityConfigurationChangeItem item = ActivityConfigurationChangeItem
+                .obtain(mActivityToken, mConfiguration);
+        final Context context = item.getContextToUpdate(mHandler);
+
+        assertEquals(mActivity, context);
+    }
+
+    @Test
+    public void testActivityRelaunchItem_getContextToUpdate() {
+        final ActivityRelaunchItem item = ActivityRelaunchItem
+                .obtain(mActivityToken, null /* pendingResults */, null  /* pendingNewIntents */,
+                        0 /* configChange */, mMergedConfiguration, false /* preserveWindow */);
+        final Context context = item.getContextToUpdate(mHandler);
+
+        assertEquals(mActivity, context);
+    }
+
+    @Test
+    public void testConfigurationChangeItem_getContextToUpdate() {
+        final ConfigurationChangeItem item = ConfigurationChangeItem
+                .obtain(mConfiguration, DEVICE_ID_DEFAULT);
+        final Context context = item.getContextToUpdate(mHandler);
+
+        assertEquals(ActivityThread.currentApplication(), context);
+    }
+
+    @Test
+    public void testDestroyActivityItem_preExecute() {
+        final DestroyActivityItem item = DestroyActivityItem
+                .obtain(mActivityToken, false /* finished */, 123 /* configChanges */);
+        item.preExecute(mHandler);
+
+        assertEquals(1, mActivitiesToBeDestroyed.size());
+        assertEquals(item, mActivitiesToBeDestroyed.get(mActivityToken));
+    }
+
+    @Test
+    public void testDestroyActivityItem_postExecute() {
+        final DestroyActivityItem item = DestroyActivityItem
+                .obtain(mActivityToken, false /* finished */, 123 /* configChanges */);
+        item.preExecute(mHandler);
+        item.postExecute(mHandler, mPendingActions);
+
+        assertTrue(mActivitiesToBeDestroyed.isEmpty());
+    }
+
+    @Test
+    public void testDestroyActivityItem_execute() {
+        final DestroyActivityItem item = DestroyActivityItem
+                .obtain(mActivityToken, false /* finished */, 123 /* configChanges */);
+        item.execute(mHandler, mActivityClientRecord, mPendingActions);
+
+        verify(mHandler).handleDestroyActivity(eq(mActivityClientRecord), eq(false) /* finishing */,
+                eq(123) /* configChanges */, eq(false) /* getNonConfigInstance */, any());
+    }
+
+    @Test
+    public void testLaunchActivityItem_getContextToUpdate() {
+        final LaunchActivityItem item = new TestUtils.LaunchActivityItemBuilder(
+                mActivityToken, new Intent(), new ActivityInfo())
+                .build();
+
+        final Context context = item.getContextToUpdate(mHandler);
+
+        assertEquals(ActivityThread.currentApplication(), context);
+    }
+
+    @Test
+    public void testMoveToDisplayItem_getContextToUpdate() {
+        final MoveToDisplayItem item = MoveToDisplayItem
+                .obtain(mActivityToken, DEFAULT_DISPLAY, mConfiguration);
+        final Context context = item.getContextToUpdate(mHandler);
+
+        assertEquals(mActivity, context);
+    }
+
+    @Test
+    public void testWindowContextInfoChangeItem_execute() {
+        final WindowContextInfoChangeItem item = WindowContextInfoChangeItem
+                .obtain(mWindowClientToken, mConfiguration, DEFAULT_DISPLAY);
+        item.execute(mHandler, mPendingActions);
+
+        verify(mHandler).handleWindowContextInfoChanged(mWindowClientToken,
+                new WindowContextInfo(mConfiguration, DEFAULT_DISPLAY));
+    }
+
+    @Test
+    public void testWindowContextInfoChangeItem_getContextToUpdate() {
+        doReturn(mWindowContext).when(mHandler).getWindowContext(mWindowClientToken);
+
+        final WindowContextInfoChangeItem item = WindowContextInfoChangeItem
+                .obtain(mWindowClientToken, mConfiguration, DEFAULT_DISPLAY);
+        final Context context = item.getContextToUpdate(mHandler);
+
+        assertEquals(mWindowContext, context);
+    }
+
+    @Test
+    public void testWindowContextWindowRemovalItem_execute() {
+        final WindowContextWindowRemovalItem item = WindowContextWindowRemovalItem.obtain(
+                mWindowClientToken);
+        item.execute(mHandler, mPendingActions);
+
+        verify(mHandler).handleWindowContextWindowRemoval(mWindowClientToken);
+    }
+
+    @Test
+    public void testWindowStateResizeItem_execute() throws RemoteException {
+        final WindowStateResizeItem item = WindowStateResizeItem.obtain(mWindow, mFrames,
+                true /* reportDraw */, mMergedConfiguration, mInsetsState, true /* forceLayout */,
+                true /* alwaysConsumeSystemBars */, 123 /* displayId */, 321 /* syncSeqId */,
+                true /* dragResizing */);
+        item.execute(mHandler, mPendingActions);
+
+        verify(mWindow).resized(mFrames,
+                true /* reportDraw */, mMergedConfiguration, mInsetsState, true /* forceLayout */,
+                true /* alwaysConsumeSystemBars */, 123 /* displayId */, 321 /* syncSeqId */,
+                true /* dragResizing */);
+    }
+
+    @Test
+    public void testWindowStateResizeItem_getContextToUpdate() {
+        final WindowStateResizeItem item = WindowStateResizeItem.obtain(mWindow, mFrames,
+                true /* reportDraw */, mMergedConfiguration, mInsetsState, true /* forceLayout */,
+                true /* alwaysConsumeSystemBars */, 123 /* displayId */, 321 /* syncSeqId */,
+                true /* dragResizing */);
+        final Context context = item.getContextToUpdate(mHandler);
+
+        assertEquals(ActivityThread.currentApplication(), context);
+    }
+
+}
diff --git a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java
index 930b1a4..95d5049 100644
--- a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java
+++ b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java
@@ -65,7 +65,7 @@
         mHandler = getInstrumentation().getContext().getMainThreadHandler();
         mController = spy(ClientTransactionListenerController.createInstanceForTesting(
                 mDisplayManager));
-        doReturn(true).when(mController).isSyncWindowConfigUpdateFlagEnabled();
+        doReturn(true).when(mController).isBundleClientTransactionFlagEnabled();
     }
 
     @Test
diff --git a/core/tests/coretests/src/android/app/servertransaction/ConfigurationChangeItemTest.java b/core/tests/coretests/src/android/app/servertransaction/ConfigurationChangeItemTest.java
deleted file mode 100644
index d9f5523..0000000
--- a/core/tests/coretests/src/android/app/servertransaction/ConfigurationChangeItemTest.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app.servertransaction;
-
-import static android.content.Context.DEVICE_ID_DEFAULT;
-
-import static org.junit.Assert.assertEquals;
-
-import android.app.ActivityThread;
-import android.app.ClientTransactionHandler;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.platform.test.annotations.Presubmit;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-/**
- * Tests for {@link ConfigurationChangeItem}.
- *
- * Build/Install/Run:
- *  atest FrameworksCoreTests:ConfigurationChangeItemTest
- */
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-@Presubmit
-public class ConfigurationChangeItemTest {
-
-    @Mock
-    private ClientTransactionHandler mHandler;
-    // Can't mock final class.
-    private final Configuration mConfiguration = new Configuration();
-
-    @Before
-    public void setup() {
-        MockitoAnnotations.initMocks(this);
-    }
-
-    @Test
-    public void testGetContextToUpdate() {
-        final ConfigurationChangeItem item = ConfigurationChangeItem
-                .obtain(mConfiguration, DEVICE_ID_DEFAULT);
-        final Context context = item.getContextToUpdate(mHandler);
-
-        assertEquals(ActivityThread.currentApplication(), context);
-    }
-}
diff --git a/core/tests/coretests/src/android/app/servertransaction/DestroyActivityItemTest.java b/core/tests/coretests/src/android/app/servertransaction/DestroyActivityItemTest.java
deleted file mode 100644
index ecd75a8..0000000
--- a/core/tests/coretests/src/android/app/servertransaction/DestroyActivityItemTest.java
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app.servertransaction;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.verify;
-
-import android.app.ActivityThread.ActivityClientRecord;
-import android.app.ClientTransactionHandler;
-import android.os.IBinder;
-import android.platform.test.annotations.Presubmit;
-import android.util.ArrayMap;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-/**
- * Tests for {@link DestroyActivityItem}.
- *
- * Build/Install/Run:
- *  atest FrameworksCoreTests:DestroyActivityItemTest
- */
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-@Presubmit
-public class DestroyActivityItemTest {
-
-    @Mock
-    private ClientTransactionHandler mHandler;
-    @Mock
-    private PendingTransactionActions mPendingActions;
-    @Mock
-    private IBinder mActivityToken;
-
-    // Can't mock final class.
-    private ActivityClientRecord mActivityClientRecord;
-
-    private ArrayMap<IBinder, DestroyActivityItem> mActivitiesToBeDestroyed;
-    private DestroyActivityItem mItem;
-
-    @Before
-    public void setup() {
-        MockitoAnnotations.initMocks(this);
-        mItem = DestroyActivityItem.obtain(
-                mActivityToken, false /* finished */, 123 /* configChanges */);
-        mActivityClientRecord = new ActivityClientRecord();
-        mActivitiesToBeDestroyed = new ArrayMap<>();
-
-        doReturn(mActivitiesToBeDestroyed).when(mHandler).getActivitiesToBeDestroyed();
-    }
-
-    @Test
-    public void testPreExecute() {
-        mItem.preExecute(mHandler);
-
-        assertEquals(1, mActivitiesToBeDestroyed.size());
-        assertEquals(mItem, mActivitiesToBeDestroyed.get(mActivityToken));
-    }
-
-    @Test
-    public void testPostExecute() {
-        mItem.preExecute(mHandler);
-        mItem.postExecute(mHandler, mPendingActions);
-
-        assertTrue(mActivitiesToBeDestroyed.isEmpty());
-    }
-
-    @Test
-    public void testExecute() {
-        mItem.execute(mHandler, mActivityClientRecord, mPendingActions);
-
-        verify(mHandler).handleDestroyActivity(eq(mActivityClientRecord), eq(false) /* finishing */,
-                eq(123) /* configChanges */, eq(false) /* getNonConfigInstance */, any());
-    }
-}
diff --git a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
index 723c081..a796a0f 100644
--- a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
@@ -171,7 +171,8 @@
 
     @Test
     public void testRecycleStartActivityItem() {
-        testRecycle(() -> StartActivityItem.obtain(mActivityToken, ActivityOptions.makeBasic()));
+        testRecycle(() -> StartActivityItem.obtain(mActivityToken,
+                new ActivityOptions.SceneTransitionInfo()));
     }
 
     @Test
diff --git a/core/tests/coretests/src/android/app/servertransaction/TestUtils.java b/core/tests/coretests/src/android/app/servertransaction/TestUtils.java
index c0e2a49..3823033 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TestUtils.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TestUtils.java
@@ -255,9 +255,9 @@
             return LaunchActivityItem.obtain(mActivityToken, mIntent, mIdent, mInfo,
                     mCurConfig, mOverrideConfig, mDeviceId, mReferrer, mVoiceInteractor,
                     mProcState, mState, mPersistentState, mPendingResults, mPendingNewIntents,
-                    mActivityOptions, mIsForward, mProfilerInfo, mAssistToken,
-                    null /* activityClientController */, mShareableActivityToken,
-                    mLaunchedFromBubble, mTaskFragmentToken);
+                    mActivityOptions != null ? mActivityOptions.getSceneTransitionInfo() : null,
+                    mIsForward, mProfilerInfo, mAssistToken, null /* activityClientController */,
+                    mShareableActivityToken, mLaunchedFromBubble, mTaskFragmentToken);
         }
     }
 }
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
index 07921bf..952cdd9 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
@@ -262,7 +262,7 @@
     public void testStart() {
         // Write to parcel
         StartActivityItem item = StartActivityItem.obtain(mActivityToken,
-                ActivityOptions.makeBasic());
+                new ActivityOptions.SceneTransitionInfo());
         writeAndPrepareForReading(item);
 
         // Read from parcel and assert
diff --git a/core/tests/coretests/src/android/app/servertransaction/WindowContextInfoChangeItemTest.java b/core/tests/coretests/src/android/app/servertransaction/WindowContextInfoChangeItemTest.java
deleted file mode 100644
index a801a76..0000000
--- a/core/tests/coretests/src/android/app/servertransaction/WindowContextInfoChangeItemTest.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app.servertransaction;
-
-import static android.view.Display.DEFAULT_DISPLAY;
-
-import static org.junit.Assert.assertEquals;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.verify;
-
-import android.app.ClientTransactionHandler;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.os.IBinder;
-import android.platform.test.annotations.Presubmit;
-import android.window.WindowContext;
-import android.window.WindowContextInfo;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-/**
- * Tests for {@link WindowContextInfoChangeItem}.
- *
- * Build/Install/Run:
- *  atest FrameworksCoreTests:WindowContextInfoChangeItemTest
- */
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-@Presubmit
-public class WindowContextInfoChangeItemTest {
-
-    @Mock
-    private ClientTransactionHandler mHandler;
-    @Mock
-    private PendingTransactionActions mPendingActions;
-    @Mock
-    private IBinder mClientToken;
-    @Mock
-    private WindowContext mWindowContext;
-    // Can't mock final class.
-    private final Configuration mConfiguration = new Configuration();
-
-    @Before
-    public void setup() {
-        MockitoAnnotations.initMocks(this);
-    }
-
-    @Test
-    public void testExecute() {
-        final WindowContextInfoChangeItem item = WindowContextInfoChangeItem
-                .obtain(mClientToken, mConfiguration, DEFAULT_DISPLAY);
-        item.execute(mHandler, mPendingActions);
-
-        verify(mHandler).handleWindowContextInfoChanged(mClientToken,
-                new WindowContextInfo(mConfiguration, DEFAULT_DISPLAY));
-    }
-
-    @Test
-    public void testGetContextToUpdate() {
-        doReturn(mWindowContext).when(mHandler).getWindowContext(mClientToken);
-
-        final WindowContextInfoChangeItem item = WindowContextInfoChangeItem
-                .obtain(mClientToken, mConfiguration, DEFAULT_DISPLAY);
-        final Context context = item.getContextToUpdate(mHandler);
-
-        assertEquals(mWindowContext, context);
-    }
-}
diff --git a/core/tests/coretests/src/android/app/servertransaction/WindowContextWindowRemovalItemTest.java b/core/tests/coretests/src/android/app/servertransaction/WindowContextWindowRemovalItemTest.java
deleted file mode 100644
index cf9935f..0000000
--- a/core/tests/coretests/src/android/app/servertransaction/WindowContextWindowRemovalItemTest.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app.servertransaction;
-
-import static org.mockito.Mockito.verify;
-
-import android.app.ClientTransactionHandler;
-import android.os.IBinder;
-import android.platform.test.annotations.Presubmit;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-/**
- * Tests for {@link WindowContextWindowRemovalItem}.
- *
- * Build/Install/Run:
- *  atest FrameworksCoreTests:WindowContextWindowRemovalItemTest
- */
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-@Presubmit
-public class WindowContextWindowRemovalItemTest {
-
-    @Mock
-    private ClientTransactionHandler mHandler;
-    @Mock
-    private PendingTransactionActions mPendingActions;
-    @Mock
-    private IBinder mClientToken;
-
-    @Before
-    public void setup() {
-        MockitoAnnotations.initMocks(this);
-    }
-
-    @Test
-    public void testExecute() {
-        final WindowContextWindowRemovalItem item = WindowContextWindowRemovalItem.obtain(
-                mClientToken);
-        item.execute(mHandler, mPendingActions);
-
-        verify(mHandler).handleWindowContextWindowRemoval(mClientToken);
-    }
-}
diff --git a/core/tests/coretests/src/android/app/servertransaction/WindowStateResizeItemTest.java b/core/tests/coretests/src/android/app/servertransaction/WindowStateResizeItemTest.java
deleted file mode 100644
index 4d45daf..0000000
--- a/core/tests/coretests/src/android/app/servertransaction/WindowStateResizeItemTest.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app.servertransaction;
-
-import static org.mockito.Mockito.verify;
-
-import android.app.ClientTransactionHandler;
-import android.os.RemoteException;
-import android.platform.test.annotations.Presubmit;
-import android.util.MergedConfiguration;
-import android.view.IWindow;
-import android.view.InsetsState;
-import android.window.ClientWindowFrames;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-/**
- * Tests for {@link WindowStateResizeItem}.
- *
- * Build/Install/Run:
- *  atest FrameworksCoreTests:WindowStateResizeItemTest
- */
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-@Presubmit
-public class WindowStateResizeItemTest {
-
-    @Mock
-    private ClientTransactionHandler mHandler;
-    @Mock
-    private PendingTransactionActions mPendingActions;
-    @Mock
-    private IWindow mWindow;
-
-    private InsetsState mInsetsState;
-    private ClientWindowFrames mFrames;
-    private MergedConfiguration mConfiguration;
-
-    @Before
-    public void setup() {
-        MockitoAnnotations.initMocks(this);
-
-        mInsetsState = new InsetsState();
-        mFrames = new ClientWindowFrames();
-        mConfiguration = new MergedConfiguration();
-    }
-
-    @Test
-    public void testExecute() throws RemoteException {
-        final WindowStateResizeItem item = WindowStateResizeItem.obtain(mWindow, mFrames,
-                true /* reportDraw */, mConfiguration, mInsetsState, true /* forceLayout */,
-                true /* alwaysConsumeSystemBars */, 123 /* displayId */, 321 /* syncSeqId */,
-                true /* dragResizing */);
-        item.execute(mHandler, mPendingActions);
-
-        verify(mWindow).resized(mFrames,
-                true /* reportDraw */, mConfiguration, mInsetsState, true /* forceLayout */,
-                true /* alwaysConsumeSystemBars */, 123 /* displayId */, 321 /* syncSeqId */,
-                true /* dragResizing */);
-    }
-}
diff --git a/core/tests/coretests/src/android/content/pm/ModuleInfoTest.java b/core/tests/coretests/src/android/content/pm/ModuleInfoTest.java
new file mode 100644
index 0000000..4366e02c
--- /dev/null
+++ b/core/tests/coretests/src/android/content/pm/ModuleInfoTest.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Parcel;
+import android.platform.test.annotations.AppModeFull;
+import android.text.TextUtils;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+@AppModeFull
+public class ModuleInfoTest {
+
+    private static final String APEX_MODULE_NAME = "apexModuleName";
+    private static final String APK_IN_APEX_PACKAGE_NAME = "apkInApexPackageName";
+    private static final String MODULE_PACKAGE_NAME = "modulePackageName";
+    private static final String MODULE_NAME = "moduleName";
+
+    @Test
+    public void testSimple() {
+        ModuleInfo info = new ModuleInfo();
+        assertThat(info.toString()).isNotNull();
+    }
+
+    @Test
+    public void testDefaultCopy() {
+        ModuleInfo oldInfo = new ModuleInfo();
+        ModuleInfo newInfo = new ModuleInfo(oldInfo);
+        assertThat(newInfo).isEqualTo(oldInfo);
+    }
+
+    @Test
+    public void testCopy() {
+        boolean isHidden = false;
+        ModuleInfo info = new ModuleInfo();
+        info.setHidden(isHidden);
+        info.setApexModuleName(APEX_MODULE_NAME);
+        info.setPackageName(MODULE_PACKAGE_NAME);
+        info.setName(MODULE_NAME);
+        info.setApkInApexPackageNames(List.of(APK_IN_APEX_PACKAGE_NAME));
+
+        ModuleInfo newInfo = new ModuleInfo(info);
+        assertThat(newInfo).isEqualTo(info);
+    }
+
+    @Test
+    public void testGetApkInApexPackageNamesReturnEmptyListInDefault() {
+        ModuleInfo info = new ModuleInfo();
+        assertThat(info.getApkInApexPackageNames()).isNotNull();
+        assertThat(info.getApkInApexPackageNames()).isEmpty();
+    }
+
+    @Test
+    public void testModuleInfoParcelizeDeparcelize() {
+        boolean isHidden = false;
+        ModuleInfo info = new ModuleInfo();
+        info.setHidden(isHidden);
+        info.setApexModuleName(APEX_MODULE_NAME);
+        info.setPackageName(MODULE_PACKAGE_NAME);
+        info.setName(MODULE_NAME);
+        info.setApkInApexPackageNames(List.of(APK_IN_APEX_PACKAGE_NAME));
+
+        final Parcel p = Parcel.obtain();
+        info.writeToParcel(p, 0);
+        p.setDataPosition(0);
+
+        final ModuleInfo targetInfo = ModuleInfo.CREATOR.createFromParcel(p);
+        p.recycle();
+
+        assertThat(info.isHidden()).isEqualTo(targetInfo.isHidden());
+        assertThat(info.getApexModuleName()).isEqualTo(targetInfo.getApexModuleName());
+        assertThat(info.getPackageName()).isEqualTo(targetInfo.getPackageName());
+        assertThat(TextUtils.equals(info.getName(), targetInfo.getName())).isTrue();
+        assertThat(info.getApkInApexPackageNames().toArray()).isEqualTo(
+                targetInfo.getApkInApexPackageNames().toArray());
+    }
+}
diff --git a/core/tests/coretests/src/android/content/res/FontScaleConverterFactoryTest.kt b/core/tests/coretests/src/android/content/res/FontScaleConverterFactoryTest.kt
index 8308e7c..1617eda 100644
--- a/core/tests/coretests/src/android/content/res/FontScaleConverterFactoryTest.kt
+++ b/core/tests/coretests/src/android/content/res/FontScaleConverterFactoryTest.kt
@@ -20,12 +20,15 @@
 import android.platform.test.annotations.RequiresFlagsEnabled
 import android.platform.test.flag.junit.CheckFlagsRule
 import android.platform.test.flag.junit.DeviceFlagsValueProvider
+import android.util.SparseArray
 import androidx.core.util.forEach
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.LargeTest
 import androidx.test.filters.SmallTest
 import com.google.common.truth.Truth.assertThat
 import com.google.common.truth.Truth.assertWithMessage
+import org.junit.After
+import org.junit.Before
 import org.junit.Rule
 import kotlin.math.ceil
 import kotlin.math.floor
@@ -45,6 +48,19 @@
     @get:Rule
     val checkFlagsRule: CheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
 
+    private lateinit var defaultLookupTables: SparseArray<FontScaleConverter>
+
+    @Before
+    fun setup() {
+        defaultLookupTables = FontScaleConverterFactory.sLookupTables.clone()
+    }
+
+    @After
+    fun teardown() {
+        // Restore the default tables (since some tests will have added extras to the cache)
+        FontScaleConverterFactory.sLookupTables = defaultLookupTables
+    }
+
     @Test
     fun scale200IsTwiceAtSmallSizes() {
         val table = FontScaleConverterFactory.forScale(2F)!!
@@ -245,7 +261,7 @@
     }
 
     companion object {
-        private const val CONVERSION_TOLERANCE = 0.05f
+        private const val CONVERSION_TOLERANCE = 0.18f
     }
 }
 
diff --git a/core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java b/core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java
index 3fc08ee..e118c98d 100644
--- a/core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java
+++ b/core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java
@@ -26,6 +26,9 @@
 import android.database.Cursor;
 import android.database.DatabaseUtils;
 import android.os.SystemClock;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 import android.test.AndroidTestCase;
 import android.util.Log;
 
@@ -35,6 +38,7 @@
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -53,6 +57,10 @@
 @SmallTest
 public class SQLiteDatabaseTest {
 
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule =
+            DeviceFlagsValueProvider.createCheckFlagsRule();
+
     private static final String TAG = "SQLiteDatabaseTest";
 
     private final Context mContext = InstrumentationRegistry.getInstrumentation().getContext();
@@ -247,6 +255,7 @@
 
         final String query = "--comment\nSELECT count(*) from t1";
 
+        database.beginTransactionReadOnly();
         try {
             for (int i = count; i > 0; i--) {
                 ticker.arriveAndAwaitAdvance();
@@ -260,6 +269,7 @@
         } catch (Throwable t) {
             errors.add(t);
         } finally {
+            database.endTransaction();
             ticker.arriveAndDeregister();
         }
     }
@@ -347,4 +357,50 @@
 
         assertTrue("ReadThread failed with errors: " + errors, errors.isEmpty());
     }
+
+    @RequiresFlagsEnabled(Flags.FLAG_SQLITE_ALLOW_TEMP_TABLES)
+    @Test
+    public void testTempTable() {
+        boolean allowed;
+        allowed = true;
+        mDatabase.beginTransactionReadOnly();
+        try {
+            mDatabase.execSQL("CREATE TEMP TABLE t1 (i int, j int);");
+            mDatabase.execSQL("INSERT INTO t1 (i, j) VALUES (2, 20)");
+            mDatabase.execSQL("INSERT INTO t1 (i, j) VALUES (3, 30)");
+
+            final String sql = "SELECT i FROM t1 WHERE j = 30";
+            try (SQLiteRawStatement s = mDatabase.createRawStatement(sql)) {
+                assertTrue(s.step());
+                assertEquals(3, s.getColumnInt(0));
+            }
+
+        } catch (SQLiteException e) {
+            allowed = false;
+        } finally {
+            mDatabase.endTransaction();
+        }
+        assertTrue(allowed);
+
+        // Repeat the test on the main schema.
+        allowed = true;
+        mDatabase.beginTransactionReadOnly();
+        try {
+            mDatabase.execSQL("CREATE TABLE t2 (i int, j int);");
+            mDatabase.execSQL("INSERT INTO t2 (i, j) VALUES (2, 20)");
+            mDatabase.execSQL("INSERT INTO t2 (i, j) VALUES (3, 30)");
+
+            final String sql = "SELECT i FROM t2 WHERE j = 30";
+            try (SQLiteRawStatement s = mDatabase.createRawStatement(sql)) {
+                assertTrue(s.step());
+                assertEquals(3, s.getColumnInt(0));
+            }
+
+        } catch (SQLiteException e) {
+            allowed = false;
+        } finally {
+            mDatabase.endTransaction();
+        }
+        assertFalse(allowed);
+    }
 }
diff --git a/core/tests/coretests/src/android/graphics/PaintTest.java b/core/tests/coretests/src/android/graphics/PaintTest.java
index bf56df1..0dec756 100644
--- a/core/tests/coretests/src/android/graphics/PaintTest.java
+++ b/core/tests/coretests/src/android/graphics/PaintTest.java
@@ -19,6 +19,7 @@
 import static org.junit.Assert.assertNotEquals;
 
 import android.test.InstrumentationTestCase;
+import android.text.TextUtils;
 
 import androidx.test.filters.SmallTest;
 
@@ -362,4 +363,44 @@
         //    = 30
         assertEquals(30.0f, p.getUnderlineThickness(), 0.5f);
     }
+
+    private int getClusterCount(Paint p, String text) {
+        Paint.RunInfo runInfo = new Paint.RunInfo();
+        p.getRunCharacterAdvance(text, 0, text.length(), 0, text.length(), false, 0, null, 0, null,
+                runInfo);
+        int ccByString = runInfo.getClusterCount();
+        runInfo.setClusterCount(0);
+        char[] buf = new char[text.length()];
+        TextUtils.getChars(text, 0, text.length(), buf, 0);
+        p.getRunCharacterAdvance(buf, 0, buf.length, 0, buf.length, false, 0, null, 0, null,
+                runInfo);
+        int ccByChars = runInfo.getClusterCount();
+        assertEquals(ccByChars, ccByString);
+        return ccByChars;
+    }
+
+    public void testCluster() {
+        final Paint p = new Paint();
+        p.setTextSize(100);
+
+        // Regular String
+        assertEquals(1, getClusterCount(p, "A"));
+        assertEquals(2, getClusterCount(p, "AB"));
+
+        // Ligature is in the same cluster
+        assertEquals(1, getClusterCount(p, "fi"));  // Ligature
+        p.setFontFeatureSettings("'liga' off");
+        assertEquals(2, getClusterCount(p, "fi"));  // Ligature is disabled
+        p.setFontFeatureSettings("");
+
+        // Combining character
+        assertEquals(1, getClusterCount(p, "\u0061\u0300"));  // A + COMBINING GRAVE ACCENT
+
+        // BiDi
+        final String rtlStr = "\u05D0\u05D1\u05D2";
+        final String ltrStr = "abc";
+        assertEquals(3, getClusterCount(p, rtlStr));
+        assertEquals(6, getClusterCount(p, rtlStr + ltrStr));
+        assertEquals(9, getClusterCount(p, ltrStr + rtlStr + ltrStr));
+    }
 }
diff --git a/core/tests/coretests/src/android/os/BuildTest.java b/core/tests/coretests/src/android/os/BuildTest.java
index 3162e6d..2d3e123 100644
--- a/core/tests/coretests/src/android/os/BuildTest.java
+++ b/core/tests/coretests/src/android/os/BuildTest.java
@@ -45,7 +45,8 @@
     public final RavenwoodRule mRavenwood = new RavenwoodRule();
 
     @Rule
-    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(
+                SetFlagsRule.DefaultInitValueType.NULL_DEFAULT);
 
     /**
      * Asserts that a String is non-null and non-empty.  If it is not,
diff --git a/core/tests/coretests/src/android/os/PerformanceHintManagerTest.java b/core/tests/coretests/src/android/os/PerformanceHintManagerTest.java
index 12a2844..a28bb69 100644
--- a/core/tests/coretests/src/android/os/PerformanceHintManagerTest.java
+++ b/core/tests/coretests/src/android/os/PerformanceHintManagerTest.java
@@ -195,9 +195,30 @@
         Session s = createSession();
         assumeNotNull(s);
         s.updateTargetWorkDuration(16);
-        s.reportActualWorkDuration(new WorkDuration(1, 12, 8, 6));
-        s.reportActualWorkDuration(new WorkDuration(1, 33, 14, 20));
-        s.reportActualWorkDuration(new WorkDuration(1, 14, 10, 6));
+        {
+            WorkDuration workDuration = new WorkDuration();
+            workDuration.setWorkPeriodStartTimestampNanos(1);
+            workDuration.setActualTotalDurationNanos(12);
+            workDuration.setActualCpuDurationNanos(8);
+            workDuration.setActualGpuDurationNanos(6);
+            s.reportActualWorkDuration(workDuration);
+        }
+        {
+            WorkDuration workDuration = new WorkDuration();
+            workDuration.setWorkPeriodStartTimestampNanos(1);
+            workDuration.setActualTotalDurationNanos(33);
+            workDuration.setActualCpuDurationNanos(14);
+            workDuration.setActualGpuDurationNanos(20);
+            s.reportActualWorkDuration(workDuration);
+        }
+        {
+            WorkDuration workDuration = new WorkDuration();
+            workDuration.setWorkPeriodStartTimestampNanos(1);
+            workDuration.setActualTotalDurationNanos(14);
+            workDuration.setActualCpuDurationNanos(10);
+            workDuration.setActualGpuDurationNanos(6);
+            s.reportActualWorkDuration(workDuration);
+        }
     }
 
     @Test
@@ -206,25 +227,25 @@
         assumeNotNull(s);
         s.updateTargetWorkDuration(16);
         assertThrows(IllegalArgumentException.class, () -> {
-            s.reportActualWorkDuration(new WorkDuration(-1, 12, 8, 6));
+            s.reportActualWorkDuration(new WorkDuration(-1, 12, 8, 6, 1));
         });
         assertThrows(IllegalArgumentException.class, () -> {
-            s.reportActualWorkDuration(new WorkDuration(0, 12, 8, 6));
+            s.reportActualWorkDuration(new WorkDuration(0, 12, 8, 6, 1));
         });
         assertThrows(IllegalArgumentException.class, () -> {
-            s.reportActualWorkDuration(new WorkDuration(1, -1, 8, 6));
+            s.reportActualWorkDuration(new WorkDuration(1, -1, 8, 6, 1));
         });
         assertThrows(IllegalArgumentException.class, () -> {
-            s.reportActualWorkDuration(new WorkDuration(1, 0, 8, 6));
+            s.reportActualWorkDuration(new WorkDuration(1, 0, 8, 6, 1));
         });
         assertThrows(IllegalArgumentException.class, () -> {
-            s.reportActualWorkDuration(new WorkDuration(1, 12, -1, 6));
+            s.reportActualWorkDuration(new WorkDuration(1, 12, -1, 6, 1));
         });
         assertThrows(IllegalArgumentException.class, () -> {
-            s.reportActualWorkDuration(new WorkDuration(1, 12, 0, 6));
+            s.reportActualWorkDuration(new WorkDuration(1, 12, 0, 6, 1));
         });
         assertThrows(IllegalArgumentException.class, () -> {
-            s.reportActualWorkDuration(new WorkDuration(1, 12, 8, -1));
+            s.reportActualWorkDuration(new WorkDuration(1, 12, 8, -1, 1));
         });
     }
 }
diff --git a/core/tests/coretests/src/android/os/WorkDurationUnitTest.java b/core/tests/coretests/src/android/os/WorkDurationUnitTest.java
new file mode 100644
index 0000000..c70da6e
--- /dev/null
+++ b/core/tests/coretests/src/android/os/WorkDurationUnitTest.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import static org.junit.Assert.assertThrows;
+
+import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.platform.test.ravenwood.RavenwoodRule;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidJUnit4.class)
+@IgnoreUnderRavenwood(blockedBy = WorkDuration.class)
+public class WorkDurationUnitTest {
+    @Rule
+    public final RavenwoodRule mRavenwood = new RavenwoodRule();
+
+    // Required for RequiresFlagsEnabled and RequiresFlagsDisabled annotations to take effect.
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule = RavenwoodRule.isUnderRavenwood() ? null
+            : DeviceFlagsValueProvider.createCheckFlagsRule();
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_ADPF_GPU_REPORT_ACTUAL_WORK_DURATION)
+    public void testWorkDurationSetters_IllegalArgument() {
+        WorkDuration workDuration = new WorkDuration();
+        assertThrows(IllegalArgumentException.class, () -> {
+            workDuration.setWorkPeriodStartTimestampNanos(-1);
+        });
+        assertThrows(IllegalArgumentException.class, () -> {
+            workDuration.setWorkPeriodStartTimestampNanos(0);
+        });
+        assertThrows(IllegalArgumentException.class, () -> {
+            workDuration.setActualTotalDurationNanos(-1);
+        });
+        assertThrows(IllegalArgumentException.class, () -> {
+            workDuration.setActualTotalDurationNanos(0);
+        });
+        assertThrows(IllegalArgumentException.class, () -> {
+            workDuration.setActualCpuDurationNanos(-1);
+        });
+        assertThrows(IllegalArgumentException.class, () -> {
+            workDuration.setActualCpuDurationNanos(0);
+        });
+        assertThrows(IllegalArgumentException.class, () -> {
+            workDuration.setActualGpuDurationNanos(-1);
+        });
+    }
+}
diff --git a/core/tests/coretests/src/android/text/TextLineTest.java b/core/tests/coretests/src/android/text/TextLineTest.java
index 34842a0..a31992c 100644
--- a/core/tests/coretests/src/android/text/TextLineTest.java
+++ b/core/tests/coretests/src/android/text/TextLineTest.java
@@ -50,11 +50,11 @@
         tl.set(paint, line, 0, line.length(), Layout.DIR_LEFT_TO_RIGHT,
                 Layout.DIRS_ALL_LEFT_TO_RIGHT, false /* hasTabs */, null /* tabStops */,
                 0, 0 /* no ellipsis */, false /* useFallbackLinespace */);
-        final float originalWidth = tl.metrics(null, null, false);
+        final float originalWidth = tl.metrics(null, null, false, null);
         final float expandedWidth = 2 * originalWidth;
 
         tl.justify(expandedWidth);
-        final float newWidth = tl.metrics(null, null, false);
+        final float newWidth = tl.metrics(null, null, false, null);
         TextLine.recycle(tl);
         return Math.abs(newWidth - expandedWidth) < 0.5;
     }
@@ -128,7 +128,7 @@
     private void assertMeasurements(final TextLine tl, final int length, boolean trailing,
             final float[] expected) {
         for (int offset = 0; offset <= length; ++offset) {
-            assertEquals(expected[offset], tl.measure(offset, trailing, null, null), 0.0f);
+            assertEquals(expected[offset], tl.measure(offset, trailing, null, null, null), 0.0f);
         }
 
         final boolean[] trailings = new boolean[length + 1];
@@ -318,7 +318,7 @@
         tl.set(new TextPaint(), text, 0, text.length(), 1, Layout.DIRS_ALL_LEFT_TO_RIGHT,
                 false /* hasTabs */, null /* tabStops */, 9, 12,
                 false /* useFallbackLineSpacing */);
-        tl.measure(text.length(), false /* trailing */, null /* fmi */, null);
+        tl.measure(text.length(), false /* trailing */, null /* fmi */, null, null);
 
         assertFalse(span.mIsUsed);
     }
@@ -335,7 +335,7 @@
         tl.set(new TextPaint(), text, 0, text.length(), 1, Layout.DIRS_ALL_LEFT_TO_RIGHT,
                 false /* hasTabs */, null /* tabStops */, 9, 12,
                 false /* useFallbackLineSpacing */);
-        tl.measure(text.length(), false /* trailing */, null /* fmi */, null);
+        tl.measure(text.length(), false /* trailing */, null /* fmi */, null, null);
 
         assertTrue(span.mIsUsed);
     }
@@ -352,7 +352,7 @@
         tl.set(new TextPaint(), text, 0, text.length(), 1, Layout.DIRS_ALL_LEFT_TO_RIGHT,
                 false /* hasTabs */, null /* tabStops */, 9, 12,
                 false /* useFallbackLineSpacing */);
-        tl.measure(text.length(), false /* trailing */, null /* fmi */, null);
+        tl.measure(text.length(), false /* trailing */, null /* fmi */, null, null);
         assertTrue(span.mIsUsed);
     }
 
diff --git a/core/tests/coretests/src/android/view/DisplayCutoutTest.java b/core/tests/coretests/src/android/view/DisplayCutoutTest.java
index faeae2c..0d1dde3 100644
--- a/core/tests/coretests/src/android/view/DisplayCutoutTest.java
+++ b/core/tests/coretests/src/android/view/DisplayCutoutTest.java
@@ -16,6 +16,8 @@
 
 package android.view;
 
+import static android.view.DisplayCutout.BOUNDS_POSITION_BOTTOM;
+import static android.view.DisplayCutout.BOUNDS_POSITION_TOP;
 import static android.view.DisplayCutout.NO_CUTOUT;
 import static android.view.DisplayCutout.extractBoundsFromList;
 import static android.view.DisplayCutout.fromSpec;
@@ -180,7 +182,7 @@
         final int displayHeight = 400;
         final float density = 1f;
         final DisplayCutout cutout = fromSpec(cutoutSpecString, displayWidth, displayHeight,
-                density, Insets.NONE);
+                density, Insets.NONE, null);
         assertThat(cutout.getCutoutPath(), notNullValue());
     }
 
@@ -191,9 +193,9 @@
         final int displayHeight = 400;
         final float density = 1f;
         final Path first = fromSpec(cutoutSpecString, displayWidth, displayHeight,
-                density, Insets.NONE).getCutoutPath();
+                density, Insets.NONE, null).getCutoutPath();
         final Path second = fromSpec(cutoutSpecString, displayWidth, displayHeight,
-                density, Insets.NONE).getCutoutPath();
+                density, Insets.NONE, null).getCutoutPath();
         assertThat(first, equalTo(second));
     }
 
@@ -203,9 +205,9 @@
         final int displayHeight = 400;
         final float density = 1f;
         final Path first = fromSpec("L1,0 L1,1 L0,1 z", displayWidth, displayHeight,
-                density, Insets.NONE).getCutoutPath();
+                density, Insets.NONE, null).getCutoutPath();
         final Path second = fromSpec("L2,0 L2,2 L0,2 z", displayWidth, displayHeight,
-                density, Insets.NONE).getCutoutPath();
+                density, Insets.NONE, null).getCutoutPath();
         assertThat(first, not(equalTo(second)));
     }
 
@@ -216,7 +218,7 @@
         final int displayHeight = 400;
         final float density = 1f;
         final DisplayCutout cutout = fromSpec(cutoutSpecString, displayWidth, displayHeight,
-                density, Insets.NONE);
+                density, Insets.NONE, null);
         assertThat(displayWidth, equalTo(cutout.getCutoutPathParserInfo().getDisplayWidth()));
         assertThat(displayHeight, equalTo(cutout.getCutoutPathParserInfo().getDisplayHeight()));
         assertThat(density, equalTo(cutout.getCutoutPathParserInfo().getDensity()));
@@ -360,63 +362,64 @@
     @Test
     public void fromSpec_caches() {
         Insets waterfallInsets = Insets.of(0, 20, 0, 20);
-        DisplayCutout cached = fromSpec("L1,0 L1,1 L0,1 z", 200, 400, 1f, waterfallInsets);
+        DisplayCutout cached = fromSpec("L1,0 L1,1 L0,1 z", 200, 400, 1f, waterfallInsets, null);
         assertThat(
-                fromSpec("L1,0 L1,1 L0,1 z", 200, 400, 1f, waterfallInsets),
+                fromSpec("L1,0 L1,1 L0,1 z", 200, 400, 1f, waterfallInsets, null),
                 sameInstance(cached));
     }
 
     @Test
     public void fromSpec_wontCacheIfSpecChanges() {
-        DisplayCutout cached = fromSpec("L1,0 L1000,1000 L0,1 z", 200, 400, 1f, Insets.NONE);
+        DisplayCutout cached = fromSpec("L1,0 L1000,1000 L0,1 z", 200, 400, 1f, Insets.NONE, null);
         assertThat(
-                fromSpec("L1,0 L1,1 L0,1 z", 200, 400, 1f, Insets.NONE),
+                fromSpec("L1,0 L1,1 L0,1 z", 200, 400, 1f, Insets.NONE, null),
                 not(sameInstance(cached)));
     }
 
     @Test
     public void fromSpec_wontCacheIfScreenWidthChanges() {
-        DisplayCutout cached = fromSpec("L1,0 L1,1 L0,1 z", 2000, 400, 1f, Insets.NONE);
+        DisplayCutout cached = fromSpec("L1,0 L1,1 L0,1 z", 2000, 400, 1f, Insets.NONE, null);
         assertThat(
-                fromSpec("L1,0 L1,1 L0,1 z", 200, 400, 1f, Insets.NONE),
+                fromSpec("L1,0 L1,1 L0,1 z", 200, 400, 1f, Insets.NONE, null),
                 not(sameInstance(cached)));
     }
 
     @Test
     public void fromSpec_wontCacheIfScreenHeightChanges() {
-        DisplayCutout cached = fromSpec("L1,0 L1,1 L0,1 z", 200, 4000, 1f, Insets.NONE);
+        DisplayCutout cached = fromSpec("L1,0 L1,1 L0,1 z", 200, 4000, 1f, Insets.NONE, null);
         assertThat(
-                fromSpec("L1,0 L1,1 L0,1 z", 200, 400, 1f, Insets.NONE),
+                fromSpec("L1,0 L1,1 L0,1 z", 200, 400, 1f, Insets.NONE, null),
                 not(sameInstance(cached)));
     }
 
     @Test
     public void fromSpec_wontCacheIfDensityChanges() {
-        DisplayCutout cached = fromSpec("L1,0 L1,1 L0,1 z", 200, 400, 2f, Insets.NONE);
+        DisplayCutout cached = fromSpec("L1,0 L1,1 L0,1 z", 200, 400, 2f, Insets.NONE, null);
         assertThat(
-                fromSpec("L1,0 L1,1 L0,1 z", 200, 400, 1f, Insets.NONE),
+                fromSpec("L1,0 L1,1 L0,1 z", 200, 400, 1f, Insets.NONE, null),
                 not(sameInstance(cached)));
     }
 
     @Test
     public void fromSpec_wontCacheIfWaterfallInsetsChange() {
         Insets waterfallInsets = Insets.of(0, 20, 0, 20);
-        DisplayCutout cached = fromSpec("L1,0 L1,1 L0,1 z", 200, 400, 2f, Insets.NONE);
+        DisplayCutout cached = fromSpec("L1,0 L1,1 L0,1 z", 200, 400, 2f, Insets.NONE, null);
         assertThat(
-                fromSpec("L1,0 L1,1 L0,1 z", 200, 400, 2f, waterfallInsets),
+                fromSpec("L1,0 L1,1 L0,1 z", 200, 400, 2f, waterfallInsets, null),
                 not(sameInstance(cached)));
     }
 
     @Test
     public void fromSpec_setsSafeInsets_top() {
-        DisplayCutout cutout = fromSpec("M -50,0 v 20 h 100 v -20 z", 200, 400, 2f, Insets.NONE);
+        DisplayCutout cutout = fromSpec("M -50,0 v 20 h 100 v -20 z", 200, 400, 2f,
+                Insets.NONE, null);
         assertThat(cutout.getSafeInsets(), equalTo(new Rect(0, 20, 0, 0)));
     }
 
     @Test
     public void fromSpec_setsSafeInsets_top_and_bottom() {
         DisplayCutout cutout = fromSpec("M -50,0 v 20 h 100 v -20 z"
-                + "@bottom M -50,0 v -10,0 h 100 v 20 z", 200, 400, 2f, Insets.NONE);
+                + "@bottom M -50,0 v -10,0 h 100 v 20 z", 200, 400, 2f, Insets.NONE, null);
         assertThat(cutout.getSafeInsets(), equalTo(new Rect(0, 20, 0, 10)));
         assertThat(cutout.getBoundingRectsAll(), equalTo(new Rect[]{
                 ZERO_RECT, new Rect(50, 0, 150, 20),
@@ -426,33 +429,35 @@
 
     @Test
     public void fromSpec_setsSafeInsets_waterfallTopBottom() {
-        DisplayCutout cutout = fromSpec("", 200, 400, 2f, Insets.of(0, 30, 0, 30));
+        DisplayCutout cutout = fromSpec("", 200, 400, 2f, Insets.of(0, 30, 0, 30), null);
         assertThat(cutout.getSafeInsets(), equalTo(new Rect(0, 30, 0, 30)));
     }
 
     @Test
     public void fromSpec_setsSafeInsets_waterfallLeftRight() {
-        DisplayCutout cutout = fromSpec("", 200, 400, 2f, Insets.of(30, 0, 30, 0));
+        DisplayCutout cutout = fromSpec("", 200, 400, 2f, Insets.of(30, 0, 30, 0), null);
         assertThat(cutout.getSafeInsets(), equalTo(new Rect(30, 0, 30, 0)));
     }
 
     @Test
     public void fromSpec_setsSafeInsets_waterfall_allEdges() {
-        DisplayCutout cutout = fromSpec("", 200, 400, 2f, Insets.of(30, 30, 30, 30));
+        DisplayCutout cutout = fromSpec("", 200, 400, 2f, Insets.of(30, 30, 30, 30), null);
         assertThat(cutout.getSafeInsets(), equalTo(new Rect(30, 30, 30, 30)));
     }
 
     @Test
     public void fromSpec_setsSafeInsets_cutoutTopBottom_waterfallTopBottom() {
         DisplayCutout cutout = fromSpec("M -50,0 v 20 h 100 v -20 z"
-                + "@bottom M -50,0 v -20,0 h 100 v 20 z", 200, 400, 2f, Insets.of(0, 30, 0, 30));
+                + "@bottom M -50,0 v -20,0 h 100 v 20 z", 200, 400, 2f,
+                Insets.of(0, 30, 0, 30), null);
         assertThat(cutout.getSafeInsets(), equalTo(new Rect(0, 30, 0, 30)));
     }
 
     @Test
     public void fromSpec_setsSafeInsets_cutoutTopBottom_waterfallLeftRight() {
         DisplayCutout cutout = fromSpec("M -50,0 v 20 h 100 v -20 z"
-                + "@bottom M -50,0 v -20,0 h 100 v 20 z", 200, 400, 2f, Insets.of(30, 0, 30, 0));
+                + "@bottom M -50,0 v -20,0 h 100 v 20 z", 200, 400, 2f,
+                    Insets.of(30, 0, 30, 0), null);
         assertThat(cutout.getSafeInsets(), equalTo(new Rect(30, 20, 30, 20)));
     }
 
@@ -568,7 +573,84 @@
         DisplayCutout rotated = cutout.getRotated(displayH, displayW, ROTATION_90, ROTATION_180);
         assertEquals(expected, rotated);
     }
+    @Test
+    public void testGetRotatedCutoutWithOverride_top_rot0() {
+        int displayW = 500, displayH = 1000;
+        final int[] sideOverrides = new int[] {BOUNDS_POSITION_TOP, BOUNDS_POSITION_BOTTOM,
+                BOUNDS_POSITION_BOTTOM, BOUNDS_POSITION_TOP};
+        DisplayCutout expected = new DisplayCutout(Insets.of(20, 100, 20, 0),
+                ZERO_RECT, new Rect(50, 0, 75, 100), ZERO_RECT, ZERO_RECT,
+                Insets.of(20, 0, 20, 0), null, sideOverrides);
+        DisplayCutout cutout = new DisplayCutout(Insets.of(20, 100, 20, 0),
+                ZERO_RECT, new Rect(50, 0, 75, 100), ZERO_RECT, ZERO_RECT,
+                Insets.of(20, 0, 20, 0), null, sideOverrides);
+        DisplayCutout rotated = cutout.getRotated(displayW, displayH, ROTATION_0, ROTATION_0);
+        assertEquals(expected, rotated);
+    }
 
+    @Test
+    public void testGetRotatedCutoutWithOverride_top_rot90() {
+        int displayW = 500, displayH = 1000;
+        final int[] sideOverrides = new int[] {BOUNDS_POSITION_TOP, BOUNDS_POSITION_BOTTOM,
+                BOUNDS_POSITION_BOTTOM, BOUNDS_POSITION_TOP};
+        DisplayCutout expected = new DisplayCutout(Insets.of(0, 20, 0, 75),
+                ZERO_RECT, ZERO_RECT, ZERO_RECT, new Rect(0, displayW - 75, 100, displayW - 50),
+                Insets.of(0, 20, 0, 20), createParserInfo(ROTATION_90), sideOverrides);
+        DisplayCutout cutout = new DisplayCutout(Insets.of(20, 100, 20, 0),
+                ZERO_RECT, new Rect(50, 0, 75, 100), ZERO_RECT, ZERO_RECT,
+                Insets.of(20, 0, 20, 0), null, sideOverrides);
+        DisplayCutout rotated = cutout.getRotated(displayW, displayH, ROTATION_0, ROTATION_90);
+        assertEquals(expected, rotated);
+    }
+
+    @Test
+    public void testGetRotatedCutoutWithOverride_top_rot180() {
+        int displayW = 500, displayH = 1000;
+        final int[] sideOverrides = new int[] {BOUNDS_POSITION_TOP, BOUNDS_POSITION_BOTTOM,
+                BOUNDS_POSITION_BOTTOM, BOUNDS_POSITION_TOP};
+        DisplayCutout expected = new DisplayCutout(Insets.of(20, 0, 20, 100),
+                ZERO_RECT, ZERO_RECT, ZERO_RECT,
+                new Rect(displayW - 75, displayH - 100, displayW - 50, displayH - 0),
+                Insets.of(20, 0, 20, 0), createParserInfo(ROTATION_180), sideOverrides);
+        DisplayCutout cutout = new DisplayCutout(Insets.of(20, 100, 20, 0),
+                ZERO_RECT, new Rect(50, 0, 75, 100), ZERO_RECT, ZERO_RECT,
+                Insets.of(20, 0, 20, 0), null, sideOverrides);
+        DisplayCutout rotated = cutout.getRotated(displayW, displayH, ROTATION_0, ROTATION_180);
+        assertEquals(expected, rotated);
+    }
+
+    @Test
+    public void testGetRotatedCutoutWithOverride_top_rot270() {
+        int displayW = 500, displayH = 1000;
+        final int[] sideOverrides = new int[] {BOUNDS_POSITION_TOP, BOUNDS_POSITION_BOTTOM,
+                BOUNDS_POSITION_BOTTOM, BOUNDS_POSITION_TOP};
+        DisplayCutout expected = new DisplayCutout(Insets.of(0, 75, 0, 20),
+                ZERO_RECT, new Rect(displayH - 100, 50, displayH - 0, 75), ZERO_RECT, ZERO_RECT,
+                Insets.of(0, 20, 0, 20), createParserInfo(ROTATION_270), sideOverrides);
+        DisplayCutout cutout = new DisplayCutout(Insets.of(20, 100, 20, 0),
+                ZERO_RECT, new Rect(50, 0, 75, 100), ZERO_RECT, ZERO_RECT,
+                Insets.of(20, 0, 20, 0), null, sideOverrides);
+        DisplayCutout rotated = cutout.getRotated(displayW, displayH, ROTATION_0, ROTATION_270);
+        assertEquals(expected, rotated);
+    }
+
+    @Test
+    public void testGetRotatedCutoutWithOverride_top_rot90to180() {
+        int displayW = 500, displayH = 1000;
+        final int[] sideOverrides = new int[] {BOUNDS_POSITION_TOP, BOUNDS_POSITION_BOTTOM,
+                BOUNDS_POSITION_BOTTOM, BOUNDS_POSITION_TOP};
+        DisplayCutout expected = new DisplayCutout(Insets.of(20, 0, 20, 100),
+                ZERO_RECT, ZERO_RECT, ZERO_RECT,
+                new Rect(displayW - 75, displayH - 100, displayW - 50, displayH - 0),
+                Insets.of(20, 0, 20, 0), createParserInfo(ROTATION_180),
+                sideOverrides);
+        DisplayCutout cutout = new DisplayCutout(Insets.of(0, 20, 0, 75),
+                ZERO_RECT, ZERO_RECT, ZERO_RECT, new Rect(0, displayW - 75, 100, displayW - 50),
+                Insets.of(0, 20, 0, 20), null, sideOverrides);
+        // starting from 90, so the start displayW/H are swapped:
+        DisplayCutout rotated = cutout.getRotated(displayH, displayW, ROTATION_90, ROTATION_180);
+        assertEquals(expected, rotated);
+    }
     private static DisplayCutout createCutoutTop() {
         return createCutoutWithInsets(0, 100, 0, 0);
     }
diff --git a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureManagerTest.java b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureManagerTest.java
index 35ddfdb..e52aa1b 100644
--- a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureManagerTest.java
+++ b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureManagerTest.java
@@ -153,7 +153,7 @@
         final ContentCaptureManager manager =
                 new ContentCaptureManager(mMockContext, mMockContentCaptureManager, EMPTY_OPTIONS);
         // Ensure main session is created.
-        final MainContentCaptureSession unused = manager.getMainContentCaptureSession();
+        final ContentCaptureSession unused = manager.getMainContentCaptureSession();
         final WindowManager.LayoutParams initialParam = new WindowManager.LayoutParams();
         initialParam.flags |= WindowManager.LayoutParams.FLAG_SECURE;
 
@@ -167,7 +167,7 @@
         final ContentCaptureManager manager =
                 new ContentCaptureManager(mMockContext, mMockContentCaptureManager, EMPTY_OPTIONS);
         // Ensure main session is created.
-        final MainContentCaptureSession unused = manager.getMainContentCaptureSession();
+        final ContentCaptureSession unused = manager.getMainContentCaptureSession();
         final WindowManager.LayoutParams initialParam = new WindowManager.LayoutParams();
         initialParam.flags |= WindowManager.LayoutParams.FLAG_SECURE;
         // Default param does not have FLAG_SECURE set.
@@ -184,7 +184,7 @@
         final ContentCaptureManager manager =
                 new ContentCaptureManager(mMockContext, mMockContentCaptureManager, EMPTY_OPTIONS);
         // Ensure main session is created.
-        final MainContentCaptureSession unused = manager.getMainContentCaptureSession();
+        final ContentCaptureSession unused = manager.getMainContentCaptureSession();
         // Default param does not have FLAG_SECURE set.
         final WindowManager.LayoutParams resetParam = new WindowManager.LayoutParams();
 
diff --git a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java
index 23b9b9b..4a4c693 100644
--- a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java
+++ b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java
@@ -21,12 +21,19 @@
 import static org.testng.Assert.assertThrows;
 
 import android.compat.testing.PlatformCompatChangeRule;
+import android.content.ComponentName;
 import android.graphics.Insets;
+import android.graphics.Rect;
+import android.os.IBinder;
+import android.util.SparseArray;
 import android.view.View;
 import android.view.ViewStructure;
 import android.view.autofill.AutofillId;
 import android.view.contentcapture.ViewNode.ViewStructureImpl;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
 import com.google.common.collect.ImmutableMap;
 
 import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
@@ -40,6 +47,7 @@
 import org.mockito.Mock;
 import org.mockito.junit.MockitoJUnitRunner;
 
+import java.util.ArrayList;
 import java.util.Map;
 
 /**
@@ -195,6 +203,22 @@
         }
 
         @Override
+        void start(@NonNull IBinder token, @NonNull IBinder shareableActivityToken,
+                @NonNull ComponentName component, int flags) {
+            throw new UnsupportedOperationException("should not have been called");
+        }
+
+        @Override
+        boolean isDisabled() {
+            throw new UnsupportedOperationException("should not have been called");
+        }
+
+        @Override
+        boolean setDisabled(boolean disabled) {
+            throw new UnsupportedOperationException("should not have been called");
+        }
+
+        @Override
         ContentCaptureSession newChild(ContentCaptureContext context) {
             throw new UnsupportedOperationException("should not have been called");
         }
@@ -210,20 +234,20 @@
         }
 
         @Override
-        void internalNotifyViewAppeared(ViewStructureImpl node) {
+        void internalNotifyViewAppeared(final int sessionId, ViewStructureImpl node) {
             throw new UnsupportedOperationException("should not have been called");
         }
 
         @Override
-        void internalNotifyViewDisappeared(AutofillId id) {}
+        void internalNotifyViewDisappeared(final int sessionId, AutofillId id) {}
 
         @Override
-        void internalNotifyViewTextChanged(AutofillId id, CharSequence text) {
+        void internalNotifyViewTextChanged(final int sessionId, AutofillId id, CharSequence text) {
             throw new UnsupportedOperationException("should not have been called");
         }
 
         @Override
-        public void internalNotifyViewTreeEvent(boolean started) {
+        public void internalNotifyViewTreeEvent(final int sessionId, boolean started) {
             if (started) {
                 mInternalNotifyViewTreeEventStartedCount += 1;
             } else {
@@ -242,7 +266,34 @@
         }
 
         @Override
-        void internalNotifyViewInsetsChanged(Insets viewInsets) {
+        void internalNotifyChildSessionStarted(int parentSessionId, int childSessionId,
+                @NonNull ContentCaptureContext clientContext) {
+            throw new UnsupportedOperationException("should not have been called");
+        }
+
+        @Override
+        void internalNotifyChildSessionFinished(int parentSessionId, int childSessionId) {
+            throw new UnsupportedOperationException("should not have been called");
+        }
+
+        @Override
+        void internalNotifyContextUpdated(int sessionId, @Nullable ContentCaptureContext context) {
+            throw new UnsupportedOperationException("should not have been called");
+        }
+
+        @Override
+        public void notifyWindowBoundsChanged(int sessionId, @NonNull Rect bounds) {
+            throw new UnsupportedOperationException("should not have been called");
+        }
+
+        @Override
+        public void notifyContentCaptureEvents(
+                @NonNull SparseArray<ArrayList<Object>> contentCaptureEvents) {
+
+        }
+
+        @Override
+        void internalNotifyViewInsetsChanged(final int sessionId, Insets viewInsets) {
             throw new UnsupportedOperationException("should not have been called");
         }
 
diff --git a/core/tests/coretests/src/android/view/contentcapture/MainContentCaptureSessionV2Test.java b/core/tests/coretests/src/android/view/contentcapture/MainContentCaptureSessionV2Test.java
new file mode 100644
index 0000000..f0f3a96
--- /dev/null
+++ b/core/tests/coretests/src/android/view/contentcapture/MainContentCaptureSessionV2Test.java
@@ -0,0 +1,530 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.contentcapture;
+
+import static android.view.contentcapture.ContentCaptureEvent.TYPE_SESSION_STARTED;
+import static android.view.contentcapture.ContentCaptureSession.FLUSH_REASON_VIEW_TREE_APPEARED;
+import static android.view.contentcapture.ContentCaptureSession.FLUSH_REASON_VIEW_TREE_APPEARING;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+
+import android.content.ComponentName;
+import android.content.ContentCaptureOptions;
+import android.content.Context;
+import android.content.pm.ParceledListSlice;
+import android.graphics.Insets;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.util.SparseArray;
+import android.view.View;
+import android.view.autofill.AutofillId;
+import android.view.contentprotection.ContentProtectionEventProcessor;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Test for {@link MainContentCaptureSessionV2}.
+ *
+ * <p>Run with: {@code atest
+ * FrameworksCoreTests:android.view.contentcapture.MainContentCaptureSessionV2Test}
+ */
+@RunWith(AndroidTestingRunner.class)
+@SmallTest
+@TestableLooper.RunWithLooper
+public class MainContentCaptureSessionV2Test {
+
+    private static final int BUFFER_SIZE = 100;
+
+    private static final int REASON = 123;
+
+    private static final ContentCaptureEvent EVENT =
+            new ContentCaptureEvent(/* sessionId= */ 0, TYPE_SESSION_STARTED);
+
+    private static final ComponentName COMPONENT_NAME =
+            new ComponentName("com.test.package", "TestClass");
+
+    private static final Context sContext = ApplicationProvider.getApplicationContext();
+
+    private static final ContentCaptureManager.StrippedContext sStrippedContext =
+            new ContentCaptureManager.StrippedContext(sContext);
+
+    private TestableLooper mTestableLooper;
+
+    @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
+
+    @Mock private IContentCaptureManager mMockSystemServerInterface;
+
+    @Mock private ContentProtectionEventProcessor mMockContentProtectionEventProcessor;
+
+    @Mock private IContentCaptureDirectManager mMockContentCaptureDirectManager;
+
+    @Before
+    public void setup() {
+        mTestableLooper = TestableLooper.get(this);
+    }
+
+    @Test
+    public void onSessionStarted_contentProtectionEnabled_processorCreated() {
+        MainContentCaptureSessionV2 session = createSession();
+        assertThat(session.mContentProtectionEventProcessor).isNull();
+
+        session.onSessionStarted(/* resultCode= */ 0, /* binder= */ null);
+        mTestableLooper.processAllMessages();
+
+        assertThat(session.mContentProtectionEventProcessor).isNotNull();
+    }
+
+    @Test
+    public void onSessionStarted_contentProtectionDisabled_processorNotCreated() {
+        MainContentCaptureSessionV2 session =
+                createSession(
+                        /* enableContentCaptureReceiver= */ true,
+                        /* enableContentProtectionReceiver= */ false);
+        session.mContentProtectionEventProcessor = mMockContentProtectionEventProcessor;
+
+        session.onSessionStarted(/* resultCode= */ 0, /* binder= */ null);
+        mTestableLooper.processAllMessages();
+
+        assertThat(session.mContentProtectionEventProcessor).isNull();
+        verifyZeroInteractions(mMockContentProtectionEventProcessor);
+    }
+
+    @Test
+    public void onSessionStarted_contentProtectionNoBuffer_processorNotCreated() {
+        ContentCaptureOptions options =
+                createOptions(
+                        /* enableContentCaptureReceiver= */ true,
+                        new ContentCaptureOptions.ContentProtectionOptions(
+                                /* enableReceiver= */ true,
+                                -BUFFER_SIZE,
+                                /* requiredGroups= */ List.of(List.of("a")),
+                                /* optionalGroups= */ Collections.emptyList(),
+                                /* optionalGroupsThreshold= */ 0));
+        MainContentCaptureSessionV2 session = createSession(options);
+        session.mContentProtectionEventProcessor = mMockContentProtectionEventProcessor;
+
+        session.onSessionStarted(/* resultCode= */ 0, /* binder= */ null);
+        mTestableLooper.processAllMessages();
+
+        assertThat(session.mContentProtectionEventProcessor).isNull();
+        verifyZeroInteractions(mMockContentProtectionEventProcessor);
+    }
+
+    @Test
+    public void onSessionStarted_contentProtectionNoGroups_processorNotCreated() {
+        ContentCaptureOptions options =
+                createOptions(
+                        /* enableContentCaptureReceiver= */ true,
+                        new ContentCaptureOptions.ContentProtectionOptions(
+                                /* enableReceiver= */ true,
+                                BUFFER_SIZE,
+                                /* requiredGroups= */ Collections.emptyList(),
+                                /* optionalGroups= */ Collections.emptyList(),
+                                /* optionalGroupsThreshold= */ 0));
+        MainContentCaptureSessionV2 session = createSession(options);
+        session.mContentProtectionEventProcessor = mMockContentProtectionEventProcessor;
+
+        session.onSessionStarted(/* resultCode= */ 0, /* binder= */ null);
+        mTestableLooper.processAllMessages();
+
+        assertThat(session.mContentProtectionEventProcessor).isNull();
+        verifyZeroInteractions(mMockContentProtectionEventProcessor);
+    }
+
+    @Test
+    public void onSessionStarted_noComponentName_processorNotCreated() {
+        MainContentCaptureSessionV2 session = createSession();
+        session.mComponentName = null;
+
+        session.onSessionStarted(/* resultCode= */ 0, /* binder= */ null);
+        mTestableLooper.processAllMessages();
+
+        assertThat(session.mContentProtectionEventProcessor).isNull();
+    }
+
+    @Test
+    public void sendEvent_contentCaptureDisabled_contentProtectionDisabled() {
+        MainContentCaptureSessionV2 session =
+                createSession(
+                        /* enableContentCaptureReceiver= */ false,
+                        /* enableContentProtectionReceiver= */ false);
+        session.mContentProtectionEventProcessor = mMockContentProtectionEventProcessor;
+
+        session.sendEvent(EVENT);
+        mTestableLooper.processAllMessages();
+
+        verifyZeroInteractions(mMockContentProtectionEventProcessor);
+        assertThat(session.mEvents).isNull();
+    }
+
+    @Test
+    public void sendEvent_contentCaptureDisabled_contentProtectionEnabled() {
+        MainContentCaptureSessionV2 session =
+                createSession(
+                        /* enableContentCaptureReceiver= */ false,
+                        /* enableContentProtectionReceiver= */ true);
+        session.mContentProtectionEventProcessor = mMockContentProtectionEventProcessor;
+
+        session.sendEvent(EVENT);
+        mTestableLooper.processAllMessages();
+
+        verify(mMockContentProtectionEventProcessor).processEvent(EVENT);
+        assertThat(session.mEvents).isNull();
+    }
+
+    @Test
+    public void sendEvent_contentCaptureEnabled_contentProtectionDisabled() {
+        MainContentCaptureSessionV2 session =
+                createSession(
+                        /* enableContentCaptureReceiver= */ true,
+                        /* enableContentProtectionReceiver= */ false);
+        session.mContentProtectionEventProcessor = mMockContentProtectionEventProcessor;
+
+        session.sendEvent(EVENT);
+        mTestableLooper.processAllMessages();
+
+        verifyZeroInteractions(mMockContentProtectionEventProcessor);
+        assertThat(session.mEvents).isNotNull();
+        assertThat(session.mEvents).containsExactly(EVENT);
+    }
+
+    @Test
+    public void sendEvent_contentCaptureEnabled_contentProtectionEnabled() {
+        MainContentCaptureSessionV2 session = createSession();
+        session.mContentProtectionEventProcessor = mMockContentProtectionEventProcessor;
+
+        session.sendEvent(EVENT);
+        mTestableLooper.processAllMessages();
+
+        verify(mMockContentProtectionEventProcessor).processEvent(EVENT);
+        assertThat(session.mEvents).isNotNull();
+        assertThat(session.mEvents).containsExactly(EVENT);
+    }
+
+    @Test
+    public void sendEvent_contentProtectionEnabled_processorNotCreated() {
+        MainContentCaptureSessionV2 session =
+                createSession(
+                        /* enableContentCaptureReceiver= */ false,
+                        /* enableContentProtectionReceiver= */ true);
+
+        session.sendEvent(EVENT);
+        mTestableLooper.processAllMessages();
+
+        verifyZeroInteractions(mMockContentProtectionEventProcessor);
+        assertThat(session.mEvents).isNull();
+    }
+
+    @Test
+    public void flush_contentCaptureDisabled_contentProtectionDisabled() throws Exception {
+        ContentCaptureOptions options =
+                createOptions(
+                        /* enableContentCaptureReceiver= */ false,
+                        /* enableContentProtectionReceiver= */ false);
+        MainContentCaptureSessionV2 session = createSession(options);
+        session.mEvents = new ArrayList<>(Arrays.asList(EVENT));
+        session.mDirectServiceInterface = mMockContentCaptureDirectManager;
+
+        session.flush(REASON);
+        mTestableLooper.processAllMessages();
+
+        verifyZeroInteractions(mMockContentProtectionEventProcessor);
+        verifyZeroInteractions(mMockContentCaptureDirectManager);
+        assertThat(session.mEvents).containsExactly(EVENT);
+    }
+
+    @Test
+    public void flush_contentCaptureDisabled_contentProtectionEnabled() {
+        MainContentCaptureSessionV2 session =
+                createSession(
+                        /* enableContentCaptureReceiver= */ false,
+                        /* enableContentProtectionReceiver= */ true);
+        session.mEvents = new ArrayList<>(Arrays.asList(EVENT));
+        session.mDirectServiceInterface = mMockContentCaptureDirectManager;
+
+        session.flush(REASON);
+        mTestableLooper.processAllMessages();
+
+        verifyZeroInteractions(mMockContentProtectionEventProcessor);
+        verifyZeroInteractions(mMockContentCaptureDirectManager);
+        assertThat(session.mEvents).containsExactly(EVENT);
+    }
+
+    @Test
+    public void flush_contentCaptureEnabled_contentProtectionDisabled() throws Exception {
+        ContentCaptureOptions options =
+                createOptions(
+                        /* enableContentCaptureReceiver= */ true,
+                        /* enableContentProtectionReceiver= */ false);
+        MainContentCaptureSessionV2 session = createSession(options);
+        session.mEvents = new ArrayList<>(Arrays.asList(EVENT));
+        session.mDirectServiceInterface = mMockContentCaptureDirectManager;
+
+        session.flush(REASON);
+        mTestableLooper.processAllMessages();
+
+        verifyZeroInteractions(mMockContentProtectionEventProcessor);
+        assertThat(session.mEvents).isEmpty();
+        assertEventFlushedContentCapture(options);
+    }
+
+    @Test
+    public void flush_contentCaptureEnabled_contentProtectionEnabled() throws Exception {
+        ContentCaptureOptions options =
+                createOptions(
+                        /* enableContentCaptureReceiver= */ true,
+                        /* enableContentProtectionReceiver= */ true);
+        MainContentCaptureSessionV2 session = createSession(options);
+        session.mEvents = new ArrayList<>(Arrays.asList(EVENT));
+        session.mDirectServiceInterface = mMockContentCaptureDirectManager;
+
+        session.flush(REASON);
+        mTestableLooper.processAllMessages();
+
+        verifyZeroInteractions(mMockContentProtectionEventProcessor);
+        assertThat(session.mEvents).isEmpty();
+        assertEventFlushedContentCapture(options);
+    }
+
+    @Test
+    public void destroySession() throws Exception {
+        MainContentCaptureSessionV2 session = createSession();
+        session.mContentProtectionEventProcessor = mMockContentProtectionEventProcessor;
+
+        session.destroySession();
+        mTestableLooper.processAllMessages();
+
+        verify(mMockSystemServerInterface).finishSession(anyInt());
+        verifyZeroInteractions(mMockContentProtectionEventProcessor);
+        assertThat(session.mDirectServiceInterface).isNull();
+        assertThat(session.mContentProtectionEventProcessor).isNull();
+    }
+
+    @Test
+    public void resetSession() {
+        MainContentCaptureSessionV2 session = createSession();
+        session.mContentProtectionEventProcessor = mMockContentProtectionEventProcessor;
+
+        session.resetSession(/* newState= */ 0);
+        mTestableLooper.processAllMessages();
+
+        verifyZeroInteractions(mMockSystemServerInterface);
+        verifyZeroInteractions(mMockContentProtectionEventProcessor);
+        assertThat(session.mDirectServiceInterface).isNull();
+        assertThat(session.mContentProtectionEventProcessor).isNull();
+    }
+
+    @Test
+    @SuppressWarnings("GuardedBy")
+    public void notifyContentCaptureEvents_notStarted_ContentCaptureDisabled_ProtectionDisabled() {
+        ContentCaptureOptions options =
+                createOptions(
+                        /* enableContentCaptureReceiver= */ false,
+                        /* enableContentProtectionReceiver= */ false);
+        MainContentCaptureSessionV2 session = createSession(options);
+
+        notifyContentCaptureEvents(session);
+        mTestableLooper.processAllMessages();
+
+        verifyZeroInteractions(mMockContentCaptureDirectManager);
+        verifyZeroInteractions(mMockContentProtectionEventProcessor);
+        assertThat(session.mEvents).isNull();
+    }
+
+    @Test
+    @SuppressWarnings("GuardedBy")
+    public void notifyContentCaptureEvents_started_ContentCaptureDisabled_ProtectionDisabled() {
+        ContentCaptureOptions options =
+                createOptions(
+                        /* enableContentCaptureReceiver= */ false,
+                        /* enableContentProtectionReceiver= */ false);
+        MainContentCaptureSessionV2 session = createSession(options);
+
+        session.onSessionStarted(0x2, null);
+        notifyContentCaptureEvents(session);
+        mTestableLooper.processAllMessages();
+
+        verifyZeroInteractions(mMockContentCaptureDirectManager);
+        verifyZeroInteractions(mMockContentProtectionEventProcessor);
+        assertThat(session.mEvents).isNull();
+    }
+
+    @Test
+    @SuppressWarnings("GuardedBy")
+    public void notifyContentCaptureEvents_notStarted_ContentCaptureEnabled_ProtectionEnabled() {
+        ContentCaptureOptions options =
+                createOptions(
+                        /* enableContentCaptureReceiver= */ true,
+                        /* enableContentProtectionReceiver= */ true);
+        MainContentCaptureSessionV2 session = createSession(options);
+        session.mDirectServiceInterface = mMockContentCaptureDirectManager;
+        session.mContentProtectionEventProcessor = mMockContentProtectionEventProcessor;
+
+        notifyContentCaptureEvents(session);
+        mTestableLooper.processAllMessages();
+
+        verifyZeroInteractions(mMockContentCaptureDirectManager);
+        verifyZeroInteractions(mMockContentProtectionEventProcessor);
+        assertThat(session.mEvents).isNull();
+    }
+
+    @Test
+    @SuppressWarnings("GuardedBy")
+    public void notifyContentCaptureEvents_started_ContentCaptureEnabled_ProtectionEnabled()
+            throws RemoteException {
+        ContentCaptureOptions options =
+                createOptions(
+                        /* enableContentCaptureReceiver= */ true,
+                        /* enableContentProtectionReceiver= */ true);
+        MainContentCaptureSessionV2 session = createSession(options);
+        session.mDirectServiceInterface = mMockContentCaptureDirectManager;
+
+        session.onSessionStarted(0x2, null);
+        // Override the processor for interaction verification.
+        session.mContentProtectionEventProcessor = mMockContentProtectionEventProcessor;
+        notifyContentCaptureEvents(session);
+        mTestableLooper.processAllMessages();
+
+        // Force flush will happen twice.
+        verify(mMockContentCaptureDirectManager, times(1))
+                .sendEvents(any(), eq(FLUSH_REASON_VIEW_TREE_APPEARING), any());
+        verify(mMockContentCaptureDirectManager, times(1))
+                .sendEvents(any(), eq(FLUSH_REASON_VIEW_TREE_APPEARED), any());
+        // Other than the five view events, there will be two additional tree appearing events.
+        verify(mMockContentProtectionEventProcessor, times(7)).processEvent(any());
+        assertThat(session.mEvents).isEmpty();
+    }
+
+    /** Simulates the regular content capture events sequence. */
+    private void notifyContentCaptureEvents(final MainContentCaptureSessionV2 session) {
+        final ArrayList<Object> events = new ArrayList<>(
+                List.of(
+                        prepareView(session),
+                        prepareView(session),
+                        new AutofillId(0),
+                        prepareView(session),
+                        Insets.of(0, 0, 0, 0)
+                )
+        );
+
+        final SparseArray<ArrayList<Object>> contentCaptureEvents = new SparseArray<>();
+        contentCaptureEvents.set(session.getId(), events);
+
+        session.notifyContentCaptureEvents(contentCaptureEvents);
+    }
+
+    private View prepareView(final MainContentCaptureSessionV2 session) {
+        final View view = new View(sContext);
+        view.setContentCaptureSession(session);
+        return view;
+    }
+
+    private static ContentCaptureOptions createOptions(
+            boolean enableContentCaptureReceiver,
+            ContentCaptureOptions.ContentProtectionOptions contentProtectionOptions) {
+        return new ContentCaptureOptions(
+                /* loggingLevel= */ 0,
+                BUFFER_SIZE,
+                /* idleFlushingFrequencyMs= */ 0,
+                /* textChangeFlushingFrequencyMs= */ 0,
+                /* logHistorySize= */ 0,
+                /* disableFlushForViewTreeAppearing= */ false,
+                enableContentCaptureReceiver,
+                contentProtectionOptions,
+                /* whitelistedComponents= */ null);
+    }
+
+    private static ContentCaptureOptions createOptions(
+            boolean enableContentCaptureReceiver, boolean enableContentProtectionReceiver) {
+        return createOptions(
+                enableContentCaptureReceiver,
+                new ContentCaptureOptions.ContentProtectionOptions(
+                        enableContentProtectionReceiver,
+                        BUFFER_SIZE,
+                        /* requiredGroups= */ List.of(List.of("a")),
+                        /* optionalGroups= */ Collections.emptyList(),
+                        /* optionalGroupsThreshold= */ 0));
+    }
+
+    private ContentCaptureManager createManager(ContentCaptureOptions options) {
+        return new ContentCaptureManager(sContext, mMockSystemServerInterface, options);
+    }
+
+    private MainContentCaptureSessionV2 createSession(ContentCaptureManager manager) {
+        final Handler testHandler = Handler.createAsync(mTestableLooper.getLooper());
+        MainContentCaptureSessionV2 session =
+                new MainContentCaptureSessionV2(
+                        sStrippedContext,
+                        manager,
+                        testHandler,
+                        testHandler,
+                        mMockSystemServerInterface);
+        session.mComponentName = COMPONENT_NAME;
+        return session;
+    }
+
+    private MainContentCaptureSessionV2 createSession(ContentCaptureOptions options) {
+        return createSession(createManager(options));
+    }
+
+    private MainContentCaptureSessionV2 createSession(
+            boolean enableContentCaptureReceiver, boolean enableContentProtectionReceiver) {
+        return createSession(
+                createOptions(enableContentCaptureReceiver, enableContentProtectionReceiver));
+    }
+
+    private MainContentCaptureSessionV2 createSession() {
+        return createSession(
+                /* enableContentCaptureReceiver= */ true,
+                /* enableContentProtectionReceiver= */ true);
+    }
+
+    private void assertEventFlushedContentCapture(ContentCaptureOptions options) throws Exception {
+        ArgumentCaptor<ParceledListSlice> captor = ArgumentCaptor.forClass(ParceledListSlice.class);
+        verify(mMockContentCaptureDirectManager)
+                .sendEvents(captor.capture(), eq(REASON), eq(options));
+
+        assertThat(captor.getValue()).isNotNull();
+        List<ContentCaptureEvent> actual = captor.getValue().getList();
+        assertThat(actual).isNotNull();
+        assertThat(actual).containsExactly(EVENT);
+    }
+}
diff --git a/core/tests/coretests/src/android/window/flags/WindowFlagsTest.java b/core/tests/coretests/src/android/window/flags/WindowFlagsTest.java
index a5bbeb5..9292f66 100644
--- a/core/tests/coretests/src/android/window/flags/WindowFlagsTest.java
+++ b/core/tests/coretests/src/android/window/flags/WindowFlagsTest.java
@@ -16,7 +16,6 @@
 
 package android.window.flags;
 
-import static com.android.window.flags.Flags.syncWindowConfigUpdateFlag;
 import static com.android.window.flags.Flags.taskFragmentSystemOrganizerFlag;
 
 import android.platform.test.annotations.Presubmit;
@@ -39,12 +38,6 @@
 public class WindowFlagsTest {
 
     @Test
-    public void testSyncWindowConfigUpdateFlag() {
-        // No crash when accessing the flag.
-        syncWindowConfigUpdateFlag();
-    }
-
-    @Test
     public void testTaskFragmentSystemOrganizerFlag() {
         // No crash when accessing the flag.
         taskFragmentSystemOrganizerFlag();
diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
index b6813ff..b209c7c 100644
--- a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
@@ -30,6 +30,7 @@
 import static com.android.internal.app.ResolverDataProvider.createPackageManagerMockedInfo;
 import static com.android.internal.app.ResolverWrapperActivity.sOverrides;
 
+import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertTrue;
 
 import static org.hamcrest.CoreMatchers.allOf;
@@ -46,6 +47,7 @@
 import android.net.Uri;
 import android.os.RemoteException;
 import android.os.UserHandle;
+import android.platform.test.flag.junit.SetFlagsRule;
 import android.text.TextUtils;
 import android.view.View;
 import android.widget.RelativeLayout;
@@ -88,7 +90,8 @@
     public ActivityTestRule<ResolverWrapperActivity> mActivityRule =
             new ActivityTestRule<>(ResolverWrapperActivity.class, false,
                     false);
-
+    @Rule
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
     @Before
     public void cleanOverrideData() {
         sOverrides.reset();
@@ -1156,6 +1159,97 @@
                 sOverrides.cloneProfileUserHandle)));
     }
 
+    @Test
+    public void testTriggerFromPrivateProfile_withoutWorkProfile() throws RemoteException {
+        mSetFlagsRule.enableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE,
+                android.multiuser.Flags.FLAG_ALLOW_RESOLVER_SHEET_FOR_PRIVATE_SPACE);
+        markPrivateProfileUserAvailable();
+        Intent sendIntent = createSendImageIntent();
+        List<ResolvedComponentInfo> privateResolvedComponentInfos =
+                createResolvedComponentsForTest(3, sOverrides.privateProfileUserHandle);
+        setupResolverControllers(privateResolvedComponentInfos);
+        final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
+        waitForIdle();
+        onView(withId(R.id.tabs)).check(matches(not(isDisplayed())));
+        assertThat(activity.getPersonalListAdapter().getCount(), is(3));
+        onView(withId(R.id.button_once)).check(matches(not(isEnabled())));
+        onView(withId(R.id.button_always)).check(matches(not(isEnabled())));
+        for (ResolvedComponentInfo resolvedInfo : privateResolvedComponentInfos) {
+            assertEquals(resolvedInfo.getResolveInfoAt(0).userHandle,
+                    sOverrides.privateProfileUserHandle);
+        }
+    }
+
+    @Test
+    public void testTriggerFromPrivateProfile_withWorkProfilePresent(){
+        mSetFlagsRule.enableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE,
+                android.multiuser.Flags.FLAG_ALLOW_RESOLVER_SHEET_FOR_PRIVATE_SPACE);
+        ResolverActivity.ENABLE_TABBED_VIEW = false;
+        markPrivateProfileUserAvailable();
+        markWorkProfileUserAvailable();
+        Intent sendIntent = createSendImageIntent();
+        List<ResolvedComponentInfo> privateResolvedComponentInfos =
+                createResolvedComponentsForTest(3, sOverrides.privateProfileUserHandle);
+        setupResolverControllers(privateResolvedComponentInfos);
+        final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
+        waitForIdle();
+        assertThat(activity.getPersonalListAdapter().getCount(), is(3));
+        onView(withId(R.id.tabs)).check(matches(not(isDisplayed())));
+        assertEquals(activity.getMultiProfilePagerAdapterCount(), 1);
+        for (ResolvedComponentInfo resolvedInfo : privateResolvedComponentInfos) {
+            assertEquals(resolvedInfo.getResolveInfoAt(0).userHandle,
+                    sOverrides.privateProfileUserHandle);
+        }
+    }
+
+    @Test
+    public void testPrivateProfile_triggerFromPrimaryUser_withWorkProfilePresent(){
+        mSetFlagsRule.enableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE,
+                android.multiuser.Flags.FLAG_ALLOW_RESOLVER_SHEET_FOR_PRIVATE_SPACE);
+        markPrivateProfileUserAvailable();
+        markWorkProfileUserAvailable();
+        Intent sendIntent = createSendImageIntent();
+        List<ResolvedComponentInfo> personalResolvedComponentInfos =
+                createResolvedComponentsForTestWithOtherProfile(3, PERSONAL_USER_HANDLE);
+        List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4,
+                sOverrides.workProfileUserHandle);
+        setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
+        final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
+        waitForIdle();
+        assertThat(activity.getAdapter().getCount(), is(2));
+        assertThat(activity.getWorkListAdapter().getCount(), is(4));
+        onView(withId(R.id.tabs)).check(matches(isDisplayed()));
+        for (ResolvedComponentInfo resolvedInfo : personalResolvedComponentInfos) {
+            assertEquals(resolvedInfo.getResolveInfoAt(0).userHandle,
+                    activity.getPersonalProfileUserHandle());
+        }
+    }
+
+    @Test
+    public void testPrivateProfile_triggerFromWorkProfile(){
+        mSetFlagsRule.enableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE,
+                android.multiuser.Flags.FLAG_ALLOW_RESOLVER_SHEET_FOR_PRIVATE_SPACE);
+        markPrivateProfileUserAvailable();
+        markWorkProfileUserAvailable();
+        Intent sendIntent = createSendImageIntent();
+
+        List<ResolvedComponentInfo> personalResolvedComponentInfos =
+                createResolvedComponentsForTestWithOtherProfile(3, PERSONAL_USER_HANDLE);
+        List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4,
+                sOverrides.workProfileUserHandle);
+        setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
+        final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
+        waitForIdle();
+        assertThat(activity.getAdapter().getCount(), is(2));
+        assertThat(activity.getWorkListAdapter().getCount(), is(4));
+        onView(withId(R.id.tabs)).check(matches(isDisplayed()));
+        for (ResolvedComponentInfo resolvedInfo : personalResolvedComponentInfos) {
+            assertTrue(resolvedInfo.getResolveInfoAt(0).userHandle.equals(
+                    activity.getPersonalProfileUserHandle()) || resolvedInfo.getResolveInfoAt(
+                    0).userHandle.equals(activity.getWorkProfileUserHandle()));
+        }
+    }
+
     private Intent createSendImageIntent() {
         Intent sendIntent = new Intent();
         sendIntent.setAction(Intent.ACTION_SEND);
@@ -1237,6 +1331,10 @@
         ResolverWrapperActivity.sOverrides.cloneProfileUserHandle = UserHandle.of(11);
     }
 
+    private void markPrivateProfileUserAvailable() {
+        ResolverWrapperActivity.sOverrides.privateProfileUserHandle = UserHandle.of(12);
+    }
+
     private void setupResolverControllers(
             List<ResolvedComponentInfo> personalResolvedComponentInfos,
             List<ResolvedComponentInfo> workResolvedComponentInfos) {
@@ -1256,4 +1354,13 @@
                 eq(UserHandle.SYSTEM)))
                 .thenReturn(new ArrayList<>(personalResolvedComponentInfos));
     }
+
+    private void setupResolverControllers(
+            List<ResolvedComponentInfo> resolvedComponentInfos) {
+        when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
+                Mockito.anyBoolean(),
+                Mockito.anyBoolean(),
+                Mockito.isA(List.class)))
+                .thenReturn(new ArrayList<>(resolvedComponentInfos));
+    }
 }
diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverWrapperActivity.java b/core/tests/coretests/src/com/android/internal/app/ResolverWrapperActivity.java
index e193de0..862cbd5 100644
--- a/core/tests/coretests/src/com/android/internal/app/ResolverWrapperActivity.java
+++ b/core/tests/coretests/src/com/android/internal/app/ResolverWrapperActivity.java
@@ -88,6 +88,10 @@
         return ((ResolverListAdapter) mMultiProfilePagerAdapter.getAdapterForIndex(1));
     }
 
+    int getMultiProfilePagerAdapterCount(){
+        return mMultiProfilePagerAdapter.getCount();
+    }
+
     @Override
     public boolean isVoiceInteraction() {
         if (sOverrides.isVoiceInteraction != null) {
@@ -144,6 +148,11 @@
     }
 
     @Override
+    protected UserHandle getPrivateProfileUserHandle() {
+        return sOverrides.privateProfileUserHandle;
+    }
+
+    @Override
     protected UserHandle getTabOwnerUserHandleForLaunch() {
         if (sOverrides.tabOwnerUserHandleForLaunch == null) {
             return super.getTabOwnerUserHandleForLaunch();
@@ -176,6 +185,7 @@
         public Boolean isVoiceInteraction;
         public UserHandle workProfileUserHandle;
         public UserHandle cloneProfileUserHandle;
+        public UserHandle privateProfileUserHandle;
         public UserHandle tabOwnerUserHandleForLaunch;
         public Integer myUserId;
         public boolean hasCrossProfileIntents;
@@ -191,6 +201,7 @@
             workResolverListController = mock(ResolverListController.class);
             workProfileUserHandle = null;
             cloneProfileUserHandle = null;
+            privateProfileUserHandle = null;
             tabOwnerUserHandleForLaunch = null;
             myUserId = null;
             hasCrossProfileIntents = true;
diff --git a/core/tests/coretests/src/com/android/internal/os/LongArrayMultiStateCounterTest.java b/core/tests/coretests/src/com/android/internal/os/LongArrayMultiStateCounterTest.java
index c9536b9..533b799 100644
--- a/core/tests/coretests/src/com/android/internal/os/LongArrayMultiStateCounterTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/LongArrayMultiStateCounterTest.java
@@ -22,7 +22,6 @@
 
 import android.os.BadParcelableException;
 import android.os.Parcel;
-import android.platform.test.annotations.IgnoreUnderRavenwood;
 import android.platform.test.ravenwood.RavenwoodRule;
 
 import androidx.test.filters.SmallTest;
@@ -34,7 +33,6 @@
 
 @RunWith(AndroidJUnit4.class)
 @SmallTest
-@IgnoreUnderRavenwood(blockedBy = LongArrayMultiStateCounter.class)
 public class LongArrayMultiStateCounterTest {
     @Rule
     public final RavenwoodRule mRavenwood = new RavenwoodRule();
diff --git a/core/tests/overlaytests/Android.mk b/core/tests/overlaytests/Android.mk
deleted file mode 100644
index b798d87..0000000
--- a/core/tests/overlaytests/Android.mk
+++ /dev/null
@@ -1,15 +0,0 @@
-# Copyright (C) 2017 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-include $(call all-subdir-makefiles)
diff --git a/core/tests/overlaytests/host/test-apps/Android.mk b/core/tests/overlaytests/host/test-apps/Android.mk
deleted file mode 100644
index 5c7187e..0000000
--- a/core/tests/overlaytests/host/test-apps/Android.mk
+++ /dev/null
@@ -1,16 +0,0 @@
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-include $(call all-subdir-makefiles)
-
diff --git a/core/tests/overlaytests/host/test-apps/SignatureOverlay/Android.bp b/core/tests/overlaytests/host/test-apps/SignatureOverlay/Android.bp
new file mode 100644
index 0000000..bb7d63e
--- /dev/null
+++ b/core/tests/overlaytests/host/test-apps/SignatureOverlay/Android.bp
@@ -0,0 +1,57 @@
+// 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.
+
+// Error: Cannot get the name of the license module in the
+// ./Android.bp file.
+// If no such license module exists, please add one there first.
+// Then reset the default_applicable_licenses property below with the license module name.
+package {
+    // See: http://go/android-license-faq
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test_helper_app {
+    name: "OverlayHostTests_NonPlatformSignatureOverlay",
+    sdk_version: "current",
+    test_suites: ["device-tests"],
+    aaptflags: [
+        "--custom-package com.android.server.om.hosttest.signature_overlay_bad",
+    ],
+}
+
+android_test_helper_app {
+    name: "OverlayHostTests_PlatformSignatureStaticOverlay",
+    sdk_version: "current",
+    test_suites: ["device-tests"],
+    certificate: "platform",
+    manifest: "static/AndroidManifest.xml",
+    aaptflags: [
+        "--custom-package com.android.server.om.hosttest.signature_overlay_static",
+    ],
+}
+
+android_test_helper_app {
+    name: "OverlayHostTests_PlatformSignatureOverlay",
+    sdk_version: "current",
+    test_suites: ["device-tests"],
+    certificate: "platform",
+    aaptflags: [
+        "--custom-package",
+        "com.android.server.om.hosttest.signature_overlay_v1",
+        "--version-code",
+        "1",
+        "--version-name",
+        "v1",
+    ],
+}
diff --git a/core/tests/overlaytests/host/test-apps/SignatureOverlay/Android.mk b/core/tests/overlaytests/host/test-apps/SignatureOverlay/Android.mk
deleted file mode 100644
index b453cde9..0000000
--- a/core/tests/overlaytests/host/test-apps/SignatureOverlay/Android.mk
+++ /dev/null
@@ -1,56 +0,0 @@
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH := $(call my-dir)
-
-my_package_prefix := com.android.server.om.hosttest.signature_overlay
-
-include $(CLEAR_VARS)
-LOCAL_MODULE_TAGS := tests
-LOCAL_PACKAGE_NAME := OverlayHostTests_NonPlatformSignatureOverlay
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE  := $(LOCAL_PATH)/../../../../../../NOTICE
-LOCAL_SDK_VERSION := current
-LOCAL_COMPATIBILITY_SUITE := device-tests
-LOCAL_AAPT_FLAGS := --custom-package $(my_package_prefix)_bad
-include $(BUILD_PACKAGE)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE_TAGS := tests
-LOCAL_PACKAGE_NAME := OverlayHostTests_PlatformSignatureStaticOverlay
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE  := $(LOCAL_PATH)/../../../../../../NOTICE
-LOCAL_SDK_VERSION := current
-LOCAL_COMPATIBILITY_SUITE := device-tests
-LOCAL_CERTIFICATE := platform
-LOCAL_MANIFEST_FILE := static/AndroidManifest.xml
-LOCAL_AAPT_FLAGS := --custom-package $(my_package_prefix)_static
-include $(BUILD_PACKAGE)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE_TAGS := tests
-LOCAL_PACKAGE_NAME := OverlayHostTests_PlatformSignatureOverlay
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE  := $(LOCAL_PATH)/../../../../../../NOTICE
-LOCAL_SDK_VERSION := current
-LOCAL_COMPATIBILITY_SUITE := device-tests
-LOCAL_CERTIFICATE := platform
-LOCAL_AAPT_FLAGS := --custom-package $(my_package_prefix)_v1
-LOCAL_AAPT_FLAGS += --version-code 1 --version-name v1
-include $(BUILD_PACKAGE)
-
-my_package_prefix :=
diff --git a/core/tests/overlaytests/host/test-apps/UpdateOverlay/Android.bp b/core/tests/overlaytests/host/test-apps/UpdateOverlay/Android.bp
new file mode 100644
index 0000000..ee0c0e5
--- /dev/null
+++ b/core/tests/overlaytests/host/test-apps/UpdateOverlay/Android.bp
@@ -0,0 +1,97 @@
+// 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.
+
+// Error: Cannot get the name of the license module in the
+// ./Android.bp file.
+// If no such license module exists, please add one there first.
+// Then reset the default_applicable_licenses property below with the license module name.
+package {
+    // See: http://go/android-license-faq
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test_helper_app {
+    name: "OverlayHostTests_UpdateOverlay",
+    srcs: ["src/**/*.java"],
+    sdk_version: "current",
+    test_suites: ["device-tests"],
+    static_libs: ["androidx.test.rules"],
+    aaptflags: ["--no-resource-removal"],
+}
+
+android_test_helper_app {
+    name: "OverlayHostTests_FrameworkOverlayV1",
+    sdk_version: "current",
+    test_suites: ["device-tests"],
+    certificate: "platform",
+    aaptflags: [
+        "--custom-package",
+        "com.android.server.om.hosttest.framework_overlay_v1",
+        "--version-code",
+        "1",
+        "--version-name",
+        "v1",
+    ],
+    resource_dirs: ["framework/v1/res"],
+    manifest: "framework/AndroidManifest.xml",
+}
+
+android_test_helper_app {
+    name: "OverlayHostTests_FrameworkOverlayV2",
+    sdk_version: "current",
+    test_suites: ["device-tests"],
+    certificate: "platform",
+    aaptflags: [
+        "--custom-package",
+        "com.android.server.om.hosttest.framework_overlay_v2",
+        "--version-code",
+        "2",
+        "--version-name",
+        "v2",
+    ],
+    resource_dirs: ["framework/v2/res"],
+    manifest: "framework/AndroidManifest.xml",
+}
+
+android_test_helper_app {
+    name: "OverlayHostTests_AppOverlayV1",
+    sdk_version: "current",
+    test_suites: ["device-tests"],
+    aaptflags: [
+        "--custom-package",
+        "com.android.server.om.hosttest.app_overlay_v1",
+        "--version-code",
+        "1",
+        "--version-name",
+        "v1",
+    ],
+    resource_dirs: ["app/v1/res"],
+    manifest: "app/v1/AndroidManifest.xml",
+}
+
+android_test_helper_app {
+    name: "OverlayHostTests_AppOverlayV2",
+    sdk_version: "current",
+    test_suites: ["device-tests"],
+    aaptflags: [
+        "--custom-package",
+        "com.android.server.om.hosttest.app_overlay_v2",
+        "--version-code",
+        "2",
+        "--version-name",
+        "v2",
+    ],
+    resource_dirs: ["app/v2/res"],
+    manifest: "app/v2/AndroidManifest.xml",
+}
diff --git a/core/tests/overlaytests/host/test-apps/UpdateOverlay/Android.mk b/core/tests/overlaytests/host/test-apps/UpdateOverlay/Android.mk
deleted file mode 100644
index 77fc887..0000000
--- a/core/tests/overlaytests/host/test-apps/UpdateOverlay/Android.mk
+++ /dev/null
@@ -1,93 +0,0 @@
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE_TAGS := tests
-LOCAL_SRC_FILES := $(call all-java-files-under,src)
-LOCAL_PACKAGE_NAME := OverlayHostTests_UpdateOverlay
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE  := $(LOCAL_PATH)/../../../../../../NOTICE
-LOCAL_SDK_VERSION := current
-LOCAL_COMPATIBILITY_SUITE := device-tests
-LOCAL_STATIC_JAVA_LIBRARIES := androidx.test.rules
-LOCAL_USE_AAPT2 := true
-LOCAL_AAPT_FLAGS := --no-resource-removal
-include $(BUILD_PACKAGE)
-
-my_package_prefix := com.android.server.om.hosttest.framework_overlay
-
-include $(CLEAR_VARS)
-LOCAL_MODULE_TAGS := tests
-LOCAL_PACKAGE_NAME := OverlayHostTests_FrameworkOverlayV1
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE  := $(LOCAL_PATH)/../../../../../../NOTICE
-LOCAL_SDK_VERSION := current
-LOCAL_COMPATIBILITY_SUITE := device-tests
-LOCAL_CERTIFICATE := platform
-LOCAL_AAPT_FLAGS := --custom-package $(my_package_prefix)_v1
-LOCAL_AAPT_FLAGS += --version-code 1 --version-name v1
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/framework/v1/res
-LOCAL_MANIFEST_FILE := framework/AndroidManifest.xml
-include $(BUILD_PACKAGE)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE_TAGS := tests
-LOCAL_PACKAGE_NAME := OverlayHostTests_FrameworkOverlayV2
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE  := $(LOCAL_PATH)/../../../../../../NOTICE
-LOCAL_SDK_VERSION := current
-LOCAL_COMPATIBILITY_SUITE := device-tests
-LOCAL_CERTIFICATE := platform
-LOCAL_AAPT_FLAGS := --custom-package $(my_package_prefix)_v2
-LOCAL_AAPT_FLAGS += --version-code 2 --version-name v2
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/framework/v2/res
-LOCAL_MANIFEST_FILE := framework/AndroidManifest.xml
-include $(BUILD_PACKAGE)
-
-my_package_prefix := com.android.server.om.hosttest.app_overlay
-
-include $(CLEAR_VARS)
-LOCAL_MODULE_TAGS := tests
-LOCAL_PACKAGE_NAME := OverlayHostTests_AppOverlayV1
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE  := $(LOCAL_PATH)/../../../../../../NOTICE
-LOCAL_SDK_VERSION := current
-LOCAL_COMPATIBILITY_SUITE := device-tests
-LOCAL_AAPT_FLAGS := --custom-package $(my_package_prefix)_v1
-LOCAL_AAPT_FLAGS += --version-code 1 --version-name v1
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/app/v1/res
-LOCAL_MANIFEST_FILE := app/v1/AndroidManifest.xml
-include $(BUILD_PACKAGE)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE_TAGS := tests
-LOCAL_PACKAGE_NAME := OverlayHostTests_AppOverlayV2
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE  := $(LOCAL_PATH)/../../../../../../NOTICE
-LOCAL_SDK_VERSION := current
-LOCAL_COMPATIBILITY_SUITE := device-tests
-LOCAL_AAPT_FLAGS := --custom-package $(my_package_prefix)_v2
-LOCAL_AAPT_FLAGS += --version-code 2 --version-name v2
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/app/v2/res
-LOCAL_MANIFEST_FILE := app/v2/AndroidManifest.xml
-include $(BUILD_PACKAGE)
-
-my_package_prefix :=
diff --git a/data/etc/com.android.systemui.xml b/data/etc/com.android.systemui.xml
index 43683ff..ce2543a 100644
--- a/data/etc/com.android.systemui.xml
+++ b/data/etc/com.android.systemui.xml
@@ -56,6 +56,7 @@
         <permission name="android.permission.REAL_GET_TASKS"/>
         <permission name="android.permission.REQUEST_NETWORK_SCORES"/>
         <permission name="android.permission.RECEIVE_MEDIA_RESOURCE_USAGE"/>
+        <permission name="android.permission.SATELLITE_COMMUNICATION"/>
         <permission name="android.permission.SET_WALLPAPER_DIM_AMOUNT"/>
         <permission name="android.permission.START_ACTIVITIES_FROM_BACKGROUND" />
         <permission name="android.permission.START_ACTIVITY_AS_CALLER"/>
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index c4530f6..13d38d2 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -347,7 +347,9 @@
     <!-- Allow IMS service entitlement app to schedule jobs to run when app in background. -->
     <allow-in-power-save-except-idle package="com.android.imsserviceentitlement" />
 
-    <!-- Allow device lock controller app to schedule jobs and alarms when app in background,
-        otherwise, it may not be able to enforce provision for managed devices. -->
+    <!-- Allow device lock controller app to schedule jobs and alarms, and have network access
+         when app in background; otherwise, it may not be able to enforce provision for managed
+         devices. -->
     <allow-in-power-save package="com.android.devicelockcontroller" />
+    <allow-in-data-usage-save package="com.android.devicelockcontroller" />
 </permissions>
diff --git a/data/etc/preinstalled-packages-platform.xml b/data/etc/preinstalled-packages-platform.xml
index 421bc25..bf60944 100644
--- a/data/etc/preinstalled-packages-platform.xml
+++ b/data/etc/preinstalled-packages-platform.xml
@@ -128,4 +128,9 @@
     <install-in-user-type package="com.android.wallpaperbackup">
         <install-in user-type="FULL" />
     </install-in-user-type>
+
+    <!-- AvatarPicker (AvatarPicker app)-->
+    <install-in-user-type package="com.android.avatarpicker">
+        <install-in user-type="FULL" />
+    </install-in-user-type>
 </config>
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index c6f920f..a1ea2b8 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -134,6 +134,7 @@
         <permission name="android.permission.CONTROL_INCALL_EXPERIENCE"/>
         <permission name="android.permission.DUMP"/>
         <permission name="android.permission.INTERACT_ACROSS_USERS"/>
+        <permission name="android.permission.LOCATION_BYPASS"/>
         <permission name="android.permission.LOCAL_MAC_ADDRESS"/>
         <permission name="android.permission.MANAGE_USERS"/>
         <permission name="android.permission.MANAGE_SUBSCRIPTION_PLANS" />
@@ -149,6 +150,7 @@
         <permission name="android.permission.REGISTER_CALL_PROVIDER"/>
         <permission name="android.permission.REGISTER_SIM_SUBSCRIPTION"/>
         <permission name="android.permission.REGISTER_STATS_PULL_ATOM"/>
+        <permission name="android.permission.SATELLITE_COMMUNICATION"/>
         <permission name="android.permission.SEND_RESPOND_VIA_MESSAGE"/>
         <permission name="android.permission.SHUTDOWN"/>
         <permission name="android.permission.START_ACTIVITIES_FROM_BACKGROUND"/>
@@ -575,6 +577,7 @@
         <permission name="android.permission.SET_WALLPAPER_COMPONENT"/>
         <permission name="android.permission.BIND_WALLPAPER"/>
         <permission name="android.permission.CUSTOMIZE_SYSTEM_UI"/>
+        <permission name="android.permission.SET_WALLPAPER_DIM_AMOUNT"/>
     </privapp-permissions>
 
     <privapp-permissions package="com.android.dynsystem">
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 742d5a2..917a300 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -4453,6 +4453,12 @@
       "group": "WM_DEBUG_BACK_PREVIEW",
       "at": "com\/android\/server\/wm\/BackNavigationController.java"
     },
+    "1946983717": {
+      "message": "Waiting for screen on due to %s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_STATES",
+      "at": "com\/android\/server\/wm\/TaskFragment.java"
+    },
     "1947239194": {
       "message": "Deferring rotation, still finishing previous rotation",
       "level": "VERBOSE",
diff --git a/data/keyboards/Android.bp b/data/keyboards/Android.bp
new file mode 100644
index 0000000..f15c153
--- /dev/null
+++ b/data/keyboards/Android.bp
@@ -0,0 +1,29 @@
+// Copyright 2010 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+genrule {
+    name: "validate_framework_keymaps",
+    srcs: [
+        "*.kl",
+        "*.kcm",
+        "*.idc",
+    ],
+    tools: ["validatekeymaps"],
+    out: ["stamp"],
+    cmd: "$(location validatekeymaps) -q $(in) " +
+        "&& touch $(out)",
+    dist: {
+        targets: ["droidcore"],
+    },
+}
diff --git a/data/keyboards/Android.mk b/data/keyboards/Android.mk
deleted file mode 100644
index 6ae8800..0000000
--- a/data/keyboards/Android.mk
+++ /dev/null
@@ -1,44 +0,0 @@
-# Copyright (C) 2010 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# This makefile performs build time validation of framework keymap files.
-
-LOCAL_PATH := $(call my-dir)
-
-include $(LOCAL_PATH)/common.mk
-
-# Validate all key maps.
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := validate_framework_keymaps
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../NOTICE
-intermediates := $(call intermediates-dir-for,ETC,$(LOCAL_MODULE),,COMMON)
-LOCAL_BUILT_MODULE := $(intermediates)/stamp
-
-validatekeymaps := $(HOST_OUT_EXECUTABLES)/validatekeymaps$(HOST_EXECUTABLE_SUFFIX)
-$(LOCAL_BUILT_MODULE): PRIVATE_VALIDATEKEYMAPS := $(validatekeymaps)
-$(LOCAL_BUILT_MODULE) : $(framework_keylayouts) $(framework_keycharmaps) $(framework_keyconfigs) | $(validatekeymaps)
-	$(hide) $(PRIVATE_VALIDATEKEYMAPS) -q $^
-	$(hide) mkdir -p $(dir $@) && touch $@
-
-# Run validatekeymaps uncondionally for platform build.
-droidcore : $(LOCAL_BUILT_MODULE)
-
-# Reset temp vars.
-validatekeymaps :=
-framework_keylayouts :=
-framework_keycharmaps :=
-framework_keyconfigs :=
diff --git a/data/keyboards/Vendor_0428_Product_4001.kl b/data/keyboards/Vendor_0428_Product_4001.kl
new file mode 100644
index 0000000..7d1dd12
--- /dev/null
+++ b/data/keyboards/Vendor_0428_Product_4001.kl
@@ -0,0 +1,27 @@
+# Gravis GamePad Pro USB
+
+# Yellow
+key 0x131   BUTTON_A
+# Green
+key 0x132   BUTTON_B
+# Red
+key 0x130   BUTTON_X
+# Blue
+key 0x133   BUTTON_Y
+
+# Left Upper Shoulder "1"
+key 0x134   BUTTON_L1
+# Right Upper Shoulder "1"
+key 0x135   BUTTON_R1
+# Left Lower Shoulder "2"
+key 0x136   BUTTON_L2
+# Right Lower Shoulder "2"
+key 0x137   BUTTON_R2
+
+# Select & Start
+key 0x138   BUTTON_SELECT
+key 0x139   BUTTON_START
+
+# D-Pad
+axis 0x00   HAT_X
+axis 0x01   HAT_Y
diff --git a/core/tests/overlaytests/host/Android.mk b/data/keyboards/Vendor_0957_Product_0031.idc
similarity index 71%
rename from core/tests/overlaytests/host/Android.mk
rename to data/keyboards/Vendor_0957_Product_0031.idc
index d58d939..f0320c8 100644
--- a/core/tests/overlaytests/host/Android.mk
+++ b/data/keyboards/Vendor_0957_Product_0031.idc
@@ -1,4 +1,4 @@
-# Copyright (C) 2018 The Android Open Source Project
+# Copyright 2024 The Android Open Source Project
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -11,9 +11,11 @@
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
+#
 
-LOCAL_PATH := $(call my-dir)
+# Input Device Configuration file for Google Reference RCU Remote.
+# PID 0031 is for old GPIO pin
 
-# Include to build test-apps.
-include $(call all-makefiles-under,$(LOCAL_PATH))
-
+# Basic Parameters
+keyboard.doNotWakeByDefault = 1
+audio.mic = 1
\ No newline at end of file
diff --git a/data/keyboards/Vendor_0957_Product_0031.kl b/data/keyboards/Vendor_0957_Product_0031.kl
new file mode 100644
index 0000000..b47ee58
--- /dev/null
+++ b/data/keyboards/Vendor_0957_Product_0031.kl
@@ -0,0 +1,82 @@
+# Copyright 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# Key Layout file for Google Reference RCU Remote with customizable button.
+#
+
+key 116   TV_POWER      WAKE
+key 217   ASSIST        WAKE
+key 423   MACRO_1       WAKE
+
+key 103   DPAD_UP
+key 108   DPAD_DOWN
+key 105   DPAD_LEFT
+key 106   DPAD_RIGHT
+key 353   DPAD_CENTER
+
+key 158   BACK
+key 172   HOME          WAKE
+
+key 113   VOLUME_MUTE
+key 114   VOLUME_DOWN
+key 115   VOLUME_UP
+
+key 2     1
+key 3     2
+key 4     3
+key 5     4
+key 6     5
+key 7     6
+key 8     7
+key 9     8
+key 10    9
+key 11    0
+
+# custom keys
+key usage 0x000c01BB    TV_INPUT
+
+key usage 0x000c0185    TV_TELETEXT
+key usage 0x000c0061    CAPTIONS
+
+key usage 0x000c01BD    INFO
+key usage 0x000c0037    PERIOD
+
+key usage 0x000c0069    PROG_RED
+key usage 0x000c006A    PROG_GREEN
+key usage 0x000c006C    PROG_YELLOW
+key usage 0x000c006B    PROG_BLUE
+key usage 0x000c00B4    MEDIA_SKIP_BACKWARD
+key usage 0x000c00CD    MEDIA_PLAY_PAUSE
+key usage 0x000c00B2    MEDIA_RECORD
+key usage 0x000c00B3    MEDIA_SKIP_FORWARD
+
+key usage 0x000c022A    BOOKMARK
+key usage 0x000c01A2    ALL_APPS
+key usage 0x000c019C    PROFILE_SWITCH
+
+key usage 0x000c0096    SETTINGS
+key usage 0x000c009F    NOTIFICATION
+
+key usage 0x000c008D    GUIDE
+key usage 0x000c0089    TV
+
+key usage 0x000c0187    FEATURED_APP_1    WAKE #FreeTv
+
+key usage 0x000c009C    CHANNEL_UP
+key usage 0x000c009D    CHANNEL_DOWN
+
+key usage 0x000c0077    BUTTON_3     WAKE #YouTube
+key usage 0x000c0078    BUTTON_4     WAKE #Netflix
+key usage 0x000c0079    BUTTON_6     WAKE
+key usage 0x000c007A    BUTTON_7     WAKE
\ No newline at end of file
diff --git a/core/tests/overlaytests/host/Android.mk b/data/keyboards/Vendor_0957_Product_0034.idc
similarity index 60%
copy from core/tests/overlaytests/host/Android.mk
copy to data/keyboards/Vendor_0957_Product_0034.idc
index d58d939..52ed0bc 100644
--- a/core/tests/overlaytests/host/Android.mk
+++ b/data/keyboards/Vendor_0957_Product_0034.idc
@@ -1,4 +1,4 @@
-# Copyright (C) 2018 The Android Open Source Project
+# Copyright 2024 The Android Open Source Project
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -11,9 +11,13 @@
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
+#
 
-LOCAL_PATH := $(call my-dir)
+# Input Device Configuration file for Google Reference RCU Remote.
+# PID 0034 is for new GPIO pin PCB.
 
-# Include to build test-apps.
-include $(call all-makefiles-under,$(LOCAL_PATH))
-
+# Basic Parameters
+keyboard.layout = Vendor_0957_Product_0031
+# The reason why we set is follow https://docs.partner.android.com/tv/build/gtv/boot-resume
+keyboard.doNotWakeByDefault = 1
+audio.mic = 1
\ No newline at end of file
diff --git a/data/keyboards/common.mk b/data/keyboards/common.mk
deleted file mode 100644
index d75b691..0000000
--- a/data/keyboards/common.mk
+++ /dev/null
@@ -1,22 +0,0 @@
-# Copyright (C) 2010 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# This is the list of framework provided keylayouts and key character maps to include.
-# Used by Android.mk and keyboards.mk.
-
-framework_keylayouts := $(wildcard $(LOCAL_PATH)/*.kl)
-
-framework_keycharmaps := $(wildcard $(LOCAL_PATH)/*.kcm)
-
-framework_keyconfigs := $(wildcard $(LOCAL_PATH)/*.idc)
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index f10cdb8..c5a2f98 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -65,6 +65,8 @@
     private long mNativeShader;
     private long mNativeColorFilter;
 
+    private static boolean sIsRobolectric = Build.FINGERPRINT.equals("robolectric");
+
     // Use a Holder to allow static initialization of Paint in the boot image.
     private static class NoImagePreloadHolder {
         public static final NativeAllocationRegistry sRegistry =
@@ -2474,6 +2476,19 @@
         nGetFontMetricsInt(mNativePaint, metrics, true);
     }
 
+    /** @hide */
+    public static final class RunInfo {
+        private int mClusterCount = 0;
+
+        public int getClusterCount() {
+            return mClusterCount;
+        }
+
+        public void setClusterCount(int clusterCount) {
+            mClusterCount = clusterCount;
+        }
+    }
+
     /**
      * Return the recommend line spacing based on the current typeface and
      * text size.
@@ -3320,7 +3335,7 @@
             int contextEnd, boolean isRtl, int offset,
             @Nullable float[] advances, int advancesIndex) {
         return getRunCharacterAdvance(text, start, end, contextStart, contextEnd, isRtl, offset,
-                advances, advancesIndex, null);
+                advances, advancesIndex, null, null);
     }
 
     /**
@@ -3339,12 +3354,14 @@
      * @param advances the array that receives the computed character advances
      * @param advancesIndex the start index from which the advances array is filled
      * @param drawBounds the output parameter for the bounding box of drawing text, optional
+     * @param runInfo the output parameter for storing run information.
      * @return width measurement between start and offset
-     * @hide
+     * @hide TODO: Reorganize APIs
      */
     public float getRunCharacterAdvance(@NonNull char[] text, int start, int end, int contextStart,
             int contextEnd, boolean isRtl, int offset,
-            @Nullable float[] advances, int advancesIndex, @Nullable RectF drawBounds) {
+            @Nullable float[] advances, int advancesIndex, @Nullable RectF drawBounds,
+            @Nullable RunInfo runInfo) {
         if (text == null) {
             throw new IllegalArgumentException("text cannot be null");
         }
@@ -3370,11 +3387,19 @@
         }
 
         if (end == start) {
+            if (runInfo != null) {
+                runInfo.setClusterCount(0);
+            }
             return 0.0f;
         }
 
-        return nGetRunCharacterAdvance(mNativePaint, text, start, end, contextStart, contextEnd,
-                isRtl, offset, advances, advancesIndex, drawBounds);
+        if (sIsRobolectric) {
+            return nGetRunCharacterAdvance(mNativePaint, text, start, end,
+                    contextStart, contextEnd, isRtl, offset, advances, advancesIndex, drawBounds);
+        } else {
+            return nGetRunCharacterAdvance(mNativePaint, text, start, end, contextStart, contextEnd,
+                    isRtl, offset, advances, advancesIndex, drawBounds, runInfo);
+        }
     }
 
     /**
@@ -3402,7 +3427,7 @@
             int contextStart, int contextEnd, boolean isRtl, int offset,
             @Nullable float[] advances, int advancesIndex) {
         return getRunCharacterAdvance(text, start, end, contextStart, contextEnd, isRtl, offset,
-                advances, advancesIndex, null);
+                advances, advancesIndex, null, null);
     }
 
     /**
@@ -3418,12 +3443,14 @@
      * @param advances the array that receives the computed character advances
      * @param advancesIndex the start index from which the advances array is filled
      * @param drawBounds the output parameter for the bounding box of drawing text, optional
+     * @param runInfo an optional output parameter for filling run information.
      * @return width measurement between start and offset
-     * @hide
+     * @hide  TODO: Reorganize APIs
      */
     public float getRunCharacterAdvance(@NonNull CharSequence text, int start, int end,
             int contextStart, int contextEnd, boolean isRtl, int offset,
-            @Nullable float[] advances, int advancesIndex, @Nullable RectF drawBounds) {
+            @Nullable float[] advances, int advancesIndex, @Nullable RectF drawBounds,
+            @Nullable RunInfo runInfo) {
         if (text == null) {
             throw new IllegalArgumentException("text cannot be null");
         }
@@ -3456,7 +3483,7 @@
         TextUtils.getChars(text, contextStart, contextEnd, buf, 0);
         final float result = getRunCharacterAdvance(buf, start - contextStart, end - contextStart,
                 0, contextEnd - contextStart, isRtl, offset - contextStart,
-                advances, advancesIndex, drawBounds);
+                advances, advancesIndex, drawBounds, runInfo);
         TemporaryBuffer.recycle(buf);
         return result;
     }
@@ -3574,7 +3601,7 @@
             int contextStart, int contextEnd, boolean isRtl, int offset);
     private static native float nGetRunCharacterAdvance(long paintPtr, char[] text, int start,
             int end, int contextStart, int contextEnd, boolean isRtl, int offset, float[] advances,
-            int advancesIndex, RectF drawingBounds);
+            int advancesIndex, RectF drawingBounds, RunInfo runInfo);
     private static native int nGetOffsetForAdvance(long paintPtr, char[] text, int start, int end,
             int contextStart, int contextEnd, boolean isRtl, float advance);
     private static native void nGetFontMetricsIntForText(long paintPtr, char[] text,
@@ -3729,4 +3756,11 @@
     private static native void nSetTextSize(long paintPtr, float textSize);
     @CriticalNative
     private static native boolean nEqualsForTextMeasurement(long leftPaintPtr, long rightPaintPtr);
+
+
+    // Following Native methods are kept for old Robolectric JNI signature used by
+    // SystemUIGoogleRoboRNGTests
+    private static native float nGetRunCharacterAdvance(long paintPtr, char[] text,
+            int start, int end, int contextStart, int contextEnd, boolean isRtl, int offset,
+            float[] advances, int advancesIndex, RectF drawingBounds);
 }
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java
index ed99501..29cf054 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java
@@ -55,7 +55,7 @@
     // TODO(b/241126279) Introduce constants to better version functionality
     @Override
     public int getVendorApiLevel() {
-        return 4;
+        return 5;
     }
 
     @NonNull
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
index ca3d8d1..80afb16d 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
@@ -19,6 +19,7 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.window.TaskFragmentOperation.OP_TYPE_REORDER_TO_FRONT;
 import static android.window.TaskFragmentOperation.OP_TYPE_SET_ANIMATION_PARAMS;
+import static android.window.TaskFragmentOperation.OP_TYPE_SET_DIM_ON_TASK;
 import static android.window.TaskFragmentOperation.OP_TYPE_SET_ISOLATED_NAVIGATION;
 
 import static androidx.window.extensions.embedding.SplitContainer.getFinishPrimaryWithSecondaryBehavior;
@@ -356,6 +357,13 @@
         wct.addTaskFragmentOperation(fragmentToken, operation);
     }
 
+    void setTaskFragmentDimOnTask(@NonNull WindowContainerTransaction wct,
+            @NonNull IBinder fragmentToken, boolean dimOnTask) {
+        final TaskFragmentOperation operation = new TaskFragmentOperation.Builder(
+                OP_TYPE_SET_DIM_ON_TASK).setDimOnTask(dimOnTask).build();
+        wct.addTaskFragmentOperation(fragmentToken, operation);
+    }
+
     void updateTaskFragmentInfo(@NonNull TaskFragmentInfo taskFragmentInfo) {
         mFragmentInfos.put(taskFragmentInfo.getFragmentToken(), taskFragmentInfo);
     }
@@ -374,9 +382,13 @@
         if (splitAttributes == null) {
             return TaskFragmentAnimationParams.DEFAULT;
         }
-        return new TaskFragmentAnimationParams.Builder()
-                // TODO(b/263047900): Update extensions API.
-                // .setAnimationBackgroundColor(splitAttributes.getAnimationBackgroundColor())
-                .build();
+        final AnimationBackground animationBackground = splitAttributes.getAnimationBackground();
+        if (animationBackground instanceof AnimationBackground.ColorBackground colorBackground) {
+            return new TaskFragmentAnimationParams.Builder()
+                    .setAnimationBackgroundColor(colorBackground.getColor())
+                    .build();
+        } else {
+            return TaskFragmentAnimationParams.DEFAULT;
+        }
     }
 }
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
index 15ee4e1..066f38b 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -47,6 +47,7 @@
 import static androidx.window.extensions.embedding.SplitPresenter.getMinDimensions;
 import static androidx.window.extensions.embedding.SplitPresenter.shouldShowSplit;
 
+import android.annotation.CallbackExecutor;
 import android.app.Activity;
 import android.app.ActivityClient;
 import android.app.ActivityOptions;
@@ -63,6 +64,7 @@
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.SystemProperties;
+import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Log;
 import android.util.Pair;
@@ -85,6 +87,7 @@
 import androidx.window.extensions.WindowExtensionsImpl;
 import androidx.window.extensions.core.util.function.Consumer;
 import androidx.window.extensions.core.util.function.Function;
+import androidx.window.extensions.core.util.function.Predicate;
 import androidx.window.extensions.embedding.TransactionManager.TransactionRecord;
 import androidx.window.extensions.layout.WindowLayoutComponentImpl;
 
@@ -158,8 +161,20 @@
     /** Callback to Jetpack to notify about changes to split states. */
     @GuardedBy("mLock")
     @Nullable
-    private Consumer<List<SplitInfo>> mEmbeddingCallback;
+    private Consumer<List<SplitInfo>> mSplitInfoCallback;
     private final List<SplitInfo> mLastReportedSplitStates = new ArrayList<>();
+
+    /**
+     * Stores callbacks to Jetpack to notify about changes to {@link ActivityStack activityStacks}
+     * and corresponding {@link Executor executors} to dispatch the callback.
+     */
+    @GuardedBy("mLock")
+    @NonNull
+    private final ArrayMap<Consumer<List<ActivityStack>>, Executor> mActivityStackCallbacks =
+            new ArrayMap<>();
+
+    private final List<ActivityStack> mLastReportedActivityStacks = new ArrayList<>();
+
     private final Handler mHandler;
     final Object mLock = new Object();
     private final ActivityStartMonitor mActivityStartMonitor;
@@ -283,7 +298,7 @@
     }
 
     @Override
-    public void unpinTopActivityStack(int taskId){
+    public void unpinTopActivityStack(int taskId) {
         synchronized (mLock) {
             Log.i(TAG, "Request to unpin top activity stack.");
             final TaskContainer task = getTaskContainer(taskId);
@@ -333,6 +348,9 @@
     public void setActivityStackAttributesCalculator(
             @NonNull Function<ActivityStackAttributesCalculatorParams, ActivityStackAttributes>
                     calculator) {
+        if (!Flags.activityEmbeddingOverlayPresentationFlag()) {
+            return;
+        }
         synchronized (mLock) {
             mActivityStackAttributesCalculator = calculator;
         }
@@ -340,6 +358,9 @@
 
     @Override
     public void clearActivityStackAttributesCalculator() {
+        if (!Flags.activityEmbeddingOverlayPresentationFlag()) {
+            return;
+        }
         synchronized (mLock) {
             mActivityStackAttributesCalculator = null;
         }
@@ -351,7 +372,7 @@
         return mSplitAttributesCalculator;
     }
 
-    @Override
+    // TODO(b/295993745): remove after we migrate to the bundle approach.
     @NonNull
     public ActivityOptions setLaunchingActivityStack(@NonNull ActivityOptions options,
             @NonNull IBinder token) {
@@ -368,6 +389,7 @@
 
     /**
      * Registers the split organizer callback to notify about changes to active splits.
+     *
      * @deprecated Use {@link #setSplitInfoCallback(Consumer)} starting with
      * {@link WindowExtensionsImpl#getVendorApiLevel()} 2.
      */
@@ -381,12 +403,14 @@
 
     /**
      * Registers the split organizer callback to notify about changes to active splits.
+     *
      * @since {@link WindowExtensionsImpl#getVendorApiLevel()} 2
      */
+    @Override
     public void setSplitInfoCallback(Consumer<List<SplitInfo>> callback) {
         synchronized (mLock) {
-            mEmbeddingCallback = callback;
-            updateCallbackIfNecessary();
+            mSplitInfoCallback = callback;
+            updateSplitInfoCallbackIfNecessary();
         }
     }
 
@@ -396,7 +420,35 @@
     @Override
     public void clearSplitInfoCallback() {
         synchronized (mLock) {
-            mEmbeddingCallback = null;
+            mSplitInfoCallback = null;
+        }
+    }
+
+    /**
+     * Registers the callback for the {@link ActivityStack} state change.
+     *
+     * @param executor The executor to dispatch the callback.
+     * @param callback The callback for this {@link ActivityStack} state change.
+     */
+    @Override
+    public void registerActivityStackCallback(@NonNull @CallbackExecutor Executor executor,
+            @NonNull Consumer<List<ActivityStack>> callback) {
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(callback);
+
+        synchronized (mLock) {
+            mActivityStackCallbacks.put(callback, executor);
+            updateActivityStackCallbackIfNecessary();
+        }
+    }
+
+    /** @see #registerActivityStackCallback(Executor, Consumer) */
+    @Override
+    public void unregisterActivityStackCallback(@NonNull Consumer<List<ActivityStack>> callback) {
+        Objects.requireNonNull(callback);
+
+        synchronized (mLock) {
+            mActivityStackCallbacks.remove(callback);
         }
     }
 
@@ -408,13 +460,11 @@
         synchronized (mLock) {
             // Translate ActivityStack to TaskFragmentContainer.
             final List<TaskFragmentContainer> pendingFinishingContainers =
-                    activityStackTokens.stream()
-                    .map(token -> {
+                    activityStackTokens.stream().map(token -> {
                         synchronized (mLock) {
                             return getContainer(token);
                         }
-                    }).filter(Objects::nonNull)
-                    .toList();
+                    }).filter(Objects::nonNull).toList();
 
             if (pendingFinishingContainers.isEmpty()) {
                 return;
@@ -497,6 +547,68 @@
         }
     }
 
+    @Override
+    public void updateActivityStackAttributes(@NonNull IBinder activityStackToken,
+                                              @NonNull ActivityStackAttributes attributes) {
+        if (!Flags.activityEmbeddingOverlayPresentationFlag()) {
+            return;
+        }
+        Objects.requireNonNull(activityStackToken);
+        Objects.requireNonNull(attributes);
+
+        synchronized (mLock) {
+            final TaskFragmentContainer container = getContainer(activityStackToken);
+            if (container == null) {
+                Log.w(TAG, "Cannot find TaskFragmentContainer for token:" + activityStackToken);
+                return;
+            }
+            if (!container.isOverlay()) {
+                Log.w(TAG, "Updating non-overlay container has not supported yet!");
+                return;
+            }
+
+            final TransactionRecord transactionRecord = mTransactionManager.startNewTransaction();
+            final WindowContainerTransaction wct = transactionRecord.getTransaction();
+            mPresenter.applyActivityStackAttributes(wct, container, attributes);
+            transactionRecord.apply(false /* shouldApplyIndependently */);
+        }
+    }
+
+    @Override
+    @Nullable
+    public ParentContainerInfo getParentContainerInfo(@NonNull IBinder activityStackToken) {
+        if (!Flags.activityEmbeddingOverlayPresentationFlag()) {
+            return null;
+        }
+        Objects.requireNonNull(activityStackToken);
+        synchronized (mLock) {
+            final TaskFragmentContainer container = getContainer(activityStackToken);
+            if (container == null) {
+                return null;
+            }
+            final TaskContainer.TaskProperties properties = container.getTaskContainer()
+                    .getTaskProperties();
+            return mPresenter.createParentContainerInfoFromTaskProperties(properties);
+        }
+    }
+
+    @Override
+    @Nullable
+    public IBinder getActivityStackToken(@NonNull String tag) {
+        if (!Flags.activityEmbeddingOverlayPresentationFlag()) {
+            return null;
+        }
+        Objects.requireNonNull(tag);
+        synchronized (mLock) {
+            final TaskFragmentContainer taskFragmentContainer =
+                    getContainer(container -> tag.equals(container.getOverlayTag()));
+            if (taskFragmentContainer == null) {
+                return null;
+            }
+            return taskFragmentContainer.getTaskFragmentToken();
+        }
+    }
+
     /**
      * Called when the transaction is ready so that the organizer can update the TaskFragments based
      * on the changes in transaction.
@@ -565,8 +677,9 @@
     /**
      * Called when a TaskFragment is created and organized by this organizer.
      *
-     * @param wct   The {@link WindowContainerTransaction} to make any changes with if needed.
-     * @param taskFragmentInfo  Info of the TaskFragment that is created.
+     * @param wct              The {@link WindowContainerTransaction} to make any changes with if
+     *                         needed.
+     * @param taskFragmentInfo Info of the TaskFragment that is created.
      */
     // Suppress GuardedBy warning because lint ask to mark this method as
     // @GuardedBy(container.mController.mLock), which is mLock itself
@@ -574,7 +687,7 @@
     @VisibleForTesting
     @GuardedBy("mLock")
     void onTaskFragmentAppeared(@NonNull WindowContainerTransaction wct,
-            @NonNull TaskFragmentInfo taskFragmentInfo) {
+                                @NonNull TaskFragmentInfo taskFragmentInfo) {
         final TaskFragmentContainer container = getContainer(taskFragmentInfo.getFragmentToken());
         if (container == null) {
             return;
@@ -594,8 +707,9 @@
     /**
      * Called when the status of an organized TaskFragment is changed.
      *
-     * @param wct   The {@link WindowContainerTransaction} to make any changes with if needed.
-     * @param taskFragmentInfo  Info of the TaskFragment that is changed.
+     * @param wct              The {@link WindowContainerTransaction} to make any changes with if
+     *                         needed.
+     * @param taskFragmentInfo Info of the TaskFragment that is changed.
      */
     // Suppress GuardedBy warning because lint ask to mark this method as
     // @GuardedBy(container.mController.mLock), which is mLock itself
@@ -665,8 +779,9 @@
     /**
      * Called when an organized TaskFragment is removed.
      *
-     * @param wct   The {@link WindowContainerTransaction} to make any changes with if needed.
-     * @param taskFragmentInfo  Info of the TaskFragment that is removed.
+     * @param wct              The {@link WindowContainerTransaction} to make any changes with if
+     *                         needed.
+     * @param taskFragmentInfo Info of the TaskFragment that is removed.
      */
     @VisibleForTesting
     @GuardedBy("mLock")
@@ -686,14 +801,14 @@
      * Called when the parent leaf Task of organized TaskFragments is changed.
      * When the leaf Task is changed, the organizer may want to update the TaskFragments in one
      * transaction.
-     *
+     * <p>
      * For case like screen size change, it will trigger {@link #onTaskFragmentParentInfoChanged}
      * with new Task bounds, but may not trigger {@link #onTaskFragmentInfoChanged} because there
      * can be an override bounds.
      *
-     * @param wct   The {@link WindowContainerTransaction} to make any changes with if needed.
-     * @param taskId    Id of the parent Task that is changed.
-     * @param parentInfo  {@link TaskFragmentParentInfo} of the parent Task.
+     * @param wct        The {@link WindowContainerTransaction} to make any changes with if needed.
+     * @param taskId     Id of the parent Task that is changed.
+     * @param parentInfo {@link TaskFragmentParentInfo} of the parent Task.
      */
     @VisibleForTesting
     @GuardedBy("mLock")
@@ -704,14 +819,20 @@
             Log.e(TAG, "onTaskFragmentParentInfoChanged on empty Task id=" + taskId);
             return;
         }
+        // Checks if container should be updated before apply new parentInfo.
+        final boolean shouldUpdateContainer = taskContainer.shouldUpdateContainer(parentInfo);
         taskContainer.updateTaskFragmentParentInfo(parentInfo);
         if (!taskContainer.isVisible()) {
             // Don't update containers if the task is not visible. We only update containers when
             // parentInfo#isVisibleRequested is true.
             return;
         }
-        if (isInPictureInPicture(parentInfo.getConfiguration())) {
-            // No need to update presentation in PIP until the Task exit PIP.
+
+        // If the last direct activity of the host task is dismissed and the overlay container is
+        // the only taskFragment, the overlay container should also be dismissed.
+        dismissOverlayContainerIfNeeded(wct, taskContainer);
+
+        if (!shouldUpdateContainer) {
             return;
         }
         updateContainersInTask(wct, taskContainer);
@@ -746,20 +867,20 @@
      * original Task. In this case, we need to notify the organizer so that it can check if the
      * Activity matches any split rule.
      *
-     * @param wct   The {@link WindowContainerTransaction} to make any changes with if needed.
-     * @param taskId            The Task that the activity is reparented to.
-     * @param activityIntent    The intent that the activity is original launched with.
-     * @param activityToken     If the activity belongs to the same process as the organizer, this
-     *                          will be the actual activity token; if the activity belongs to a
-     *                          different process, the server will generate a temporary token that
-     *                          the organizer can use to reparent the activity through
-     *                          {@link WindowContainerTransaction} if needed.
+     * @param wct            The {@link WindowContainerTransaction} to make any changes with if
+     *                       needed.
+     * @param taskId         The Task that the activity is reparented to.
+     * @param activityIntent The intent that the activity is original launched with.
+     * @param activityToken  If the activity belongs to the same process as the organizer, this
+     *                       will be the actual activity token; if the activity belongs to a
+     *                       different process, the server will generate a temporary token that
+     *                       the organizer can use to reparent the activity through
+     *                       {@link WindowContainerTransaction} if needed.
      */
     @VisibleForTesting
     @GuardedBy("mLock")
     void onActivityReparentedToTask(@NonNull WindowContainerTransaction wct,
-            int taskId, @NonNull Intent activityIntent,
-            @NonNull IBinder activityToken) {
+            int taskId, @NonNull Intent activityIntent, @NonNull IBinder activityToken) {
         // If the activity belongs to the current app process, we treat it as a new activity
         // launch.
         final Activity activity = getActivity(activityToken);
@@ -807,14 +928,15 @@
      * Called when the {@link WindowContainerTransaction} created with
      * {@link WindowContainerTransaction#setErrorCallbackToken(IBinder)} failed on the server side.
      *
-     * @param wct   The {@link WindowContainerTransaction} to make any changes with if needed.
-     * @param errorCallbackToken    token set in
-     *                             {@link WindowContainerTransaction#setErrorCallbackToken(IBinder)}
-     * @param taskFragmentInfo  The {@link TaskFragmentInfo}. This could be {@code null} if no
-     *                          TaskFragment created.
-     * @param opType            The {@link WindowContainerTransaction.HierarchyOp} of the failed
-     *                          transaction operation.
-     * @param exception             exception from the server side.
+     * @param wct                The {@link WindowContainerTransaction} to make any changes with if
+     *                           needed.
+     * @param errorCallbackToken token set in
+     *                           {@link WindowContainerTransaction#setErrorCallbackToken(IBinder)}
+     * @param taskFragmentInfo   The {@link TaskFragmentInfo}. This could be {@code null} if no
+     *                           TaskFragment created.
+     * @param opType             The {@link WindowContainerTransaction.HierarchyOp} of the failed
+     *                           transaction operation.
+     * @param exception          exception from the server side.
      */
     // Suppress GuardedBy warning because lint ask to mark this method as
     // @GuardedBy(container.mController.mLock), which is mLock itself
@@ -854,7 +976,9 @@
         }
     }
 
-    /** Called on receiving {@link #onTaskFragmentVanished} for cleanup. */
+    /**
+     * Called on receiving {@link #onTaskFragmentVanished} for cleanup.
+     */
     @GuardedBy("mLock")
     private void cleanupTaskFragment(@NonNull IBinder taskFragmentToken) {
         for (int i = mTaskContainers.size() - 1; i >= 0; i--) {
@@ -881,11 +1005,12 @@
     /**
      * Checks if the new added activity should be routed to a particular container. It can create a
      * new container for the activity and a new split container if necessary.
-     * @param activity      the activity that is newly added to the Task.
-     * @param isOnReparent  whether the activity is reparented to the Task instead of new launched.
-     *                      We only support to split as primary for reparented activity for now.
+     *
+     * @param activity     the activity that is newly added to the Task.
+     * @param isOnReparent whether the activity is reparented to the Task instead of new launched.
+     *                     We only support to split as primary for reparented activity for now.
      * @return {@code true} if the activity has been handled, such as placed in a TaskFragment, or
-     *         in a state that the caller shouldn't handle.
+     * in a state that the caller shouldn't handle.
      */
     @VisibleForTesting
     @GuardedBy("mLock")
@@ -918,7 +1043,7 @@
         final TaskContainer taskContainer = container != null ? container.getTaskContainer() : null;
         if (!isOnReparent && taskContainer != null
                 && taskContainer.getTopNonFinishingTaskFragmentContainer(false /* includePin */)
-                        != container) {
+                != container) {
             // Do not resolve if the launched activity is not the top-most container (excludes
             // the pinned and overlay container) in the Task.
             return true;
@@ -943,6 +1068,7 @@
     /**
      * Resolves the activity to a {@link TaskFragmentContainer} according to the Split-rules.
      */
+    @GuardedBy("mLock")
     boolean resolveActivityToContainerByRule(@NonNull WindowContainerTransaction wct,
             @NonNull Activity activity, @Nullable TaskFragmentContainer container,
             boolean isOnReparent) {
@@ -1027,7 +1153,7 @@
     @GuardedBy("mLock")
     @VisibleForTesting
     void placeActivityInTopContainer(@NonNull WindowContainerTransaction wct,
-            @NonNull Activity activity) {
+                                     @NonNull Activity activity) {
         if (getContainerWithActivity(activity) != null) {
             // The activity has already been put in a TaskFragment. This is likely to be done by
             // the server when the activity is started.
@@ -1077,7 +1203,7 @@
      */
     @GuardedBy("mLock")
     private void expandActivity(@NonNull WindowContainerTransaction wct,
-            @NonNull Activity activity) {
+                                @NonNull Activity activity) {
         final TaskFragmentContainer container = getContainerWithActivity(activity);
         if (shouldContainerBeExpanded(container)) {
             // Make sure that the existing container is expanded.
@@ -1089,7 +1215,9 @@
         }
     }
 
-    /** Whether the given new launched activity is in a split with a rule matched. */
+    /**
+     * Whether the given new launched activity is in a split with a rule matched.
+     */
     // Suppress GuardedBy warning because lint asks to mark this method as
     // @GuardedBy(mPresenter.mController.mLock), which is mLock itself
     @SuppressWarnings("GuardedBy")
@@ -1147,7 +1275,9 @@
         return getSplitRule(primaryActivity, launchedActivity) != null;
     }
 
-    /** Finds the activity below the given activity. */
+    /**
+     * Finds the activity below the given activity.
+     */
     @VisibleForTesting
     @Nullable
     @GuardedBy("mLock")
@@ -1198,8 +1328,8 @@
                 getActivitiesMinDimensionsPair(primaryActivity, secondaryActivity));
         if (splitContainer != null && primaryContainer == splitContainer.getPrimaryContainer()
                 && canReuseContainer(splitRule, splitContainer.getSplitRule(),
-                        taskProperties.getTaskMetrics(),
-                        calculatedSplitAttributes, splitContainer.getCurrentSplitAttributes())) {
+                taskProperties.getTaskMetrics(),
+                calculatedSplitAttributes, splitContainer.getCurrentSplitAttributes())) {
             // Can launch in the existing secondary container if the rules share the same
             // presentation.
             final TaskFragmentContainer secondaryContainer = splitContainer.getSecondaryContainer();
@@ -1333,7 +1463,7 @@
      *                          prioritize to split the new activity with it if it is not
      *                          {@code null}.
      * @return the {@link TaskFragmentContainer} to start the new activity in. {@code null} if there
-     *         is no embedding rule matched.
+     * is no embedding rule matched.
      */
     @VisibleForTesting
     @Nullable
@@ -1478,7 +1608,7 @@
         final Rect taskBounds = taskContainer.getTaskProperties().getTaskMetrics().getBounds();
         final Rect sanitizedBounds = sanitizeBounds(bounds, intent, taskBounds);
         final int windowingMode = taskContainer
-                .getWindowingModeForSplitTaskFragment(sanitizedBounds);
+                .getWindowingModeForTaskFragment(sanitizedBounds);
         mPresenter.createTaskFragment(wct, taskFragmentToken, activityInTask.getActivityToken(),
                 sanitizedBounds, windowingMode);
         mPresenter.updateAnimationParams(wct, taskFragmentToken,
@@ -1495,7 +1625,7 @@
      */
     @NonNull
     private static Rect sanitizeBounds(@NonNull Rect bounds, @NonNull Intent intent,
-                                       @NonNull Rect taskBounds) {
+            @NonNull Rect taskBounds) {
         if (bounds.isEmpty()) {
             // Don't need to check if the bounds follows the task bounds.
             return bounds;
@@ -1534,11 +1664,11 @@
                 getActivityIntentMinDimensionsPair(primaryActivity, intent));
         if (splitContainer != null && existingContainer == splitContainer.getPrimaryContainer()
                 && (canReuseContainer(splitRule, splitContainer.getSplitRule(), taskWindowMetrics,
-                        calculatedSplitAttributes, splitContainer.getCurrentSplitAttributes())
+                calculatedSplitAttributes, splitContainer.getCurrentSplitAttributes())
                 // TODO(b/231845476) we should always respect clearTop.
                 || !respectClearTop)
                 && mPresenter.expandSplitContainerIfNeeded(wct, splitContainer, primaryActivity,
-                        null /* secondaryActivity */, intent) != RESULT_EXPAND_FAILED_NO_TF_INFO) {
+                null /* secondaryActivity */, intent) != RESULT_EXPAND_FAILED_NO_TF_INFO) {
             // Can launch in the existing secondary container if the rules share the same
             // presentation.
             return splitContainer.getSecondaryContainer();
@@ -1563,29 +1693,15 @@
     TaskFragmentContainer getContainerWithActivity(@NonNull IBinder activityToken) {
         // Check pending appeared activity first because there can be a delay for the server
         // update.
-        for (int i = mTaskContainers.size() - 1; i >= 0; i--) {
-            final List<TaskFragmentContainer> containers = mTaskContainers.valueAt(i)
-                    .getTaskFragmentContainers();
-            for (int j = containers.size() - 1; j >= 0; j--) {
-                final TaskFragmentContainer container = containers.get(j);
-                if (container.hasPendingAppearedActivity(activityToken)) {
-                    return container;
-                }
-            }
+        TaskFragmentContainer taskFragmentContainer =
+                getContainer(container -> container.hasPendingAppearedActivity(activityToken));
+        if (taskFragmentContainer != null) {
+            return taskFragmentContainer;
         }
 
+
         // Check appeared activity if there is no such pending appeared activity.
-        for (int i = mTaskContainers.size() - 1; i >= 0; i--) {
-            final List<TaskFragmentContainer> containers = mTaskContainers.valueAt(i)
-                    .getTaskFragmentContainers();
-            for (int j = containers.size() - 1; j >= 0; j--) {
-                final TaskFragmentContainer container = containers.get(j);
-                if (container.hasAppearedActivity(activityToken)) {
-                    return container;
-                }
-            }
-        }
-        return null;
+        return getContainer(container -> container.hasAppearedActivity(activityToken));
     }
 
     @GuardedBy("mLock")
@@ -1611,8 +1727,8 @@
 
     @GuardedBy("mLock")
     TaskFragmentContainer newContainer(@NonNull Intent pendingAppearedIntent,
-                                       @NonNull Activity activityInTask, int taskId,
-                                       @NonNull TaskFragmentContainer pairedPrimaryContainer) {
+            @NonNull Activity activityInTask, int taskId,
+            @NonNull TaskFragmentContainer pairedPrimaryContainer) {
         return newContainer(null /* pendingAppearedActivity */, pendingAppearedIntent,
                 activityInTask, taskId, pairedPrimaryContainer, null /* tag */,
                 null /* launchOptions */);
@@ -1622,18 +1738,18 @@
      * Creates and registers a new organized container with an optional activity that will be
      * re-parented to it in a WCT.
      *
-     * @param pendingAppearedActivity   the activity that will be reparented to the TaskFragment.
-     * @param pendingAppearedIntent     the Intent that will be started in the TaskFragment.
-     * @param activityInTask            activity in the same Task so that we can get the Task bounds
-     *                                  if needed.
-     * @param taskId                    parent Task of the new TaskFragment.
-     * @param pairedPrimaryContainer    the paired primary {@link TaskFragmentContainer}. When it is
-     *                                  set, the new container will be added right above it.
-     * @param overlayTag                The tag for the new created overlay container. It must be
-     *                                  needed if {@code isOverlay} is {@code true}. Otherwise,
-     *                                  it should be {@code null}.
-     * @param launchOptions             The launch options bundle to create a container. Must be
-     *                                  specified for overlay container.
+     * @param pendingAppearedActivity the activity that will be reparented to the TaskFragment.
+     * @param pendingAppearedIntent   the Intent that will be started in the TaskFragment.
+     * @param activityInTask          activity in the same Task so that we can get the Task bounds
+     *                                if needed.
+     * @param taskId                  parent Task of the new TaskFragment.
+     * @param pairedPrimaryContainer  the paired primary {@link TaskFragmentContainer}. When it is
+     *                                set, the new container will be added right above it.
+     * @param overlayTag              The tag for the new created overlay container. It must be
+     *                                needed if {@code isOverlay} is {@code true}. Otherwise,
+     *                                it should be {@code null}.
+     * @param launchOptions           The launch options bundle to create a container. Must be
+     *                                specified for overlay container.
      */
     @GuardedBy("mLock")
     TaskFragmentContainer newContainer(@Nullable Activity pendingAppearedActivity,
@@ -1674,7 +1790,9 @@
         primaryContainer.getTaskContainer().addSplitContainer(splitContainer);
     }
 
-    /** Cleanups all the dependencies when the TaskFragment is entering PIP. */
+    /**
+     * Cleanups all the dependencies when the TaskFragment is entering PIP.
+     */
     @GuardedBy("mLock")
     private void cleanupForEnterPip(@NonNull WindowContainerTransaction wct,
             @NonNull TaskFragmentContainer container) {
@@ -1833,16 +1951,42 @@
     @SuppressWarnings("GuardedBy")
     @GuardedBy("mLock")
     void updateOverlayContainer(@NonNull WindowContainerTransaction wct,
-                                @NonNull TaskFragmentContainer container) {
+            @NonNull TaskFragmentContainer container) {
         final TaskContainer taskContainer = container.getTaskContainer();
+
+        if (dismissOverlayContainerIfNeeded(wct, taskContainer)) {
+            return;
+        }
+
+        if (mActivityStackAttributesCalculator != null) {
+            final ActivityStackAttributesCalculatorParams params =
+                    new ActivityStackAttributesCalculatorParams(
+                            mPresenter.createParentContainerInfoFromTaskProperties(
+                                    taskContainer.getTaskProperties()),
+                            container.getOverlayTag(),
+                            container.getLaunchOptions());
+            final ActivityStackAttributes attributes = mActivityStackAttributesCalculator
+                    .apply(params);
+            mPresenter.applyActivityStackAttributes(wct, container, attributes);
+        }
+    }
+
+    /** Dismisses the overlay container in the {@code taskContainer} if needed. */
+    @GuardedBy("mLock")
+    private boolean dismissOverlayContainerIfNeeded(@NonNull WindowContainerTransaction wct,
+            @NonNull TaskContainer taskContainer) {
+        final TaskFragmentContainer overlayContainer = taskContainer.getOverlayContainer();
+        if (overlayContainer == null) {
+            return false;
+        }
         // Dismiss the overlay container if it's the only container in the task and there's no
         // direct activity in the parent task.
         if (taskContainer.getTaskFragmentContainers().size() == 1
                 && !taskContainer.hasDirectActivity()) {
-            container.finish(false /* shouldFinishDependent */, mPresenter, wct, this);
+            mPresenter.cleanupContainer(wct, overlayContainer, false /* shouldFinishDependant */);
+            return true;
         }
-
-        // TODO(b/295805054): Add the logic to update overlay container
+        return false;
     }
 
     /**
@@ -1851,11 +1995,10 @@
      * are {@code null}, the {@link SplitAttributes} will be calculated with
      * {@link SplitPresenter#computeSplitAttributes}.
      *
-     * @param splitContainer The {@link SplitContainer} to update
+     * @param splitContainer  The {@link SplitContainer} to update
      * @param splitAttributes Update with this {@code splitAttributes} if it is not {@code null}.
      *                        Otherwise, use the value calculated by
      *                        {@link SplitPresenter#computeSplitAttributes}
-     *
      * @return {@code true} if the update succeed. Otherwise, returns {@code false}.
      */
     @VisibleForTesting
@@ -1890,7 +2033,9 @@
         return true;
     }
 
-    /** Whether the given split is the topmost split in the Task. */
+    /**
+     * Whether the given split is the topmost split in the Task.
+     */
     private boolean isTopMostSplit(@NonNull SplitContainer splitContainer) {
         final List<SplitContainer> splitContainers = splitContainer.getPrimaryContainer()
                 .getTaskContainer().getSplitContainers();
@@ -1997,7 +2142,9 @@
         return true;
     }
 
-    /** Whether or not to allow activity in this container to launch placeholder. */
+    /**
+     * Whether or not to allow activity in this container to launch placeholder.
+     */
     @GuardedBy("mLock")
     private boolean allowLaunchPlaceholder(@NonNull TaskFragmentContainer container) {
         final TaskFragmentContainer topContainer = container.getTaskContainer()
@@ -2031,8 +2178,9 @@
     /**
      * Gets the activity options for starting the placeholder activity. In case the placeholder is
      * launched when the Task is in the background, we don't want to bring the Task to the front.
-     * @param primaryActivity   the primary activity to launch the placeholder from.
-     * @param isOnCreated       whether this happens during the primary activity onCreated.
+     *
+     * @param primaryActivity the primary activity to launch the placeholder from.
+     * @param isOnCreated     whether this happens during the primary activity onCreated.
      */
     @VisibleForTesting
     @GuardedBy("mLock")
@@ -2104,7 +2252,16 @@
     @VisibleForTesting
     @GuardedBy("mLock")
     void updateCallbackIfNecessary() {
-        if (mEmbeddingCallback == null || !readyToReportToClient()) {
+        updateSplitInfoCallbackIfNecessary();
+        updateActivityStackCallbackIfNecessary();
+    }
+
+    /**
+     * Notifies callbacks about changes to split states if necessary.
+     */
+    @GuardedBy("mLock")
+    private void updateSplitInfoCallbackIfNecessary() {
+        if (!readyToReportToClient() || mSplitInfoCallback == null) {
             return;
         }
         final List<SplitInfo> currentSplitStates = getActiveSplitStatesIfStable();
@@ -2113,7 +2270,32 @@
         }
         mLastReportedSplitStates.clear();
         mLastReportedSplitStates.addAll(currentSplitStates);
-        mEmbeddingCallback.accept(currentSplitStates);
+        mSplitInfoCallback.accept(currentSplitStates);
+    }
+
+    /**
+     * Notifies callbacks about changes to {@link ActivityStack} states if necessary.
+     */
+    @GuardedBy("mLock")
+    private void updateActivityStackCallbackIfNecessary() {
+        if (!readyToReportToClient() || mActivityStackCallbacks.isEmpty()) {
+            return;
+        }
+        final List<ActivityStack> currentActivityStacks = getActivityStacksIfStable();
+        if (currentActivityStacks == null
+                || mLastReportedActivityStacks.equals(currentActivityStacks)) {
+            return;
+        }
+        mLastReportedActivityStacks.clear();
+        mLastReportedActivityStacks.addAll(currentActivityStacks);
+        // Copy the map in case a callback is removed during the for-loop.
+        final ArrayMap<Consumer<List<ActivityStack>>, Executor> callbacks =
+                new ArrayMap<>(mActivityStackCallbacks);
+        for (int i = callbacks.size() - 1; i >= 0; --i) {
+            final Executor executor = callbacks.valueAt(i);
+            final Consumer<List<ActivityStack>> callback = callbacks.keyAt(i);
+            executor.execute(() -> callback.accept(currentActivityStacks));
+        }
     }
 
     /**
@@ -2138,6 +2320,27 @@
     }
 
     /**
+     * Returns a list of currently active {@link ActivityStack activityStacks}.
+     *
+     * @return a list of {@link ActivityStack activityStacks} if all the containers are in
+     * a stable state, or {@code null} otherwise.
+     */
+    @GuardedBy("mLock")
+    @Nullable
+    private List<ActivityStack> getActivityStacksIfStable() {
+        final List<ActivityStack> activityStacks = new ArrayList<>();
+        for (int i = mTaskContainers.size() - 1; i >= 0; i--) {
+            final List<ActivityStack> taskActivityStacks =
+                    mTaskContainers.valueAt(i).getActivityStacksIfStable();
+            if (taskActivityStacks == null) {
+                return null;
+            }
+            activityStacks.addAll(taskActivityStacks);
+        }
+        return activityStacks;
+    }
+
+    /**
      * Whether we can now report the split states to the client.
      */
     @GuardedBy("mLock")
@@ -2207,11 +2410,18 @@
     @Nullable
     @GuardedBy("mLock")
     TaskFragmentContainer getContainer(@NonNull IBinder fragmentToken) {
+        return getContainer(container -> fragmentToken.equals(container.getTaskFragmentToken()));
+    }
+
+    @Nullable
+    @GuardedBy("mLock")
+    TaskFragmentContainer getContainer(@NonNull Predicate<TaskFragmentContainer> predicate) {
         for (int i = mTaskContainers.size() - 1; i >= 0; i--) {
             final List<TaskFragmentContainer> containers = mTaskContainers.valueAt(i)
                     .getTaskFragmentContainers();
-            for (TaskFragmentContainer container : containers) {
-                if (container.getTaskFragmentToken().equals(fragmentToken)) {
+            for (int j = containers.size() - 1; j >= 0; j--) {
+                final TaskFragmentContainer container = containers.get(j);
+                if (predicate.test(container)) {
                     return container;
                 }
             }
@@ -2304,6 +2514,7 @@
      * container. There is a case when primary containers for placeholders should be retained
      * despite the rule configuration to finish primary with secondary - if they are marked as
      * 'sticky' and the placeholder was finished when fully overlapping the primary container.
+     *
      * @return {@code true} if the associated container should be retained (and not be finished).
      */
     // Suppress GuardedBy warning because lint ask to mark this method as
@@ -2388,8 +2599,9 @@
         // If the requested bounds of OverlayCreateParams are smaller than minimum dimensions
         // specified by Intent, expand the overlay container to fill the parent task instead.
         final ActivityStackAttributesCalculatorParams params =
-                new ActivityStackAttributesCalculatorParams(mPresenter.toParentContainerInfo(
-                        mPresenter.getTaskProperties(launchActivity)), overlayTag, options);
+                new ActivityStackAttributesCalculatorParams(
+                        mPresenter.createParentContainerInfoFromTaskProperties(
+                                mPresenter.getTaskProperties(launchActivity)), overlayTag, options);
         // Fallback to expand the bounds if there's no activityStackAttributes calculator.
         final Rect relativeBounds = mActivityStackAttributesCalculator != null
                 ? new Rect(mActivityStackAttributesCalculator.apply(params).getRelativeBounds())
@@ -2407,26 +2619,31 @@
                         && taskId == overlayContainer.getTaskId()) {
                     // If there's an overlay container with different tag shown in the same
                     // task, dismiss the existing overlay container.
-                    overlayContainer.finish(false /* shouldFinishDependant */, mPresenter,
-                            wct, SplitController.this);
+                    mPresenter.cleanupContainer(wct, overlayContainer,
+                            false /* shouldFinishDependant */);
                 }
                 if (overlayTag.equals(overlayContainer.getOverlayTag())
                         && taskId != overlayContainer.getTaskId()) {
                     // If there's an overlay container with same tag in a different task,
                     // dismiss the overlay container since the tag must be unique per process.
-                    overlayContainer.finish(false /* shouldFinishDependant */, mPresenter,
-                            wct, SplitController.this);
+                    mPresenter.cleanupContainer(wct, overlayContainer,
+                            false /* shouldFinishDependant */);
                 }
                 if (overlayTag.equals(overlayContainer.getOverlayTag())
                         && taskId == overlayContainer.getTaskId()) {
                     // If there's an overlay container with the same tag and task ID, we treat
                     // the OverlayCreateParams as the update to the container.
-                    final Rect taskBounds = overlayContainer.getTaskContainer().getTaskProperties()
-                            .getTaskMetrics().getBounds();
                     final IBinder overlayToken = overlayContainer.getTaskFragmentToken();
+                    final TaskContainer taskContainer = overlayContainer.getTaskContainer();
+                    final Rect taskBounds = taskContainer.getTaskProperties().getTaskMetrics()
+                            .getBounds();
                     final Rect sanitizedBounds = sanitizeBounds(relativeBounds, intent, taskBounds);
+
                     mPresenter.resizeTaskFragment(wct, overlayToken, sanitizedBounds);
-                    mPresenter.setTaskFragmentIsolatedNavigation(wct, overlayToken,
+                    final int windowingMode = taskContainer
+                            .getWindowingModeForTaskFragment(sanitizedBounds);
+                    mPresenter.updateWindowingMode(wct, overlayToken, windowingMode);
+                    mPresenter.setTaskFragmentIsolatedNavigation(wct, overlayContainer,
                             !sanitizedBounds.isEmpty());
                     // We can just return the updated overlay container and don't need to
                     // check other condition since we only have one OverlayCreateParams, and
@@ -2539,7 +2756,9 @@
         }
     }
 
-    /** Executor that posts on the main application thread. */
+    /**
+     * Executor that posts on the main application thread.
+     */
     private static class MainThreadExecutor implements Executor {
         private final Handler mHandler = new Handler(Looper.getMainLooper());
 
@@ -2687,7 +2906,9 @@
                 && calculatedSplitAttributes.equals(containerSplitAttributes);
     }
 
-    /** Whether the two rules have the same presentation. */
+    /**
+     * Whether the two rules have the same presentation.
+     */
     @VisibleForTesting
     static boolean areRulesSamePresentation(@NonNull SplitPairRule rule1,
             @NonNull SplitPairRule rule2, @NonNull WindowMetrics parentWindowMetrics) {
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
index acfd8e4..8b7fd10 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
@@ -16,8 +16,12 @@
 
 package androidx.window.extensions.embedding;
 
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
 import static android.content.pm.PackageManager.MATCH_ALL;
 
+import static androidx.window.extensions.embedding.WindowAttributes.DIM_AREA_ON_TASK;
+
 import android.app.Activity;
 import android.app.ActivityThread;
 import android.app.WindowConfiguration;
@@ -54,6 +58,7 @@
 import androidx.window.extensions.layout.WindowLayoutInfo;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.window.flags.Flags;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -187,7 +192,7 @@
         final Rect secondaryRelBounds = getRelBoundsForPosition(POSITION_END, taskProperties,
                 splitAttributes);
         final int windowingMode = mController.getTaskContainer(taskId)
-                .getWindowingModeForSplitTaskFragment(secondaryRelBounds);
+                .getWindowingModeForTaskFragment(secondaryRelBounds);
         createTaskFragment(wct, secondaryContainer.getTaskFragmentToken(),
                 primaryActivity.getActivityToken(), secondaryRelBounds, windowingMode);
         updateAnimationParams(wct, secondaryContainer.getTaskFragmentToken(), splitAttributes);
@@ -259,7 +264,7 @@
         if (container == null || container == containerToAvoid) {
             container = mController.newContainer(activity, taskId);
             final int windowingMode = mController.getTaskContainer(taskId)
-                    .getWindowingModeForSplitTaskFragment(relBounds);
+                    .getWindowingModeForTaskFragment(relBounds);
             final IBinder reparentActivityToken = activity.getActivityToken();
             createTaskFragment(wct, container.getTaskFragmentToken(), reparentActivityToken,
                     relBounds, windowingMode, reparentActivityToken);
@@ -268,7 +273,7 @@
         } else {
             resizeTaskFragmentIfRegistered(wct, container, relBounds);
             final int windowingMode = mController.getTaskContainer(taskId)
-                    .getWindowingModeForSplitTaskFragment(relBounds);
+                    .getWindowingModeForTaskFragment(relBounds);
             updateTaskFragmentWindowingModeIfRegistered(wct, container, windowingMode);
         }
         updateAnimationParams(wct, container.getTaskFragmentToken(), splitAttributes);
@@ -310,7 +315,7 @@
                 // Pass in the primary container to make sure it is added right above the primary.
                 primaryContainer);
         final TaskContainer taskContainer = mController.getTaskContainer(taskId);
-        final int windowingMode = taskContainer.getWindowingModeForSplitTaskFragment(
+        final int windowingMode = taskContainer.getWindowingModeForTaskFragment(
                 primaryRelBounds);
         mController.registerSplit(wct, primaryContainer, launchingActivity, secondaryContainer,
                 rule, splitAttributes);
@@ -347,6 +352,7 @@
                 && secondaryContainer.areLastRequestedBoundsEqual(null /* bounds */)
                 && !secondaryRelBounds.isEmpty();
 
+        // TODO(b/243518738): remove usages of XXXIfRegistered.
         // If the task fragments are not registered yet, the positions will be updated after they
         // are created again.
         resizeTaskFragmentIfRegistered(wct, primaryContainer, primaryRelBounds);
@@ -357,7 +363,7 @@
             // When placeholder is shown in split, we should keep the focus on the primary.
             wct.requestFocusOnTaskFragment(primaryContainer.getTaskFragmentToken());
         }
-        final int windowingMode = taskContainer.getWindowingModeForSplitTaskFragment(
+        final int windowingMode = taskContainer.getWindowingModeForTaskFragment(
                 primaryRelBounds);
         updateTaskFragmentWindowingModeIfRegistered(wct, primaryContainer, windowingMode);
         updateTaskFragmentWindowingModeIfRegistered(wct, secondaryContainer, windowingMode);
@@ -381,6 +387,13 @@
         setCompanionTaskFragment(wct, primaryContainer.getTaskFragmentToken(),
                 secondaryContainer.getTaskFragmentToken(), splitRule, isStacked);
 
+        // Sets the dim area when the two TaskFragments are adjacent.
+        final boolean dimOnTask = !isStacked
+                && splitAttributes.getWindowAttributes().getDimArea() == DIM_AREA_ON_TASK
+                && Flags.fullscreenDimFlag();
+        setTaskFragmentDimOnTask(wct, primaryContainer.getTaskFragmentToken(), dimOnTask);
+        setTaskFragmentDimOnTask(wct, secondaryContainer.getTaskFragmentToken(), dimOnTask);
+
         // Setting isolated navigation and clear non-sticky pinned container if needed.
         final SplitPinRule splitPinRule =
                 splitRule instanceof SplitPinRule ? (SplitPinRule) splitRule : null;
@@ -398,13 +411,13 @@
      * Sets whether to enable isolated navigation for this {@link TaskFragmentContainer}
      */
     void setTaskFragmentIsolatedNavigation(@NonNull WindowContainerTransaction wct,
-                                           @NonNull TaskFragmentContainer taskFragmentContainer,
+                                           @NonNull TaskFragmentContainer container,
                                            boolean isolatedNavigationEnabled) {
-        if (taskFragmentContainer.isIsolatedNavigationEnabled() == isolatedNavigationEnabled) {
+        if (container.isIsolatedNavigationEnabled() == isolatedNavigationEnabled) {
             return;
         }
-        taskFragmentContainer.setIsolatedNavigationEnabled(isolatedNavigationEnabled);
-        setTaskFragmentIsolatedNavigation(wct, taskFragmentContainer.getTaskFragmentToken(),
+        container.setIsolatedNavigationEnabled(isolatedNavigationEnabled);
+        setTaskFragmentIsolatedNavigation(wct, container.getTaskFragmentToken(),
                 isolatedNavigationEnabled);
     }
 
@@ -566,6 +579,32 @@
         super.setCompanionTaskFragment(wct, primary, secondary);
     }
 
+    void applyActivityStackAttributes(@NonNull WindowContainerTransaction wct,
+            @NonNull TaskFragmentContainer container, @NonNull ActivityStackAttributes attributes) {
+        final Rect bounds = attributes.getRelativeBounds();
+
+        resizeTaskFragment(wct, container.getTaskFragmentToken(), bounds);
+        updateWindowingMode(wct, container.getTaskFragmentToken(),
+                bounds.isEmpty() ? WINDOWING_MODE_FULLSCREEN : WINDOWING_MODE_MULTI_WINDOW);
+    }
+
+    @Override
+    void setTaskFragmentDimOnTask(@NonNull WindowContainerTransaction wct,
+            @NonNull IBinder fragmentToken, boolean dimOnTask) {
+        final TaskFragmentContainer container = mController.getContainer(fragmentToken);
+        if (container == null) {
+            throw new IllegalStateException("setTaskFragmentDimOnTask on TaskFragment that is"
+                    + " not registered with controller.");
+        }
+
+        if (container.isLastDimOnTask() == dimOnTask) {
+            return;
+        }
+
+        container.setLastDimOnTask(dimOnTask);
+        super.setTaskFragmentDimOnTask(wct, fragmentToken, dimOnTask);
+    }
+
     /**
      * Expands the split container if the current split bounds are smaller than the Activity or
      * Intent that is added to the container.
@@ -854,8 +893,7 @@
         return new SplitAttributes.Builder()
                 .setSplitType(splitTypeToUpdate)
                 .setLayoutDirection(splitAttributes.getLayoutDirection())
-                // TODO(b/263047900): Update extensions API.
-                // .setAnimationBackgroundColor(splitAttributes.getAnimationBackgroundColor())
+                .setAnimationBackground(splitAttributes.getAnimationBackground())
                 .build();
     }
 
@@ -1086,7 +1124,8 @@
     }
 
     @NonNull
-    ParentContainerInfo toParentContainerInfo(@NonNull TaskProperties taskProperties) {
+    ParentContainerInfo createParentContainerInfoFromTaskProperties(
+            @NonNull TaskProperties taskProperties) {
         final Configuration configuration = taskProperties.getConfiguration();
         final WindowLayoutInfo windowLayoutInfo = mWindowLayoutComponent
                 .getCurrentWindowLayoutInfo(taskProperties.getDisplayId(),
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
index 028e75f..71195b6 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
@@ -138,6 +138,21 @@
     }
 
     /**
+     * Returns {@code true} if the container should be updated with {@code info}.
+     */
+    boolean shouldUpdateContainer(@NonNull TaskFragmentParentInfo info) {
+        final Configuration configuration = info.getConfiguration();
+
+        return info.isVisible()
+                // No need to update presentation in PIP until the Task exit PIP.
+                && !isInPictureInPicture(configuration)
+                // If the task properties equals regardless of starting position, don't need to
+                // update the container.
+                && (mConfiguration.diffPublicOnly(configuration) != 0
+                || mDisplayId != info.getDisplayId());
+    }
+
+    /**
      * Returns the windowing mode for the TaskFragments below this Task, which should be split with
      * other TaskFragments.
      *
@@ -145,7 +160,7 @@
      *                              the pair of TaskFragments are stacked due to the limited space.
      */
     @WindowingMode
-    int getWindowingModeForSplitTaskFragment(@Nullable Rect taskFragmentBounds) {
+    int getWindowingModeForTaskFragment(@Nullable Rect taskFragmentBounds) {
         // Only set to multi-windowing mode if the pair are showing side-by-side. Otherwise, it
         // will be set to UNDEFINED which will then inherit the Task windowing mode.
         if (taskFragmentBounds == null || taskFragmentBounds.isEmpty() || isInPictureInPicture()) {
@@ -161,7 +176,11 @@
     }
 
     boolean isInPictureInPicture() {
-        return getWindowingMode() == WINDOWING_MODE_PINNED;
+        return isInPictureInPicture(mConfiguration);
+    }
+
+    private static boolean isInPictureInPicture(@NonNull Configuration configuration) {
+        return configuration.windowConfiguration.getWindowingMode() == WINDOWING_MODE_PINNED;
     }
 
     boolean isInMultiWindow() {
@@ -443,6 +462,26 @@
         return splitStates;
     }
 
+    // TODO(b/317358445): Makes ActivityStack and SplitInfo callback more stable.
+    /**
+     * Returns a list of currently active {@link ActivityStack activityStacks}.
+     *
+     * @return a list of {@link ActivityStack activityStacks} if all the containers are in
+     * a stable state, or {@code null} otherwise.
+     */
+    @Nullable
+    List<ActivityStack> getActivityStacksIfStable() {
+        final List<ActivityStack> activityStacks = new ArrayList<>();
+        for (TaskFragmentContainer container : mContainers) {
+            final ActivityStack activityStack = container.toActivityStackIfStable();
+            if (activityStack == null) {
+                return null;
+            }
+            activityStacks.add(activityStack);
+        }
+        return activityStacks;
+    }
+
     /** A wrapper class which contains the information of {@link TaskContainer} */
     static final class TaskProperties {
         private final int mDisplayId;
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
index afd554b..6fe8e50 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
@@ -107,11 +107,11 @@
     private final String mOverlayTag;
 
     /**
-     * The launch options that was used to create this container. Must not be {@code null} for
-     * {@link #isOverlay()} container.
+     * The launch options that was used to create this container. Must not {@link Bundle#isEmpty()}
+     * for {@link #isOverlay()} container.
      */
-    @Nullable
-    private final Bundle mLaunchOptions;
+    @NonNull
+    private final Bundle mLaunchOptions = new Bundle();
 
     /** Indicates whether the container was cleaned up after the last activity was removed. */
     private boolean mIsFinished;
@@ -172,6 +172,11 @@
     private boolean mIsIsolatedNavigationEnabled;
 
     /**
+     * Whether to apply dimming on the parent Task that was requested last.
+     */
+    private boolean mLastDimOnTask;
+
+    /**
      * @see #TaskFragmentContainer(Activity, Intent, TaskContainer, SplitController,
      * TaskFragmentContainer, String, Bundle)
      */
@@ -210,7 +215,9 @@
         if (overlayTag != null) {
             Objects.requireNonNull(launchOptions);
         }
-        mLaunchOptions = launchOptions;
+        if (launchOptions != null) {
+            mLaunchOptions.putAll(launchOptions);
+        }
 
         if (pairedPrimaryContainer != null) {
             // The TaskFragment will be positioned right above the paired container.
@@ -609,6 +616,9 @@
      * Removes all activities that belong to this process and finishes other containers/activities
      * configured to finish together.
      */
+    // Suppress GuardedBy warning because lint ask to mark this method as
+    // @GuardedBy(container.mController.mLock), which is mLock itself
+    @SuppressWarnings("GuardedBy")
     @GuardedBy("mController.mLock")
     void finish(boolean shouldFinishDependent, @NonNull SplitPresenter presenter,
             @NonNull WindowContainerTransaction wct, @NonNull SplitController controller) {
@@ -834,6 +844,16 @@
         mIsIsolatedNavigationEnabled = isolatedNavigationEnabled;
     }
 
+    /** Sets whether to apply dim on the parent Task. */
+    void setLastDimOnTask(boolean lastDimOnTask) {
+        mLastDimOnTask = lastDimOnTask;
+    }
+
+    /** Returns whether to apply dim on the parent Task. */
+    boolean isLastDimOnTask() {
+        return mLastDimOnTask;
+    }
+
     /**
      * Adds the pending appeared activity that has requested to be launched in this task fragment.
      * @see android.app.ActivityClient#isRequestedToLaunchInTaskFragment
@@ -925,6 +945,17 @@
         return mOverlayTag;
     }
 
+    /**
+     * Returns the options that was used to launch this {@link TaskFragmentContainer}.
+     * {@link Bundle#isEmpty()} means there's no launch option for this container.
+     * <p>
+     * Note that WM Jetpack owns the logic. The WM Extension library must not modify this object.
+     */
+    @NonNull
+    Bundle getLaunchOptions() {
+        return mLaunchOptions;
+    }
+
     @Override
     public String toString() {
         return toString(true /* includeContainersToFinishOnExit */);
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TransactionManager.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TransactionManager.java
index 396956e..6624c70 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TransactionManager.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TransactionManager.java
@@ -77,9 +77,11 @@
     @NonNull
     TransactionRecord startNewTransaction(@Nullable IBinder taskFragmentTransactionToken) {
         if (mCurrentTransaction != null) {
+            final TransactionRecord lastTransaction = mCurrentTransaction;
             mCurrentTransaction = null;
             throw new IllegalStateException(
-                    "The previous transaction has not been applied or aborted,");
+                    "The previous transaction:" + lastTransaction + " has not been applied or "
+                            + "aborted.");
         }
         mCurrentTransaction = new TransactionRecord(taskFragmentTransactionToken);
         return mCurrentTransaction;
@@ -199,5 +201,15 @@
                     ? mOriginType
                     : TASK_FRAGMENT_TRANSIT_CHANGE;
         }
+
+        @Override
+        @NonNull
+        public String toString() {
+            return TransactionRecord.class.getSimpleName() + "{"
+                    + "token=" + mTaskFragmentTransactionToken
+                    + ", type=" + getTransactionTransitionType()
+                    + ", transaction=" + mTransaction
+                    + "}";
+        }
     }
 }
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/WindowExtensionsTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/WindowExtensionsTest.java
index 60beb0b..f471af0 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/WindowExtensionsTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/WindowExtensionsTest.java
@@ -25,6 +25,7 @@
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
+import androidx.window.extensions.embedding.AnimationBackground;
 import androidx.window.extensions.embedding.SplitAttributes;
 
 import org.junit.Before;
@@ -70,7 +71,7 @@
                 .isEqualTo(SplitAttributes.LayoutDirection.LOCALE);
         assertThat(splitAttributes.getSplitType())
                 .isEqualTo(new SplitAttributes.SplitType.RatioSplitType(0.5f));
-        // TODO(b/263047900): Update extensions API.
-        // assertThat(splitAttributes.getAnimationBackgroundColor()).isEqualTo(0);
+        assertThat(splitAttributes.getAnimationBackground())
+                .isEqualTo(AnimationBackground.ANIMATION_BACKGROUND_DEFAULT);
     }
 }
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
index 678bdef..bc92101 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
@@ -16,6 +16,7 @@
 
 package androidx.window.extensions.embedding;
 
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.view.Display.DEFAULT_DISPLAY;
 
 import static androidx.window.extensions.embedding.ActivityEmbeddingOptionsProperties.KEY_OVERLAY_TAG;
@@ -287,10 +288,10 @@
         createOrUpdateOverlayTaskFragmentIfNeeded("test");
 
         verify(mSplitPresenter).resizeTaskFragment(mTransaction, overlayToken, new Rect());
-        verify(mSplitPresenter).setTaskFragmentIsolatedNavigation(mTransaction, overlayToken,
+        verify(mSplitPresenter).updateWindowingMode(mTransaction, overlayToken,
+                WINDOWING_MODE_UNDEFINED);
+        verify(mSplitPresenter).setTaskFragmentIsolatedNavigation(mTransaction, overlayContainer,
                 false);
-        assertThat(mSplitController.getAllOverlayTaskFragmentContainers())
-                .containsExactly(overlayContainer);
     }
 
     @Test
@@ -315,8 +316,10 @@
         createOrUpdateOverlayTaskFragmentIfNeeded("test");
 
         verify(mSplitPresenter).resizeTaskFragment(mTransaction, overlayToken, new Rect());
-        verify(mSplitPresenter).setTaskFragmentIsolatedNavigation(mTransaction, overlayToken,
-                false);
+        verify(mSplitPresenter).updateWindowingMode(mTransaction,
+                overlayToken, WINDOWING_MODE_UNDEFINED);
+        verify(mSplitPresenter).setTaskFragmentIsolatedNavigation(mTransaction,
+                overlayContainer, false);
         assertThat(mSplitController.getAllOverlayTaskFragmentContainers())
                 .containsExactly(overlayContainer);
     }
@@ -425,6 +428,73 @@
                 .that(taskContainer.getTaskFragmentContainers()).isEmpty();
     }
 
+    @Test
+    public void testUpdateActivityStackAttributes_nullParams_throwException() {
+        assertThrows(NullPointerException.class, () ->
+                mSplitController.updateActivityStackAttributes(null,
+                        new ActivityStackAttributes.Builder().build()));
+
+        assertThrows(NullPointerException.class, () ->
+                mSplitController.updateActivityStackAttributes(new Binder(), null));
+
+        verify(mSplitPresenter, never()).applyActivityStackAttributes(any(), any(), any());
+    }
+
+    @Test
+    public void testUpdateActivityStackAttributes_nullContainer_earlyReturn() {
+        final TaskFragmentContainer container = mSplitController.newContainer(mActivity,
+                mActivity.getTaskId());
+        mSplitController.updateActivityStackAttributes(container.getTaskFragmentToken(),
+                new ActivityStackAttributes.Builder().build());
+
+        verify(mSplitPresenter, never()).applyActivityStackAttributes(any(), any(), any());
+    }
+
+    @Test
+    public void testUpdateActivityStackAttributes_notOverlay_earlyReturn() {
+        final TaskFragmentContainer container = createMockTaskFragmentContainer(mActivity);
+
+        mSplitController.updateActivityStackAttributes(container.getTaskFragmentToken(),
+                new ActivityStackAttributes.Builder().build());
+
+        verify(mSplitPresenter, never()).applyActivityStackAttributes(any(), any(), any());
+    }
+
+    @Test
+    public void testUpdateActivityStackAttributes() {
+        final TaskFragmentContainer container = createTestOverlayContainer(TASK_ID, "test");
+        doNothing().when(mSplitPresenter).applyActivityStackAttributes(any(), any(), any());
+        final ActivityStackAttributes attrs = new ActivityStackAttributes.Builder().build();
+        final IBinder token = container.getTaskFragmentToken();
+
+        mSplitController.updateActivityStackAttributes(token, attrs);
+
+        verify(mSplitPresenter).applyActivityStackAttributes(any(), eq(container), eq(attrs));
+    }
+
+    @Test
+    public void testOnTaskFragmentParentInfoChanged_positionOnlyChange_earlyReturn() {
+        final TaskFragmentContainer overlayContainer = createTestOverlayContainer(TASK_ID, "test");
+
+        final TaskContainer taskContainer = overlayContainer.getTaskContainer();
+        spyOn(taskContainer);
+        final TaskContainer.TaskProperties taskProperties = taskContainer.getTaskProperties();
+        final TaskFragmentParentInfo parentInfo = new TaskFragmentParentInfo(
+                new Configuration(taskProperties.getConfiguration()), taskProperties.getDisplayId(),
+                true /* visible */, false /* hasDirectActivity */, null /* decorSurface */);
+        parentInfo.getConfiguration().windowConfiguration.getBounds().offset(10, 10);
+
+        mSplitController.onTaskFragmentParentInfoChanged(mTransaction, TASK_ID, parentInfo);
+
+        // The parent info must be applied to the task container
+        verify(taskContainer).updateTaskFragmentParentInfo(parentInfo);
+        verify(mSplitController, never()).updateContainer(any(), any());
+
+        assertWithMessage("The overlay container must still be dismissed even if "
+                + "#updateContainer is not called")
+                .that(taskContainer.getOverlayContainer()).isNull();
+    }
+
     /**
      * A simplified version of {@link SplitController.ActivityStartMonitor
      * #createOrUpdateOverlayTaskFragmentIfNeeded}
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
index bab4e91..b60943a 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
@@ -354,7 +354,7 @@
         bundle.putBinder(ActivityOptions.KEY_LAUNCH_TASK_FRAGMENT_TOKEN,
                 container.getTaskFragmentToken());
         monitor.mCurrentIntent = intent;
-        doReturn(container).when(mSplitController).getContainer(any());
+        doReturn(container).when(mSplitController).getContainer(any(IBinder.class));
 
         monitor.onStartActivityResult(START_CANCELED, bundle);
         assertNull(container.getPendingAppearedIntent());
@@ -1642,7 +1642,7 @@
         // We need to set those in case we are not respecting clear top.
         // TODO(b/231845476) we should always respect clearTop.
         final int windowingMode = mSplitController.getTaskContainer(primaryContainer.getTaskId())
-                .getWindowingModeForSplitTaskFragment(TASK_BOUNDS);
+                .getWindowingModeForTaskFragment(TASK_BOUNDS);
         primaryContainer.setLastRequestedWindowingMode(windowingMode);
         secondaryContainer.setLastRequestedWindowingMode(windowingMode);
         primaryContainer.setLastRequestedBounds(getSplitBounds(true /* isPrimary */));
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java
index 6981d9d..941b4e1 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java
@@ -235,6 +235,19 @@
     }
 
     @Test
+    public void testSetTaskFragmentDimOnTask() {
+        final TaskFragmentContainer container = mController.newContainer(mActivity, TASK_ID);
+
+        mPresenter.setTaskFragmentDimOnTask(mTransaction, container.getTaskFragmentToken(), true);
+        verify(mTransaction).addTaskFragmentOperation(eq(container.getTaskFragmentToken()), any());
+
+        // No request to set the same adjacent TaskFragments.
+        clearInvocations(mTransaction);
+        mPresenter.setTaskFragmentDimOnTask(mTransaction, container.getTaskFragmentToken(), true);
+        verify(mTransaction, never()).addTaskFragmentOperation(any(), any());
+    }
+
+    @Test
     public void testUpdateAnimationParams() {
         final TaskFragmentContainer container = mController.newContainer(mActivity, TASK_ID);
 
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java
index 7b77235..a5995a3 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java
@@ -75,7 +75,7 @@
         final Configuration configuration = new Configuration();
 
         assertEquals(WINDOWING_MODE_MULTI_WINDOW,
-                taskContainer.getWindowingModeForSplitTaskFragment(splitBounds));
+                taskContainer.getWindowingModeForTaskFragment(splitBounds));
 
         configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
         taskContainer.updateTaskFragmentParentInfo(new TaskFragmentParentInfo(configuration,
@@ -83,7 +83,7 @@
                 null /* decorSurface */));
 
         assertEquals(WINDOWING_MODE_MULTI_WINDOW,
-                taskContainer.getWindowingModeForSplitTaskFragment(splitBounds));
+                taskContainer.getWindowingModeForTaskFragment(splitBounds));
 
         configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
         taskContainer.updateTaskFragmentParentInfo(new TaskFragmentParentInfo(configuration,
@@ -91,12 +91,12 @@
                 null /* decorSurface */));
 
         assertEquals(WINDOWING_MODE_FREEFORM,
-                taskContainer.getWindowingModeForSplitTaskFragment(splitBounds));
+                taskContainer.getWindowingModeForTaskFragment(splitBounds));
 
         // Empty bounds means the split pair are stacked, so it should be UNDEFINED which will then
         // inherit the Task windowing mode
         assertEquals(WINDOWING_MODE_UNDEFINED,
-                taskContainer.getWindowingModeForSplitTaskFragment(new Rect()));
+                taskContainer.getWindowingModeForTaskFragment(new Rect()));
     }
 
     @Test
diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp
index 5ad144d..45540e0 100644
--- a/libs/WindowManager/Shell/Android.bp
+++ b/libs/WindowManager/Shell/Android.bp
@@ -175,3 +175,74 @@
     plugins: ["dagger2-compiler"],
     use_resource_processor: true,
 }
+
+android_app {
+    name: "WindowManagerShellRobolectric",
+    platform_apis: true,
+    static_libs: [
+        "WindowManager-Shell",
+    ],
+    manifest: "multivalentTests/AndroidManifestRobolectric.xml",
+    use_resource_processor: true,
+}
+
+android_robolectric_test {
+    name: "WMShellRobolectricTests",
+    instrumentation_for: "WindowManagerShellRobolectric",
+    upstream: true,
+    java_resource_dirs: [
+        "multivalentTests/robolectric/config",
+    ],
+    srcs: [
+        "multivalentTests/src/**/*.kt",
+    ],
+    static_libs: [
+        "junit",
+        "androidx.test.runner",
+        "androidx.test.rules",
+        "androidx.test.ext.junit",
+        "mockito-robolectric-prebuilt",
+        "mockito-kotlin2",
+        "truth",
+    ],
+}
+
+android_test {
+    name: "WMShellMultivalentTestsOnDevice",
+    srcs: [
+        "multivalentTests/src/**/*.kt",
+    ],
+    static_libs: [
+        "WindowManager-Shell",
+        "junit",
+        "androidx.test.runner",
+        "androidx.test.rules",
+        "androidx.test.ext.junit",
+        "frameworks-base-testutils",
+        "mockito-kotlin2",
+        "mockito-target-extended-minus-junit4",
+        "truth",
+        "platform-test-annotations",
+        "platform-test-rules",
+    ],
+    libs: [
+        "android.test.base",
+        "android.test.runner",
+    ],
+    jni_libs: [
+        "libdexmakerjvmtiagent",
+        "libstaticjvmtiagent",
+    ],
+    kotlincflags: ["-Xjvm-default=all"],
+    optimize: {
+        enabled: false,
+    },
+    test_suites: ["device-tests"],
+    platform_apis: true,
+    certificate: "platform",
+    aaptflags: [
+        "--extra-packages",
+        "com.android.wm.shell",
+    ],
+    manifest: "multivalentTests/AndroidManifest.xml",
+}
diff --git a/libs/WindowManager/Shell/multivalentTests/AndroidManifest.xml b/libs/WindowManager/Shell/multivalentTests/AndroidManifest.xml
new file mode 100644
index 0000000..f8f8338
--- /dev/null
+++ b/libs/WindowManager/Shell/multivalentTests/AndroidManifest.xml
@@ -0,0 +1,13 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.wm.shell.multivalenttests">
+
+    <application android:debuggable="true" android:supportsRtl="true" >
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation
+        android:name="androidx.test.runner.AndroidJUnitRunner"
+        android:label="Multivalent tests for WindowManager-Shell"
+        android:targetPackage="com.android.wm.shell.multivalenttests">
+    </instrumentation>
+</manifest>
diff --git a/libs/WindowManager/Shell/multivalentTests/AndroidManifestRobolectric.xml b/libs/WindowManager/Shell/multivalentTests/AndroidManifestRobolectric.xml
new file mode 100644
index 0000000..ffcd7d4
--- /dev/null
+++ b/libs/WindowManager/Shell/multivalentTests/AndroidManifestRobolectric.xml
@@ -0,0 +1,3 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.wm.shell.multivalenttests">
+</manifest>
diff --git a/libs/WindowManager/Shell/multivalentTests/AndroidTest.xml b/libs/WindowManager/Shell/multivalentTests/AndroidTest.xml
new file mode 100644
index 0000000..36fe8ec
--- /dev/null
+++ b/libs/WindowManager/Shell/multivalentTests/AndroidTest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2023 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<configuration description="Runs Tests for WindowManagerShellLib">
+    <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="WMShellMultivalentTestsOnDevice.apk" />
+    </target_preparer>
+
+    <option name="test-suite-tag" value="apct" />
+    <option name="test-suite-tag" value="framework-base-presubmit" />
+    <option name="test-tag" value="WMShellMultivalentTestsOnDevice" />
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="com.android.wm.shell.multivalenttests" />
+        <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+        <option name="hidden-api-checks" value="false"/>
+    </test>
+</configuration>
diff --git a/libs/WindowManager/Shell/multivalentTests/robolectric/config/robolectric.properties b/libs/WindowManager/Shell/multivalentTests/robolectric/config/robolectric.properties
new file mode 100644
index 0000000..7a0527c
--- /dev/null
+++ b/libs/WindowManager/Shell/multivalentTests/robolectric/config/robolectric.properties
@@ -0,0 +1,2 @@
+sdk=NEWEST_SDK
+
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubblePositionerTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubblePositionerTest.kt
new file mode 100644
index 0000000..ea7c6ed
--- /dev/null
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubblePositionerTest.kt
@@ -0,0 +1,481 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.wm.shell.bubbles
+
+import android.content.Context
+import android.content.Intent
+import android.content.pm.ShortcutInfo
+import android.graphics.Insets
+import android.graphics.PointF
+import android.graphics.Rect
+import android.os.UserHandle
+import android.view.WindowManager
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.wm.shell.R
+import com.android.wm.shell.bubbles.BubblePositioner.MAX_HEIGHT
+import com.google.common.truth.Truth.assertThat
+import com.google.common.util.concurrent.MoreExecutors.directExecutor
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/** Tests operations and the resulting state managed by [BubblePositioner]. */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class BubblePositionerTest {
+
+    private lateinit var positioner: BubblePositioner
+    private val context = ApplicationProvider.getApplicationContext<Context>()
+    private val defaultDeviceConfig =
+        DeviceConfig(
+            windowBounds = Rect(0, 0, 1000, 2000),
+            isLargeScreen = false,
+            isSmallTablet = false,
+            isLandscape = false,
+            isRtl = false,
+            insets = Insets.of(0, 0, 0, 0)
+        )
+
+    @Before
+    fun setUp() {
+        val windowManager = context.getSystemService(WindowManager::class.java)
+        positioner = BubblePositioner(context, windowManager)
+    }
+
+    @Test
+    fun testUpdate() {
+        val insets = Insets.of(10, 20, 5, 15)
+        val screenBounds = Rect(0, 0, 1000, 1200)
+        val availableRect = Rect(screenBounds)
+        availableRect.inset(insets)
+        positioner.update(defaultDeviceConfig.copy(insets = insets, windowBounds = screenBounds))
+        assertThat(positioner.availableRect).isEqualTo(availableRect)
+        assertThat(positioner.isLandscape).isFalse()
+        assertThat(positioner.isLargeScreen).isFalse()
+        assertThat(positioner.insets).isEqualTo(insets)
+    }
+
+    @Test
+    fun testShowBubblesVertically_phonePortrait() {
+        positioner.update(defaultDeviceConfig)
+        assertThat(positioner.showBubblesVertically()).isFalse()
+    }
+
+    @Test
+    fun testShowBubblesVertically_phoneLandscape() {
+        positioner.update(defaultDeviceConfig.copy(isLandscape = true))
+        assertThat(positioner.isLandscape).isTrue()
+        assertThat(positioner.showBubblesVertically()).isTrue()
+    }
+
+    @Test
+    fun testShowBubblesVertically_tablet() {
+        positioner.update(defaultDeviceConfig.copy(isLargeScreen = true))
+        assertThat(positioner.showBubblesVertically()).isTrue()
+    }
+
+    /** If a resting position hasn't been set, calling it will return the default position. */
+    @Test
+    fun testGetRestingPosition_returnsDefaultPosition() {
+        positioner.update(defaultDeviceConfig)
+        val restingPosition = positioner.getRestingPosition()
+        val defaultPosition = positioner.defaultStartPosition
+        assertThat(restingPosition).isEqualTo(defaultPosition)
+    }
+
+    /** If a resting position has been set, it'll return that instead of the default position. */
+    @Test
+    fun testGetRestingPosition_returnsRestingPosition() {
+        positioner.update(defaultDeviceConfig)
+        val restingPosition = PointF(100f, 100f)
+        positioner.restingPosition = restingPosition
+        assertThat(positioner.getRestingPosition()).isEqualTo(restingPosition)
+    }
+
+    /** Test that the default resting position on phone is in upper left. */
+    @Test
+    fun testGetRestingPosition_bubble_onPhone() {
+        positioner.update(defaultDeviceConfig)
+        val allowableStackRegion = positioner.getAllowableStackPositionRegion(1 /* bubbleCount */)
+        val restingPosition = positioner.getRestingPosition()
+        assertThat(restingPosition.x).isEqualTo(allowableStackRegion.left)
+        assertThat(restingPosition.y).isEqualTo(defaultYPosition)
+    }
+
+    @Test
+    fun testGetRestingPosition_bubble_onPhone_RTL() {
+        positioner.update(defaultDeviceConfig.copy(isRtl = true))
+        val allowableStackRegion = positioner.getAllowableStackPositionRegion(1 /* bubbleCount */)
+        val restingPosition = positioner.getRestingPosition()
+        assertThat(restingPosition.x).isEqualTo(allowableStackRegion.right)
+        assertThat(restingPosition.y).isEqualTo(defaultYPosition)
+    }
+
+    /** Test that the default resting position on tablet is middle left. */
+    @Test
+    fun testGetRestingPosition_chatBubble_onTablet() {
+        positioner.update(defaultDeviceConfig.copy(isLargeScreen = true))
+        val allowableStackRegion = positioner.getAllowableStackPositionRegion(1 /* bubbleCount */)
+        val restingPosition = positioner.getRestingPosition()
+        assertThat(restingPosition.x).isEqualTo(allowableStackRegion.left)
+        assertThat(restingPosition.y).isEqualTo(defaultYPosition)
+    }
+
+    @Test
+    fun testGetRestingPosition_chatBubble_onTablet_RTL() {
+        positioner.update(defaultDeviceConfig.copy(isLargeScreen = true, isRtl = true))
+        val allowableStackRegion = positioner.getAllowableStackPositionRegion(1 /* bubbleCount */)
+        val restingPosition = positioner.getRestingPosition()
+        assertThat(restingPosition.x).isEqualTo(allowableStackRegion.right)
+        assertThat(restingPosition.y).isEqualTo(defaultYPosition)
+    }
+
+    /** Test that the default resting position on tablet is middle right. */
+    @Test
+    fun testGetDefaultPosition_appBubble_onTablet() {
+        positioner.update(defaultDeviceConfig.copy(isLargeScreen = true))
+        val allowableStackRegion = positioner.getAllowableStackPositionRegion(1 /* bubbleCount */)
+        val startPosition = positioner.getDefaultStartPosition(true /* isAppBubble */)
+        assertThat(startPosition.x).isEqualTo(allowableStackRegion.right)
+        assertThat(startPosition.y).isEqualTo(defaultYPosition)
+    }
+
+    @Test
+    fun testGetRestingPosition_appBubble_onTablet_RTL() {
+        positioner.update(defaultDeviceConfig.copy(isLargeScreen = true, isRtl = true))
+        val allowableStackRegion = positioner.getAllowableStackPositionRegion(1 /* bubbleCount */)
+        val startPosition = positioner.getDefaultStartPosition(true /* isAppBubble */)
+        assertThat(startPosition.x).isEqualTo(allowableStackRegion.left)
+        assertThat(startPosition.y).isEqualTo(defaultYPosition)
+    }
+
+    @Test
+    fun testHasUserModifiedDefaultPosition_false() {
+        positioner.update(defaultDeviceConfig.copy(isLargeScreen = true, isRtl = true))
+        assertThat(positioner.hasUserModifiedDefaultPosition()).isFalse()
+        positioner.restingPosition = positioner.defaultStartPosition
+        assertThat(positioner.hasUserModifiedDefaultPosition()).isFalse()
+    }
+
+    @Test
+    fun testHasUserModifiedDefaultPosition_true() {
+        positioner.update(defaultDeviceConfig.copy(isLargeScreen = true, isRtl = true))
+        assertThat(positioner.hasUserModifiedDefaultPosition()).isFalse()
+        positioner.restingPosition = PointF(0f, 100f)
+        assertThat(positioner.hasUserModifiedDefaultPosition()).isTrue()
+    }
+
+    @Test
+    fun testGetExpandedViewHeight_max() {
+        val deviceConfig =
+            defaultDeviceConfig.copy(
+                isLargeScreen = true,
+                insets = Insets.of(10, 20, 5, 15),
+                windowBounds = Rect(0, 0, 1800, 2600)
+            )
+        positioner.update(deviceConfig)
+        val intent = Intent(Intent.ACTION_VIEW).setPackage(context.packageName)
+        val bubble = Bubble.createAppBubble(intent, UserHandle(1), null, directExecutor())
+
+        assertThat(positioner.getExpandedViewHeight(bubble)).isEqualTo(MAX_HEIGHT)
+    }
+
+    @Test
+    fun testGetExpandedViewHeight_customHeight_valid() {
+        val deviceConfig =
+            defaultDeviceConfig.copy(
+                isLargeScreen = true,
+                insets = Insets.of(10, 20, 5, 15),
+                windowBounds = Rect(0, 0, 1800, 2600)
+            )
+        positioner.update(deviceConfig)
+        val minHeight =
+            context.resources.getDimensionPixelSize(R.dimen.bubble_expanded_default_height)
+        val bubble =
+            Bubble(
+                "key",
+                ShortcutInfo.Builder(context, "id").build(),
+                minHeight + 100 /* desiredHeight */,
+                0 /* desiredHeightResId */,
+                "title",
+                0 /* taskId */,
+                null /* locus */,
+                true /* isDismissable */,
+                directExecutor()) {}
+
+        // Ensure the height is the same as the desired value
+        assertThat(positioner.getExpandedViewHeight(bubble))
+            .isEqualTo(bubble.getDesiredHeight(context))
+    }
+
+    @Test
+    fun testGetExpandedViewHeight_customHeight_tooSmall() {
+        val deviceConfig =
+            defaultDeviceConfig.copy(
+                isLargeScreen = true,
+                insets = Insets.of(10, 20, 5, 15),
+                windowBounds = Rect(0, 0, 1800, 2600)
+            )
+        positioner.update(deviceConfig)
+
+        val bubble =
+            Bubble(
+                "key",
+                ShortcutInfo.Builder(context, "id").build(),
+                10 /* desiredHeight */,
+                0 /* desiredHeightResId */,
+                "title",
+                0 /* taskId */,
+                null /* locus */,
+                true /* isDismissable */,
+                directExecutor()) {}
+
+        // Ensure the height is the same as the desired value
+        val minHeight =
+            context.resources.getDimensionPixelSize(R.dimen.bubble_expanded_default_height)
+        assertThat(positioner.getExpandedViewHeight(bubble)).isEqualTo(minHeight)
+    }
+
+    @Test
+    fun testGetMaxExpandedViewHeight_onLargeTablet() {
+        val deviceConfig =
+            defaultDeviceConfig.copy(
+                isLargeScreen = true,
+                insets = Insets.of(10, 20, 5, 15),
+                windowBounds = Rect(0, 0, 1800, 2600)
+            )
+        positioner.update(deviceConfig)
+
+        val manageButtonHeight =
+            context.resources.getDimensionPixelSize(R.dimen.bubble_manage_button_height)
+        val pointerWidth = context.resources.getDimensionPixelSize(R.dimen.bubble_pointer_width)
+        val expandedViewPadding =
+            context.resources.getDimensionPixelSize(R.dimen.bubble_expanded_view_padding)
+        val expectedHeight =
+            1800 - 2 * 20 - manageButtonHeight - pointerWidth - expandedViewPadding * 2
+        assertThat(positioner.getMaxExpandedViewHeight(false /* isOverflow */))
+            .isEqualTo(expectedHeight)
+    }
+
+    @Test
+    fun testAreBubblesBottomAligned_largeScreen_true() {
+        val deviceConfig =
+            defaultDeviceConfig.copy(
+                isLargeScreen = true,
+                insets = Insets.of(10, 20, 5, 15),
+                windowBounds = Rect(0, 0, 1800, 2600)
+            )
+        positioner.update(deviceConfig)
+
+        assertThat(positioner.areBubblesBottomAligned()).isTrue()
+    }
+
+    @Test
+    fun testAreBubblesBottomAligned_largeScreen_landscape_false() {
+        val deviceConfig =
+            defaultDeviceConfig.copy(
+                isLargeScreen = true,
+                isLandscape = true,
+                insets = Insets.of(10, 20, 5, 15),
+                windowBounds = Rect(0, 0, 1800, 2600)
+            )
+        positioner.update(deviceConfig)
+
+        assertThat(positioner.areBubblesBottomAligned()).isFalse()
+    }
+
+    @Test
+    fun testAreBubblesBottomAligned_smallTablet_false() {
+        val deviceConfig =
+            defaultDeviceConfig.copy(
+                isLargeScreen = true,
+                isSmallTablet = true,
+                insets = Insets.of(10, 20, 5, 15),
+                windowBounds = Rect(0, 0, 1800, 2600)
+            )
+        positioner.update(deviceConfig)
+
+        assertThat(positioner.areBubblesBottomAligned()).isFalse()
+    }
+
+    @Test
+    fun testAreBubblesBottomAligned_phone_false() {
+        val deviceConfig =
+            defaultDeviceConfig.copy(
+                insets = Insets.of(10, 20, 5, 15),
+                windowBounds = Rect(0, 0, 1800, 2600)
+            )
+        positioner.update(deviceConfig)
+
+        assertThat(positioner.areBubblesBottomAligned()).isFalse()
+    }
+
+    @Test
+    fun testExpandedViewY_phoneLandscape() {
+        val deviceConfig =
+            defaultDeviceConfig.copy(
+                isLandscape = true,
+                insets = Insets.of(10, 20, 5, 15),
+                windowBounds = Rect(0, 0, 1800, 2600)
+            )
+        positioner.update(deviceConfig)
+
+        val intent = Intent(Intent.ACTION_VIEW).setPackage(context.packageName)
+        val bubble = Bubble.createAppBubble(intent, UserHandle(1), null, directExecutor())
+
+        // This bubble will have max height so it'll always be top aligned
+        assertThat(positioner.getExpandedViewY(bubble, 0f /* bubblePosition */))
+            .isEqualTo(positioner.getExpandedViewYTopAligned())
+    }
+
+    @Test
+    fun testExpandedViewY_phonePortrait() {
+        val deviceConfig =
+            defaultDeviceConfig.copy(
+                insets = Insets.of(10, 20, 5, 15),
+                windowBounds = Rect(0, 0, 1800, 2600)
+            )
+        positioner.update(deviceConfig)
+
+        val intent = Intent(Intent.ACTION_VIEW).setPackage(context.packageName)
+        val bubble = Bubble.createAppBubble(intent, UserHandle(1), null, directExecutor())
+
+        // Always top aligned in phone portrait
+        assertThat(positioner.getExpandedViewY(bubble, 0f /* bubblePosition */))
+            .isEqualTo(positioner.getExpandedViewYTopAligned())
+    }
+
+    @Test
+    fun testExpandedViewY_smallTabletLandscape() {
+        val deviceConfig =
+            defaultDeviceConfig.copy(
+                isSmallTablet = true,
+                isLandscape = true,
+                insets = Insets.of(10, 20, 5, 15),
+                windowBounds = Rect(0, 0, 1800, 2600)
+            )
+        positioner.update(deviceConfig)
+
+        val intent = Intent(Intent.ACTION_VIEW).setPackage(context.packageName)
+        val bubble = Bubble.createAppBubble(intent, UserHandle(1), null, directExecutor())
+
+        // This bubble will have max height which is always top aligned on small tablets
+        assertThat(positioner.getExpandedViewY(bubble, 0f /* bubblePosition */))
+            .isEqualTo(positioner.getExpandedViewYTopAligned())
+    }
+
+    @Test
+    fun testExpandedViewY_smallTabletPortrait() {
+        val deviceConfig =
+            defaultDeviceConfig.copy(
+                isSmallTablet = true,
+                insets = Insets.of(10, 20, 5, 15),
+                windowBounds = Rect(0, 0, 1800, 2600)
+            )
+        positioner.update(deviceConfig)
+
+        val intent = Intent(Intent.ACTION_VIEW).setPackage(context.packageName)
+        val bubble = Bubble.createAppBubble(intent, UserHandle(1), null, directExecutor())
+
+        // This bubble will have max height which is always top aligned on small tablets
+        assertThat(positioner.getExpandedViewY(bubble, 0f /* bubblePosition */))
+            .isEqualTo(positioner.getExpandedViewYTopAligned())
+    }
+
+    @Test
+    fun testExpandedViewY_largeScreenLandscape() {
+        val deviceConfig =
+            defaultDeviceConfig.copy(
+                isLargeScreen = true,
+                isLandscape = true,
+                insets = Insets.of(10, 20, 5, 15),
+                windowBounds = Rect(0, 0, 1800, 2600)
+            )
+        positioner.update(deviceConfig)
+
+        val intent = Intent(Intent.ACTION_VIEW).setPackage(context.packageName)
+        val bubble = Bubble.createAppBubble(intent, UserHandle(1), null, directExecutor())
+
+        // This bubble will have max height which is always top aligned on landscape, large tablet
+        assertThat(positioner.getExpandedViewY(bubble, 0f /* bubblePosition */))
+            .isEqualTo(positioner.getExpandedViewYTopAligned())
+    }
+
+    @Test
+    fun testExpandedViewY_largeScreenPortrait() {
+        val deviceConfig =
+            defaultDeviceConfig.copy(
+                isLargeScreen = true,
+                insets = Insets.of(10, 20, 5, 15),
+                windowBounds = Rect(0, 0, 1800, 2600)
+            )
+        positioner.update(deviceConfig)
+
+        val intent = Intent(Intent.ACTION_VIEW).setPackage(context.packageName)
+        val bubble = Bubble.createAppBubble(intent, UserHandle(1), null, directExecutor())
+
+        val manageButtonHeight =
+            context.resources.getDimensionPixelSize(R.dimen.bubble_manage_button_height)
+        val manageButtonPlusMargin =
+            manageButtonHeight +
+                2 * context.resources.getDimensionPixelSize(R.dimen.bubble_manage_button_margin)
+        val pointerWidth = context.resources.getDimensionPixelSize(R.dimen.bubble_pointer_width)
+
+        val expectedExpandedViewY =
+            positioner.availableRect.bottom -
+                manageButtonPlusMargin -
+                positioner.getExpandedViewHeightForLargeScreen() -
+                pointerWidth
+
+        // Bubbles are bottom aligned on portrait, large tablet
+        assertThat(positioner.getExpandedViewY(bubble, 0f /* bubblePosition */))
+            .isEqualTo(expectedExpandedViewY)
+    }
+
+    private val defaultYPosition: Float
+        /**
+         * Calculates the Y position bubbles should be placed based on the config. Based on the
+         * calculations in [BubblePositioner.getDefaultStartPosition] and
+         * [BubbleStackView.RelativeStackPosition].
+         */
+        get() {
+            val isTablet = positioner.isLargeScreen
+
+            // On tablet the position is centered, on phone it is an offset from the top.
+            val desiredY =
+                if (isTablet) {
+                    positioner.screenRect.height() / 2f - positioner.bubbleSize / 2f
+                } else {
+                    context.resources
+                        .getDimensionPixelOffset(R.dimen.bubble_stack_starting_offset_y)
+                        .toFloat()
+                }
+            // Since we're visually centering the bubbles on tablet, use total screen height rather
+            // than the available height.
+            val height =
+                if (isTablet) {
+                    positioner.screenRect.height()
+                } else {
+                    positioner.availableRect.height()
+                }
+            val offsetPercent = (desiredY / height).coerceIn(0f, 1f)
+            val allowableStackRegion =
+                positioner.getAllowableStackPositionRegion(1 /* bubbleCount */)
+            return allowableStackRegion.top + allowableStackRegion.height() * offsetPercent
+        }
+}
diff --git a/libs/WindowManager/Shell/multivalentTestsForDevice b/libs/WindowManager/Shell/multivalentTestsForDevice
new file mode 120000
index 0000000..20ee34a
--- /dev/null
+++ b/libs/WindowManager/Shell/multivalentTestsForDevice
@@ -0,0 +1 @@
+multivalentTests
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/multivalentTestsForDeviceless b/libs/WindowManager/Shell/multivalentTestsForDeviceless
new file mode 120000
index 0000000..20ee34a
--- /dev/null
+++ b/libs/WindowManager/Shell/multivalentTestsForDeviceless
@@ -0,0 +1 @@
+multivalentTests
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/layout/bubble_bar_expanded_view.xml b/libs/WindowManager/Shell/res/layout/bubble_bar_expanded_view.xml
index 681a52b..e04ab81 100644
--- a/libs/WindowManager/Shell/res/layout/bubble_bar_expanded_view.xml
+++ b/libs/WindowManager/Shell/res/layout/bubble_bar_expanded_view.xml
@@ -21,4 +21,9 @@
     android:orientation="vertical"
     android:id="@+id/bubble_bar_expanded_view">
 
+    <com.android.wm.shell.bubbles.bar.BubbleBarHandleView
+        android:id="@+id/bubble_bar_handle_view"
+        android:layout_height="wrap_content"
+        android:layout_width="wrap_content" />
+
 </com.android.wm.shell.bubbles.bar.BubbleBarExpandedView>
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml
index 85bf2c1..e4f793c 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml
@@ -30,8 +30,8 @@
         android:orientation="horizontal"
         android:clickable="true"
         android:focusable="true"
-        android:paddingStart="16dp">
-
+        android:paddingStart="6dp"
+        android:paddingEnd="8dp">
         <ImageView
             android:id="@+id/application_icon"
             android:layout_width="@dimen/desktop_mode_caption_icon_radius"
@@ -43,7 +43,7 @@
             android:id="@+id/application_name"
             android:layout_width="0dp"
             android:layout_height="20dp"
-            android:minWidth="80dp"
+            android:maxWidth="86dp"
             android:textAppearance="@android:style/TextAppearance.Material.Title"
             android:textSize="14sp"
             android:textFontWeight="500"
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_focused_window_decor.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_focused_window_decor.xml
index cec7ee2..ef7478c 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_focused_window_decor.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_focused_window_decor.xml
@@ -18,13 +18,13 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:tools="http://schemas.android.com/tools"
     android:id="@+id/desktop_mode_caption"
-    android:layout_width="match_parent"
+    android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:gravity="center_horizontal">
 
     <ImageButton
         android:id="@+id/caption_handle"
-        android:layout_width="128dp"
+        android:layout_width="@dimen/desktop_mode_fullscreen_decor_caption_width"
         android:layout_height="@dimen/desktop_mode_fullscreen_decor_caption_height"
         android:paddingVertical="16dp"
         android:contentDescription="@string/handle_text"
diff --git a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
index 6ad1728..9d4e6f0 100644
--- a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
@@ -57,7 +57,7 @@
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"橂需退ć‡șïŒŒèŻ·ä»Žć±ćč•ćș•éƒšć‘äžŠæ»‘ćŠšïŒŒæˆ–ç‚č按ćș”甚䞊æ–čçš„ä»»æ„äœçœź"</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"ćŻćŠšć•æ‰‹æšĄćŒ"</string>
     <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"退ć‡șć•æ‰‹æšĄćŒ"</string>
-    <string name="bubbles_settings_button_description" msgid="1301286017420516912">"<xliff:g id="APP_NAME">%1$s</xliff:g>ćŻčèŻæłĄçš„èźŸçœź"</string>
+    <string name="bubbles_settings_button_description" msgid="1301286017420516912">"<xliff:g id="APP_NAME">%1$s</xliff:g>æ¶ˆæŻæ°”æłĄçš„èźŸçœź"</string>
     <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"èœć•"</string>
     <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"é‡æ–°ćŠ ć…„ć æ”Ÿ"</string>
     <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="APP_NAME">%2$s</xliff:g><xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
@@ -69,22 +69,22 @@
     <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"ć±•ćŒ€“<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>”"</string>
     <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"æ”¶è”·“<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>”"</string>
     <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>èźŸçœź"</string>
-    <string name="bubble_dismiss_text" msgid="8816558050659478158">"慳闭ćŻčèŻæłĄ"</string>
-    <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"䞍仄ćŻčèŻæłĄćœąćŒæ˜Ÿç€șćŻčèŻ"</string>
-    <string name="bubbles_user_education_title" msgid="2112319053732691899">"äœżç”šćŻčèŻæłĄèŠć€©"</string>
-    <string name="bubbles_user_education_description" msgid="4215862563054175407">"新ćŻčèŻäŒšä»„æ”źćŠšć›Ÿæ ‡æˆ–ćŻčèŻæłĄćœąćŒæ˜Ÿç€ș。ç‚čæŒ‰ćłćŻæ‰“ćŒ€ćŻčèŻæłĄă€‚æ‹–ćŠšćłćŻç§»ćŠšćŻčèŻæłĄă€‚"</string>
-    <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"éšæ—¶æŽ§ćˆ¶ćŻčèŻæłĄ"</string>
-    <string name="bubbles_user_education_manage" msgid="3460756219946517198">"ç‚č按“缡理”æŒ‰é’źïŒŒćŻć…łé—­æ„è‡Șæ­€ćș”甚的ćŻčèŻæłĄ"</string>
+    <string name="bubble_dismiss_text" msgid="8816558050659478158">"ć…łé—­æ¶ˆæŻæ°”æłĄ"</string>
+    <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"äžä»„æ¶ˆæŻæ°”æłĄćœąćŒæ˜Ÿç€șćŻčèŻ"</string>
+    <string name="bubbles_user_education_title" msgid="2112319053732691899">"äœżç”šæ¶ˆæŻæ°”æłĄèŠć€©"</string>
+    <string name="bubbles_user_education_description" msgid="4215862563054175407">"新ćŻčèŻäŒšä»„æ”źćŠšć›Ÿæ ‡æˆ–æ¶ˆæŻæ°”æłĄćœąćŒæ˜Ÿç€ș。ç‚čæŒ‰ćłćŻæ‰“ćŒ€æ¶ˆæŻæ°”æłĄă€‚æ‹–ćŠšćłćŻç§»ćŠšæ¶ˆæŻæ°”æłĄă€‚"</string>
+    <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"éšæ—¶æŽ§ćˆ¶æ¶ˆæŻæ°”æłĄ"</string>
+    <string name="bubbles_user_education_manage" msgid="3460756219946517198">"ç‚č按“缡理”æŒ‰é’źïŒŒćŻć…łé—­æ„è‡Șæ­€ćș”ç”šçš„æ¶ˆæŻæ°”æłĄ"</string>
     <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"矄道äș†"</string>
-    <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"æœ€èż‘æČĄæœ‰ćŻčèŻæłĄ"</string>
-    <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"æ­€ć€„äŒšæ˜Ÿç€șæœ€èż‘çš„ćŻčèŻæłĄć’Œć·Č慳闭的ćŻčèŻæłĄ"</string>
-    <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"äœżç”šćŻčèŻæłĄèŠć€©"</string>
+    <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"æœ€èż‘æČĄæœ‰æ¶ˆæŻæ°”æłĄ"</string>
+    <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"æ­€ć€„äŒšæ˜Ÿç€șæœ€èż‘çš„æ¶ˆæŻæ°”æłĄć’Œć·Čć…łé—­çš„æ¶ˆæŻæ°”æłĄ"</string>
+    <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"äœżç”šæ¶ˆæŻæ°”æłĄèŠć€©"</string>
     <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"新ćŻčèŻäŒšä»„ć›Ÿæ ‡ćœąćŒæ˜Ÿç€șćœšć±ćč•ćș•éƒšçš„è§’èœäž­ă€‚ç‚čæŒ‰ć›Ÿæ ‡ćłćŻć±•ćŒ€ćŻčèŻïŒŒæ‹–ćŠšć›Ÿæ ‡ćłćŻć…łé—­ćŻčèŻă€‚"</string>
-    <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"éšæ—¶æŽ§ćˆ¶ćŻčèŻæłĄ"</string>
-    <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"ç‚čæŒ‰æ­€ć€„ćłćŻçźĄç†ć“Șäș›ćș”ç”šć’ŒćŻčèŻćŻä»„æ˜Ÿç€șćŻčèŻæłĄ"</string>
+    <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"éšæ—¶æŽ§ćˆ¶æ¶ˆæŻæ°”æłĄ"</string>
+    <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"ç‚čæŒ‰æ­€ć€„ćłćŻçźĄç†ć“Șäș›ćș”ç”šć’ŒćŻčèŻćŻä»„æ˜Ÿç€șæ¶ˆæŻæ°”æłĄ"</string>
     <string name="notification_bubble_title" msgid="6082910224488253378">"æ°”æłĄ"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"缡理"</string>
-    <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ć·Č慳闭ćŻčèŻæłĄă€‚"</string>
+    <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ć·Čć…łé—­æ¶ˆæŻæ°”æłĄă€‚"</string>
     <string name="restart_button_description" msgid="4564728020654658478">"ç‚čæŒ‰ćłćŻé‡ćŻæ­€ćș”ç”šïŒŒèŽ·ćŸ—æ›Žć„œçš„è§†è§‰äœ“éȘŒ"</string>
     <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"朹“èźŸçœź”䞭曎æ”čæ­€ćș”ç”šçš„ćźœé«˜æŻ”"</string>
     <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"曎æ”čé«˜ćźœæŻ”"</string>
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index 8f9de61..28e7098 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -413,6 +413,28 @@
     <!-- Height of desktop mode caption for fullscreen tasks. -->
     <dimen name="desktop_mode_fullscreen_decor_caption_height">36dp</dimen>
 
+    <!-- Width of desktop mode caption for fullscreen tasks. -->
+    <dimen name="desktop_mode_fullscreen_decor_caption_width">128dp</dimen>
+
+    <!-- Required empty space to be visible for partially offscreen tasks. -->
+    <dimen name="freeform_required_visible_empty_space_in_header">48dp</dimen>
+
+    <!-- Required empty space to be visible for partially offscreen tasks on a smaller screen. -->
+    <dimen name="small_screen_required_visible_empty_space_in_header">12dp</dimen>
+
+    <!-- 32dp width back button + 10dp margin -->
+    <dimen name="caption_left_buttons_width">32dp</dimen>
+
+    <!-- (32 dp buttons + 10dp margins) * 3 buttons-->
+    <dimen name="caption_right_buttons_width">126dp</dimen>
+
+    <!-- 2 buttons * 48dp button size. -->
+    <dimen name="desktop_mode_right_edge_buttons_width">96dp</dimen>
+
+    <!-- 22dp padding + 24dp app icon + 16dp expand button.
+         Text varies in size, we will calculate that width separately. -->
+    <dimen name="desktop_mode_app_details_width_minus_text">62dp</dimen>
+
     <!-- The width of the maximize menu in desktop mode. -->
     <dimen name="desktop_mode_maximize_menu_width">287dp</dimen>
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java
index ac75c73..44ee561 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java
@@ -20,6 +20,7 @@
 import static android.view.WindowManager.TRANSIT_CLOSE;
 import static android.view.WindowManagerPolicyConstants.TYPE_LAYER_OFFSET;
 import static android.window.TransitionInfo.FLAG_IS_BEHIND_STARTING_WINDOW;
+import static android.window.TransitionInfo.FLAG_TRANSLUCENT;
 
 import static com.android.wm.shell.activityembedding.ActivityEmbeddingAnimationSpec.createShowSnapshotForClosingAnimation;
 import static com.android.wm.shell.transition.TransitionAnimationHelper.addBackgroundToTransition;
@@ -330,6 +331,11 @@
             if (!animation.hasExtension()) {
                 continue;
             }
+            if (adapter.mChange.hasFlags(FLAG_TRANSLUCENT)
+                    && adapter.mChange.getActivityComponent() != null) {
+                // Skip edge extension for translucent activity.
+                continue;
+            }
             final TransitionInfo.Change change = adapter.mChange;
             if (TransitionUtil.isOpeningType(adapter.mChange.getMode())) {
                 // Need to screenshot after startTransaction is applied otherwise activity
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
index a498236..bb433db 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
@@ -176,6 +176,10 @@
     private StatusBarCustomizer mCustomizer;
     private boolean mTrackingLatency;
 
+    // Keep previous navigation type before remove mBackNavigationInfo.
+    @BackNavigationInfo.BackTargetType
+    private int mPreviousNavigationType;
+
     public BackAnimationController(
             @NonNull ShellInit shellInit,
             @NonNull ShellController shellController,
@@ -403,8 +407,8 @@
         mCurrentTracker.updateStartLocation();
         // Dispatch onBackStarted, only to app callbacks.
         // System callbacks will receive onBackStarted when the remote animation starts.
-        if (!shouldDispatchToAnimator()) {
-            tryDispatchOnBackStarted(mActiveCallback, mCurrentTracker.createStartEvent(null));
+        if (!shouldDispatchToAnimator() && mActiveCallback != null) {
+            tryDispatchAppOnBackStarted(mActiveCallback, mCurrentTracker.createStartEvent(null));
         }
     }
 
@@ -507,7 +511,7 @@
             mActiveCallback = mBackNavigationInfo.getOnBackInvokedCallback();
             // App is handling back animation. Cancel system animation latency tracking.
             cancelLatencyTracking();
-            tryDispatchOnBackStarted(mActiveCallback, touchTracker.createStartEvent(null));
+            tryDispatchAppOnBackStarted(mActiveCallback, touchTracker.createStartEvent(null));
         }
     }
 
@@ -551,14 +555,24 @@
                 && mBackNavigationInfo.isPrepareRemoteAnimation();
     }
 
-    private void tryDispatchOnBackStarted(IOnBackInvokedCallback callback,
+    private void tryDispatchAppOnBackStarted(
+            IOnBackInvokedCallback callback,
             BackMotionEvent backEvent) {
-        if (callback == null || mOnBackStartDispatched) {
+        if (mOnBackStartDispatched && callback != null) {
+            return;
+        }
+        dispatchOnBackStarted(callback, backEvent);
+        mOnBackStartDispatched = true;
+    }
+
+    private void dispatchOnBackStarted(
+            IOnBackInvokedCallback callback,
+            BackMotionEvent backEvent) {
+        if (callback == null) {
             return;
         }
         try {
             callback.onBackStarted(backEvent);
-            mOnBackStartDispatched = true;
         } catch (RemoteException e) {
             Log.e(TAG, "dispatchOnBackStarted error: ", e);
         }
@@ -861,6 +875,7 @@
         mShellBackAnimationRegistry.resetDefaultCrossActivity();
         cancelLatencyTracking();
         if (mBackNavigationInfo != null) {
+            mPreviousNavigationType = mBackNavigationInfo.getType();
             mBackNavigationInfo.onBackNavigationFinished(triggerBack);
             mBackNavigationInfo = null;
         }
@@ -940,9 +955,17 @@
 
                                     if (apps.length >= 1) {
                                         mCurrentTracker.updateStartLocation();
-                                        tryDispatchOnBackStarted(
-                                                mActiveCallback,
-                                                mCurrentTracker.createStartEvent(apps[0]));
+                                        BackMotionEvent startEvent =
+                                                mCurrentTracker.createStartEvent(apps[0]);
+                                        // {@code mActiveCallback} is the callback from
+                                        // the BackAnimationRunners and not a real app-side
+                                        // callback. We also dispatch to the app-side callback
+                                        // (which should be a system callback with PRIORITY_SYSTEM)
+                                        // to keep consistent with app registered callbacks.
+                                        dispatchOnBackStarted(mActiveCallback, startEvent);
+                                        tryDispatchAppOnBackStarted(
+                                                mBackNavigationInfo.getOnBackInvokedCallback(),
+                                                startEvent);
                                     }
 
                                     // Dispatch the first progress after animation start for
@@ -965,7 +988,9 @@
                         mShellExecutor.execute(
                                 () -> {
                                     if (!mShellBackAnimationRegistry.cancel(
-                                            mBackNavigationInfo.getType())) {
+                                            mBackNavigationInfo != null
+                                                    ? mBackNavigationInfo.getType()
+                                                    : mPreviousNavigationType)) {
                                         return;
                                     }
                                     if (!mBackGestureStarted) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java
index 80fc3a8..ac2a1c8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java
@@ -136,6 +136,9 @@
         mStartTaskRect.set(mClosingTarget.windowConfiguration.getBounds());
         mStartTaskRect.offsetTo(0, 0);
 
+        // inset bottom in case of pinned taskbar being present
+        mStartTaskRect.inset(0, 0, 0, mClosingTarget.contentInsets.bottom);
+
         // Draw background.
         mBackground.ensureBackground(mClosingTarget.windowConfiguration.getBounds(),
                 BACKGROUNDCOLOR, mTransaction);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/TouchTracker.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/TouchTracker.java
index 6213f62..8f04f12 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/TouchTracker.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/TouchTracker.java
@@ -63,6 +63,10 @@
         if ((touchX < mStartThresholdX && mSwipeEdge == BackEvent.EDGE_LEFT)
                 || (touchX > mStartThresholdX && mSwipeEdge == BackEvent.EDGE_RIGHT)) {
             mStartThresholdX = touchX;
+            if ((mSwipeEdge == BackEvent.EDGE_LEFT && mStartThresholdX < mInitTouchX)
+                    || (mSwipeEdge == BackEvent.EDGE_RIGHT && mStartThresholdX > mInitTouchX)) {
+                mInitTouchX = mStartThresholdX;
+            }
         }
         mLatestTouchX = touchX;
         mLatestTouchY = touchY;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
index 662a5c4..a76bd26 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
@@ -95,7 +95,6 @@
     private int mMinimumFlyoutWidthLargeScreen;
 
     private PointF mRestingStackPosition;
-    private int[] mPaddings = new int[4];
 
     private boolean mShowingInBubbleBar;
     private final Point mBubbleBarPosition = new Point();
@@ -344,46 +343,44 @@
         final int pointerTotalHeight = getPointerSize();
         final int expandedViewLargeScreenInsetFurthestEdge =
                 getExpandedViewLargeScreenInsetFurthestEdge(isOverflow);
+        int[] paddings = new int[4];
         if (mDeviceConfig.isLargeScreen()) {
             // Note:
             // If we're in portrait OR if we're a small tablet, then the two insets values will
             // be equal. If we're landscape and a large tablet, the two values will be different.
             // [left, top, right, bottom]
-            mPaddings[0] = onLeft
+            paddings[0] = onLeft
                     ? mExpandedViewLargeScreenInsetClosestEdge - pointerTotalHeight
                     : expandedViewLargeScreenInsetFurthestEdge;
-            mPaddings[1] = 0;
-            mPaddings[2] = onLeft
+            paddings[1] = 0;
+            paddings[2] = onLeft
                     ? expandedViewLargeScreenInsetFurthestEdge
                     : mExpandedViewLargeScreenInsetClosestEdge - pointerTotalHeight;
             // Overflow doesn't show manage button / get padding from it so add padding here
-            mPaddings[3] = isOverflow ? mExpandedViewPadding : 0;
-            return mPaddings;
+            paddings[3] = isOverflow ? mExpandedViewPadding : 0;
+            return paddings;
         } else {
             int leftPadding = mInsets.left + mExpandedViewPadding;
             int rightPadding = mInsets.right + mExpandedViewPadding;
-            final float expandedViewWidth = isOverflow
-                    ? mOverflowWidth
-                    : mExpandedViewLargeScreenWidth;
             if (showBubblesVertically()) {
                 if (!onLeft) {
                     rightPadding += mBubbleSize - pointerTotalHeight;
                     leftPadding += isOverflow
-                            ? (mPositionRect.width() - rightPadding - expandedViewWidth)
+                            ? (mPositionRect.width() - rightPadding - mOverflowWidth)
                             : 0;
                 } else {
                     leftPadding += mBubbleSize - pointerTotalHeight;
                     rightPadding += isOverflow
-                            ? (mPositionRect.width() - leftPadding - expandedViewWidth)
+                            ? (mPositionRect.width() - leftPadding - mOverflowWidth)
                             : 0;
                 }
             }
             // [left, top, right, bottom]
-            mPaddings[0] = leftPadding;
-            mPaddings[1] = showBubblesVertically() ? 0 : mPointerMargin;
-            mPaddings[2] = rightPadding;
-            mPaddings[3] = 0;
-            return mPaddings;
+            paddings[0] = leftPadding;
+            paddings[1] = showBubblesVertically() ? 0 : mPointerMargin;
+            paddings[2] = rightPadding;
+            paddings[3] = 0;
+            return paddings;
         }
     }
 
@@ -395,7 +392,7 @@
     }
 
     /** Gets the y position of the expanded view if it was top-aligned. */
-    public float getExpandedViewYTopAligned() {
+    public int getExpandedViewYTopAligned() {
         final int top = getAvailableRect().top;
         if (showBubblesVertically()) {
             return top - mPointerWidth + mExpandedViewPadding;
@@ -413,7 +410,7 @@
             return getExpandedViewHeightForLargeScreen();
         }
         // Subtract top insets because availableRect.height would account for that
-        int expandedContainerY = (int) getExpandedViewYTopAligned() - getInsets().top;
+        int expandedContainerY = getExpandedViewYTopAligned() - getInsets().top;
         int paddingTop = showBubblesVertically()
                 ? 0
                 : mPointerHeight;
@@ -474,11 +471,11 @@
     public float getExpandedViewY(BubbleViewProvider bubble, float bubblePosition) {
         boolean isOverflow = bubble == null || BubbleOverflow.KEY.equals(bubble.getKey());
         float expandedViewHeight = getExpandedViewHeight(bubble);
-        float topAlignment = getExpandedViewYTopAligned();
+        int topAlignment = getExpandedViewYTopAligned();
         int manageButtonHeight =
                 isOverflow ? mExpandedViewPadding : mManageButtonHeightIncludingMargins;
 
-        // On largescreen portrait bubbles are bottom aligned.
+        // On large screen portrait bubbles are bottom aligned.
         if (areBubblesBottomAligned() && expandedViewHeight == MAX_HEIGHT) {
             return mPositionRect.bottom - manageButtonHeight
                     - getExpandedViewHeightForLargeScreen() - mPointerWidth;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index 470a825..ca28312 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -1177,14 +1177,17 @@
             if (mStackAnimationController.isStackOnLeftSide()) {
                 int availableRectOffsetX =
                         mPositioner.getAvailableRect().left - mPositioner.getScreenRect().left;
-                animate().translationX(-(mBubbleSize + availableRectOffsetX)).start();
+                mBubbleContainer
+                        .animate()
+                        .translationX(-(mBubbleSize + availableRectOffsetX))
+                        .start();
             } else {
                 int availableRectOffsetX =
                         mPositioner.getAvailableRect().right - mPositioner.getScreenRect().right;
-                animate().translationX(mBubbleSize - availableRectOffsetX).start();
+                mBubbleContainer.animate().translationX(mBubbleSize - availableRectOffsetX).start();
             }
         } else {
-            animate().translationX(0).start();
+            mBubbleContainer.animate().translationX(0).start();
         }
     };
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java
index 02af2d0..7798aa7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java
@@ -420,7 +420,7 @@
             bubbleView.setTranslationY(y);
         }
 
-        final float expandedY = mPositioner.getExpandedViewYTopAligned();
+        final int expandedY = mPositioner.getExpandedViewYTopAligned();
         final boolean draggedOutEnough =
                 y > expandedY + mBubbleSizePx || y < expandedY - mBubbleSizePx;
         if (draggedOutEnough != mBubbleDraggedOutEnough) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java
index d073f1d..66c0c96 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java
@@ -70,7 +70,7 @@
     private @Nullable Supplier<Rect> mLayerBoundsSupplier;
     private @Nullable Listener mListener;
 
-    private BubbleBarHandleView mHandleView = new BubbleBarHandleView(getContext());
+    private BubbleBarHandleView mHandleView;
     private @Nullable TaskView mTaskView;
     private @Nullable BubbleOverflowContainerView mOverflowView;
 
@@ -111,7 +111,7 @@
         setElevation(getResources().getDimensionPixelSize(R.dimen.bubble_elevation));
         mCaptionHeight = context.getResources().getDimensionPixelSize(
                 R.dimen.bubble_bar_expanded_view_caption_height);
-        addView(mHandleView);
+        mHandleView = findViewById(R.id.bubble_bar_handle_view);
         applyThemeAttrs();
         setClipToOutline(true);
         setOutlineProvider(new ViewOutlineProvider() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenUtils.java
index 0693543..662f325 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenUtils.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenUtils.java
@@ -16,7 +16,7 @@
 
 package com.android.wm.shell.common.split;
 
-import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_ALL_KINDS_WITH_ALL_PINNED;
 
 import static com.android.wm.shell.common.split.SplitScreenConstants.CONTROLLED_ACTIVITY_TYPES;
 import static com.android.wm.shell.common.split.SplitScreenConstants.CONTROLLED_WINDOWING_MODES;
@@ -24,19 +24,27 @@
 import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
 import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
 
-import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.PendingIntent;
-import android.content.Context;
+import android.content.ComponentName;
 import android.content.Intent;
+import android.content.pm.LauncherApps;
+import android.content.pm.ShortcutInfo;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.graphics.Rect;
+import android.os.UserHandle;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 
 import com.android.internal.util.ArrayUtils;
 import com.android.wm.shell.Flags;
 import com.android.wm.shell.ShellTaskOrganizer;
 
+import java.util.Arrays;
+import java.util.List;
+
 /** Helper utility class for split screen components to use. */
 public class SplitScreenUtils {
     /** Reverse the split position. */
@@ -135,4 +143,28 @@
             return isLandscape;
         }
     }
+
+    /** Returns the component from a PendingIntent */
+    @Nullable
+    public static ComponentName getComponent(@Nullable PendingIntent pendingIntent) {
+        if (pendingIntent == null || pendingIntent.getIntent() == null) {
+            return null;
+        }
+        return pendingIntent.getIntent().getComponent();
+    }
+
+    /** Returns the component from a shortcut */
+    @Nullable
+    public static ComponentName getShortcutComponent(@NonNull String packageName, String shortcutId,
+            @NonNull UserHandle user, @NonNull LauncherApps launcherApps) {
+        LauncherApps.ShortcutQuery query = new LauncherApps.ShortcutQuery();
+        query.setPackage(packageName);
+        query.setShortcutIds(Arrays.asList(shortcutId));
+        query.setQueryFlags(FLAG_MATCH_ALL_KINDS_WITH_ALL_PINNED);
+        List<ShortcutInfo> shortcuts = launcherApps.getShortcuts(query, user);
+        ShortcutInfo info = shortcuts != null && shortcuts.size() > 0
+                ? shortcuts.get(0)
+                : null;
+        return info != null ? info.getActivity() : null;
+    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManager.java
index afd3b14..81d1399 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManager.java
@@ -232,6 +232,7 @@
         return taskInfo.appCompatTaskInfo.topActivityEligibleForUserAspectRatioButton
                 && (taskInfo.appCompatTaskInfo.topActivityBoundsLetterboxed
                     || taskInfo.appCompatTaskInfo.isUserFullscreenOverrideEnabled)
+                && !taskInfo.appCompatTaskInfo.isSystemFullscreenOverrideEnabled
                 && Intent.ACTION_MAIN.equals(intent.getAction())
                 && intent.hasCategory(Intent.CATEGORY_LAUNCHER)
                 && (!mUserAspectRatioButtonShownChecker.get() || isShowingButton());
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index 71bf487..36f06e8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -210,7 +210,6 @@
             SyncTransactionQueue syncQueue,
             Transitions transitions,
             Optional<DesktopTasksController> desktopTasksController,
-            RecentsTransitionHandler recentsTransitionHandler,
             RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer) {
         if (DesktopModeStatus.isEnabled()) {
             return new DesktopModeWindowDecorViewModel(
@@ -226,7 +225,6 @@
                     syncQueue,
                     transitions,
                     desktopTasksController,
-                    recentsTransitionHandler,
                     rootTaskDisplayAreaOrganizer);
         }
         return new CaptionWindowDecorViewModel(
@@ -235,7 +233,8 @@
                 mainChoreographer,
                 taskOrganizer,
                 displayController,
-                syncQueue);
+                syncQueue,
+                transitions);
     }
 
     //
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 dc82fc1..88949b2a 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
@@ -55,6 +55,26 @@
             "persist.wm.debug.desktop_stashing", false);
 
     /**
+     * Flag to indicate whether to apply shadows to windows in desktop mode.
+     */
+    private static final boolean USE_WINDOW_SHADOWS = SystemProperties.getBoolean(
+            "persist.wm.debug.desktop_use_window_shadows", true);
+
+    /**
+     * Flag to indicate whether to apply shadows to the focused window in desktop mode.
+     *
+     * Note: this flag is only relevant if USE_WINDOW_SHADOWS is false.
+     */
+    private static final boolean USE_WINDOW_SHADOWS_FOCUSED_WINDOW = SystemProperties.getBoolean(
+            "persist.wm.debug.desktop_use_window_shadows_focused_window", false);
+
+    /**
+     * Flag to indicate whether to apply shadows to windows in desktop mode.
+     */
+    private static final boolean USE_ROUNDED_CORNERS = SystemProperties.getBoolean(
+            "persist.wm.debug.desktop_use_rounded_corners", true);
+
+    /**
      * Return {@code true} is desktop windowing proto 2 is enabled
      */
     public static boolean isEnabled() {
@@ -81,4 +101,21 @@
     public static boolean isStashingEnabled() {
         return IS_STASHING_ENABLED;
     }
+
+    /**
+     * Return whether to use window shadows.
+     *
+     * @param isFocusedWindow whether the window to apply shadows to is focused
+     */
+    public static boolean useWindowShadow(boolean isFocusedWindow) {
+        return USE_WINDOW_SHADOWS
+            || (USE_WINDOW_SHADOWS_FOCUSED_WINDOW && isFocusedWindow);
+    }
+
+    /**
+     * Return whether to use rounded corners for windows.
+     */
+    public static boolean useRoundedCorners() {
+        return USE_ROUNDED_CORNERS;
+    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
index 144555d..b1c43c1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -320,9 +320,8 @@
     }
 
     /** Move a task with given `taskId` to fullscreen */
-    fun moveToFullscreen(taskId: Int, windowDecor: DesktopModeWindowDecoration) {
+    fun moveToFullscreen(taskId: Int) {
         shellTaskOrganizer.getRunningTaskInfo(taskId)?.let { task ->
-            windowDecor.incrementRelayoutBlock()
             moveToFullscreenWithAnimation(task, task.positionInParent)
         }
     }
@@ -721,6 +720,9 @@
             finishTransaction: SurfaceControl.Transaction
     ) {
         // Add rounded corners to freeform windows
+        if (!DesktopModeStatus.useRoundedCorners()) {
+            return
+        }
         val cornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(context)
         info.changes
                 .filter { it.taskInfo?.windowingMode == WINDOWING_MODE_FREEFORM }
@@ -903,20 +905,17 @@
      * @param position position of surface when drag ends.
      * @param inputCoordinate the coordinates of the motion event
      * @param taskBounds the updated bounds of the task being dragged.
-     * @param windowDecor the window decoration for the task being dragged
      */
     fun onDragPositioningEnd(
         taskInfo: RunningTaskInfo,
         position: Point,
         inputCoordinate: PointF,
-        taskBounds: Rect,
-        windowDecor: DesktopModeWindowDecoration
+        taskBounds: Rect
     ) {
         if (taskInfo.configuration.windowConfiguration.windowingMode != WINDOWING_MODE_FREEFORM) {
             return
         }
         if (taskBounds.top <= transitionAreaHeight) {
-            windowDecor.incrementRelayoutBlock()
             moveToFullscreenWithAnimation(taskInfo, position)
         }
         if (inputCoordinate.x <= transitionAreaWidth) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ToggleResizeDesktopTaskTransitionHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ToggleResizeDesktopTaskTransitionHandler.kt
index 9debb25..0218493 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ToggleResizeDesktopTaskTransitionHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ToggleResizeDesktopTaskTransitionHandler.kt
@@ -54,8 +54,6 @@
         taskId: Int,
         windowDecoration: DesktopModeWindowDecoration
     ) {
-        // Pause relayout until the transition animation finishes.
-        windowDecoration.incrementRelayoutBlock()
         transitions.startTransition(TRANSIT_DESKTOP_MODE_TOGGLE_RESIZE, wct, this)
         taskToDecorationMap.put(taskId, windowDecoration)
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java
index 6b6a7bc..ffcc526 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java
@@ -112,7 +112,6 @@
                     onChangeTransitionReady(change, startT, finishT);
                     break;
             }
-            mWindowDecorViewModel.onTransitionReady(transition, info, change);
         }
         mTransitionToTaskInfo.put(transition, taskInfoList);
     }
@@ -153,8 +152,6 @@
 
     @Override
     public void onTransitionMerged(@NonNull IBinder merged, @NonNull IBinder playing) {
-        mWindowDecorViewModel.onTransitionMerged(merged, playing);
-
         final List<ActivityManager.RunningTaskInfo> infoOfMerged =
                 mTransitionToTaskInfo.get(merged);
         if (infoOfMerged == null) {
@@ -178,7 +175,6 @@
         final List<ActivityManager.RunningTaskInfo> taskInfo =
                 mTransitionToTaskInfo.getOrDefault(transition, Collections.emptyList());
         mTransitionToTaskInfo.remove(transition);
-        mWindowDecorViewModel.onTransitionFinished(transition);
         for (int i = 0; i < taskInfo.size(); ++i) {
             mWindowDecorViewModel.destroyWindowDecoration(taskInfo.get(i));
         }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/IPip.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/IPip.aidl
index 3906599..8b3de62 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/IPip.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/IPip.aidl
@@ -52,9 +52,10 @@
      * @param componentName ComponentName represents the Activity
      * @param destinationBounds the destination bounds the PiP window lands into
      * @param overlay an optional overlay to fade out after entering PiP
+     * @param appBounds the bounds used to set the buffer size of the optional content overlay
      */
     oneway void stopSwipePipToHome(int taskId, in ComponentName componentName,
-            in Rect destinationBounds, in SurfaceControl overlay) = 2;
+            in Rect destinationBounds, in SurfaceControl overlay, in Rect appBounds) = 2;
 
     /**
      * Notifies the swiping Activity to PiP onto home transition is aborted
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index 3635165..a9a3f78 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -334,6 +334,16 @@
     @Nullable
     SurfaceControl mPipOverlay;
 
+    /**
+     * The app bounds used for the buffer size of the
+     * {@link com.android.wm.shell.pip.PipContentOverlay.PipAppIconOverlay}.
+     *
+     * Note that this is empty if the overlay is removed or if it's some other type of overlay
+     * defined in {@link PipContentOverlay}.
+     */
+    @NonNull
+    final Rect mAppBounds = new Rect();
+
     public PipTaskOrganizer(Context context,
             @NonNull SyncTransactionQueue syncTransactionQueue,
             @NonNull PipTransitionState pipTransitionState,
@@ -464,15 +474,15 @@
      * Expect {@link #onTaskAppeared(ActivityManager.RunningTaskInfo, SurfaceControl)} afterwards.
      */
     public void stopSwipePipToHome(int taskId, ComponentName componentName, Rect destinationBounds,
-            SurfaceControl overlay) {
+            SurfaceControl overlay, Rect appBounds) {
         ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
-                "stopSwipePipToHome: %s, state=%s", componentName, mPipTransitionState);
+                "stopSwipePipToHome: %s, stat=%s", componentName, mPipTransitionState);
         // do nothing if there is no startSwipePipToHome being called before
         if (!mPipTransitionState.getInSwipePipToHomeTransition()) {
             return;
         }
         mPipBoundsState.setBounds(destinationBounds);
-        mPipOverlay = overlay;
+        setContentOverlay(overlay, appBounds);
         if (ENABLE_SHELL_TRANSITIONS && overlay != null) {
             // With Shell transition, the overlay was attached to the remote transition leash, which
             // will be removed when the current transition is finished, so we need to reparent it
@@ -1888,7 +1898,7 @@
                         "%s: trying to remove overlay (%s) which is not local reference (%s)",
                         TAG, surface, mPipOverlay);
             }
-            mPipOverlay = null;
+            clearContentOverlay();
         }
         if (mPipTransitionState.getTransitionState() == PipTransitionState.UNDEFINED) {
             // Avoid double removal, which is fatal.
@@ -1905,6 +1915,20 @@
         if (callback != null) callback.run();
     }
 
+    void clearContentOverlay() {
+        mPipOverlay = null;
+        mAppBounds.setEmpty();
+    }
+
+    void setContentOverlay(@Nullable SurfaceControl leash, @NonNull Rect appBounds) {
+        mPipOverlay = leash;
+        if (mPipOverlay != null) {
+            mAppBounds.set(appBounds);
+        } else {
+            mAppBounds.setEmpty();
+        }
+    }
+
     private void resetShadowRadius() {
         if (mPipTransitionState.getTransitionState() == PipTransitionState.UNDEFINED) {
             // mLeash is undefined when in PipTransitionState.UNDEFINED
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 f5f15d8..8e375a9 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
@@ -141,8 +141,6 @@
     /** Whether the PIP window has fade out for fixed rotation. */
     private boolean mHasFadeOut;
 
-    private Rect mInitBounds = new Rect();
-
     /** Used for setting transform to a transaction from animator. */
     private final PipAnimationController.PipTransactionHandler mTransactionConsumer =
             new PipAnimationController.PipTransactionHandler() {
@@ -465,12 +463,13 @@
                     mSurfaceTransactionHelper.crop(tx, leash, destinationBounds)
                             .resetScale(tx, leash, destinationBounds)
                             .round(tx, leash, true /* applyCornerRadius */);
-                    if (mPipOrganizer.mPipOverlay != null && !mInitBounds.isEmpty()) {
+                    final Rect appBounds = mPipOrganizer.mAppBounds;
+                    if (mPipOrganizer.mPipOverlay != null && !appBounds.isEmpty()) {
                         // Resetting the scale for pinned task while re-adjusting its crop,
                         // also scales the overlay. So we need to update the overlay leash too.
                         Rect overlayBounds = new Rect(destinationBounds);
                         final int overlaySize = PipContentOverlay.PipAppIconOverlay
-                                .getOverlaySize(mInitBounds, destinationBounds);
+                                .getOverlaySize(appBounds, destinationBounds);
 
                         overlayBounds.offsetTo(
                                 (destinationBounds.width() - overlaySize) / 2,
@@ -479,7 +478,6 @@
                                 mPipOrganizer.mPipOverlay, overlayBounds);
                     }
                 }
-                mInitBounds.setEmpty();
                 wct.setBoundsChangeTransaction(taskInfo.token, tx);
             }
             final int displayRotation = taskInfo.getConfiguration().windowConfiguration
@@ -617,7 +615,7 @@
         // if overlay is present remove it immediately, as exit transition came before it faded out
         if (mPipOrganizer.mPipOverlay != null) {
             startTransaction.remove(mPipOrganizer.mPipOverlay);
-            clearPipOverlay();
+            mPipOrganizer.clearContentOverlay();
         }
         if (pipChange == null) {
             ProtoLog.w(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
@@ -951,9 +949,6 @@
         final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds();
         final Rect currentBounds = pipChange.getStartAbsBounds();
 
-        // Cache the start bounds for overlay manipulations as a part of finishCallback.
-        mInitBounds.set(currentBounds);
-
         int rotationDelta = deltaRotation(startRotation, endRotation);
         Rect sourceHintRect = PipBoundsAlgorithm.getValidSourceHintRect(
                 taskInfo.pictureInPictureParams, currentBounds, destinationBounds);
@@ -992,10 +987,12 @@
                     0 /* startingAngle */, rotationDelta);
             if (sourceHintRect == null) {
                 // We use content overlay when there is no source rect hint to enter PiP use bounds
-                // animation.
+                // animation. We also temporarily disallow app icon overlay and use color overlay
+                // instead when in fixed rotation enter PiP in button nav with no sourceRectHint.
+                // TODO(b/319286295): Fix App Icon Overlay animation in fixed rotation in btn nav.
                 // TODO(b/272819817): cleanup the null-check and extra logging.
                 final boolean hasTopActivityInfo = taskInfo.topActivityInfo != null;
-                if (hasTopActivityInfo) {
+                if (hasTopActivityInfo && mFixedRotationState != FIXED_ROTATION_TRANSITION) {
                     animator.setAppIconContentOverlay(
                             mContext, currentBounds, destinationBounds, taskInfo.topActivityInfo,
                             mPipBoundsState.getLauncherState().getAppIconSizePx());
@@ -1022,7 +1019,7 @@
         } else {
             throw new RuntimeException("Unrecognized animation type: " + enterAnimationType);
         }
-        mPipOrganizer.mPipOverlay = animator.getContentOverlayLeash();
+        mPipOrganizer.setContentOverlay(animator.getContentOverlayLeash(), currentBounds);
         animator.setTransitionDirection(TRANSITION_DIRECTION_TO_PIP)
                 .setPipAnimationCallback(mPipAnimationCallback)
                 .setDuration(mEnterExitAnimationDuration);
@@ -1073,10 +1070,6 @@
             ProtoLog.w(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
                     "%s: SwipePipToHome should not use fixed rotation %d", TAG, mEndFixedRotation);
         }
-        Rect appBounds = pipTaskInfo.configuration.windowConfiguration.getAppBounds();
-        if (mFixedRotationState == FIXED_ROTATION_CALLBACK && appBounds != null) {
-            mInitBounds.set(appBounds);
-        }
         final SurfaceControl swipePipToHomeOverlay = mPipOrganizer.mPipOverlay;
         if (swipePipToHomeOverlay != null) {
             // Launcher fade in the overlay on top of the fullscreen Task. It is possible we
@@ -1106,7 +1099,7 @@
         sendOnPipTransitionFinished(TRANSITION_DIRECTION_TO_PIP);
         if (swipePipToHomeOverlay != null) {
             mPipOrganizer.fadeOutAndRemoveOverlay(swipePipToHomeOverlay,
-                    this::clearPipOverlay /* callback */, false /* withStartDelay */);
+                    null /* callback */, false /* withStartDelay */);
         }
         mPipTransitionState.setInSwipePipToHomeTransition(false);
     }
@@ -1250,10 +1243,6 @@
         mPipMenuController.updateMenuBounds(destinationBounds);
     }
 
-    private void clearPipOverlay() {
-        mPipOrganizer.mPipOverlay = null;
-    }
-
     @Override
     public void dump(PrintWriter pw, String prefix) {
         final String innerPrefix = prefix + "  ";
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index 63f20fd..238e6b5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -982,8 +982,9 @@
     }
 
     private void stopSwipePipToHome(int taskId, ComponentName componentName, Rect destinationBounds,
-            SurfaceControl overlay) {
-        mPipTaskOrganizer.stopSwipePipToHome(taskId, componentName, destinationBounds, overlay);
+            SurfaceControl overlay, Rect appBounds) {
+        mPipTaskOrganizer.stopSwipePipToHome(taskId, componentName, destinationBounds, overlay,
+                appBounds);
     }
 
     private void abortSwipePipToHome(int taskId, ComponentName componentName) {
@@ -1280,13 +1281,13 @@
 
         @Override
         public void stopSwipePipToHome(int taskId, ComponentName componentName,
-                Rect destinationBounds, SurfaceControl overlay) {
+                Rect destinationBounds, SurfaceControl overlay, Rect appBounds) {
             if (overlay != null) {
                 overlay.setUnreleasedWarningCallSite("PipController.stopSwipePipToHome");
             }
             executeRemoteCallWithTaskPermission(mController, "stopSwipePipToHome",
                     (controller) -> controller.stopSwipePipToHome(
-                            taskId, componentName, destinationBounds, overlay));
+                            taskId, componentName, destinationBounds, overlay, appBounds));
         }
 
         @Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
index 48a0a46..3b0e7c1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
@@ -19,6 +19,7 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.view.WindowManager.TRANSIT_OPEN;
 import static android.view.WindowManager.TRANSIT_PIP;
+import static android.view.WindowManager.TRANSIT_TO_FRONT;
 
 import static com.android.wm.shell.transition.Transitions.TRANSIT_EXIT_PIP;
 
@@ -54,6 +55,8 @@
     @Nullable
     private WindowContainerToken mPipTaskToken;
     @Nullable
+    private IBinder mEnterTransition;
+    @Nullable
     private IBinder mAutoEnterButtonNavTransition;
     @Nullable
     private IBinder mExitViaExpandTransition;
@@ -98,11 +101,8 @@
     @Override
     public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
             @NonNull TransitionRequestInfo request) {
-        if (isAutoEnterInButtonNavigation(request)) {
-            mAutoEnterButtonNavTransition = transition;
-            return getEnterPipTransaction(transition, request);
-        } else if (isLegacyEnter(request)) {
-            mLegacyEnterTransition = transition;
+        if (isAutoEnterInButtonNavigation(request) || isEnterPictureInPictureModeRequest(request)) {
+            mEnterTransition = transition;
             return getEnterPipTransaction(transition, request);
         }
         return null;
@@ -111,12 +111,9 @@
     @Override
     public void augmentRequest(@NonNull IBinder transition, @NonNull TransitionRequestInfo request,
             @NonNull WindowContainerTransaction outWct) {
-        if (isAutoEnterInButtonNavigation(request)) {
+        if (isAutoEnterInButtonNavigation(request) || isEnterPictureInPictureModeRequest(request)) {
             outWct.merge(getEnterPipTransaction(transition, request), true /* transfer */);
-            mAutoEnterButtonNavTransition = transition;
-        } else if (isLegacyEnter(request)) {
-            outWct.merge(getEnterPipTransaction(transition, request), true /* transfer */);
-            mLegacyEnterTransition = transition;
+            mEnterTransition = transition;
         }
     }
 
@@ -162,7 +159,7 @@
                 && pipTask.pictureInPictureParams.isAutoEnterEnabled();
     }
 
-    private boolean isLegacyEnter(@NonNull TransitionRequestInfo requestInfo) {
+    private boolean isEnterPictureInPictureModeRequest(@NonNull TransitionRequestInfo requestInfo) {
         return requestInfo.getType() == TRANSIT_PIP;
     }
 
@@ -172,13 +169,15 @@
             @NonNull SurfaceControl.Transaction startTransaction,
             @NonNull SurfaceControl.Transaction finishTransaction,
             @NonNull Transitions.TransitionFinishCallback finishCallback) {
-        if (transition == mAutoEnterButtonNavTransition) {
-            mAutoEnterButtonNavTransition = null;
-            return startAutoEnterButtonNavAnimation(info, startTransaction, finishTransaction,
-                    finishCallback);
-        } else if (transition == mLegacyEnterTransition) {
-            mLegacyEnterTransition = null;
-            return startLegacyEnterAnimation(info, startTransaction, finishTransaction,
+        if (transition == mEnterTransition) {
+            mEnterTransition = null;
+            if (isLegacyEnter(info)) {
+                // If this is a legacy-enter-pip (auto-enter is off and PiP activity went to pause),
+                // then we should run an ALPHA type (cross-fade) animation.
+                return startAlphaTypeEnterAnimation(info, startTransaction, finishTransaction,
+                        finishCallback);
+            }
+            return startBoundsTypeEnterAnimation(info, startTransaction, finishTransaction,
                     finishCallback);
         } else if (transition == mExitViaExpandTransition) {
             mExitViaExpandTransition = null;
@@ -187,7 +186,15 @@
         return false;
     }
 
-    private boolean startAutoEnterButtonNavAnimation(@NonNull TransitionInfo info,
+    private boolean isLegacyEnter(@NonNull TransitionInfo info) {
+        TransitionInfo.Change pipChange = getPipChange(info);
+        // If the only change in the changes list is a TO_FRONT mode PiP task,
+        // then this is legacy-enter PiP.
+        return pipChange != null && pipChange.getMode() == TRANSIT_TO_FRONT
+                && info.getChanges().size() == 1;
+    }
+
+    private boolean startBoundsTypeEnterAnimation(@NonNull TransitionInfo info,
             @NonNull SurfaceControl.Transaction startTransaction,
             @NonNull SurfaceControl.Transaction finishTransaction,
             @NonNull Transitions.TransitionFinishCallback finishCallback) {
@@ -205,7 +212,7 @@
         return true;
     }
 
-    private boolean startLegacyEnterAnimation(@NonNull TransitionInfo info,
+    private boolean startAlphaTypeEnterAnimation(@NonNull TransitionInfo info,
             @NonNull SurfaceControl.Transaction startTransaction,
             @NonNull SurfaceControl.Transaction finishTransaction,
             @NonNull Transitions.TransitionFinishCallback finishCallback) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
index ccc3438..e421356 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
@@ -347,7 +347,7 @@
                 continue;
             }
 
-            final int pairedTaskId = mSplitTasks.get(taskInfo.taskId);
+            final int pairedTaskId = mSplitTasks.get(taskInfo.taskId, INVALID_TASK_ID);
             if (pairedTaskId != INVALID_TASK_ID && rawMapping.contains(
                     pairedTaskId)) {
                 final ActivityManager.RecentTaskInfo pairedTaskInfo = rawMapping.get(pairedTaskId);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index 7b57097..880d952 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -23,12 +23,15 @@
 import static android.content.Intent.FLAG_ACTIVITY_NO_USER_ACTION;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.RemoteAnimationTarget.MODE_OPENING;
+import static android.view.WindowManager.PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI;
 
 import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission;
 import static com.android.wm.shell.common.split.SplitScreenConstants.KEY_EXTRA_WIDGET_INTENT;
 import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
 import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
 import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
+import static com.android.wm.shell.common.split.SplitScreenUtils.getComponent;
+import static com.android.wm.shell.common.split.SplitScreenUtils.getShortcutComponent;
 import static com.android.wm.shell.common.split.SplitScreenUtils.isValidToSplit;
 import static com.android.wm.shell.common.split.SplitScreenUtils.reverseSplitPosition;
 import static com.android.wm.shell.common.split.SplitScreenUtils.samePackage;
@@ -47,6 +50,8 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.LauncherApps;
+import android.content.pm.PackageManager;
 import android.content.pm.ShortcutInfo;
 import android.graphics.Rect;
 import android.os.Bundle;
@@ -171,6 +176,8 @@
     private final ShellTaskOrganizer mTaskOrganizer;
     private final SyncTransactionQueue mSyncQueue;
     private final Context mContext;
+    private final PackageManager mPackageManager;
+    private final LauncherApps mLauncherApps;
     private final RootTaskDisplayAreaOrganizer mRootTDAOrganizer;
     private final ShellExecutor mMainExecutor;
     private final SplitScreenImpl mImpl = new SplitScreenImpl();
@@ -186,7 +193,8 @@
     private final Optional<WindowDecorViewModel> mWindowDecorViewModel;
     private final Optional<DesktopTasksController> mDesktopTasksController;
     private final SplitScreenShellCommandHandler mSplitScreenShellCommandHandler;
-    private final String[] mAppsSupportMultiInstances;
+    // A static allow list of apps which support multi-instance
+    private final String[] mAppsSupportingMultiInstance;
 
     @VisibleForTesting
     StageCoordinator mStageCoordinator;
@@ -220,6 +228,8 @@
         mTaskOrganizer = shellTaskOrganizer;
         mSyncQueue = syncQueue;
         mContext = context;
+        mPackageManager = context.getPackageManager();
+        mLauncherApps = context.getSystemService(LauncherApps.class);
         mRootTDAOrganizer = rootTDAOrganizer;
         mMainExecutor = mainExecutor;
         mDisplayController = displayController;
@@ -242,7 +252,7 @@
 
         // TODO(255224696): Remove the config once having a way for client apps to opt-in
         //                  multi-instances split.
-        mAppsSupportMultiInstances = mContext.getResources()
+        mAppsSupportingMultiInstance = mContext.getResources()
                 .getStringArray(R.array.config_appsSupportMultiInstancesSplit);
     }
 
@@ -266,12 +276,15 @@
             WindowDecorViewModel windowDecorViewModel,
             DesktopTasksController desktopTasksController,
             ShellExecutor mainExecutor,
-            StageCoordinator stageCoordinator) {
+            StageCoordinator stageCoordinator,
+            String[] appsSupportingMultiInstance) {
         mShellCommandHandler = shellCommandHandler;
         mShellController = shellController;
         mTaskOrganizer = shellTaskOrganizer;
         mSyncQueue = syncQueue;
         mContext = context;
+        mPackageManager = context.getPackageManager();
+        mLauncherApps = context.getSystemService(LauncherApps.class);
         mRootTDAOrganizer = rootTDAOrganizer;
         mMainExecutor = mainExecutor;
         mDisplayController = displayController;
@@ -288,8 +301,7 @@
         mStageCoordinator = stageCoordinator;
         mSplitScreenShellCommandHandler = new SplitScreenShellCommandHandler(this);
         shellInit.addInitCallback(this::onInit, this);
-        mAppsSupportMultiInstances = mContext.getResources()
-                .getStringArray(R.array.config_appsSupportMultiInstancesSplit);
+        mAppsSupportingMultiInstance = appsSupportingMultiInstance;
     }
 
     public SplitScreen asSplitScreen() {
@@ -588,7 +600,8 @@
 
         if (samePackage(packageName, getPackageName(reverseSplitPosition(position)),
                 user.getIdentifier(), getUserId(reverseSplitPosition(position)))) {
-            if (supportMultiInstancesSplit(packageName)) {
+            if (supportsMultiInstanceSplit(getShortcutComponent(packageName, shortcutId, user,
+                    mLauncherApps))) {
                 activityOptions.setApplyMultipleTaskFlagForShortcut(true);
                 ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
             } else if (isSplitScreenVisible()) {
@@ -609,7 +622,7 @@
                 activityOptions.toBundle(), user);
     }
 
-    void startShortcutAndTaskWithLegacyTransition(ShortcutInfo shortcutInfo,
+    void startShortcutAndTaskWithLegacyTransition(@NonNull ShortcutInfo shortcutInfo,
             @Nullable Bundle options1, int taskId, @Nullable Bundle options2,
             @SplitPosition int splitPosition, @PersistentSnapPosition int snapPosition,
             RemoteAnimationAdapter adapter, InstanceId instanceId) {
@@ -621,7 +634,7 @@
         final int userId1 = shortcutInfo.getUserId();
         final int userId2 = SplitScreenUtils.getUserId(taskId, mTaskOrganizer);
         if (samePackage(packageName1, packageName2, userId1, userId2)) {
-            if (supportMultiInstancesSplit(shortcutInfo.getPackage())) {
+            if (supportsMultiInstanceSplit(shortcutInfo.getActivity())) {
                 activityOptions.setApplyMultipleTaskFlagForShortcut(true);
                 ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
             } else {
@@ -640,7 +653,7 @@
                 instanceId);
     }
 
-    void startShortcutAndTask(ShortcutInfo shortcutInfo, @Nullable Bundle options1,
+    void startShortcutAndTask(@NonNull ShortcutInfo shortcutInfo, @Nullable Bundle options1,
             int taskId, @Nullable Bundle options2, @SplitPosition int splitPosition,
             @PersistentSnapPosition int snapPosition, @Nullable RemoteTransition remoteTransition,
             InstanceId instanceId) {
@@ -653,7 +666,7 @@
         final int userId1 = shortcutInfo.getUserId();
         final int userId2 = SplitScreenUtils.getUserId(taskId, mTaskOrganizer);
         if (samePackage(packageName1, packageName2, userId1, userId2)) {
-            if (supportMultiInstancesSplit(packageName1)) {
+            if (supportsMultiInstanceSplit(shortcutInfo.getActivity())) {
                 activityOptions.setApplyMultipleTaskFlagForShortcut(true);
                 ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
             } else {
@@ -692,7 +705,7 @@
         final String packageName2 = SplitScreenUtils.getPackageName(taskId, mTaskOrganizer);
         final int userId2 = SplitScreenUtils.getUserId(taskId, mTaskOrganizer);
         if (samePackage(packageName1, packageName2, userId1, userId2)) {
-            if (supportMultiInstancesSplit(packageName1)) {
+            if (supportsMultiInstanceSplit(getComponent(pendingIntent))) {
                 fillInIntent = new Intent();
                 fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
                 ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
@@ -722,7 +735,7 @@
         final int userId2 = SplitScreenUtils.getUserId(taskId, mTaskOrganizer);
         boolean setSecondIntentMultipleTask = false;
         if (samePackage(packageName1, packageName2, userId1, userId2)) {
-            if (supportMultiInstancesSplit(packageName1)) {
+            if (supportsMultiInstanceSplit(getComponent(pendingIntent))) {
                 setSecondIntentMultipleTask = true;
                 ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
             } else {
@@ -757,7 +770,7 @@
         final String packageName1 = SplitScreenUtils.getPackageName(pendingIntent1);
         final String packageName2 = SplitScreenUtils.getPackageName(pendingIntent2);
         if (samePackage(packageName1, packageName2, userId1, userId2)) {
-            if (supportMultiInstancesSplit(packageName1)) {
+            if (supportsMultiInstanceSplit(getComponent(pendingIntent1))) {
                 fillInIntent1 = new Intent();
                 fillInIntent1.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
                 fillInIntent2 = new Intent();
@@ -794,7 +807,7 @@
                 ? ActivityOptions.fromBundle(options2) : ActivityOptions.makeBasic();
         boolean setSecondIntentMultipleTask = false;
         if (samePackage(packageName1, packageName2, userId1, userId2)) {
-            if (supportMultiInstancesSplit(packageName1)) {
+            if (supportsMultiInstanceSplit(getComponent(pendingIntent1))) {
                 fillInIntent1 = new Intent();
                 fillInIntent1.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
                 setSecondIntentMultipleTask = true;
@@ -856,7 +869,7 @@
             return;
         }
         if (samePackage(packageName1, packageName2, userId1, userId2)) {
-            if (supportMultiInstancesSplit(packageName1)) {
+            if (supportsMultiInstanceSplit(getComponent(intent))) {
                 // Flag with MULTIPLE_TASK if this is launching the same activity into both sides of
                 // the split and there is no reusable background task.
                 fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
@@ -915,16 +928,63 @@
         return taskInfo != null ? taskInfo.userId : -1;
     }
 
+    /**
+     * Returns whether a specific component desires to be launched in multiple instances for
+     * split screen.
+     */
     @VisibleForTesting
-    boolean supportMultiInstancesSplit(String packageName) {
-        if (packageName != null) {
-            for (int i = 0; i < mAppsSupportMultiInstances.length; i++) {
-                if (mAppsSupportMultiInstances[i].equals(packageName)) {
-                    return true;
-                }
+    boolean supportsMultiInstanceSplit(@Nullable ComponentName componentName) {
+        if (componentName == null || componentName.getPackageName() == null) {
+            // TODO(b/262864589): Handle empty component case
+            return false;
+        }
+
+        // Check the pre-defined allow list
+        final String packageName = componentName.getPackageName();
+        for (int i = 0; i < mAppsSupportingMultiInstance.length; i++) {
+            if (mAppsSupportingMultiInstance[i].equals(packageName)) {
+                ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
+                        "application=%s in allowlist supports multi-instance", packageName);
+                return true;
             }
         }
 
+        // Check the activity property first
+        try {
+            final PackageManager.Property activityProp = mPackageManager.getProperty(
+                    PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI, componentName);
+            // If the above call doesn't throw a NameNotFoundException, then the activity property
+            // should override the application property value
+            if (activityProp.isBoolean()) {
+                ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
+                        "activity=%s supports multi-instance", componentName);
+                return activityProp.getBoolean();
+            } else {
+                ProtoLog.w(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
+                        "Warning: property=%s for activity=%s has non-bool type=%d",
+                        PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI, packageName,
+                        activityProp.getType());
+            }
+        } catch (PackageManager.NameNotFoundException nnfe) {
+            // Not specified in the activity, fall through
+        }
+
+        // Check the application property otherwise
+        try {
+            final PackageManager.Property appProp = mPackageManager.getProperty(
+                    PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI, packageName);
+            if (appProp.isBoolean()) {
+                ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
+                        "application=%s supports multi-instance", packageName);
+                return appProp.getBoolean();
+            } else {
+                ProtoLog.w(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
+                        "Warning: property=%s for application=%s has non-bool type=%d",
+                        PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI, packageName, appProp.getType());
+            }
+        } catch (PackageManager.NameNotFoundException nnfe) {
+            // Not specified in either application or activity
+        }
         return false;
     }
 
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 7dec12a..5de8a9b 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
@@ -388,6 +388,7 @@
 
     IBinder startResizeTransition(WindowContainerTransaction wct,
             Transitions.TransitionHandler handler,
+            @Nullable TransitionConsumedCallback consumedCallback,
             @Nullable TransitionFinishedCallback finishCallback) {
         if (mPendingResize != null) {
             mPendingResize.cancel(null);
@@ -396,13 +397,14 @@
         }
 
         IBinder transition = mTransitions.startTransition(TRANSIT_CHANGE, wct, handler);
-        setResizeTransition(transition, finishCallback);
+        setResizeTransition(transition, consumedCallback, finishCallback);
         return transition;
     }
 
     void setResizeTransition(@NonNull IBinder transition,
+            @Nullable TransitionConsumedCallback consumedCallback,
             @Nullable TransitionFinishedCallback finishCallback) {
-        mPendingResize = new TransitSession(transition, null /* consumedCb */, finishCallback);
+        mPendingResize = new TransitSession(transition, consumedCallback, finishCallback);
         ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "  splitTransition "
                 + " deduced Resize split screen");
     }
@@ -479,18 +481,20 @@
     private void startFadeAnimation(@NonNull SurfaceControl leash, boolean show) {
         final float end = show ? 1.f : 0.f;
         final float start = 1.f - end;
-        final SurfaceControl.Transaction transaction = mTransactionPool.acquire();
         final ValueAnimator va = ValueAnimator.ofFloat(start, end);
         va.setDuration(FADE_DURATION);
         va.setInterpolator(show ? ALPHA_IN : ALPHA_OUT);
         va.addUpdateListener(animation -> {
             float fraction = animation.getAnimatedFraction();
+            final SurfaceControl.Transaction transaction = mTransactionPool.acquire();
             transaction.setAlpha(leash, start * (1.f - fraction) + end * fraction);
             transaction.apply();
+            mTransactionPool.release(transaction);
         });
         va.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationEnd(Animator animation) {
+                final SurfaceControl.Transaction transaction = mTransactionPool.acquire();
                 transaction.setAlpha(leash, end);
                 transaction.apply();
                 mTransactionPool.release(transaction);
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 96e57e7..0781a9e 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
@@ -2236,8 +2236,11 @@
         sendOnBoundsChanged();
         if (ENABLE_SHELL_TRANSITIONS) {
             mSplitLayout.setDividerInteractive(false, false, "onSplitResizeStart");
-            mSplitTransitions.startResizeTransition(wct, this, (finishWct, t) ->
-                    mSplitLayout.setDividerInteractive(true, false, "onSplitResizeFinish"));
+            mSplitTransitions.startResizeTransition(wct, this, (aborted) -> {
+                mSplitLayout.setDividerInteractive(true, false, "onSplitResizeConsumed");
+            }, (finishWct, t) -> {
+                mSplitLayout.setDividerInteractive(true, false, "onSplitResizeFinish");
+            });
         } else {
             // Only need screenshot for legacy case because shell transition should screenshot
             // itself during transition.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
index f58aeac..a666e20 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
@@ -73,6 +73,7 @@
 import com.android.internal.graphics.palette.Palette;
 import com.android.internal.graphics.palette.Quantizer;
 import com.android.internal.graphics.palette.VariationalKMeansQuantizer;
+import com.android.internal.policy.PhoneWindow;
 import com.android.internal.protolog.common.ProtoLog;
 import com.android.launcher3.icons.BaseIconFactory;
 import com.android.launcher3.icons.IconProvider;
@@ -245,16 +246,19 @@
         } else {
             windowFlags |= WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
         }
-        params.layoutInDisplayCutoutMode = a.getInt(
-                R.styleable.Window_windowLayoutInDisplayCutoutMode,
-                WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS);
-        params.windowAnimations = a.getResourceId(R.styleable.Window_windowAnimationStyle, 0);
-        a.recycle();
 
         final ActivityManager.RunningTaskInfo taskInfo = windowInfo.taskInfo;
         final ActivityInfo activityInfo = windowInfo.targetActivityInfo != null
                 ? windowInfo.targetActivityInfo
                 : taskInfo.topActivityInfo;
+        params.layoutInDisplayCutoutMode = a.getInt(
+                R.styleable.Window_windowLayoutInDisplayCutoutMode,
+                PhoneWindow.isEdgeToEdgeEnforced(activityInfo.applicationInfo, false /* local */)
+                        ? WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
+                        : params.layoutInDisplayCutoutMode);
+        params.windowAnimations = a.getResourceId(R.styleable.Window_windowAnimationStyle, 0);
+        a.recycle();
+
         final int displayId = taskInfo.displayId;
         // Assumes it's safe to show starting windows of launched apps while
         // the keyguard is being hidden. This is okay because starting windows never show
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellController.java
index 0eb7c2d..a7843e2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellController.java
@@ -293,11 +293,7 @@
     private class ShellInterfaceImpl implements ShellInterface {
         @Override
         public void onInit() {
-            try {
-                mMainExecutor.executeBlocking(() -> ShellController.this.handleInit());
-            } catch (InterruptedException e) {
-                throw new RuntimeException("Failed to initialize the Shell in 2s", e);
-            }
+            mMainExecutor.execute(ShellController.this::handleInit);
         }
 
         @Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java
index 34c015f..84f21f6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java
@@ -330,7 +330,7 @@
                     continue;
                 }
                 if (isHide) {
-                    if (pending.mType == TRANSIT_TO_BACK) {
+                    if (pending != null && pending.mType == TRANSIT_TO_BACK) {
                         // TO_BACK is only used when setting the task view visibility immediately,
                         // so in that case we can also hide the surface immediately
                         startTransaction.hide(chg.getLeash());
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
index 9f20f49..bf783e6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
@@ -21,9 +21,9 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.WindowManager.TRANSIT_CHANGE;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_UNOCCLUDING;
 import static android.view.WindowManager.TRANSIT_PIP;
 import static android.view.WindowManager.TRANSIT_TO_BACK;
-import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_UNOCCLUDING;
 import static android.window.TransitionInfo.FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY;
 import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
 
@@ -84,7 +84,7 @@
     private UnfoldTransitionHandler mUnfoldHandler;
     private ActivityEmbeddingController mActivityEmbeddingController;
 
-    private class MixedTransition {
+    private static class MixedTransition {
         static final int TYPE_ENTER_PIP_FROM_SPLIT = 1;
 
         /** Both the display and split-state (enter/exit) is changing */
@@ -124,6 +124,16 @@
         int mAnimType = ANIM_TYPE_DEFAULT;
         final IBinder mTransition;
 
+        private final Transitions mPlayer;
+        private final DefaultMixedHandler mMixedHandler;
+        private final PipTransitionController mPipHandler;
+        private final RecentsTransitionHandler mRecentsHandler;
+        private final StageCoordinator mSplitHandler;
+        private final KeyguardTransitionHandler mKeyguardHandler;
+        private final DesktopTasksController mDesktopTasksController;
+        private final UnfoldTransitionHandler mUnfoldHandler;
+        private final ActivityEmbeddingController mActivityEmbeddingController;
+
         Transitions.TransitionHandler mLeftoversHandler = null;
         TransitionInfo mInfo = null;
         WindowContainerTransaction mFinishWCT = null;
@@ -144,9 +154,408 @@
          */
         int mInFlightSubAnimations = 0;
 
-        MixedTransition(int type, IBinder transition) {
+        MixedTransition(int type, IBinder transition, Transitions player,
+                DefaultMixedHandler mixedHandler, PipTransitionController pipHandler,
+                RecentsTransitionHandler recentsHandler, StageCoordinator splitHandler,
+                KeyguardTransitionHandler keyguardHandler,
+                DesktopTasksController desktopTasksController,
+                UnfoldTransitionHandler unfoldHandler,
+                ActivityEmbeddingController activityEmbeddingController) {
             mType = type;
             mTransition = transition;
+            mPlayer = player;
+            mMixedHandler = mixedHandler;
+            mPipHandler = pipHandler;
+            mRecentsHandler = recentsHandler;
+            mSplitHandler = splitHandler;
+            mKeyguardHandler = keyguardHandler;
+            mDesktopTasksController = desktopTasksController;
+            mUnfoldHandler = unfoldHandler;
+            mActivityEmbeddingController = activityEmbeddingController;
+
+            switch (type) {
+                case TYPE_RECENTS_DURING_DESKTOP:
+                case TYPE_RECENTS_DURING_KEYGUARD:
+                case TYPE_RECENTS_DURING_SPLIT:
+                    mLeftoversHandler = mRecentsHandler;
+                    break;
+                case TYPE_UNFOLD:
+                    mLeftoversHandler = mUnfoldHandler;
+                    break;
+                case TYPE_DISPLAY_AND_SPLIT_CHANGE:
+                case TYPE_ENTER_PIP_FROM_ACTIVITY_EMBEDDING:
+                case TYPE_ENTER_PIP_FROM_SPLIT:
+                case TYPE_KEYGUARD:
+                case TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE:
+                default:
+                    break;
+            }
+        }
+
+        boolean startAnimation(
+                @NonNull IBinder transition, @NonNull TransitionInfo info,
+                @NonNull SurfaceControl.Transaction startTransaction,
+                @NonNull SurfaceControl.Transaction finishTransaction,
+                @NonNull Transitions.TransitionFinishCallback finishCallback) {
+            switch (mType) {
+                case TYPE_ENTER_PIP_FROM_SPLIT:
+                    return animateEnterPipFromSplit(this, info, startTransaction, finishTransaction,
+                            finishCallback, mPlayer, mMixedHandler, mPipHandler, mSplitHandler);
+                case TYPE_ENTER_PIP_FROM_ACTIVITY_EMBEDDING:
+                    return animateEnterPipFromActivityEmbedding(
+                            info, startTransaction, finishTransaction, finishCallback);
+                case TYPE_DISPLAY_AND_SPLIT_CHANGE:
+                    return false;
+                case TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE:
+                    final boolean handledToPip = animateOpenIntentWithRemoteAndPip(
+                            info, startTransaction, finishTransaction, finishCallback);
+                    // Consume the transition on remote handler if the leftover handler already
+                    // handle this transition. And if it cannot, the transition will be handled by
+                    // remote handler, so don't consume here.
+                    // Need to check leftOverHandler as it may change in
+                    // #animateOpenIntentWithRemoteAndPip
+                    if (handledToPip && mHasRequestToRemote
+                            && mLeftoversHandler != mPlayer.getRemoteTransitionHandler()) {
+                        mPlayer.getRemoteTransitionHandler().onTransitionConsumed(
+                                transition, false, null);
+                    }
+                    return handledToPip;
+                case TYPE_RECENTS_DURING_SPLIT:
+                    for (int i = info.getChanges().size() - 1; i >= 0; --i) {
+                        final TransitionInfo.Change change = info.getChanges().get(i);
+                        // Pip auto-entering info might be appended to recent transition like
+                        // pressing home-key in 3-button navigation. This offers split handler the
+                        // opportunity to handle split to pip animation.
+                        if (mPipHandler.isEnteringPip(change, info.getType())
+                                && mSplitHandler.getSplitItemPosition(change.getLastParent())
+                                != SPLIT_POSITION_UNDEFINED) {
+                            return animateEnterPipFromSplit(
+                                    this, info, startTransaction, finishTransaction, finishCallback,
+                                    mPlayer, mMixedHandler, mPipHandler, mSplitHandler);
+                        }
+                    }
+
+                    return animateRecentsDuringSplit(
+                            info, startTransaction, finishTransaction, finishCallback);
+                case TYPE_KEYGUARD:
+                    return animateKeyguard(this, info, startTransaction, finishTransaction,
+                            finishCallback, mKeyguardHandler, mPipHandler);
+                case TYPE_RECENTS_DURING_KEYGUARD:
+                    return animateRecentsDuringKeyguard(
+                            info, startTransaction, finishTransaction, finishCallback);
+                case TYPE_RECENTS_DURING_DESKTOP:
+                    return animateRecentsDuringDesktop(
+                            info, startTransaction, finishTransaction, finishCallback);
+                case TYPE_UNFOLD:
+                    return animateUnfold(
+                            info, startTransaction, finishTransaction, finishCallback);
+                default:
+                    throw new IllegalStateException(
+                            "Starting mixed animation without a known mixed type? " + mType);
+            }
+        }
+
+        private boolean animateEnterPipFromActivityEmbedding(
+                @NonNull TransitionInfo info,
+                @NonNull SurfaceControl.Transaction startTransaction,
+                @NonNull SurfaceControl.Transaction finishTransaction,
+                @NonNull Transitions.TransitionFinishCallback finishCallback) {
+            ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Animating a mixed transition for "
+                    + "entering PIP from an Activity Embedding window");
+            // Split into two transitions (wct)
+            TransitionInfo.Change pipChange = null;
+            final TransitionInfo everythingElse =
+                    subCopy(info, TRANSIT_TO_BACK, true /* changes */);
+            for (int i = info.getChanges().size() - 1; i >= 0; --i) {
+                TransitionInfo.Change change = info.getChanges().get(i);
+                if (mPipHandler.isEnteringPip(change, info.getType())) {
+                    if (pipChange != null) {
+                        throw new IllegalStateException("More than 1 pip-entering changes in one"
+                                + " transition? " + info);
+                    }
+                    pipChange = change;
+                    // going backwards, so remove-by-index is fine.
+                    everythingElse.getChanges().remove(i);
+                }
+            }
+
+            final Transitions.TransitionFinishCallback finishCB = (wct) -> {
+                --mInFlightSubAnimations;
+                joinFinishArgs(wct);
+                if (mInFlightSubAnimations > 0) return;
+                finishCallback.onTransitionFinished(mFinishWCT);
+            };
+
+            if (!mActivityEmbeddingController.shouldAnimate(everythingElse)) {
+                // Fallback to dispatching to other handlers.
+                return false;
+            }
+
+            // PIP window should always be on the highest Z order.
+            if (pipChange != null) {
+                mInFlightSubAnimations = 2;
+                mPipHandler.startEnterAnimation(
+                        pipChange,
+                        startTransaction.setLayer(pipChange.getLeash(), Integer.MAX_VALUE),
+                        finishTransaction,
+                        finishCB);
+            } else {
+                mInFlightSubAnimations = 1;
+            }
+
+            mActivityEmbeddingController.startAnimation(mTransition, everythingElse,
+                    startTransaction, finishTransaction, finishCB);
+            return true;
+        }
+
+        private boolean animateOpenIntentWithRemoteAndPip(
+                @NonNull TransitionInfo info,
+                @NonNull SurfaceControl.Transaction startTransaction,
+                @NonNull SurfaceControl.Transaction finishTransaction,
+                @NonNull Transitions.TransitionFinishCallback finishCallback) {
+            TransitionInfo.Change pipChange = null;
+            for (int i = info.getChanges().size() - 1; i >= 0; --i) {
+                TransitionInfo.Change change = info.getChanges().get(i);
+                if (mPipHandler.isEnteringPip(change, info.getType())) {
+                    if (pipChange != null) {
+                        throw new IllegalStateException("More than 1 pip-entering changes in one"
+                                + " transition? " + info);
+                    }
+                    pipChange = change;
+                    info.getChanges().remove(i);
+                }
+            }
+            Transitions.TransitionFinishCallback finishCB = (wct) -> {
+                --mInFlightSubAnimations;
+                joinFinishArgs(wct);
+                if (mInFlightSubAnimations > 0) return;
+                finishCallback.onTransitionFinished(mFinishWCT);
+            };
+            if (pipChange == null) {
+                if (mLeftoversHandler != null) {
+                    mInFlightSubAnimations = 1;
+                    if (mLeftoversHandler.startAnimation(
+                            mTransition, info, startTransaction, finishTransaction, finishCB)) {
+                        return true;
+                    }
+                }
+                return false;
+            }
+            ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Splitting PIP into a separate"
+                    + " animation because remote-animation likely doesn't support it");
+            // Split the transition into 2 parts: the pip part and the rest.
+            mInFlightSubAnimations = 2;
+            // make a new startTransaction because pip's startEnterAnimation "consumes" it so
+            // we need a separate one to send over to launcher.
+            SurfaceControl.Transaction otherStartT = new SurfaceControl.Transaction();
+
+            mPipHandler.startEnterAnimation(pipChange, otherStartT, finishTransaction, finishCB);
+
+            // Dispatch the rest of the transition normally.
+            if (mLeftoversHandler != null
+                    && mLeftoversHandler.startAnimation(
+                            mTransition, info, startTransaction, finishTransaction, finishCB)) {
+                return true;
+            }
+            mLeftoversHandler = mPlayer.dispatchTransition(mTransition, info,
+                    startTransaction, finishTransaction, finishCB, mMixedHandler);
+            return true;
+        }
+
+        private boolean animateRecentsDuringSplit(
+                @NonNull TransitionInfo info,
+                @NonNull SurfaceControl.Transaction startTransaction,
+                @NonNull SurfaceControl.Transaction finishTransaction,
+                @NonNull Transitions.TransitionFinishCallback finishCallback) {
+            // Split-screen is only interested in the recents transition finishing (and merging), so
+            // just wrap finish and start recents animation directly.
+            Transitions.TransitionFinishCallback finishCB = (wct) -> {
+                mInFlightSubAnimations = 0;
+                // If pair-to-pair switching, the post-recents clean-up isn't needed.
+                wct = wct != null ? wct : new WindowContainerTransaction();
+                if (mAnimType != ANIM_TYPE_PAIR_TO_PAIR) {
+                    mSplitHandler.onRecentsInSplitAnimationFinish(wct, finishTransaction);
+                } else {
+                    // notify pair-to-pair recents animation finish
+                    mSplitHandler.onRecentsPairToPairAnimationFinish(wct);
+                }
+                mSplitHandler.onTransitionAnimationComplete();
+                finishCallback.onTransitionFinished(wct);
+            };
+            mInFlightSubAnimations = 1;
+            mSplitHandler.onRecentsInSplitAnimationStart(info);
+            final boolean handled = mLeftoversHandler.startAnimation(mTransition, info,
+                    startTransaction, finishTransaction, finishCB);
+            if (!handled) {
+                mSplitHandler.onRecentsInSplitAnimationCanceled();
+            }
+            return handled;
+        }
+
+        private boolean animateRecentsDuringKeyguard(
+                @NonNull TransitionInfo info,
+                @NonNull SurfaceControl.Transaction startTransaction,
+                @NonNull SurfaceControl.Transaction finishTransaction,
+                @NonNull Transitions.TransitionFinishCallback finishCallback) {
+            if (mInfo == null) {
+                mInfo = info;
+                mFinishT = finishTransaction;
+                mFinishCB = finishCallback;
+            }
+            return startSubAnimation(mRecentsHandler, info, startTransaction, finishTransaction);
+        }
+
+        private boolean animateRecentsDuringDesktop(
+                @NonNull TransitionInfo info,
+                @NonNull SurfaceControl.Transaction startTransaction,
+                @NonNull SurfaceControl.Transaction finishTransaction,
+                @NonNull Transitions.TransitionFinishCallback finishCallback) {
+            Transitions.TransitionFinishCallback finishCB = wct -> {
+                mInFlightSubAnimations--;
+                if (mInFlightSubAnimations == 0) {
+                    finishCallback.onTransitionFinished(wct);
+                }
+            };
+
+            mInFlightSubAnimations++;
+            boolean consumed = mRecentsHandler.startAnimation(
+                    mTransition, info, startTransaction, finishTransaction, finishCB);
+            if (!consumed) {
+                mInFlightSubAnimations--;
+                return false;
+            }
+            if (mDesktopTasksController != null) {
+                mDesktopTasksController.syncSurfaceState(info, finishTransaction);
+                return true;
+            }
+
+            return false;
+        }
+
+        private boolean animateUnfold(
+                @NonNull TransitionInfo info,
+                @NonNull SurfaceControl.Transaction startTransaction,
+                @NonNull SurfaceControl.Transaction finishTransaction,
+                @NonNull Transitions.TransitionFinishCallback finishCallback) {
+            final Transitions.TransitionFinishCallback finishCB = (wct) -> {
+                mInFlightSubAnimations--;
+                if (mInFlightSubAnimations > 0) return;
+                finishCallback.onTransitionFinished(wct);
+            };
+            mInFlightSubAnimations = 1;
+            // Sync pip state.
+            if (mPipHandler != null) {
+                mPipHandler.syncPipSurfaceState(info, startTransaction, finishTransaction);
+            }
+            if (mSplitHandler != null && mSplitHandler.isSplitActive()) {
+                mSplitHandler.updateSurfaces(startTransaction);
+            }
+            return mUnfoldHandler.startAnimation(
+                    mTransition, info, startTransaction, finishTransaction, finishCB);
+        }
+
+        void mergeAnimation(
+                @NonNull IBinder transition, @NonNull TransitionInfo info,
+                @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
+                @NonNull Transitions.TransitionFinishCallback finishCallback) {
+            switch (mType) {
+                case TYPE_DISPLAY_AND_SPLIT_CHANGE:
+                    // queue since no actual animation.
+                    break;
+                case TYPE_ENTER_PIP_FROM_SPLIT:
+                    if (mAnimType == ANIM_TYPE_GOING_HOME) {
+                        boolean ended = mSplitHandler.end();
+                        // If split couldn't end (because it is remote), then don't end everything
+                        // else since we have to play out the animation anyways.
+                        if (!ended) return;
+                        mPipHandler.end();
+                        if (mLeftoversHandler != null) {
+                            mLeftoversHandler.mergeAnimation(
+                                    transition, info, t, mergeTarget, finishCallback);
+                        }
+                    } else {
+                        mPipHandler.end();
+                    }
+                    break;
+                case TYPE_ENTER_PIP_FROM_ACTIVITY_EMBEDDING:
+                    mPipHandler.end();
+                    mActivityEmbeddingController.mergeAnimation(transition, info, t, mergeTarget,
+                            finishCallback);
+                    break;
+                case TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE:
+                    mPipHandler.end();
+                    if (mLeftoversHandler != null) {
+                        mLeftoversHandler.mergeAnimation(transition, info, t, mergeTarget,
+                                finishCallback);
+                    }
+                    break;
+                case TYPE_RECENTS_DURING_SPLIT:
+                    if (mSplitHandler.isPendingEnter(transition)) {
+                        // Recents -> enter-split means that we are switching from one pair to
+                        // another pair.
+                        mAnimType = ANIM_TYPE_PAIR_TO_PAIR;
+                    }
+                    mLeftoversHandler.mergeAnimation(
+                            transition, info, t, mergeTarget, finishCallback);
+                    break;
+                case TYPE_KEYGUARD:
+                    mKeyguardHandler.mergeAnimation(
+                            transition, info, t, mergeTarget, finishCallback);
+                    break;
+                case TYPE_RECENTS_DURING_KEYGUARD:
+                    if ((info.getFlags() & TRANSIT_FLAG_KEYGUARD_UNOCCLUDING) != 0) {
+                        DefaultMixedHandler.handoverTransitionLeashes(mInfo, info, t, mFinishT);
+                        if (animateKeyguard(this, info, t, mFinishT, mFinishCB, mKeyguardHandler,
+                                mPipHandler)) {
+                            finishCallback.onTransitionFinished(null);
+                        }
+                    }
+                    mLeftoversHandler.mergeAnimation(
+                            transition, info, t, mergeTarget, finishCallback);
+                    break;
+                case TYPE_RECENTS_DURING_DESKTOP:
+                    mLeftoversHandler.mergeAnimation(
+                            transition, info, t, mergeTarget, finishCallback);
+                    break;
+                case TYPE_UNFOLD:
+                    mUnfoldHandler.mergeAnimation(transition, info, t, mergeTarget, finishCallback);
+                    break;
+                default:
+                    throw new IllegalStateException(
+                            "Playing a mixed transition with unknown type? " + mType);
+            }
+        }
+
+        void onTransitionConsumed(
+                @NonNull IBinder transition, boolean aborted,
+                @Nullable SurfaceControl.Transaction finishT) {
+            switch (mType) {
+                case TYPE_ENTER_PIP_FROM_SPLIT:
+                    mPipHandler.onTransitionConsumed(transition, aborted, finishT);
+                    break;
+                case TYPE_ENTER_PIP_FROM_ACTIVITY_EMBEDDING:
+                    mPipHandler.onTransitionConsumed(transition, aborted, finishT);
+                    mActivityEmbeddingController.onTransitionConsumed(transition, aborted, finishT);
+                    break;
+                case TYPE_RECENTS_DURING_SPLIT:
+                case TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE:
+                case TYPE_RECENTS_DURING_DESKTOP:
+                    mLeftoversHandler.onTransitionConsumed(transition, aborted, finishT);
+                    break;
+                case TYPE_KEYGUARD:
+                    mKeyguardHandler.onTransitionConsumed(transition, aborted, finishT);
+                    break;
+                case TYPE_UNFOLD:
+                    mUnfoldHandler.onTransitionConsumed(transition, aborted, finishT);
+                    break;
+                default:
+                    break;
+            }
+
+            if (mHasRequestToRemote) {
+                mPlayer.getRemoteTransitionHandler().onTransitionConsumed(
+                        transition, aborted, finishT);
+            }
         }
 
         boolean startSubAnimation(Transitions.TransitionHandler handler, TransitionInfo info,
@@ -175,7 +584,6 @@
             joinFinishArgs(wct);
 
             if (mInFlightSubAnimations == 0) {
-                mActiveTransitions.remove(MixedTransition.this);
                 mFinishCB.onTransitionFinished(mFinishWCT);
             }
         }
@@ -236,8 +644,8 @@
                 throw new IllegalStateException("Unexpected remote transition in"
                         + "pip-enter-from-split request");
             }
-            mActiveTransitions.add(new MixedTransition(MixedTransition.TYPE_ENTER_PIP_FROM_SPLIT,
-                    transition));
+            mActiveTransitions.add(createMixedTransition(
+                    MixedTransition.TYPE_ENTER_PIP_FROM_SPLIT, transition));
 
             WindowContainerTransaction out = new WindowContainerTransaction();
             mPipHandler.augmentRequest(transition, request, out);
@@ -248,7 +656,7 @@
                 mActivityEmbeddingController != null)) {
             ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
                     " Got a PiP-enter request from an Activity Embedding split");
-            mActiveTransitions.add(new MixedTransition(
+            mActiveTransitions.add(createMixedTransition(
                     MixedTransition.TYPE_ENTER_PIP_FROM_ACTIVITY_EMBEDDING, transition));
             // Postpone transition splitting to later.
             WindowContainerTransaction out = new WindowContainerTransaction();
@@ -267,7 +675,7 @@
             if (handler == null) {
                 return null;
             }
-            final MixedTransition mixed = new MixedTransition(
+            final MixedTransition mixed = createMixedTransition(
                     MixedTransition.TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE, transition);
             mixed.mLeftoversHandler = handler.first;
             mActiveTransitions.add(mixed);
@@ -293,7 +701,7 @@
                         mPlayer.getRemoteTransitionHandler(),
                         new WindowContainerTransaction());
             }
-            final MixedTransition mixed = new MixedTransition(
+            final MixedTransition mixed = createMixedTransition(
                     MixedTransition.TYPE_RECENTS_DURING_SPLIT, transition);
             mixed.mLeftoversHandler = handler.first;
             mActiveTransitions.add(mixed);
@@ -302,10 +710,8 @@
             final WindowContainerTransaction wct =
                     mUnfoldHandler.handleRequest(transition, request);
             if (wct != null) {
-                final MixedTransition mixed = new MixedTransition(
-                        MixedTransition.TYPE_UNFOLD, transition);
-                mixed.mLeftoversHandler = mUnfoldHandler;
-                mActiveTransitions.add(mixed);
+                mActiveTransitions.add(createMixedTransition(
+                        MixedTransition.TYPE_UNFOLD, transition));
             }
             return wct;
         }
@@ -331,31 +737,31 @@
     private void setRecentsTransitionDuringSplit(IBinder transition) {
         ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Got a recents request while "
                 + "Split-Screen is foreground, so treat it as Mixed.");
-        final MixedTransition mixed = new MixedTransition(
-                MixedTransition.TYPE_RECENTS_DURING_SPLIT, transition);
-        mixed.mLeftoversHandler = mRecentsHandler;
-        mActiveTransitions.add(mixed);
+        mActiveTransitions.add(createMixedTransition(
+                MixedTransition.TYPE_RECENTS_DURING_SPLIT, transition));
     }
 
     private void setRecentsTransitionDuringKeyguard(IBinder transition) {
         ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Got a recents request while "
                 + "keyguard is visible, so treat it as Mixed.");
-        final MixedTransition mixed = new MixedTransition(
-                MixedTransition.TYPE_RECENTS_DURING_KEYGUARD, transition);
-        mixed.mLeftoversHandler = mRecentsHandler;
-        mActiveTransitions.add(mixed);
+        mActiveTransitions.add(createMixedTransition(
+                MixedTransition.TYPE_RECENTS_DURING_KEYGUARD, transition));
     }
 
     private void setRecentsTransitionDuringDesktop(IBinder transition) {
         ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Got a recents request while "
                 + "desktop mode is active, so treat it as Mixed.");
-        final MixedTransition mixed = new MixedTransition(
-                MixedTransition.TYPE_RECENTS_DURING_DESKTOP, transition);
-        mixed.mLeftoversHandler = mRecentsHandler;
-        mActiveTransitions.add(mixed);
+        mActiveTransitions.add(createMixedTransition(
+                MixedTransition.TYPE_RECENTS_DURING_DESKTOP, transition));
     }
 
-    private TransitionInfo subCopy(@NonNull TransitionInfo info,
+    private MixedTransition createMixedTransition(int type, IBinder transition) {
+        return new MixedTransition(type, transition, mPlayer, this, mPipHandler, mRecentsHandler,
+                mSplitHandler, mKeyguardHandler, mDesktopTasksController, mUnfoldHandler,
+                mActivityEmbeddingController);
+    }
+
+    private static TransitionInfo subCopy(@NonNull TransitionInfo info,
             @WindowManager.TransitionType int newType, boolean withChanges) {
         final TransitionInfo out = new TransitionInfo(newType, withChanges ? info.getFlags() : 0);
         out.setTrack(info.getTrack());
@@ -372,12 +778,12 @@
         return out;
     }
 
-    private boolean isHomeOpening(@NonNull TransitionInfo.Change change) {
+    private static boolean isHomeOpening(@NonNull TransitionInfo.Change change) {
         return change.getTaskInfo() != null
                 && change.getTaskInfo().getActivityType() == ACTIVITY_TYPE_HOME;
     }
 
-    private boolean isWallpaper(@NonNull TransitionInfo.Change change) {
+    private static boolean isWallpaper(@NonNull TransitionInfo.Change change) {
         return (change.getFlags() & FLAG_IS_WALLPAPER) != 0;
     }
 
@@ -399,10 +805,15 @@
         if (KeyguardTransitionHandler.handles(info)) {
             if (mixed != null && mixed.mType != MixedTransition.TYPE_KEYGUARD) {
                 final MixedTransition keyguardMixed =
-                        new MixedTransition(MixedTransition.TYPE_KEYGUARD, transition);
+                        createMixedTransition(MixedTransition.TYPE_KEYGUARD, transition);
                 mActiveTransitions.add(keyguardMixed);
-                final boolean hasAnimateKeyguard = animateKeyguard(keyguardMixed, info,
-                        startTransaction, finishTransaction, finishCallback);
+                Transitions.TransitionFinishCallback callback = wct -> {
+                    mActiveTransitions.remove(keyguardMixed);
+                    finishCallback.onTransitionFinished(wct);
+                };
+                final boolean hasAnimateKeyguard = animateKeyguard(
+                        keyguardMixed, info, startTransaction, finishTransaction, callback,
+                        mKeyguardHandler, mPipHandler);
                 if (hasAnimateKeyguard) {
                     ProtoLog.w(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
                             "Converting mixed transition into a keyguard transition");
@@ -420,173 +831,27 @@
 
         if (mixed == null) return false;
 
-        if (mixed.mType == MixedTransition.TYPE_ENTER_PIP_FROM_SPLIT) {
-            return animateEnterPipFromSplit(mixed, info, startTransaction, finishTransaction,
-                    finishCallback);
-        } else if (mixed.mType == MixedTransition.TYPE_ENTER_PIP_FROM_ACTIVITY_EMBEDDING) {
-            return animateEnterPipFromActivityEmbedding(mixed, info, startTransaction,
-                    finishTransaction, finishCallback);
-        } else if (mixed.mType == MixedTransition.TYPE_DISPLAY_AND_SPLIT_CHANGE) {
-            return false;
-        } else if (mixed.mType == MixedTransition.TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE) {
-            final boolean handledToPip = animateOpenIntentWithRemoteAndPip(mixed, info,
-                    startTransaction, finishTransaction, finishCallback);
-            // Consume the transition on remote handler if the leftover handler already handle this
-            // transition. And if it cannot, the transition will be handled by remote handler, so
-            // don't consume here.
-            // Need to check leftOverHandler as it may change in #animateOpenIntentWithRemoteAndPip
-            if (handledToPip && mixed.mHasRequestToRemote
-                    && mixed.mLeftoversHandler != mPlayer.getRemoteTransitionHandler()) {
-                mPlayer.getRemoteTransitionHandler().onTransitionConsumed(transition, false, null);
-            }
-            return handledToPip;
-        } else if (mixed.mType == MixedTransition.TYPE_RECENTS_DURING_SPLIT) {
-            for (int i = info.getChanges().size() - 1; i >= 0; --i) {
-                final TransitionInfo.Change change = info.getChanges().get(i);
-                // Pip auto-entering info might be appended to recent transition like pressing
-                // home-key in 3-button navigation. This offers split handler the opportunity to
-                // handle split to pip animation.
-                if (mPipHandler.isEnteringPip(change, info.getType())
-                        && mSplitHandler.getSplitItemPosition(change.getLastParent())
-                        != SPLIT_POSITION_UNDEFINED) {
-                    return animateEnterPipFromSplit(mixed, info, startTransaction,
-                            finishTransaction, finishCallback);
-                }
-            }
-
-            return animateRecentsDuringSplit(mixed, info, startTransaction, finishTransaction,
-                    finishCallback);
-        } else if (mixed.mType == MixedTransition.TYPE_KEYGUARD) {
-            return animateKeyguard(mixed, info, startTransaction, finishTransaction,
-                    finishCallback);
-        } else if (mixed.mType == MixedTransition.TYPE_RECENTS_DURING_KEYGUARD) {
-            return animateRecentsDuringKeyguard(mixed, info, startTransaction, finishTransaction,
-                    finishCallback);
-        } else if (mixed.mType == MixedTransition.TYPE_RECENTS_DURING_DESKTOP) {
-            return animateRecentsDuringDesktop(mixed, info, startTransaction, finishTransaction,
-                    finishCallback);
-        } else if (mixed.mType == MixedTransition.TYPE_UNFOLD) {
-            return animateUnfold(mixed, info, startTransaction, finishTransaction, finishCallback);
-        } else {
-            mActiveTransitions.remove(mixed);
-            throw new IllegalStateException("Starting mixed animation without a known mixed type? "
-                    + mixed.mType);
-        }
-    }
-
-    private boolean animateEnterPipFromActivityEmbedding(@NonNull MixedTransition mixed,
-            @NonNull TransitionInfo info,
-            @NonNull SurfaceControl.Transaction startTransaction,
-            @NonNull SurfaceControl.Transaction finishTransaction,
-            @NonNull Transitions.TransitionFinishCallback finishCallback) {
-        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Animating a mixed transition for "
-                + "entering PIP from an Activity Embedding window");
-        // Split into two transitions (wct)
-        TransitionInfo.Change pipChange = null;
-        final TransitionInfo everythingElse = subCopy(info, TRANSIT_TO_BACK, true /* changes */);
-        for (int i = info.getChanges().size() - 1; i >= 0; --i) {
-            TransitionInfo.Change change = info.getChanges().get(i);
-            if (mPipHandler.isEnteringPip(change, info.getType())) {
-                if (pipChange != null) {
-                    throw new IllegalStateException("More than 1 pip-entering changes in one"
-                            + " transition? " + info);
-                }
-                pipChange = change;
-                // going backwards, so remove-by-index is fine.
-                everythingElse.getChanges().remove(i);
-            }
-        }
-
-        final Transitions.TransitionFinishCallback finishCB = (wct) -> {
-            --mixed.mInFlightSubAnimations;
-            mixed.joinFinishArgs(wct);
-            if (mixed.mInFlightSubAnimations > 0) return;
-            mActiveTransitions.remove(mixed);
-            finishCallback.onTransitionFinished(mixed.mFinishWCT);
+        final MixedTransition chosenTransition = mixed;
+        Transitions.TransitionFinishCallback callback = wct -> {
+            mActiveTransitions.remove(chosenTransition);
+            finishCallback.onTransitionFinished(wct);
         };
 
-        if (!mActivityEmbeddingController.shouldAnimate(everythingElse)) {
-            // Fallback to dispatching to other handlers.
-            return false;
+        boolean handled = chosenTransition.startAnimation(
+                transition, info, startTransaction, finishTransaction, callback);
+        if (!handled) {
+            mActiveTransitions.remove(chosenTransition);
         }
-
-        // PIP window should always be on the highest Z order.
-        if (pipChange != null) {
-            mixed.mInFlightSubAnimations = 2;
-            mPipHandler.startEnterAnimation(
-                    pipChange, startTransaction.setLayer(pipChange.getLeash(), Integer.MAX_VALUE),
-                    finishTransaction,
-                    finishCB);
-        } else {
-            mixed.mInFlightSubAnimations = 1;
-        }
-
-        mActivityEmbeddingController.startAnimation(mixed.mTransition, everythingElse,
-                startTransaction, finishTransaction, finishCB);
-        return true;
+        return handled;
     }
 
-    private boolean animateOpenIntentWithRemoteAndPip(@NonNull MixedTransition mixed,
+    private static boolean animateEnterPipFromSplit(@NonNull final MixedTransition mixed,
             @NonNull TransitionInfo info,
             @NonNull SurfaceControl.Transaction startTransaction,
             @NonNull SurfaceControl.Transaction finishTransaction,
-            @NonNull Transitions.TransitionFinishCallback finishCallback) {
-        TransitionInfo.Change pipChange = null;
-        for (int i = info.getChanges().size() - 1; i >= 0; --i) {
-            TransitionInfo.Change change = info.getChanges().get(i);
-            if (mPipHandler.isEnteringPip(change, info.getType())) {
-                if (pipChange != null) {
-                    throw new IllegalStateException("More than 1 pip-entering changes in one"
-                            + " transition? " + info);
-                }
-                pipChange = change;
-                info.getChanges().remove(i);
-            }
-        }
-        Transitions.TransitionFinishCallback finishCB = (wct) -> {
-            --mixed.mInFlightSubAnimations;
-            mixed.joinFinishArgs(wct);
-            if (mixed.mInFlightSubAnimations > 0) return;
-            mActiveTransitions.remove(mixed);
-            finishCallback.onTransitionFinished(mixed.mFinishWCT);
-        };
-        if (pipChange == null) {
-            if (mixed.mLeftoversHandler != null) {
-                mixed.mInFlightSubAnimations = 1;
-                if (mixed.mLeftoversHandler.startAnimation(mixed.mTransition,
-                        info, startTransaction, finishTransaction, finishCB)) {
-                    return true;
-                }
-            }
-            mActiveTransitions.remove(mixed);
-            return false;
-        }
-        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Splitting PIP into a separate"
-                        + " animation because remote-animation likely doesn't support it");
-        // Split the transition into 2 parts: the pip part and the rest.
-        mixed.mInFlightSubAnimations = 2;
-        // make a new startTransaction because pip's startEnterAnimation "consumes" it so
-        // we need a separate one to send over to launcher.
-        SurfaceControl.Transaction otherStartT = new SurfaceControl.Transaction();
-
-        mPipHandler.startEnterAnimation(pipChange, otherStartT, finishTransaction, finishCB);
-
-        // Dispatch the rest of the transition normally.
-        if (mixed.mLeftoversHandler != null
-                && mixed.mLeftoversHandler.startAnimation(mixed.mTransition, info,
-                    startTransaction, finishTransaction, finishCB)) {
-            return true;
-        }
-        mixed.mLeftoversHandler = mPlayer.dispatchTransition(mixed.mTransition, info,
-                startTransaction, finishTransaction, finishCB, this);
-        return true;
-    }
-
-    private boolean animateEnterPipFromSplit(@NonNull final MixedTransition mixed,
-            @NonNull TransitionInfo info,
-            @NonNull SurfaceControl.Transaction startTransaction,
-            @NonNull SurfaceControl.Transaction finishTransaction,
-            @NonNull Transitions.TransitionFinishCallback finishCallback) {
+            @NonNull Transitions.TransitionFinishCallback finishCallback,
+            @NonNull Transitions player, @NonNull DefaultMixedHandler mixedHandler,
+            @NonNull PipTransitionController pipHandler, @NonNull StageCoordinator splitHandler) {
         ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Animating a mixed transition for "
                 + "entering PIP while Split-Screen is foreground.");
         TransitionInfo.Change pipChange = null;
@@ -595,7 +860,7 @@
         boolean homeIsOpening = false;
         for (int i = info.getChanges().size() - 1; i >= 0; --i) {
             TransitionInfo.Change change = info.getChanges().get(i);
-            if (mPipHandler.isEnteringPip(change, info.getType())) {
+            if (pipHandler.isEnteringPip(change, info.getType())) {
                 if (pipChange != null) {
                     throw new IllegalStateException("More than 1 pip-entering changes in one"
                             + " transition? " + info);
@@ -611,7 +876,6 @@
         }
         if (pipChange == null) {
             // um, something probably went wrong.
-            mActiveTransitions.remove(mixed);
             return false;
         }
         final boolean isGoingHome = homeIsOpening;
@@ -619,13 +883,12 @@
             --mixed.mInFlightSubAnimations;
             mixed.joinFinishArgs(wct);
             if (mixed.mInFlightSubAnimations > 0) return;
-            mActiveTransitions.remove(mixed);
             if (isGoingHome) {
-                mSplitHandler.onTransitionAnimationComplete();
+                splitHandler.onTransitionAnimationComplete();
             }
             finishCallback.onTransitionFinished(mixed.mFinishWCT);
         };
-        if (isGoingHome || mSplitHandler.getSplitItemPosition(pipChange.getLastParent())
+        if (isGoingHome || splitHandler.getSplitItemPosition(pipChange.getLastParent())
                 != SPLIT_POSITION_UNDEFINED) {
             ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Animation is actually mixed "
                     + "since entering-PiP caused us to leave split and return home.");
@@ -641,7 +904,7 @@
             // we need a separate one to send over to launcher.
             SurfaceControl.Transaction otherStartT = new SurfaceControl.Transaction();
             @SplitScreen.StageType int topStageToKeep = STAGE_TYPE_UNDEFINED;
-            if (mSplitHandler.isSplitScreenVisible()) {
+            if (splitHandler.isSplitScreenVisible()) {
                 // The non-going home case, we could be pip-ing one of the split stages and keep
                 // showing the other
                 for (int i = info.getChanges().size() - 1; i >= 0; --i) {
@@ -651,7 +914,7 @@
                         continue;
                     }
                     @SplitScreen.StageType int splitItemStage =
-                            mSplitHandler.getSplitItemStage(change.getLastParent());
+                            splitHandler.getSplitItemStage(change.getLastParent());
                     if (splitItemStage != STAGE_TYPE_UNDEFINED) {
                         topStageToKeep = splitItemStage;
                         break;
@@ -659,7 +922,7 @@
                 }
             }
             // Let split update internal state for dismiss.
-            mSplitHandler.prepareDismissAnimation(topStageToKeep,
+            splitHandler.prepareDismissAnimation(topStageToKeep,
                     EXIT_REASON_CHILD_TASK_ENTER_PIP, everythingElse, otherStartT,
                     finishTransaction);
 
@@ -673,13 +936,13 @@
                 }
             }
 
-            mPipHandler.setEnterAnimationType(ANIM_TYPE_ALPHA);
-            mPipHandler.startEnterAnimation(pipChange, startTransaction, finishTransaction,
+            pipHandler.setEnterAnimationType(ANIM_TYPE_ALPHA);
+            pipHandler.startEnterAnimation(pipChange, startTransaction, finishTransaction,
                     finishCB);
             // Dispatch the rest of the transition normally. This will most-likely be taken by
             // recents or default handler.
-            mixed.mLeftoversHandler = mPlayer.dispatchTransition(mixed.mTransition, everythingElse,
-                    otherStartT, finishTransaction, finishCB, this);
+            mixed.mLeftoversHandler = player.dispatchTransition(mixed.mTransition, everythingElse,
+                    otherStartT, finishTransaction, finishCB, mixedHandler);
         } else {
             ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "  Not leaving split, so just "
                     + "forward animation to Pip-Handler.");
@@ -687,7 +950,7 @@
             // new pip task is spawned). In this case, we don't actually exit split so we can
             // just let pip transition handle the animation verbatim.
             mixed.mInFlightSubAnimations = 1;
-            mPipHandler.startAnimation(mixed.mTransition, info, startTransaction, finishTransaction,
+            pipHandler.startAnimation(mixed.mTransition, info, startTransaction, finishTransaction,
                     finishCB);
         }
         return true;
@@ -724,10 +987,15 @@
     public boolean animatePendingEnterPipFromSplit(IBinder transition, TransitionInfo info,
             SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT,
             Transitions.TransitionFinishCallback finishCallback) {
-        final MixedTransition mixed = new MixedTransition(
+        final MixedTransition mixed = createMixedTransition(
                 MixedTransition.TYPE_ENTER_PIP_FROM_SPLIT, transition);
         mActiveTransitions.add(mixed);
-        return animateEnterPipFromSplit(mixed, info, startT, finishT, finishCallback);
+        Transitions.TransitionFinishCallback callback = wct -> {
+            mActiveTransitions.remove(mixed);
+            finishCallback.onTransitionFinished(wct);
+        };
+        return animateEnterPipFromSplit(mixed, info, startT, finishT, finishCallback, mPlayer, this,
+                mPipHandler, mSplitHandler);
     }
 
     /**
@@ -750,7 +1018,7 @@
         }
         if (displayPart.getChanges().isEmpty()) return false;
         unlinkMissingParents(everythingElse);
-        final MixedTransition mixed = new MixedTransition(
+        final MixedTransition mixed = createMixedTransition(
                 MixedTransition.TYPE_DISPLAY_AND_SPLIT_CHANGE, transition);
         mActiveTransitions.add(mixed);
         ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Animation is a mix of display change "
@@ -779,116 +1047,22 @@
         return true;
     }
 
-    private boolean animateRecentsDuringSplit(@NonNull final MixedTransition mixed,
+    private static boolean animateKeyguard(@NonNull final MixedTransition mixed,
             @NonNull TransitionInfo info,
             @NonNull SurfaceControl.Transaction startTransaction,
             @NonNull SurfaceControl.Transaction finishTransaction,
-            @NonNull Transitions.TransitionFinishCallback finishCallback) {
-        // Split-screen is only interested in the recents transition finishing (and merging), so
-        // just wrap finish and start recents animation directly.
-        Transitions.TransitionFinishCallback finishCB = (wct) -> {
-            mixed.mInFlightSubAnimations = 0;
-            mActiveTransitions.remove(mixed);
-            // If pair-to-pair switching, the post-recents clean-up isn't needed.
-            wct = wct != null ? wct : new WindowContainerTransaction();
-            if (mixed.mAnimType != MixedTransition.ANIM_TYPE_PAIR_TO_PAIR) {
-                mSplitHandler.onRecentsInSplitAnimationFinish(wct, finishTransaction);
-            } else {
-                // notify pair-to-pair recents animation finish
-                mSplitHandler.onRecentsPairToPairAnimationFinish(wct);
-            }
-            mSplitHandler.onTransitionAnimationComplete();
-            finishCallback.onTransitionFinished(wct);
-        };
-        mixed.mInFlightSubAnimations = 1;
-        mSplitHandler.onRecentsInSplitAnimationStart(info);
-        final boolean handled = mixed.mLeftoversHandler.startAnimation(mixed.mTransition, info,
-                startTransaction, finishTransaction, finishCB);
-        if (!handled) {
-            mSplitHandler.onRecentsInSplitAnimationCanceled();
-            mActiveTransitions.remove(mixed);
-        }
-        return handled;
-    }
-
-    private boolean animateKeyguard(@NonNull final MixedTransition mixed,
-            @NonNull TransitionInfo info,
-            @NonNull SurfaceControl.Transaction startTransaction,
-            @NonNull SurfaceControl.Transaction finishTransaction,
-            @NonNull Transitions.TransitionFinishCallback finishCallback) {
+            @NonNull Transitions.TransitionFinishCallback finishCallback,
+            @NonNull KeyguardTransitionHandler keyguardHandler,
+            PipTransitionController pipHandler) {
         if (mixed.mFinishT == null) {
             mixed.mFinishT = finishTransaction;
             mixed.mFinishCB = finishCallback;
         }
         // Sync pip state.
-        if (mPipHandler != null) {
-            mPipHandler.syncPipSurfaceState(info, startTransaction, finishTransaction);
+        if (pipHandler != null) {
+            pipHandler.syncPipSurfaceState(info, startTransaction, finishTransaction);
         }
-        return mixed.startSubAnimation(mKeyguardHandler, info, startTransaction, finishTransaction);
-    }
-
-    private boolean animateRecentsDuringKeyguard(@NonNull final MixedTransition mixed,
-            @NonNull TransitionInfo info,
-            @NonNull SurfaceControl.Transaction startTransaction,
-            @NonNull SurfaceControl.Transaction finishTransaction,
-            @NonNull Transitions.TransitionFinishCallback finishCallback) {
-        if (mixed.mInfo == null) {
-            mixed.mInfo = info;
-            mixed.mFinishT = finishTransaction;
-            mixed.mFinishCB = finishCallback;
-        }
-        return mixed.startSubAnimation(mRecentsHandler, info, startTransaction, finishTransaction);
-    }
-
-    private boolean animateRecentsDuringDesktop(@NonNull final MixedTransition mixed,
-            @NonNull TransitionInfo info,
-            @NonNull SurfaceControl.Transaction startTransaction,
-            @NonNull SurfaceControl.Transaction finishTransaction,
-            @NonNull Transitions.TransitionFinishCallback finishCallback) {
-        Transitions.TransitionFinishCallback finishCB = wct -> {
-            mixed.mInFlightSubAnimations--;
-            if (mixed.mInFlightSubAnimations == 0) {
-                mActiveTransitions.remove(mixed);
-                finishCallback.onTransitionFinished(wct);
-            }
-        };
-
-        mixed.mInFlightSubAnimations++;
-        boolean consumed = mRecentsHandler.startAnimation(
-                mixed.mTransition, info, startTransaction, finishTransaction, finishCB);
-        if (!consumed) {
-            mixed.mInFlightSubAnimations--;
-            return false;
-        }
-        if (mDesktopTasksController != null) {
-            mDesktopTasksController.syncSurfaceState(info, finishTransaction);
-            return true;
-        }
-
-        return false;
-    }
-
-    private boolean animateUnfold(@NonNull final MixedTransition mixed,
-            @NonNull TransitionInfo info,
-            @NonNull SurfaceControl.Transaction startTransaction,
-            @NonNull SurfaceControl.Transaction finishTransaction,
-            @NonNull Transitions.TransitionFinishCallback finishCallback) {
-        final Transitions.TransitionFinishCallback finishCB = (wct) -> {
-            mixed.mInFlightSubAnimations--;
-            if (mixed.mInFlightSubAnimations > 0) return;
-            mActiveTransitions.remove(mixed);
-            finishCallback.onTransitionFinished(wct);
-        };
-        mixed.mInFlightSubAnimations = 1;
-        // Sync pip state.
-        if (mPipHandler != null) {
-            mPipHandler.syncPipSurfaceState(info, startTransaction, finishTransaction);
-        }
-        if (mSplitHandler != null && mSplitHandler.isSplitActive()) {
-            mSplitHandler.updateSurfaces(startTransaction);
-        }
-        return mUnfoldHandler.startAnimation(
-                mixed.mTransition, info, startTransaction, finishTransaction, finishCB);
+        return mixed.startSubAnimation(keyguardHandler, info, startTransaction, finishTransaction);
     }
 
     /** Use to when split use intent to enter, check if this enter transition should be mixed or
@@ -932,65 +1106,13 @@
             @NonNull Transitions.TransitionFinishCallback finishCallback) {
         for (int i = 0; i < mActiveTransitions.size(); ++i) {
             if (mActiveTransitions.get(i).mTransition != mergeTarget) continue;
+
             MixedTransition mixed = mActiveTransitions.get(i);
             if (mixed.mInFlightSubAnimations <= 0) {
                 // Already done, so no need to end it.
                 return;
             }
-            if (mixed.mType == MixedTransition.TYPE_DISPLAY_AND_SPLIT_CHANGE) {
-                // queue since no actual animation.
-            } else if (mixed.mType == MixedTransition.TYPE_ENTER_PIP_FROM_SPLIT) {
-                if (mixed.mAnimType == MixedTransition.ANIM_TYPE_GOING_HOME) {
-                    boolean ended = mSplitHandler.end();
-                    // If split couldn't end (because it is remote), then don't end everything else
-                    // since we have to play out the animation anyways.
-                    if (!ended) return;
-                    mPipHandler.end();
-                    if (mixed.mLeftoversHandler != null) {
-                        mixed.mLeftoversHandler.mergeAnimation(transition, info, t, mergeTarget,
-                                finishCallback);
-                    }
-                } else {
-                    mPipHandler.end();
-                }
-            } else if (mixed.mType == MixedTransition.TYPE_ENTER_PIP_FROM_ACTIVITY_EMBEDDING) {
-                mPipHandler.end();
-                mActivityEmbeddingController.mergeAnimation(transition, info, t, mergeTarget,
-                        finishCallback);
-            } else if (mixed.mType == MixedTransition.TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE) {
-                mPipHandler.end();
-                if (mixed.mLeftoversHandler != null) {
-                    mixed.mLeftoversHandler.mergeAnimation(transition, info, t, mergeTarget,
-                            finishCallback);
-                }
-            } else if (mixed.mType == MixedTransition.TYPE_RECENTS_DURING_SPLIT) {
-                if (mSplitHandler.isPendingEnter(transition)) {
-                    // Recents -> enter-split means that we are switching from one pair to
-                    // another pair.
-                    mixed.mAnimType = MixedTransition.ANIM_TYPE_PAIR_TO_PAIR;
-                }
-                mixed.mLeftoversHandler.mergeAnimation(transition, info, t, mergeTarget,
-                        finishCallback);
-            } else if (mixed.mType == MixedTransition.TYPE_KEYGUARD) {
-                mKeyguardHandler.mergeAnimation(transition, info, t, mergeTarget, finishCallback);
-            } else if (mixed.mType == MixedTransition.TYPE_RECENTS_DURING_KEYGUARD) {
-                if ((info.getFlags() & TRANSIT_FLAG_KEYGUARD_UNOCCLUDING) != 0) {
-                    handoverTransitionLeashes(mixed, info, t, mixed.mFinishT);
-                    if (animateKeyguard(mixed, info, t, mixed.mFinishT, mixed.mFinishCB)) {
-                        finishCallback.onTransitionFinished(null);
-                    }
-                }
-                mixed.mLeftoversHandler.mergeAnimation(transition, info, t, mergeTarget,
-                        finishCallback);
-            } else if (mixed.mType == MixedTransition.TYPE_RECENTS_DURING_DESKTOP) {
-                mixed.mLeftoversHandler.mergeAnimation(transition, info, t, mergeTarget,
-                        finishCallback);
-            } else if (mixed.mType == MixedTransition.TYPE_UNFOLD) {
-                mUnfoldHandler.mergeAnimation(transition, info, t, mergeTarget, finishCallback);
-            } else {
-                throw new IllegalStateException("Playing a mixed transition with unknown type? "
-                        + mixed.mType);
-            }
+            mixed.mergeAnimation(transition, info, t, mergeTarget, finishCallback);
         }
     }
 
@@ -1003,46 +1125,30 @@
             mixed = mActiveTransitions.remove(i);
             break;
         }
-        if (mixed == null) return;
-        if (mixed.mType == MixedTransition.TYPE_ENTER_PIP_FROM_SPLIT) {
-            mPipHandler.onTransitionConsumed(transition, aborted, finishT);
-        } else if (mixed.mType == MixedTransition.TYPE_ENTER_PIP_FROM_ACTIVITY_EMBEDDING) {
-            mPipHandler.onTransitionConsumed(transition, aborted, finishT);
-            mActivityEmbeddingController.onTransitionConsumed(transition, aborted, finishT);
-        } else if (mixed.mType == MixedTransition.TYPE_RECENTS_DURING_SPLIT) {
-            mixed.mLeftoversHandler.onTransitionConsumed(transition, aborted, finishT);
-        } else if (mixed.mType == MixedTransition.TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE) {
-            mixed.mLeftoversHandler.onTransitionConsumed(transition, aborted, finishT);
-        } else if (mixed.mType == MixedTransition.TYPE_KEYGUARD) {
-            mKeyguardHandler.onTransitionConsumed(transition, aborted, finishT);
-        } else if (mixed.mType == MixedTransition.TYPE_RECENTS_DURING_DESKTOP) {
-            mixed.mLeftoversHandler.onTransitionConsumed(transition, aborted, finishT);
-        } else if (mixed.mType == MixedTransition.TYPE_UNFOLD) {
-            mUnfoldHandler.onTransitionConsumed(transition, aborted, finishT);
-        }
-        if (mixed.mHasRequestToRemote) {
-            mPlayer.getRemoteTransitionHandler().onTransitionConsumed(transition, aborted, finishT);
+        if (mixed != null) {
+            mixed.onTransitionConsumed(transition, aborted, finishT);
         }
     }
 
     /**
-     * Update an incoming {@link TransitionInfo} with the leashes from an ongoing
-     * {@link MixedTransition} so that it can take over some parts of the animation without
+     * Update an incoming {@link TransitionInfo} with the leashes from an existing
+     * {@link TransitionInfo} so that it can take over some parts of the animation without
      * reparenting to new transition roots.
      */
-    private static void handoverTransitionLeashes(@NonNull MixedTransition mixed,
-            @NonNull TransitionInfo info,
+    private static void handoverTransitionLeashes(
+            @NonNull TransitionInfo from,
+            @NonNull TransitionInfo to,
             @NonNull SurfaceControl.Transaction startT,
             @NonNull SurfaceControl.Transaction finishT) {
 
         // Show the roots in case they contain new changes not present in the original transition.
-        for (int j = info.getRootCount() - 1; j >= 0; --j) {
-            startT.show(info.getRoot(j).getLeash());
+        for (int j = to.getRootCount() - 1; j >= 0; --j) {
+            startT.show(to.getRoot(j).getLeash());
         }
 
         // Find all of the leashes from the original transition.
         Map<WindowContainerToken, TransitionInfo.Change> originalChanges = new ArrayMap<>();
-        for (TransitionInfo.Change oldChange : mixed.mInfo.getChanges()) {
+        for (TransitionInfo.Change oldChange : from.getChanges()) {
             if (oldChange.getContainer() != null) {
                 originalChanges.put(oldChange.getContainer(), oldChange);
             }
@@ -1050,9 +1156,10 @@
 
         // Merge the animation leashes by re-using the original ones if we see the same container
         // in the new transition and the old.
-        for (TransitionInfo.Change newChange : info.getChanges()) {
+        for (TransitionInfo.Change newChange : to.getChanges()) {
             if (originalChanges.containsKey(newChange.getContainer())) {
-                final TransitionInfo.Change oldChange = originalChanges.get(newChange.getContainer());
+                final TransitionInfo.Change oldChange = originalChanges.get(
+                        newChange.getContainer());
                 startT.reparent(newChange.getLeash(), null);
                 newChange.setLeash(oldChange.getLeash());
             }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java
index 473deba..af31f5f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java
@@ -17,6 +17,7 @@
 package com.android.wm.shell.transition;
 
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.view.Display.DEFAULT_DISPLAY;
 import static android.window.TransitionInfo.FLAG_BACK_GESTURE_ANIMATED;
 
 import static com.android.wm.shell.transition.Transitions.TransitionObserver;
@@ -35,7 +36,8 @@
 
 /**
  * The {@link TransitionObserver} that observes for transitions involving the home
- * activity. It reports transitions to the caller via {@link IHomeTransitionListener}.
+ * activity on the {@link android.view.Display#DEFAULT_DISPLAY} only.
+ * It reports transitions to the caller via {@link IHomeTransitionListener}.
  */
 public class HomeTransitionObserver implements TransitionObserver,
         RemoteCallable<HomeTransitionObserver> {
@@ -58,6 +60,7 @@
         for (TransitionInfo.Change change : info.getChanges()) {
             final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
             if (taskInfo == null
+                    || taskInfo.displayId != DEFAULT_DISPLAY
                     || taskInfo.taskId == -1
                     || !taskInfo.isRunning) {
                 continue;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/IHomeTransitionListener.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/IHomeTransitionListener.aidl
index 18716c6..72fba3b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/IHomeTransitionListener.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/IHomeTransitionListener.aidl
@@ -20,12 +20,13 @@
 import android.window.TransitionFilter;
 
 /**
- *  Listener interface that Launcher attaches to SystemUI to get home activity transition callbacks.
+ * Listener interface that Launcher attaches to SystemUI to get home activity transition callbacks
+ * on the default display.
  */
-interface IHomeTransitionListener {
+oneway interface IHomeTransitionListener {
 
     /**
-     * Called when a transition changes the visibility of the home activity.
+     * Called when a transition changes the visibility of the home activity on the default display.
      */
     void onHomeVisibilityChanged(in boolean isVisible);
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
index cf16920..1a793a1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
@@ -23,13 +23,11 @@
 import android.app.ActivityManager.RunningTaskInfo;
 import android.content.Context;
 import android.os.Handler;
-import android.os.IBinder;
 import android.util.SparseArray;
 import android.view.Choreographer;
 import android.view.MotionEvent;
 import android.view.SurfaceControl;
 import android.view.View;
-import android.window.TransitionInfo;
 import android.window.WindowContainerToken;
 import android.window.WindowContainerTransaction;
 
@@ -54,6 +52,7 @@
     private final Choreographer mMainChoreographer;
     private final DisplayController mDisplayController;
     private final SyncTransactionQueue mSyncQueue;
+    private final Transitions mTransitions;
     private TaskOperations mTaskOperations;
 
     private final SparseArray<CaptionWindowDecoration> mWindowDecorByTaskId = new SparseArray<>();
@@ -64,29 +63,21 @@
             Choreographer mainChoreographer,
             ShellTaskOrganizer taskOrganizer,
             DisplayController displayController,
-            SyncTransactionQueue syncQueue) {
+            SyncTransactionQueue syncQueue,
+            Transitions transitions) {
         mContext = context;
         mMainHandler = mainHandler;
         mMainChoreographer = mainChoreographer;
         mTaskOrganizer = taskOrganizer;
         mDisplayController = displayController;
         mSyncQueue = syncQueue;
+        mTransitions = transitions;
         if (!Transitions.ENABLE_SHELL_TRANSITIONS) {
             mTaskOperations = new TaskOperations(null, mContext, mSyncQueue);
         }
     }
 
     @Override
-    public void onTransitionReady(IBinder transition, TransitionInfo info,
-            TransitionInfo.Change change) {}
-
-    @Override
-    public void onTransitionMerged(IBinder merged, IBinder playing) {}
-
-    @Override
-    public void onTransitionFinished(IBinder transition) {}
-
-    @Override
     public void setFreeformTaskTransitionStarter(FreeformTaskTransitionStarter transitionStarter) {
         mTaskOperations = new TaskOperations(transitionStarter, mContext, mSyncQueue);
     }
@@ -133,7 +124,8 @@
         if (decoration == null) {
             createWindowDecoration(taskInfo, taskSurface, startT, finishT);
         } else {
-            decoration.relayout(taskInfo, startT, finishT, false /* applyStartTransactionOnDraw */);
+            decoration.relayout(taskInfo, startT, finishT, false /* applyStartTransactionOnDraw */,
+                    false /* setTaskCropAndPosition */);
         }
     }
 
@@ -145,7 +137,8 @@
         final CaptionWindowDecoration decoration = mWindowDecorByTaskId.get(taskInfo.taskId);
         if (decoration == null) return;
 
-        decoration.relayout(taskInfo, startT, finishT, false /* applyStartTransactionOnDraw */);
+        decoration.relayout(taskInfo, startT, finishT, false /* applyStartTransactionOnDraw */,
+                false /* setTaskCropAndPosition */);
     }
 
     @Override
@@ -191,16 +184,17 @@
                         mSyncQueue);
         mWindowDecorByTaskId.put(taskInfo.taskId, windowDecoration);
 
-        final DragPositioningCallback dragPositioningCallback =
-                new FluidResizeTaskPositioner(mTaskOrganizer, windowDecoration, mDisplayController,
-                        0 /* disallowedAreaForEndBoundsHeight */);
+        final FluidResizeTaskPositioner taskPositioner =
+                new FluidResizeTaskPositioner(mTaskOrganizer, mTransitions, windowDecoration,
+                        mDisplayController, 0 /* disallowedAreaForEndBoundsHeight */);
         final CaptionTouchEventListener touchEventListener =
-                new CaptionTouchEventListener(taskInfo, dragPositioningCallback);
+                new CaptionTouchEventListener(taskInfo, taskPositioner);
         windowDecoration.setCaptionListeners(touchEventListener, touchEventListener);
-        windowDecoration.setDragPositioningCallback(dragPositioningCallback);
+        windowDecoration.setDragPositioningCallback(taskPositioner);
         windowDecoration.setDragDetector(touchEventListener.mDragDetector);
+        windowDecoration.setTaskDragResizer(taskPositioner);
         windowDecoration.relayout(taskInfo, startT, finishT,
-                false /* applyStartTransactionOnDraw */);
+                false /* applyStartTransactionOnDraw */, false /* setTaskCropAndPosition */);
         setupCaptionColor(taskInfo, windowDecoration);
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
index c12ac8b..1debb02 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
@@ -22,6 +22,7 @@
 import android.content.Context;
 import android.content.res.ColorStateList;
 import android.graphics.Color;
+import android.graphics.Rect;
 import android.graphics.drawable.GradientDrawable;
 import android.graphics.drawable.VectorDrawable;
 import android.os.Handler;
@@ -34,6 +35,7 @@
 import com.android.wm.shell.R;
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.DisplayLayout;
 import com.android.wm.shell.common.SyncTransactionQueue;
 
 /**
@@ -84,6 +86,69 @@
         mDragPositioningCallback = dragPositioningCallback;
     }
 
+    @Override
+    Rect calculateValidDragArea() {
+        final int leftButtonsWidth = loadDimensionPixelSize(mContext.getResources(),
+                R.dimen.caption_left_buttons_width);
+
+        // On a smaller screen, don't require as much empty space on screen, as offscreen
+        // drags will be restricted too much.
+        final int requiredEmptySpaceId = mDisplayController.getDisplayContext(mTaskInfo.taskId)
+                .getResources().getConfiguration().smallestScreenWidthDp >= 600
+                ? R.dimen.freeform_required_visible_empty_space_in_header :
+                R.dimen.small_screen_required_visible_empty_space_in_header;
+        final int requiredEmptySpace = loadDimensionPixelSize(mContext.getResources(),
+                requiredEmptySpaceId);
+
+        final int rightButtonsWidth = loadDimensionPixelSize(mContext.getResources(),
+                R.dimen.caption_right_buttons_width);
+        final int taskWidth = mTaskInfo.configuration.windowConfiguration.getBounds().width();
+        final DisplayLayout layout = mDisplayController.getDisplayLayout(mTaskInfo.displayId);
+        final int displayWidth = layout.width();
+        final Rect stableBounds = new Rect();
+        layout.getStableBounds(stableBounds);
+        return new Rect(
+                determineMinX(leftButtonsWidth, rightButtonsWidth, requiredEmptySpace,
+                        taskWidth),
+                stableBounds.top,
+                determineMaxX(leftButtonsWidth, rightButtonsWidth, requiredEmptySpace, taskWidth,
+                        displayWidth),
+                determineMaxY(requiredEmptySpace, stableBounds));
+    }
+
+
+    /**
+     * Determine the lowest x coordinate of a freeform task. Used for restricting drag inputs.
+     */
+    private int determineMinX(int leftButtonsWidth, int rightButtonsWidth, int requiredEmptySpace,
+            int taskWidth) {
+        // Do not let apps with < 48dp empty header space go off the left edge at all.
+        if (leftButtonsWidth + rightButtonsWidth + requiredEmptySpace > taskWidth) {
+            return 0;
+        }
+        return -taskWidth + requiredEmptySpace + rightButtonsWidth;
+    }
+
+    /**
+     * Determine the highest x coordinate of a freeform task. Used for restricting drag inputs.
+     */
+    private int determineMaxX(int leftButtonsWidth, int rightButtonsWidth, int requiredEmptySpace,
+            int taskWidth, int displayWidth) {
+        // Do not let apps with < 48dp empty header space go off the right edge at all.
+        if (leftButtonsWidth + rightButtonsWidth + requiredEmptySpace > taskWidth) {
+            return displayWidth - taskWidth;
+        }
+        return displayWidth - requiredEmptySpace - leftButtonsWidth;
+    }
+
+    /**
+     * Determine the highest y coordinate of a freeform task. Used for restricting drag inputs.
+     */
+    private int determineMaxY(int requiredEmptySpace, Rect stableBounds) {
+        return stableBounds.bottom - requiredEmptySpace;
+    }
+
+
     void setDragDetector(DragDetector dragDetector) {
         mDragDetector = dragDetector;
         mDragDetector.setTouchSlop(ViewConfiguration.get(mContext).getScaledTouchSlop());
@@ -92,15 +157,21 @@
     @Override
     void relayout(RunningTaskInfo taskInfo) {
         final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+        // The crop and position of the task should only be set when a task is fluid resizing. In
+        // all other cases, it is expected that the transition handler positions and crops the task
+        // in order to allow the handler time to animate before the task before the final
+        // position and crop are set.
+        final boolean shouldSetTaskPositionAndCrop = mTaskDragResizer.isResizingOrAnimating();
         // Use |applyStartTransactionOnDraw| so that the transaction (that applies task crop) is
         // synced with the buffer transaction (that draws the View). Both will be shown on screen
         // at the same, whereas applying them independently causes flickering. See b/270202228.
-        relayout(taskInfo, t, t, true /* applyStartTransactionOnDraw */);
+        relayout(taskInfo, t, t, true /* applyStartTransactionOnDraw */,
+                shouldSetTaskPositionAndCrop);
     }
 
     void relayout(RunningTaskInfo taskInfo,
             SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT,
-            boolean applyStartTransactionOnDraw) {
+            boolean applyStartTransactionOnDraw, boolean setTaskCropAndPosition) {
         final int shadowRadiusID = taskInfo.isFocused
                 ? R.dimen.freeform_decor_shadow_focused_thickness
                 : R.dimen.freeform_decor_shadow_unfocused_thickness;
@@ -118,6 +189,7 @@
         mRelayoutParams.mCaptionHeightId = getCaptionHeightId(taskInfo.getWindowingMode());
         mRelayoutParams.mShadowRadiusId = shadowRadiusID;
         mRelayoutParams.mApplyStartTransactionOnDraw = applyStartTransactionOnDraw;
+        mRelayoutParams.mSetTaskPositionAndCrop = setTaskCropAndPosition;
 
         relayout(mRelayoutParams, startT, finishT, wct, oldRootView, mResult);
         // After this line, mTaskInfo is up-to-date and should be used instead of taskInfo
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index ab29df1..d07c646 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -22,7 +22,6 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.view.WindowInsets.Type.statusBars;
-import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY;
 
 import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
 import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
@@ -43,7 +42,6 @@
 import android.graphics.Region;
 import android.hardware.input.InputManager;
 import android.os.Handler;
-import android.os.IBinder;
 import android.os.Looper;
 import android.util.SparseArray;
 import android.view.Choreographer;
@@ -59,8 +57,6 @@
 import android.view.SurfaceControl.Transaction;
 import android.view.View;
 import android.view.ViewConfiguration;
-import android.view.WindowManager;
-import android.window.TransitionInfo;
 import android.window.WindowContainerToken;
 import android.window.WindowContainerTransaction;
 
@@ -80,8 +76,6 @@
 import com.android.wm.shell.desktopmode.DesktopTasksController;
 import com.android.wm.shell.desktopmode.DesktopTasksController.SnapPosition;
 import com.android.wm.shell.freeform.FreeformTaskTransitionStarter;
-import com.android.wm.shell.recents.RecentsTransitionHandler;
-import com.android.wm.shell.recents.RecentsTransitionStateListener;
 import com.android.wm.shell.splitscreen.SplitScreen;
 import com.android.wm.shell.splitscreen.SplitScreen.StageType;
 import com.android.wm.shell.splitscreen.SplitScreenController;
@@ -115,7 +109,6 @@
     private final DisplayController mDisplayController;
     private final SyncTransactionQueue mSyncQueue;
     private final Optional<DesktopTasksController> mDesktopTasksController;
-    private final RecentsTransitionHandler mRecentsTransitionHandler;
     private boolean mTransitionDragActive;
 
     private SparseArray<EventReceiver> mEventReceiversByDisplay = new SparseArray<>();
@@ -154,7 +147,6 @@
             SyncTransactionQueue syncQueue,
             Transitions transitions,
             Optional<DesktopTasksController> desktopTasksController,
-            RecentsTransitionHandler recentsTransitionHandler,
             RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer
     ) {
         this(
@@ -170,7 +162,6 @@
                 syncQueue,
                 transitions,
                 desktopTasksController,
-                recentsTransitionHandler,
                 new DesktopModeWindowDecoration.Factory(),
                 new InputMonitorFactory(),
                 SurfaceControl.Transaction::new,
@@ -191,7 +182,6 @@
             SyncTransactionQueue syncQueue,
             Transitions transitions,
             Optional<DesktopTasksController> desktopTasksController,
-            RecentsTransitionHandler recentsTransitionHandler,
             DesktopModeWindowDecoration.Factory desktopModeWindowDecorFactory,
             InputMonitorFactory inputMonitorFactory,
             Supplier<SurfaceControl.Transaction> transactionFactory,
@@ -207,7 +197,6 @@
         mSyncQueue = syncQueue;
         mTransitions = transitions;
         mDesktopTasksController = desktopTasksController;
-        mRecentsTransitionHandler = recentsTransitionHandler;
         mShellCommandHandler = shellCommandHandler;
         mDesktopModeWindowDecorFactory = desktopModeWindowDecorFactory;
         mInputMonitorFactory = inputMonitorFactory;
@@ -219,12 +208,6 @@
 
     private void onInit() {
         mShellController.addKeyguardChangeListener(mDesktopModeKeyguardChangeListener);
-        mRecentsTransitionHandler.addTransitionStateListener(new RecentsTransitionStateListener() {
-            @Override
-            public void onTransitionStarted(IBinder transition) {
-                blockRelayoutOnTransitionStarted(transition);
-            }
-        });
         mShellCommandHandler.addDumpCallback(this::dump, this);
         mDisplayInsetsController.addInsetsChangedListener(mContext.getDisplayId(),
                 new DesktopModeOnInsetsChangedListener());
@@ -264,48 +247,6 @@
     }
 
     @Override
-    public void onTransitionReady(
-            @NonNull IBinder transition,
-            @NonNull TransitionInfo info,
-            @NonNull TransitionInfo.Change change) {
-        if (change.getMode() == WindowManager.TRANSIT_CHANGE
-                && (info.getType() == Transitions.TRANSIT_EXIT_DESKTOP_MODE
-                || info.getType() == Transitions.TRANSIT_DESKTOP_MODE_TOGGLE_RESIZE
-                || info.getType() == Transitions.TRANSIT_MOVE_TO_DESKTOP)) {
-            mWindowDecorByTaskId.get(change.getTaskInfo().taskId)
-                    .addTransitionPausingRelayout(transition);
-        } else if (change.getMode() == WindowManager.TRANSIT_TO_BACK
-                && info.getType() == Transitions.TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP
-                && change.getTaskInfo() != null) {
-            final DesktopModeWindowDecoration decor =
-                    mWindowDecorByTaskId.get(change.getTaskInfo().taskId);
-            if (decor != null) {
-                decor.addTransitionPausingRelayout(transition);
-            }
-        } else if (change.getMode() == WindowManager.TRANSIT_TO_FRONT
-                && ((info.getFlags() & TRANSIT_FLAG_KEYGUARD_GOING_AWAY) != 0)
-                && change.getTaskInfo() != null) {
-            blockRelayoutOnTransitionStarted(transition);
-        }
-    }
-
-    @Override
-    public void onTransitionMerged(@NonNull IBinder merged, @NonNull IBinder playing) {
-        for (int i = 0; i < mWindowDecorByTaskId.size(); i++) {
-            final DesktopModeWindowDecoration decor = mWindowDecorByTaskId.valueAt(i);
-            decor.mergeTransitionPausingRelayout(merged, playing);
-        }
-    }
-
-    @Override
-    public void onTransitionFinished(@NonNull IBinder transition) {
-        for (int i = 0; i < mWindowDecorByTaskId.size(); i++) {
-            final DesktopModeWindowDecoration decor = mWindowDecorByTaskId.valueAt(i);
-            decor.removeTransitionPausingRelayout(transition);
-        }
-    }
-
-    @Override
     public void onTaskInfoChanged(RunningTaskInfo taskInfo) {
         final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(taskInfo.taskId);
         if (decoration == null) return;
@@ -335,7 +276,8 @@
         if (decoration == null) {
             createWindowDecoration(taskInfo, taskSurface, startT, finishT);
         } else {
-            decoration.relayout(taskInfo, startT, finishT, false /* applyStartTransactionOnDraw */);
+            decoration.relayout(taskInfo, startT, finishT, false /* applyStartTransactionOnDraw */,
+                    false /* shouldSetTaskPositionAndCrop */);
         }
     }
 
@@ -347,7 +289,8 @@
         final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(taskInfo.taskId);
         if (decoration == null) return;
 
-        decoration.relayout(taskInfo, startT, finishT, false /* applyStartTransactionOnDraw */);
+        decoration.relayout(taskInfo, startT, finishT, false /* applyStartTransactionOnDraw */,
+                false /* shouldSetTaskPositionAndCrop */);
     }
 
     @Override
@@ -363,16 +306,6 @@
         }
     }
 
-    private void blockRelayoutOnTransitionStarted(IBinder transition) {
-        // Block relayout on window decorations originating from #onTaskInfoChanges until the
-        // animation completes to avoid interfering with the transition animation.
-        for (int i = 0; i < mWindowDecorByTaskId.size(); i++) {
-            final DesktopModeWindowDecoration decor = mWindowDecorByTaskId.valueAt(i);
-            decor.incrementRelayoutBlock();
-            decor.addTransitionPausingRelayout(transition);
-        }
-    }
-
     private class DesktopModeTouchEventListener extends GestureDetector.SimpleOnGestureListener
             implements View.OnClickListener, View.OnTouchListener, View.OnLongClickListener,
             DragDetector.MotionEventHandler {
@@ -423,7 +356,6 @@
                     // App sometimes draws before the insets from WindowDecoration#relayout have
                     // been added, so they must be added here
                     mWindowDecorByTaskId.get(mTaskId).addCaptionInset(wct);
-                    decoration.incrementRelayoutBlock();
                     mDesktopTasksController.get().moveToDesktop(decoration, mTaskId, wct);
                     closeOtherSplitTask(mTaskId);
                 }
@@ -434,7 +366,7 @@
                     mSplitScreenController.moveTaskToFullscreen(mTaskId);
                 } else {
                     mDesktopTasksController.ifPresent(c ->
-                            c.moveToFullscreen(mTaskId, mWindowDecorByTaskId.get(mTaskId)));
+                            c.moveToFullscreen(mTaskId));
                 }
             } else if (id == R.id.split_screen_button) {
                 decoration.closeHandleMenu();
@@ -602,7 +534,7 @@
                     mDesktopTasksController.ifPresent(c -> c.onDragPositioningEnd(taskInfo,
                             position,
                             new PointF(e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx)),
-                            newTaskBounds, mWindowDecorByTaskId.get(mTaskId)));
+                            newTaskBounds));
                     mIsDragging = false;
                     return true;
                 }
@@ -724,7 +656,7 @@
     private void handleEventOutsideFocusedCaption(MotionEvent ev,
             DesktopModeWindowDecoration relevantDecor) {
         // Returns if event occurs within caption
-        if (relevantDecor == null || relevantDecor.checkTouchEventInCaption(ev)) {
+        if (relevantDecor == null || relevantDecor.checkTouchEventInCaptionHandle(ev)) {
             return;
         }
 
@@ -759,7 +691,8 @@
                                 || windowingMode == WINDOWING_MODE_MULTI_WINDOW;
                     }
 
-                    if (dragFromStatusBarAllowed && relevantDecor.checkTouchEventInHandle(ev)) {
+                    if (dragFromStatusBarAllowed
+                            && relevantDecor.checkTouchEventInCaptionHandle(ev)) {
                         mTransitionDragActive = true;
                     }
                 }
@@ -809,20 +742,8 @@
                                     mContext, mDragToDesktopAnimationStartBounds,
                                     relevantDecor.mTaskInfo, relevantDecor.mTaskSurface);
                             mDesktopTasksController.ifPresent(
-                                    c -> {
-                                        final int taskId = relevantDecor.mTaskInfo.taskId;
-                                        relevantDecor.incrementRelayoutBlock();
-                                        if (isTaskInSplitScreen(taskId)) {
-                                            final DesktopModeWindowDecoration otherDecor =
-                                                    mWindowDecorByTaskId.get(
-                                                            getOtherSplitTask(taskId).taskId);
-                                            if (otherDecor != null) {
-                                                otherDecor.incrementRelayoutBlock();
-                                            }
-                                        }
-                                        c.startDragToDesktop(relevantDecor.mTaskInfo,
-                                                mMoveToDesktopAnimator, relevantDecor);
-                                    });
+                                    c -> c.startDragToDesktop(relevantDecor.mTaskInfo,
+                                            mMoveToDesktopAnimator, relevantDecor));
                         }
                     }
                     if (mMoveToDesktopAnimator != null) {
@@ -1010,8 +931,23 @@
         mWindowDecorByTaskId.put(taskInfo.taskId, windowDecoration);
         windowDecoration.createResizeVeil();
 
-        final DragPositioningCallback dragPositioningCallback = createDragPositioningCallback(
-                windowDecoration);
+        final DragPositioningCallback dragPositioningCallback;
+        final int transitionAreaHeight = mContext.getResources().getDimensionPixelSize(
+                R.dimen.desktop_mode_transition_area_height);
+        if (!DesktopModeStatus.isVeiledResizeEnabled()) {
+            dragPositioningCallback =  new FluidResizeTaskPositioner(
+                    mTaskOrganizer, mTransitions, windowDecoration, mDisplayController,
+                    mDragStartListener, mTransactionFactory, transitionAreaHeight);
+            windowDecoration.setTaskDragResizer(
+                    (FluidResizeTaskPositioner) dragPositioningCallback);
+        } else {
+            dragPositioningCallback =  new VeiledResizeTaskPositioner(
+                    mTaskOrganizer, windowDecoration, mDisplayController,
+                    mDragStartListener, mTransitions, transitionAreaHeight);
+            windowDecoration.setTaskDragResizer(
+                    (VeiledResizeTaskPositioner) dragPositioningCallback);
+        }
+
         final DesktopModeTouchEventListener touchEventListener =
                 new DesktopModeTouchEventListener(taskInfo, dragPositioningCallback);
 
@@ -1021,23 +957,9 @@
         windowDecoration.setDragPositioningCallback(dragPositioningCallback);
         windowDecoration.setDragDetector(touchEventListener.mDragDetector);
         windowDecoration.relayout(taskInfo, startT, finishT,
-                false /* applyStartTransactionOnDraw */);
+                false /* applyStartTransactionOnDraw */, false /* shouldSetTaskPositionAndCrop */);
         incrementEventReceiverTasks(taskInfo.displayId);
     }
-    private DragPositioningCallback createDragPositioningCallback(
-            @NonNull DesktopModeWindowDecoration windowDecoration) {
-        final int transitionAreaHeight = mContext.getResources().getDimensionPixelSize(
-                R.dimen.desktop_mode_transition_area_height);
-        if (!DesktopModeStatus.isVeiledResizeEnabled()) {
-            return new FluidResizeTaskPositioner(mTaskOrganizer, windowDecoration,
-                    mDisplayController, mDragStartListener, mTransactionFactory,
-                    transitionAreaHeight);
-        } else {
-            return new VeiledResizeTaskPositioner(mTaskOrganizer, windowDecoration,
-                    mDisplayController, mDragStartListener, mTransitions,
-                    transitionAreaHeight);
-        }
-    }
 
     private RunningTaskInfo getOtherSplitTask(int taskId) {
         @SplitPosition int remainingTaskPosition = mSplitScreenController
@@ -1138,7 +1060,6 @@
             }
         }
     }
-
 }
 
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
index 6ec91e0..5f77192 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
@@ -36,7 +36,6 @@
 import android.graphics.Region;
 import android.graphics.drawable.Drawable;
 import android.os.Handler;
-import android.os.IBinder;
 import android.util.Log;
 import android.view.Choreographer;
 import android.view.MotionEvent;
@@ -46,6 +45,7 @@
 import android.widget.ImageButton;
 import android.window.WindowContainerTransaction;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.policy.ScreenDecorationsUtils;
 import com.android.launcher3.icons.BaseIconFactory;
 import com.android.launcher3.icons.IconProvider;
@@ -61,8 +61,6 @@
 import com.android.wm.shell.windowdecor.viewholder.DesktopModeFocusedWindowDecorationViewHolder;
 import com.android.wm.shell.windowdecor.viewholder.DesktopModeWindowDecorationViewHolder;
 
-import java.util.HashSet;
-import java.util.Set;
 import java.util.function.Supplier;
 
 /**
@@ -103,8 +101,6 @@
 
     private ExclusionRegionListener mExclusionRegionListener;
 
-    private final Set<IBinder> mTransitionsPausingRelayout = new HashSet<>();
-    private int mRelayoutBlock;
     private final RootTaskDisplayAreaOrganizer mRootTaskDisplayAreaOrganizer;
 
     DesktopModeWindowDecoration(
@@ -178,63 +174,34 @@
 
     @Override
     void relayout(ActivityManager.RunningTaskInfo taskInfo) {
-        // TaskListener callbacks and shell transitions aren't synchronized, so starting a shell
-        // transition can trigger an onTaskInfoChanged call that updates the task's SurfaceControl
-        // and interferes with the transition animation that is playing at the same time.
-        if (mRelayoutBlock > 0) {
-            return;
-        }
-
         final SurfaceControl.Transaction t = mSurfaceControlTransactionSupplier.get();
+        // The crop and position of the task should only be set when a task is fluid resizing. In
+        // all other cases, it is expected that the transition handler positions and crops the task
+        // in order to allow the handler time to animate before the task before the final
+        // position and crop are set.
+        final boolean shouldSetTaskPositionAndCrop = !DesktopModeStatus.isVeiledResizeEnabled()
+                && mTaskDragResizer.isResizingOrAnimating();
         // Use |applyStartTransactionOnDraw| so that the transaction (that applies task crop) is
         // synced with the buffer transaction (that draws the View). Both will be shown on screen
         // at the same, whereas applying them independently causes flickering. See b/270202228.
-        relayout(taskInfo, t, t, true /* applyStartTransactionOnDraw */);
+        relayout(taskInfo, t, t, true /* applyStartTransactionOnDraw */,
+                shouldSetTaskPositionAndCrop);
     }
 
     void relayout(ActivityManager.RunningTaskInfo taskInfo,
             SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT,
-            boolean applyStartTransactionOnDraw) {
-        final int shadowRadiusID = taskInfo.isFocused
-                ? R.dimen.freeform_decor_shadow_focused_thickness
-                : R.dimen.freeform_decor_shadow_unfocused_thickness;
-        final boolean isFreeform =
-                taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM;
-        final boolean isDragResizeable = isFreeform && taskInfo.isResizeable;
-
+            boolean applyStartTransactionOnDraw, boolean shouldSetTaskPositionAndCrop) {
         if (isHandleMenuActive()) {
             mHandleMenu.relayout(startT);
         }
 
+        updateRelayoutParams(mRelayoutParams, mContext, taskInfo, applyStartTransactionOnDraw,
+                shouldSetTaskPositionAndCrop);
+
         final WindowDecorLinearLayout oldRootView = mResult.mRootView;
         final SurfaceControl oldDecorationSurface = mDecorationContainerSurface;
         final WindowContainerTransaction wct = new WindowContainerTransaction();
 
-        final int windowDecorLayoutId = getDesktopModeWindowDecorLayoutId(
-                taskInfo.getWindowingMode());
-        mRelayoutParams.reset();
-        mRelayoutParams.mRunningTaskInfo = taskInfo;
-        mRelayoutParams.mLayoutResId = windowDecorLayoutId;
-        mRelayoutParams.mCaptionHeightId = getCaptionHeightId(taskInfo.getWindowingMode());
-        mRelayoutParams.mShadowRadiusId = shadowRadiusID;
-        mRelayoutParams.mApplyStartTransactionOnDraw = applyStartTransactionOnDraw;
-        // The configuration used to lay out the window decoration. The system context's config is
-        // used when the task density has been overridden to a custom density so that the resources
-        // and views of the decoration aren't affected and match the rest of the System UI, if not
-        // then just use the task's configuration. A copy is made instead of using the original
-        // reference so that the configuration isn't mutated on config changes and diff checks can
-        // be made in WindowDecoration#relayout using the pre/post-relayout configuration.
-        // See b/301119301.
-        // TODO(b/301119301): consider moving the config data needed for diffs to relayout params
-        // instead of using a whole Configuration as a parameter.
-        final Configuration windowDecorConfig = new Configuration();
-        windowDecorConfig.setTo(DesktopTasksController.isDesktopDensityOverrideSet()
-                ? mContext.getResources().getConfiguration() // Use system context.
-                : mTaskInfo.configuration); // Use task configuration.
-        mRelayoutParams.mWindowDecorConfig = windowDecorConfig;
-
-        mRelayoutParams.mCornerRadius =
-                (int) ScreenDecorationsUtils.getWindowCornerRadius(mContext);
         relayout(mRelayoutParams, startT, finishT, wct, oldRootView, mResult);
         // After this line, mTaskInfo is up-to-date and should be used instead of taskInfo
 
@@ -273,6 +240,9 @@
             closeMaximizeMenu();
         }
 
+        final boolean isFreeform =
+                taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM;
+        final boolean isDragResizeable = isFreeform && taskInfo.isResizeable;
         if (!isDragResizeable) {
             if (!mTaskInfo.positionInParent.equals(mPositionInParent)) {
                 // We still want to track caption bar's exclusion region on a non-resizeable task.
@@ -323,6 +293,59 @@
         }
     }
 
+    @VisibleForTesting
+    static void updateRelayoutParams(
+            RelayoutParams relayoutParams,
+            Context context,
+            ActivityManager.RunningTaskInfo taskInfo,
+            boolean applyStartTransactionOnDraw,
+            boolean shouldSetTaskPositionAndCrop) {
+        relayoutParams.reset();
+        relayoutParams.mRunningTaskInfo = taskInfo;
+        relayoutParams.mLayoutResId =
+            getDesktopModeWindowDecorLayoutId(taskInfo.getWindowingMode());
+        relayoutParams.mCaptionHeightId = getCaptionHeightIdStatic(taskInfo.getWindowingMode());
+        relayoutParams.mCaptionWidthId = getCaptionWidthId(relayoutParams.mLayoutResId);
+        if (DesktopModeStatus.useWindowShadow(/* isFocusedWindow= */ taskInfo.isFocused)) {
+            relayoutParams.mShadowRadiusId = taskInfo.isFocused
+                    ? R.dimen.freeform_decor_shadow_focused_thickness
+                    : R.dimen.freeform_decor_shadow_unfocused_thickness;
+        }
+        relayoutParams.mApplyStartTransactionOnDraw = applyStartTransactionOnDraw;
+        relayoutParams.mSetTaskPositionAndCrop = shouldSetTaskPositionAndCrop;
+        // The configuration used to lay out the window decoration. The system context's config is
+        // used when the task density has been overridden to a custom density so that the resources
+        // and views of the decoration aren't affected and match the rest of the System UI, if not
+        // then just use the task's configuration. A copy is made instead of using the original
+        // reference so that the configuration isn't mutated on config changes and diff checks can
+        // be made in WindowDecoration#relayout using the pre/post-relayout configuration.
+        // See b/301119301.
+        // TODO(b/301119301): consider moving the config data needed for diffs to relayout params
+        // instead of using a whole Configuration as a parameter.
+        final Configuration windowDecorConfig = new Configuration();
+        windowDecorConfig.setTo(DesktopTasksController.isDesktopDensityOverrideSet()
+                ? context.getResources().getConfiguration() // Use system context.
+                : taskInfo.configuration); // Use task configuration.
+        relayoutParams.mWindowDecorConfig = windowDecorConfig;
+
+        if (DesktopModeStatus.useRoundedCorners()) {
+            relayoutParams.mCornerRadius =
+                    (int) ScreenDecorationsUtils.getWindowCornerRadius(context);
+        }
+    }
+
+    /**
+     * If task has focused window decor, return the caption id of the fullscreen caption size
+     * resource. Otherwise, return ID_NULL and caption width be set to task width.
+     */
+    private static int getCaptionWidthId(int layoutResId) {
+        if (layoutResId == R.layout.desktop_mode_focused_window_decor) {
+            return R.dimen.desktop_mode_fullscreen_decor_caption_width;
+        }
+        return Resources.ID_NULL;
+    }
+
+
     private PointF calculateMaximizeMenuPosition() {
         final PointF position = new PointF();
         final Resources resources = mContext.getResources();
@@ -443,6 +466,66 @@
     }
 
     /**
+     * Determine valid drag area for this task based on elements in the app chip.
+     */
+    @Override
+    Rect calculateValidDragArea() {
+        final int appTextWidth = ((DesktopModeAppControlsWindowDecorationViewHolder)
+                mWindowDecorViewHolder).getAppNameTextWidth();
+        final int leftButtonsWidth = loadDimensionPixelSize(mContext.getResources(),
+                R.dimen.desktop_mode_app_details_width_minus_text) + appTextWidth;
+        final int requiredEmptySpace = loadDimensionPixelSize(mContext.getResources(),
+                R.dimen.freeform_required_visible_empty_space_in_header);
+        final int rightButtonsWidth = loadDimensionPixelSize(mContext.getResources(),
+                R.dimen.desktop_mode_right_edge_buttons_width);
+        final int taskWidth = mTaskInfo.configuration.windowConfiguration.getBounds().width();
+        final DisplayLayout layout = mDisplayController.getDisplayLayout(mTaskInfo.displayId);
+        final int displayWidth = layout.width();
+        final Rect stableBounds = new Rect();
+        layout.getStableBounds(stableBounds);
+        return new Rect(
+                determineMinX(leftButtonsWidth, rightButtonsWidth, requiredEmptySpace,
+                        taskWidth),
+                stableBounds.top,
+                determineMaxX(leftButtonsWidth, rightButtonsWidth, requiredEmptySpace,
+                        taskWidth, displayWidth),
+                determineMaxY(requiredEmptySpace, stableBounds));
+    }
+
+
+    /**
+     * Determine the lowest x coordinate of a freeform task. Used for restricting drag inputs.
+     */
+    private int determineMinX(int leftButtonsWidth, int rightButtonsWidth, int requiredEmptySpace,
+            int taskWidth) {
+        // Do not let apps with < 48dp empty header space go off the left edge at all.
+        if (leftButtonsWidth + rightButtonsWidth + requiredEmptySpace > taskWidth) {
+            return 0;
+        }
+        return -taskWidth + requiredEmptySpace + rightButtonsWidth;
+    }
+
+    /**
+     * Determine the highest x coordinate of a freeform task. Used for restricting drag inputs.
+     */
+    private int determineMaxX(int leftButtonsWidth, int rightButtonsWidth, int requiredEmptySpace,
+            int taskWidth, int displayWidth) {
+        // Do not let apps with < 48dp empty header space go off the right edge at all.
+        if (leftButtonsWidth + rightButtonsWidth + requiredEmptySpace > taskWidth) {
+            return displayWidth - taskWidth;
+        }
+        return displayWidth - requiredEmptySpace - leftButtonsWidth;
+    }
+
+    /**
+     * Determine the highest y coordinate of a freeform task. Used for restricting drag inputs.
+     */
+    private int determineMaxY(int requiredEmptySpace, Rect stableBounds) {
+        return stableBounds.bottom - requiredEmptySpace;
+    }
+
+
+    /**
      * Create and display maximize menu window
      */
     void createMaximizeMenu() {
@@ -475,7 +558,6 @@
                 .setOnClickListener(mOnCaptionButtonClickListener)
                 .setOnTouchListener(mOnCaptionTouchListener)
                 .setLayoutId(mRelayoutParams.mLayoutResId)
-                .setCaptionPosition(mRelayoutParams.mCaptionX, mRelayoutParams.mCaptionY)
                 .setWindowingButtonsVisible(DesktopModeStatus.isEnabled())
                 .setCaptionHeight(mResult.mCaptionHeight)
                 .build();
@@ -552,35 +634,25 @@
                 mTaskOrganizer.getRunningTaskInfo(mTaskInfo.taskId);
         if (taskInfo == null) return result;
         final Point positionInParent = taskInfo.positionInParent;
-        result.offset(-mRelayoutParams.mCaptionX, -mRelayoutParams.mCaptionY);
         result.offset(-positionInParent.x, -positionInParent.y);
         return result;
     }
 
     /**
-     * Determine if a passed MotionEvent is in a view in caption
+     * Checks if motion event occurs in the caption handle area. This should be used in cases where
+     * onTouchListener will not work (i.e. when caption is in status bar area).
      *
      * @param ev       the {@link MotionEvent} to check
-     * @param layoutId the id of the view
      * @return {@code true} if event is inside the specified view, {@code false} if not
      */
-    private boolean checkEventInCaptionView(MotionEvent ev, int layoutId) {
-        if (mResult.mRootView == null) return false;
+    boolean checkTouchEventInCaptionHandle(MotionEvent ev) {
+        if (isHandleMenuActive() || !(mWindowDecorViewHolder
+                instanceof DesktopModeFocusedWindowDecorationViewHolder)) {
+            return false;
+        }
         final PointF inputPoint = offsetCaptionLocation(ev);
-        final View view = mResult.mRootView.findViewById(layoutId);
-        return view != null && pointInView(view, inputPoint.x, inputPoint.y);
-    }
-
-    boolean checkTouchEventInHandle(MotionEvent ev) {
-        if (isHandleMenuActive()) return false;
-        return checkEventInCaptionView(ev, R.id.caption_handle);
-    }
-
-    /**
-     * Returns true if motion event is within the caption's root view's bounds.
-     */
-    boolean checkTouchEventInCaption(MotionEvent ev) {
-        return checkEventInCaptionView(ev, getCaptionViewId());
+        return ((DesktopModeFocusedWindowDecorationViewHolder) mWindowDecorViewHolder)
+                .pointInCaption(inputPoint, mResult.mCaptionX);
     }
 
     /**
@@ -593,24 +665,19 @@
     void checkClickEvent(MotionEvent ev) {
         if (mResult.mRootView == null) return;
         if (!isHandleMenuActive()) {
+            // Click if point in caption handle view
             final View caption = mResult.mRootView.findViewById(R.id.desktop_mode_caption);
             final View handle = caption.findViewById(R.id.caption_handle);
-            clickIfPointInView(new PointF(ev.getX(), ev.getY()), handle);
+            if (checkTouchEventInCaptionHandle(ev)) {
+                mOnCaptionButtonClickListener.onClick(handle);
+            }
         } else {
             mHandleMenu.checkClickEvent(ev);
             closeHandleMenuIfNeeded(ev);
         }
     }
 
-    private boolean clickIfPointInView(PointF inputPoint, View v) {
-        if (pointInView(v, inputPoint.x, inputPoint.y)) {
-            mOnCaptionButtonClickListener.onClick(v);
-            return true;
-        }
-        return false;
-    }
-
-    boolean pointInView(View v, float x, float y) {
+    private boolean pointInView(View v, float x, float y) {
         return v != null && v.getLeft() <= x && v.getRight() >= x
                 && v.getTop() <= y && v.getBottom() >= y;
     }
@@ -624,7 +691,7 @@
         super.close();
     }
 
-    private int getDesktopModeWindowDecorLayoutId(@WindowingMode int windowingMode) {
+    private static int getDesktopModeWindowDecorLayoutId(@WindowingMode int windowingMode) {
         return windowingMode == WINDOWING_MODE_FREEFORM
                 ? R.layout.desktop_mode_app_controls_window_decor
                 : R.layout.desktop_mode_focused_window_decor;
@@ -658,18 +725,12 @@
         return exclusionRegion;
     }
 
-    /**
-     * If transition exists in mTransitionsPausingRelayout, remove the transition and decrement
-     * mRelayoutBlock
-     */
-    void removeTransitionPausingRelayout(IBinder transition) {
-        if (mTransitionsPausingRelayout.remove(transition)) {
-            mRelayoutBlock--;
-        }
-    }
-
     @Override
     int getCaptionHeightId(@WindowingMode int windowingMode) {
+        return getCaptionHeightIdStatic(windowingMode);
+    }
+
+    private static int getCaptionHeightIdStatic(@WindowingMode int windowingMode) {
         return windowingMode == WINDOWING_MODE_FULLSCREEN
                 ? R.dimen.desktop_mode_fullscreen_decor_caption_height
                 : R.dimen.desktop_mode_freeform_decor_caption_height;
@@ -684,35 +745,10 @@
         return R.id.desktop_mode_caption;
     }
 
-    /**
-     * Add transition to mTransitionsPausingRelayout
-     */
-    void addTransitionPausingRelayout(IBinder transition) {
-        mTransitionsPausingRelayout.add(transition);
-    }
-
-    /**
-     * If two transitions merge and the merged transition is in mTransitionsPausingRelayout,
-     * remove the merged transition from the set and add the transition it was merged into.
-     */
-    public void mergeTransitionPausingRelayout(IBinder merged, IBinder playing) {
-        if (mTransitionsPausingRelayout.remove(merged)) {
-            mTransitionsPausingRelayout.add(playing);
-        }
-    }
-
-    /**
-     * Increase mRelayoutBlock, stopping relayout if mRelayoutBlock is now greater than 0.
-     */
-    public void incrementRelayoutBlock() {
-        mRelayoutBlock++;
-    }
-
     @Override
     public String toString() {
         return "{"
                 + "mPositionInParent=" + mPositionInParent + ", "
-                + "mRelayoutBlock=" + mRelayoutBlock + ", "
                 + "taskId=" + mTaskInfo.taskId + ", "
                 + "windowingMode=" + windowingModeToString(mTaskInfo.getWindowingMode()) + ", "
                 + "isFocused=" + isFocused()
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtility.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtility.java
index cb0a6c7..5afbd54 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtility.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtility.java
@@ -26,9 +26,7 @@
 import android.graphics.Rect;
 import android.util.DisplayMetrics;
 import android.view.SurfaceControl;
-import android.window.WindowContainerTransaction;
 
-import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.common.DisplayController;
 
 /**
@@ -130,8 +128,7 @@
             Rect taskBoundsAtDragStart, PointF repositionStartPoint, SurfaceControl.Transaction t,
             float x, float y) {
         updateTaskBounds(repositionTaskBounds, taskBoundsAtDragStart, repositionStartPoint, x, y);
-        t.setPosition(decoration.mTaskSurface, repositionTaskBounds.left,
-                repositionTaskBounds.top);
+        t.setPosition(decoration.mTaskSurface, repositionTaskBounds.left, repositionTaskBounds.top);
     }
 
     private static void updateTaskBounds(Rect repositionTaskBounds, Rect taskBoundsAtDragStart,
@@ -162,31 +159,30 @@
 
     /**
      * Updates repositionTaskBounds to the final bounds of the task after the drag is finished. If
-     * the bounds are outside of the stable bounds, they are shifted to place task at the top of the
-     * stable bounds.
+     * the bounds are outside of the valid drag area, the task is shifted back onto the edge of the
+     * valid drag area.
      */
-    static void onDragEnd(Rect repositionTaskBounds, Rect taskBoundsAtDragStart, Rect stableBounds,
-            PointF repositionStartPoint, float x, float y)  {
+    static void onDragEnd(Rect repositionTaskBounds, Rect taskBoundsAtDragStart,
+            PointF repositionStartPoint, float x, float y, Rect validDragArea) {
         updateTaskBounds(repositionTaskBounds, taskBoundsAtDragStart, repositionStartPoint,
                 x, y);
-
-        // If task is outside of stable bounds (in the status bar area), shift the task down.
-        if (stableBounds.top > repositionTaskBounds.top) {
-            final int yShift =  stableBounds.top - repositionTaskBounds.top;
-            repositionTaskBounds.offset(0, yShift);
-        }
+        snapTaskBoundsIfNecessary(repositionTaskBounds, validDragArea);
     }
 
-    /**
-     * Apply a bounds change to a task.
-     * @param windowDecoration decor of task we are changing bounds for
-     * @param taskBounds new bounds of this task
-     * @param taskOrganizer applies the provided WindowContainerTransaction
-     */
-    static void applyTaskBoundsChange(WindowContainerTransaction wct,
-            WindowDecoration windowDecoration, Rect taskBounds, ShellTaskOrganizer taskOrganizer) {
-        wct.setBounds(windowDecoration.mTaskInfo.token, taskBounds);
-        taskOrganizer.applyTransaction(wct);
+    private static void snapTaskBoundsIfNecessary(Rect repositionTaskBounds, Rect validDragArea) {
+        // If we were never supplied a valid drag area, do not restrict movement.
+        // Otherwise, we restrict deltas to keep task position inside the Rect.
+        if (validDragArea.width() == 0) return;
+        if (repositionTaskBounds.left < validDragArea.left) {
+            repositionTaskBounds.offset(validDragArea.left - repositionTaskBounds.left, 0);
+        } else if (repositionTaskBounds.left > validDragArea.right) {
+            repositionTaskBounds.offset(validDragArea.right - repositionTaskBounds.left, 0);
+        }
+        if (repositionTaskBounds.top < validDragArea.top) {
+            repositionTaskBounds.offset(0, validDragArea.top - repositionTaskBounds.top);
+        } else if (repositionTaskBounds.top > validDragArea.bottom) {
+            repositionTaskBounds.offset(0, validDragArea.bottom - repositionTaskBounds.top);
+        }
     }
 
     private static float getMinWidth(DisplayController displayController,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositioner.java
index 3a1ea0e..6bfc7cd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositioner.java
@@ -16,23 +16,42 @@
 
 package com.android.wm.shell.windowdecor;
 
+import static android.view.WindowManager.TRANSIT_CHANGE;
+
 import android.graphics.PointF;
 import android.graphics.Rect;
+import android.os.IBinder;
 import android.view.Surface;
 import android.view.SurfaceControl;
+import android.window.TransitionInfo;
+import android.window.TransitionRequestInfo;
 import android.window.WindowContainerTransaction;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.transition.Transitions;
 
 import java.util.function.Supplier;
 
 /**
  * A task positioner that resizes/relocates task contents as it is dragged.
  * Utilizes {@link DragPositioningCallbackUtility} to determine new task bounds.
+ *
+ * This positioner applies the final bounds after a resize or drag using a shell transition in order
+ * to utilize the startAnimation callback to set the final task position and crop. In most cases,
+ * the transition will be aborted since the final bounds are usually the same bounds set in the
+ * final {@link #onDragPositioningMove} call. In this case, the cropping and positioning would be
+ * set by {@link WindowDecoration#relayout} due to the final bounds change; however, it is important
+ * that we send the final shell transition since we still utilize the {@link #onTransitionConsumed}
+ * callback.
  */
-class FluidResizeTaskPositioner implements DragPositioningCallback {
+class FluidResizeTaskPositioner implements DragPositioningCallback,
+        TaskDragResizer, Transitions.TransitionHandler {
     private final ShellTaskOrganizer mTaskOrganizer;
+    private final Transitions mTransitions;
     private final WindowDecoration mWindowDecoration;
     private final Supplier<SurfaceControl.Transaction> mTransactionSupplier;
     private DisplayController mDisplayController;
@@ -45,21 +64,28 @@
     // finalize the bounds there using WCT#setBounds
     private final int mDisallowedAreaForEndBoundsHeight;
     private boolean mHasDragResized;
+    private boolean mIsResizingOrAnimatingResize;
     private int mCtrlType;
+    private IBinder mDragResizeEndTransition;
     @Surface.Rotation private int mRotation;
 
-    FluidResizeTaskPositioner(ShellTaskOrganizer taskOrganizer, WindowDecoration windowDecoration,
-            DisplayController displayController, int disallowedAreaForEndBoundsHeight) {
-        this(taskOrganizer, windowDecoration, displayController, dragStartListener -> {},
-                SurfaceControl.Transaction::new, disallowedAreaForEndBoundsHeight);
+    FluidResizeTaskPositioner(ShellTaskOrganizer taskOrganizer, Transitions transitions,
+            WindowDecoration windowDecoration, DisplayController displayController,
+            int disallowedAreaForEndBoundsHeight) {
+        this(taskOrganizer, transitions, windowDecoration, displayController,
+                dragStartListener -> {}, SurfaceControl.Transaction::new,
+                disallowedAreaForEndBoundsHeight);
     }
 
-    FluidResizeTaskPositioner(ShellTaskOrganizer taskOrganizer, WindowDecoration windowDecoration,
+    FluidResizeTaskPositioner(ShellTaskOrganizer taskOrganizer,
+            Transitions transitions,
+            WindowDecoration windowDecoration,
             DisplayController displayController,
             DragPositioningCallbackUtility.DragStartListener dragStartListener,
             Supplier<SurfaceControl.Transaction> supplier,
             int disallowedAreaForEndBoundsHeight) {
         mTaskOrganizer = taskOrganizer;
+        mTransitions = transitions;
         mWindowDecoration = windowDecoration;
         mDisplayController = displayController;
         mDragStartListener = dragStartListener;
@@ -103,9 +129,10 @@
                 // This is the first bounds change since drag resize operation started.
                 wct.setDragResizing(mWindowDecoration.mTaskInfo.token, true /* dragResizing */);
             }
-            DragPositioningCallbackUtility.applyTaskBoundsChange(wct, mWindowDecoration,
-                    mRepositionTaskBounds, mTaskOrganizer);
+            wct.setBounds(mWindowDecoration.mTaskInfo.token, mRepositionTaskBounds);
+            mTaskOrganizer.applyTransaction(wct);
             mHasDragResized = true;
+            mIsResizingOrAnimatingResize = true;
         } else if (mCtrlType == CTRL_TYPE_UNDEFINED) {
             final SurfaceControl.Transaction t = mTransactionSupplier.get();
             DragPositioningCallbackUtility.setPositionOnDrag(mWindowDecoration,
@@ -129,16 +156,17 @@
                     mWindowDecoration)) {
                 wct.setBounds(mWindowDecoration.mTaskInfo.token, mRepositionTaskBounds);
             }
-            mTaskOrganizer.applyTransaction(wct);
+            mDragResizeEndTransition = mTransitions.startTransition(TRANSIT_CHANGE, wct, this);
         } else if (mCtrlType == CTRL_TYPE_UNDEFINED
                 && DragPositioningCallbackUtility.isBelowDisallowedArea(
                 mDisallowedAreaForEndBoundsHeight, mTaskBoundsAtDragStart, mRepositionStartPoint,
                 y)) {
             final WindowContainerTransaction wct = new WindowContainerTransaction();
             DragPositioningCallbackUtility.onDragEnd(mRepositionTaskBounds,
-                    mTaskBoundsAtDragStart, mStableBounds, mRepositionStartPoint, x, y);
+                    mTaskBoundsAtDragStart, mRepositionStartPoint, x, y,
+                    mWindowDecoration.calculateValidDragArea());
             wct.setBounds(mWindowDecoration.mTaskInfo.token, mRepositionTaskBounds);
-            mTaskOrganizer.applyTransaction(wct);
+            mTransitions.startTransition(TRANSIT_CHANGE, wct, this);
         }
 
         mTaskBoundsAtDragStart.setEmpty();
@@ -153,4 +181,51 @@
                 || (mCtrlType & CTRL_TYPE_LEFT) != 0 || (mCtrlType & CTRL_TYPE_RIGHT) != 0;
     }
 
+    @Override
+    public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
+            @NonNull SurfaceControl.Transaction startTransaction,
+            @NonNull SurfaceControl.Transaction finishTransaction,
+            @NonNull Transitions.TransitionFinishCallback finishCallback) {
+        for (TransitionInfo.Change change: info.getChanges()) {
+            final SurfaceControl sc = change.getLeash();
+            final Rect endBounds = change.getEndAbsBounds();
+            startTransaction.setWindowCrop(sc, endBounds.width(), endBounds.height())
+                    .setPosition(sc, endBounds.left, endBounds.top);
+            finishTransaction.setWindowCrop(sc, endBounds.width(), endBounds.height())
+                    .setPosition(sc, endBounds.left, endBounds.top);
+        }
+
+        startTransaction.apply();
+        if (transition.equals(mDragResizeEndTransition)) {
+            mIsResizingOrAnimatingResize = false;
+            mDragResizeEndTransition = null;
+        }
+        finishCallback.onTransitionFinished(null);
+        return true;
+    }
+
+    /**
+     * We should never reach this as this handler's transitions are only started from shell
+     * explicitly.
+     */
+    @Nullable
+    @Override
+    public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
+            @NonNull TransitionRequestInfo request) {
+        return null;
+    }
+
+    @Override
+    public void onTransitionConsumed(@NonNull IBinder transition, boolean aborted,
+            @Nullable SurfaceControl.Transaction finishTransaction) {
+        if (transition.equals(mDragResizeEndTransition)) {
+            mIsResizingOrAnimatingResize = false;
+            mDragResizeEndTransition = null;
+        }
+    }
+
+    @Override
+    public boolean isResizingOrAnimating() {
+        return mIsResizingOrAnimatingResize;
+    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java
index 652a2ed..b37dd0d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java
@@ -64,8 +64,6 @@
     private final View.OnTouchListener mOnTouchListener;
     private final RunningTaskInfo mTaskInfo;
     private final int mLayoutResId;
-    private final int mCaptionX;
-    private final int mCaptionY;
     private int mMarginMenuTop;
     private int mMarginMenuStart;
     private int mMenuHeight;
@@ -74,16 +72,13 @@
     private HandleMenuAnimator mHandleMenuAnimator;
 
 
-    HandleMenu(WindowDecoration parentDecor, int layoutResId, int captionX, int captionY,
-            View.OnClickListener onClickListener, View.OnTouchListener onTouchListener,
-            Bitmap appIcon, CharSequence appName, boolean shouldShowWindowingPill,
-            int captionHeight) {
+    HandleMenu(WindowDecoration parentDecor, int layoutResId, View.OnClickListener onClickListener,
+            View.OnTouchListener onTouchListener, Bitmap appIcon, CharSequence appName,
+            boolean shouldShowWindowingPill, int captionHeight) {
         mParentDecor = parentDecor;
         mContext = mParentDecor.mDecorWindowContext;
         mTaskInfo = mParentDecor.mTaskInfo;
         mLayoutResId = layoutResId;
-        mCaptionX = captionX;
-        mCaptionY = captionY;
         mOnClickListener = onClickListener;
         mOnTouchListener = onTouchListener;
         mAppIconBitmap = appIcon;
@@ -225,12 +220,12 @@
         if (mLayoutResId
                 == R.layout.desktop_mode_app_controls_window_decor) {
             // Align the handle menu to the left of the caption.
-            menuX = mCaptionX + mMarginMenuStart;
-            menuY = mCaptionY + mMarginMenuTop;
+            menuX = mMarginMenuStart;
+            menuY = mMarginMenuTop;
         } else {
             // Position the handle menu at the center of the caption.
-            menuX = mCaptionX + (captionWidth / 2) - (mMenuWidth / 2);
-            menuY = mCaptionY + mMarginMenuStart;
+            menuX = (captionWidth / 2) - (mMenuWidth / 2);
+            menuY = mMarginMenuStart;
         }
 
         // Handle Menu position setup.
@@ -346,8 +341,6 @@
         private View.OnClickListener mOnClickListener;
         private View.OnTouchListener mOnTouchListener;
         private int mLayoutId;
-        private int mCaptionX;
-        private int mCaptionY;
         private boolean mShowWindowingPill;
         private int mCaptionHeight;
 
@@ -381,12 +374,6 @@
             return this;
         }
 
-        Builder setCaptionPosition(int captionX, int captionY) {
-            mCaptionX = captionX;
-            mCaptionY = captionY;
-            return this;
-        }
-
         Builder setWindowingButtonsVisible(boolean windowingButtonsVisible) {
             mShowWindowingPill = windowingButtonsVisible;
             return this;
@@ -398,8 +385,8 @@
         }
 
         HandleMenu build() {
-            return new HandleMenu(mParent, mLayoutId, mCaptionX, mCaptionY, mOnClickListener,
-                    mOnTouchListener, mAppIcon, mName, mShowWindowingPill, mCaptionHeight);
+            return new HandleMenu(mParent, mLayoutId, mOnClickListener, mOnTouchListener,
+                    mAppIcon, mName, mShowWindowingPill, mCaptionHeight);
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileUser.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskDragResizer.java
similarity index 67%
copy from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileUser.kt
copy to libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskDragResizer.java
index efc7431..40421b5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileUser.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskDragResizer.java
@@ -14,12 +14,16 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.custom.di.bound
+package com.android.wm.shell.windowdecor;
 
-import javax.inject.Qualifier
+/**
+ * Holds the state of a drag resize.
+ */
+interface TaskDragResizer {
 
-/** User associated with current custom tile binding. */
-@Qualifier
-@MustBeDocumented
-@Retention(AnnotationRetention.RUNTIME)
-annotation class CustomTileUser
+    /**
+     * Returns true if task is currently being resized or animating the final transition after
+     * a resize is complete.
+     */
+    boolean isResizingOrAnimating();
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java
index 4b55a0c..c1b18f9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java
@@ -43,7 +43,7 @@
  * If the drag is repositioning, we update in the typical manner.
  */
 public class VeiledResizeTaskPositioner implements DragPositioningCallback,
-        Transitions.TransitionHandler {
+        TaskDragResizer, Transitions.TransitionHandler {
 
     private DesktopModeWindowDecoration mDesktopWindowDecoration;
     private ShellTaskOrganizer mTaskOrganizer;
@@ -59,10 +59,12 @@
     private final int mDisallowedAreaForEndBoundsHeight;
     private final Supplier<SurfaceControl.Transaction> mTransactionSupplier;
     private int mCtrlType;
+    private boolean mIsResizingOrAnimatingResize;
     @Surface.Rotation private int mRotation;
 
     public VeiledResizeTaskPositioner(ShellTaskOrganizer taskOrganizer,
-            DesktopModeWindowDecoration windowDecoration, DisplayController displayController,
+            DesktopModeWindowDecoration windowDecoration,
+            DisplayController displayController,
             DragPositioningCallbackUtility.DragStartListener dragStartListener,
             Transitions transitions,
             int disallowedAreaForEndBoundsHeight) {
@@ -71,12 +73,13 @@
     }
 
     public VeiledResizeTaskPositioner(ShellTaskOrganizer taskOrganizer,
-            DesktopModeWindowDecoration windowDecoration, DisplayController displayController,
+            DesktopModeWindowDecoration windowDecoration,
+            DisplayController displayController,
             DragPositioningCallbackUtility.DragStartListener dragStartListener,
             Supplier<SurfaceControl.Transaction> supplier, Transitions transitions,
             int disallowedAreaForEndBoundsHeight) {
-        mTaskOrganizer = taskOrganizer;
         mDesktopWindowDecoration = windowDecoration;
+        mTaskOrganizer = taskOrganizer;
         mDisplayController = displayController;
         mDragStartListener = dragStartListener;
         mTransactionSupplier = supplier;
@@ -117,6 +120,7 @@
                 mRepositionTaskBounds, mTaskBoundsAtDragStart, mStableBounds, delta,
                 mDisplayController, mDesktopWindowDecoration)) {
             mDesktopWindowDecoration.updateResizeVeil(mRepositionTaskBounds);
+            mIsResizingOrAnimatingResize = true;
         } else if (mCtrlType == CTRL_TYPE_UNDEFINED) {
             final SurfaceControl.Transaction t = mTransactionSupplier.get();
             DragPositioningCallbackUtility.setPositionOnDrag(mDesktopWindowDecoration,
@@ -138,23 +142,22 @@
                 mDesktopWindowDecoration.updateResizeVeil(mRepositionTaskBounds);
                 final WindowContainerTransaction wct = new WindowContainerTransaction();
                 wct.setBounds(mDesktopWindowDecoration.mTaskInfo.token, mRepositionTaskBounds);
-                if (Transitions.ENABLE_SHELL_TRANSITIONS) {
-                    mTransitions.startTransition(TRANSIT_CHANGE, wct, this);
-                } else {
-                    mTaskOrganizer.applyTransaction(wct);
-                }
+                mTransitions.startTransition(TRANSIT_CHANGE, wct, this);
             } else {
                 // If bounds haven't changed, perform necessary veil reset here as startAnimation
                 // won't be called.
                 mDesktopWindowDecoration.hideResizeVeil();
+                mIsResizingOrAnimatingResize = false;
             }
         } else if (DragPositioningCallbackUtility.isBelowDisallowedArea(
                 mDisallowedAreaForEndBoundsHeight, mTaskBoundsAtDragStart, mRepositionStartPoint,
                 y)) {
+            final WindowContainerTransaction wct = new WindowContainerTransaction();
             DragPositioningCallbackUtility.onDragEnd(mRepositionTaskBounds,
-                    mTaskBoundsAtDragStart, mStableBounds, mRepositionStartPoint, x, y);
-            DragPositioningCallbackUtility.applyTaskBoundsChange(new WindowContainerTransaction(),
-                    mDesktopWindowDecoration, mRepositionTaskBounds, mTaskOrganizer);
+                    mTaskBoundsAtDragStart, mRepositionStartPoint, x, y,
+                    mDesktopWindowDecoration.calculateValidDragArea());
+            wct.setBounds(mDesktopWindowDecoration.mTaskInfo.token, mRepositionTaskBounds);
+            mTransitions.startTransition(TRANSIT_CHANGE, wct, this);
         }
 
         mCtrlType = CTRL_TYPE_UNDEFINED;
@@ -173,10 +176,20 @@
             @NonNull SurfaceControl.Transaction startTransaction,
             @NonNull SurfaceControl.Transaction finishTransaction,
             @NonNull Transitions.TransitionFinishCallback finishCallback) {
+        for (TransitionInfo.Change change: info.getChanges()) {
+            final SurfaceControl sc = change.getLeash();
+            final Rect endBounds = change.getEndAbsBounds();
+            startTransaction.setWindowCrop(sc, endBounds.width(), endBounds.height())
+                    .setPosition(sc, endBounds.left, endBounds.top);
+            finishTransaction.setWindowCrop(sc, endBounds.width(), endBounds.height())
+                    .setPosition(sc, endBounds.left, endBounds.top);
+        }
+
         startTransaction.apply();
         mDesktopWindowDecoration.hideResizeVeil();
         mCtrlType = CTRL_TYPE_UNDEFINED;
         finishCallback.onTransitionFinished(null);
+        mIsResizingOrAnimatingResize = false;
         return true;
     }
 
@@ -190,4 +203,9 @@
             @NonNull TransitionRequestInfo request) {
         return null;
     }
+
+    @Override
+    public boolean isResizingOrAnimating() {
+        return mIsResizingOrAnimatingResize;
+    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorViewModel.java
index ae1a3d9..01a6012 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorViewModel.java
@@ -17,9 +17,7 @@
 package com.android.wm.shell.windowdecor;
 
 import android.app.ActivityManager;
-import android.os.IBinder;
 import android.view.SurfaceControl;
-import android.window.TransitionInfo;
 
 import com.android.wm.shell.freeform.FreeformTaskTransitionStarter;
 import com.android.wm.shell.splitscreen.SplitScreenController;
@@ -103,34 +101,4 @@
      * @param taskInfo the info of the task
      */
     void destroyWindowDecoration(ActivityManager.RunningTaskInfo taskInfo);
-
-    /**
-     * Notifies that a shell transition is about to start. If the transition is of type
-     * TRANSIT_ENTER_DESKTOP, it will save that transition to unpause relayout for the transitioning
-     * task after the transition has ended.
-     *
-     * @param transition the ready transaction
-     * @param info of Transition to check if relayout needs to be paused for a task
-     * @param change a change in the given transition
-     */
-    default void onTransitionReady(IBinder transition, TransitionInfo info,
-            TransitionInfo.Change change) {}
-
-    /**
-     * Notifies that a shell transition is about to merge with another to give the window
-     * decoration a chance to prepare for this merge.
-     *
-     * @param merged the transaction being merged
-     * @param playing the transaction being merged into
-     */
-    default void onTransitionMerged(IBinder merged, IBinder playing) {}
-
-    /**
-     * Notifies that a shell transition is about to finish  to give the window decoration a chance
-     * to clean up.
-     *
-     * @param transaction
-     */
-    default void onTransitionFinished(IBinder transaction) {}
-
 }
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
index 634b755..6a9258c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
@@ -21,6 +21,7 @@
 import static android.view.WindowInsets.Type.statusBars;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.ActivityManager.RunningTaskInfo;
 import android.app.WindowConfiguration.WindowingMode;
 import android.content.Context;
@@ -123,6 +124,7 @@
     private WindowlessWindowManager mCaptionWindowManager;
     private SurfaceControlViewHost mViewHost;
     private Configuration mWindowDecorConfig;
+    TaskDragResizer mTaskDragResizer;
     private boolean mIsCaptionVisible;
 
     private final Binder mOwner = new Binder();
@@ -178,6 +180,13 @@
      */
     abstract void relayout(RunningTaskInfo taskInfo);
 
+    /**
+     * Used by the {@link DragPositioningCallback} associated with the implementing class to
+     * enforce drags ending in a valid position. A null result means no restriction.
+     */
+    @Nullable
+    abstract Rect calculateValidDragArea();
+
     void relayout(RelayoutParams params, SurfaceControl.Transaction startT,
             SurfaceControl.Transaction finishT, WindowContainerTransaction wct, T rootView,
             RelayoutResult<T> outResult) {
@@ -270,9 +279,12 @@
         }
 
         outResult.mCaptionHeight = loadDimensionPixelSize(resources, params.mCaptionHeightId);
-        final int captionWidth = taskBounds.width();
+        final int captionWidth = params.mCaptionWidthId != Resources.ID_NULL
+                ? loadDimensionPixelSize(resources, params.mCaptionWidthId) : taskBounds.width();
+        outResult.mCaptionX = (outResult.mWidth - captionWidth) / 2;
 
         startT.setWindowCrop(mCaptionContainerSurface, captionWidth, outResult.mCaptionHeight)
+                .setPosition(mCaptionContainerSurface, outResult.mCaptionX, 0 /* y */)
                 .setLayer(mCaptionContainerSurface, CAPTION_LAYER_Z_ORDER)
                 .show(mCaptionContainerSurface);
 
@@ -283,7 +295,7 @@
             mCaptionInsetsRect.set(taskBounds);
             if (mIsCaptionVisible) {
                 mCaptionInsetsRect.bottom =
-                        mCaptionInsetsRect.top + outResult.mCaptionHeight + params.mCaptionY;
+                        mCaptionInsetsRect.top + outResult.mCaptionHeight;
                 wct.addInsetsSource(mTaskInfo.token,
                         mOwner, 0 /* index */, WindowInsets.Type.captionBar(), mCaptionInsetsRect);
                 wct.addInsetsSource(mTaskInfo.token,
@@ -303,25 +315,21 @@
         float shadowRadius;
         final Point taskPosition = mTaskInfo.positionInParent;
         if (isFullscreen) {
-            // Setting the task crop to the width/height stops input events from being sent to
-            // some regions of the app window. See b/300324920
-            // TODO(b/296921174): investigate whether crop/position needs to be set by window
-            // decorations at all when transition handlers are already taking ownership of the task
-            // surface placement/crop, especially when in fullscreen where tasks cannot be
-            // drag-resized by the window decoration.
-            startT.setWindowCrop(mTaskSurface, null);
-            finishT.setWindowCrop(mTaskSurface, null);
             // Shadow is not needed for fullscreen tasks
             shadowRadius = 0;
         } else {
-            startT.setWindowCrop(mTaskSurface, outResult.mWidth, outResult.mHeight);
-            finishT.setWindowCrop(mTaskSurface, outResult.mWidth, outResult.mHeight);
             shadowRadius = loadDimension(resources, params.mShadowRadiusId);
         }
+
+        if (params.mSetTaskPositionAndCrop) {
+            startT.setWindowCrop(mTaskSurface, outResult.mWidth, outResult.mHeight);
+            finishT.setWindowCrop(mTaskSurface, outResult.mWidth, outResult.mHeight)
+                    .setPosition(mTaskSurface, taskPosition.x, taskPosition.y);
+        }
+
         startT.setShadowRadius(mTaskSurface, shadowRadius)
                 .show(mTaskSurface);
-        finishT.setPosition(mTaskSurface, taskPosition.x, taskPosition.y)
-                .setShadowRadius(mTaskSurface, shadowRadius);
+        finishT.setShadowRadius(mTaskSurface, shadowRadius);
         if (mTaskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
             if (!DesktopModeStatus.isVeiledResizeEnabled()) {
                 // When fluid resize is enabled, add a background to freeform tasks
@@ -386,6 +394,10 @@
         }
     }
 
+    void setTaskDragResizer(TaskDragResizer taskDragResizer) {
+        mTaskDragResizer = taskDragResizer;
+    }
+
     private void setCaptionVisibility(View rootView, boolean visible) {
         if (rootView == null) {
             return;
@@ -545,12 +557,10 @@
 
         int mCornerRadius;
 
-        int mCaptionX;
-        int mCaptionY;
-
         Configuration mWindowDecorConfig;
 
         boolean mApplyStartTransactionOnDraw;
+        boolean mSetTaskPositionAndCrop;
 
         void reset() {
             mLayoutResId = Resources.ID_NULL;
@@ -560,16 +570,15 @@
 
             mCornerRadius = 0;
 
-            mCaptionX = 0;
-            mCaptionY = 0;
-
             mApplyStartTransactionOnDraw = false;
+            mSetTaskPositionAndCrop = false;
             mWindowDecorConfig = null;
         }
     }
 
     static class RelayoutResult<T extends View & TaskFocusStateConsumer> {
         int mCaptionHeight;
+        int mCaptionX;
         int mWidth;
         int mHeight;
         T mRootView;
@@ -578,6 +587,7 @@
             mWidth = 0;
             mHeight = 0;
             mCaptionHeight = 0;
+            mCaptionX = 0;
             mRootView = null;
         }
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt
index 589a813..144373f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt
@@ -42,6 +42,8 @@
     private val maximizeWindowButton: ImageButton = rootView.requireViewById(R.id.maximize_window)
     private val appNameTextView: TextView = rootView.requireViewById(R.id.application_name)
     private val appIconImageView: ImageView = rootView.requireViewById(R.id.application_icon)
+    val appNameTextWidth: Int
+        get() = appNameTextView.width
 
     init {
         captionView.setOnTouchListener(onCaptionTouchListener)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeFocusedWindowDecorationViewHolder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeFocusedWindowDecorationViewHolder.kt
index 4930cb7..5f77022 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeFocusedWindowDecorationViewHolder.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeFocusedWindowDecorationViewHolder.kt
@@ -5,6 +5,7 @@
 import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
 import android.content.res.ColorStateList
 import android.graphics.Color
+import android.graphics.PointF
 import android.view.View
 import android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS
 import android.widget.ImageButton
@@ -35,9 +36,6 @@
     }
 
     override fun bindData(taskInfo: RunningTaskInfo) {
-        taskInfo.taskDescription?.statusBarColor?.let { captionColor ->
-            captionView.setBackgroundColor(captionColor)
-        }
         captionHandle.imageTintList = ColorStateList.valueOf(getCaptionHandleBarColor(taskInfo))
     }
 
@@ -49,6 +47,17 @@
         animateCaptionHandleAlpha(startValue = 0f, endValue = 1f)
     }
 
+    /**
+     * Returns true if input point is in the caption's view.
+     * @param inputPoint the input point relative to the task in full "focus" (i.e. fullscreen).
+     */
+    fun pointInCaption(inputPoint: PointF, captionX: Int): Boolean {
+        return inputPoint.x >= captionX &&
+                inputPoint.x <= captionX + captionView.width &&
+                inputPoint.y >= 0 &&
+                inputPoint.y <= captionView.height
+    }
+
     private fun getCaptionHandleBarColor(taskInfo: RunningTaskInfo): Int {
         return if (shouldUseLightCaptionColors(taskInfo)) {
             context.getColor(R.color.desktop_mode_caption_handle_bar_light)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeWindowDecorationViewHolder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeWindowDecorationViewHolder.kt
index 690b4e4..81bc34c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeWindowDecorationViewHolder.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeWindowDecorationViewHolder.kt
@@ -17,9 +17,9 @@
    */
   abstract fun bindData(taskInfo: RunningTaskInfo)
 
-    /** Callback when the handle menu is opened. */
-    abstract fun onHandleMenuOpened()
+  /** Callback when the handle menu is opened. */
+  abstract fun onHandleMenuOpened()
 
-    /** Callback when the handle menu is closed. */
-    abstract fun onHandleMenuClosed()
+  /** Callback when the handle menu is closed. */
+  abstract fun onHandleMenuClosed()
 }
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/AppsEnterPipTransition.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/AppsEnterPipTransition.kt
index 182a908..be77171 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/AppsEnterPipTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/AppsEnterPipTransition.kt
@@ -101,7 +101,8 @@
     override fun pipLayerReduces() {
         flicker.assertLayers {
             val pipLayerList =
-                this.layers { standardAppHelper.layerMatchesAnyOf(it) && it.isVisible }
+                this.layers { standardAppHelper.packageNameMatcher.layerMatchesAnyOf(it)
+                        && it.isVisible }
             pipLayerList.zipWithNext { previous, current ->
                 current.visibleRegion.notBiggerThan(previous.visibleRegion.region)
             }
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/NetflixEnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/NetflixEnterPipTest.kt
index 32f1259..143f7a7 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/NetflixEnterPipTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/NetflixEnterPipTest.kt
@@ -69,7 +69,7 @@
         setup {
             standardAppHelper.launchViaIntent(
                 wmHelper,
-                NetflixAppHelper.getNetflixWatchVideoIntent("70184207"),
+                NetflixAppHelper.getNetflixWatchVideoIntent("81605060"),
                 ComponentNameMatcher(NetflixAppHelper.PACKAGE_NAME, NetflixAppHelper.WATCH_ACTIVITY)
             )
             standardAppHelper.waitForVideoPlaying()
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/TouchTrackerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/TouchTrackerTest.kt
index bf07dcc..6dbb1e2 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/TouchTrackerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/TouchTrackerTest.kt
@@ -170,6 +170,71 @@
         nonLinearTracker.assertProgress((touchX - INITIAL_X_LEFT_EDGE) / nonLinearTarget)
     }
 
+    @Test
+    fun restartingGesture_resetsInitialTouchX_leftEdge() {
+        val linearTracker = linearTouchTracker()
+        linearTracker.setGestureStartLocation(INITIAL_X_LEFT_EDGE, 0f, BackEvent.EDGE_LEFT)
+        var touchX = 100f
+        val velocityX = 0f
+        val velocityY = 0f
+
+        // assert that progress is increased when increasing touchX
+        linearTracker.update(touchX, 0f, velocityX, velocityY)
+        linearTracker.assertProgress((touchX - INITIAL_X_LEFT_EDGE) / MAX_DISTANCE)
+
+        // assert that progress is reset to 0 when start location is updated
+        linearTracker.updateStartLocation()
+        linearTracker.assertProgress(0f)
+
+        // assert that progress remains 0 when touchX is decreased
+        touchX -= 50
+        linearTracker.update(touchX, 0f, velocityX, velocityY)
+        linearTracker.assertProgress(0f)
+
+        // assert that progress uses new minimal touchX for progress calculation
+        val newInitialTouchX = touchX
+        touchX += 100
+        linearTracker.update(touchX, 0f, velocityX, velocityY)
+        linearTracker.assertProgress((touchX - newInitialTouchX) / MAX_DISTANCE)
+
+        // assert the same for triggerBack==true
+        linearTracker.triggerBack = true
+        linearTracker.assertProgress((touchX - newInitialTouchX) / MAX_DISTANCE)
+    }
+
+    @Test
+    fun restartingGesture_resetsInitialTouchX_rightEdge() {
+        val linearTracker = linearTouchTracker()
+        linearTracker.setGestureStartLocation(INITIAL_X_RIGHT_EDGE, 0f, BackEvent.EDGE_RIGHT)
+
+        var touchX = INITIAL_X_RIGHT_EDGE - 100f
+        val velocityX = 0f
+        val velocityY = 0f
+
+        // assert that progress is increased when decreasing touchX
+        linearTracker.update(touchX, 0f, velocityX, velocityY)
+        linearTracker.assertProgress((INITIAL_X_RIGHT_EDGE - touchX) / MAX_DISTANCE)
+
+        // assert that progress is reset to 0 when start location is updated
+        linearTracker.updateStartLocation()
+        linearTracker.assertProgress(0f)
+
+        // assert that progress remains 0 when touchX is increased
+        touchX += 50
+        linearTracker.update(touchX, 0f, velocityX, velocityY)
+        linearTracker.assertProgress(0f)
+
+        // assert that progress uses new maximal touchX for progress calculation
+        val newInitialTouchX = touchX
+        touchX -= 100
+        linearTracker.update(touchX, 0f, velocityX, velocityY)
+        linearTracker.assertProgress((newInitialTouchX - touchX) / MAX_DISTANCE)
+
+        // assert the same for triggerBack==true
+        linearTracker.triggerBack = true
+        linearTracker.assertProgress((newInitialTouchX - touchX) / MAX_DISTANCE)
+    }
+
     companion object {
         private const val MAX_DISTANCE = 500f
         private const val LINEAR_DISTANCE = 400f
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblePositionerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblePositionerTest.java
deleted file mode 100644
index 6ebee73..0000000
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblePositionerTest.java
+++ /dev/null
@@ -1,602 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.bubbles;
-
-import static com.android.wm.shell.bubbles.BubblePositioner.MAX_HEIGHT;
-
-import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.util.concurrent.MoreExecutors.directExecutor;
-
-import static org.mockito.Mockito.mock;
-
-import android.content.Intent;
-import android.content.pm.ShortcutInfo;
-import android.graphics.Insets;
-import android.graphics.PointF;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.os.UserHandle;
-import android.testing.AndroidTestingRunner;
-import android.view.WindowManager;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.wm.shell.R;
-import com.android.wm.shell.ShellTestCase;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Tests operations and the resulting state managed by {@link BubblePositioner}.
- */
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-public class BubblePositionerTest extends ShellTestCase {
-
-    private BubblePositioner mPositioner;
-
-    @Before
-    public void setUp() {
-        WindowManager windowManager = mContext.getSystemService(WindowManager.class);
-        mPositioner = new BubblePositioner(mContext, windowManager);
-    }
-
-    @Test
-    public void testUpdate() {
-        Insets insets = Insets.of(10, 20, 5, 15);
-        Rect screenBounds = new Rect(0, 0, 1000, 1200);
-        Rect availableRect = new Rect(screenBounds);
-        availableRect.inset(insets);
-
-        DeviceConfig deviceConfig = new ConfigBuilder()
-                .setInsets(insets)
-                .setScreenBounds(screenBounds)
-                .build();
-        mPositioner.update(deviceConfig);
-
-        assertThat(mPositioner.getAvailableRect()).isEqualTo(availableRect);
-        assertThat(mPositioner.isLandscape()).isFalse();
-        assertThat(mPositioner.isLargeScreen()).isFalse();
-        assertThat(mPositioner.getInsets()).isEqualTo(insets);
-    }
-
-    @Test
-    public void testShowBubblesVertically_phonePortrait() {
-        DeviceConfig deviceConfig = new ConfigBuilder().build();
-        mPositioner.update(deviceConfig);
-
-        assertThat(mPositioner.showBubblesVertically()).isFalse();
-    }
-
-    @Test
-    public void testShowBubblesVertically_phoneLandscape() {
-        DeviceConfig deviceConfig = new ConfigBuilder().setLandscape().build();
-        mPositioner.update(deviceConfig);
-
-        assertThat(mPositioner.isLandscape()).isTrue();
-        assertThat(mPositioner.showBubblesVertically()).isTrue();
-    }
-
-    @Test
-    public void testShowBubblesVertically_tablet() {
-        DeviceConfig deviceConfig = new ConfigBuilder().setLargeScreen().build();
-        mPositioner.update(deviceConfig);
-
-        assertThat(mPositioner.showBubblesVertically()).isTrue();
-    }
-
-    /** If a resting position hasn't been set, calling it will return the default position. */
-    @Test
-    public void testGetRestingPosition_returnsDefaultPosition() {
-        DeviceConfig deviceConfig = new ConfigBuilder().build();
-        mPositioner.update(deviceConfig);
-
-        PointF restingPosition = mPositioner.getRestingPosition();
-        PointF defaultPosition = mPositioner.getDefaultStartPosition();
-
-        assertThat(restingPosition).isEqualTo(defaultPosition);
-    }
-
-    /** If a resting position has been set, it'll return that instead of the default position. */
-    @Test
-    public void testGetRestingPosition_returnsRestingPosition() {
-        DeviceConfig deviceConfig = new ConfigBuilder().build();
-        mPositioner.update(deviceConfig);
-
-        PointF restingPosition = new PointF(100, 100);
-        mPositioner.setRestingPosition(restingPosition);
-
-        assertThat(mPositioner.getRestingPosition()).isEqualTo(restingPosition);
-    }
-
-    /** Test that the default resting position on phone is in upper left. */
-    @Test
-    public void testGetRestingPosition_bubble_onPhone() {
-        DeviceConfig deviceConfig = new ConfigBuilder().build();
-        mPositioner.update(deviceConfig);
-
-        RectF allowableStackRegion =
-                mPositioner.getAllowableStackPositionRegion(1 /* bubbleCount */);
-        PointF restingPosition = mPositioner.getRestingPosition();
-
-        assertThat(restingPosition.x).isEqualTo(allowableStackRegion.left);
-        assertThat(restingPosition.y).isEqualTo(getDefaultYPosition());
-    }
-
-    @Test
-    public void testGetRestingPosition_bubble_onPhone_RTL() {
-        DeviceConfig deviceConfig = new ConfigBuilder().setRtl().build();
-        mPositioner.update(deviceConfig);
-
-        RectF allowableStackRegion =
-                mPositioner.getAllowableStackPositionRegion(1 /* bubbleCount */);
-        PointF restingPosition = mPositioner.getRestingPosition();
-
-        assertThat(restingPosition.x).isEqualTo(allowableStackRegion.right);
-        assertThat(restingPosition.y).isEqualTo(getDefaultYPosition());
-    }
-
-    /** Test that the default resting position on tablet is middle left. */
-    @Test
-    public void testGetRestingPosition_chatBubble_onTablet() {
-        DeviceConfig deviceConfig = new ConfigBuilder().setLargeScreen().build();
-        mPositioner.update(deviceConfig);
-
-        RectF allowableStackRegion =
-                mPositioner.getAllowableStackPositionRegion(1 /* bubbleCount */);
-        PointF restingPosition = mPositioner.getRestingPosition();
-
-        assertThat(restingPosition.x).isEqualTo(allowableStackRegion.left);
-        assertThat(restingPosition.y).isEqualTo(getDefaultYPosition());
-    }
-
-    @Test
-    public void testGetRestingPosition_chatBubble_onTablet_RTL() {
-        DeviceConfig deviceConfig = new ConfigBuilder().setLargeScreen().setRtl().build();
-        mPositioner.update(deviceConfig);
-
-        RectF allowableStackRegion =
-                mPositioner.getAllowableStackPositionRegion(1 /* bubbleCount */);
-        PointF restingPosition = mPositioner.getRestingPosition();
-
-        assertThat(restingPosition.x).isEqualTo(allowableStackRegion.right);
-        assertThat(restingPosition.y).isEqualTo(getDefaultYPosition());
-    }
-
-    /** Test that the default resting position on tablet is middle right. */
-    @Test
-    public void testGetDefaultPosition_appBubble_onTablet() {
-        DeviceConfig deviceConfig = new ConfigBuilder().setLargeScreen().build();
-        mPositioner.update(deviceConfig);
-
-        RectF allowableStackRegion =
-                mPositioner.getAllowableStackPositionRegion(1 /* bubbleCount */);
-        PointF startPosition = mPositioner.getDefaultStartPosition(true /* isAppBubble */);
-
-        assertThat(startPosition.x).isEqualTo(allowableStackRegion.right);
-        assertThat(startPosition.y).isEqualTo(getDefaultYPosition());
-    }
-
-    @Test
-    public void testGetRestingPosition_appBubble_onTablet_RTL() {
-        DeviceConfig deviceConfig = new ConfigBuilder().setLargeScreen().setRtl().build();
-        mPositioner.update(deviceConfig);
-
-        RectF allowableStackRegion =
-                mPositioner.getAllowableStackPositionRegion(1 /* bubbleCount */);
-        PointF startPosition = mPositioner.getDefaultStartPosition(true /* isAppBubble */);
-
-        assertThat(startPosition.x).isEqualTo(allowableStackRegion.left);
-        assertThat(startPosition.y).isEqualTo(getDefaultYPosition());
-    }
-
-    @Test
-    public void testHasUserModifiedDefaultPosition_false() {
-        DeviceConfig deviceConfig = new ConfigBuilder().setLargeScreen().setRtl().build();
-        mPositioner.update(deviceConfig);
-
-        assertThat(mPositioner.hasUserModifiedDefaultPosition()).isFalse();
-
-        mPositioner.setRestingPosition(mPositioner.getDefaultStartPosition());
-
-        assertThat(mPositioner.hasUserModifiedDefaultPosition()).isFalse();
-    }
-
-    @Test
-    public void testHasUserModifiedDefaultPosition_true() {
-        DeviceConfig deviceConfig = new ConfigBuilder().setLargeScreen().setRtl().build();
-        mPositioner.update(deviceConfig);
-
-        assertThat(mPositioner.hasUserModifiedDefaultPosition()).isFalse();
-
-        mPositioner.setRestingPosition(new PointF(0, 100));
-
-        assertThat(mPositioner.hasUserModifiedDefaultPosition()).isTrue();
-    }
-
-    @Test
-    public void testGetExpandedViewHeight_max() {
-        Insets insets = Insets.of(10, 20, 5, 15);
-        Rect screenBounds = new Rect(0, 0, 1800, 2600);
-
-        DeviceConfig deviceConfig = new ConfigBuilder()
-                .setLargeScreen()
-                .setInsets(insets)
-                .setScreenBounds(screenBounds)
-                .build();
-        mPositioner.update(deviceConfig);
-
-        Intent intent = new Intent(Intent.ACTION_VIEW).setPackage(mContext.getPackageName());
-        Bubble bubble = Bubble.createAppBubble(intent, new UserHandle(1), null, directExecutor());
-
-        assertThat(mPositioner.getExpandedViewHeight(bubble)).isEqualTo(MAX_HEIGHT);
-    }
-
-    @Test
-    public void testGetExpandedViewHeight_customHeight_valid() {
-        Insets insets = Insets.of(10, 20, 5, 15);
-        Rect screenBounds = new Rect(0, 0, 1800, 2600);
-
-        DeviceConfig deviceConfig = new ConfigBuilder()
-                .setLargeScreen()
-                .setInsets(insets)
-                .setScreenBounds(screenBounds)
-                .build();
-        mPositioner.update(deviceConfig);
-
-        final int minHeight = mContext.getResources().getDimensionPixelSize(
-                R.dimen.bubble_expanded_default_height);
-        Bubble bubble = new Bubble("key",
-                mock(ShortcutInfo.class),
-                minHeight + 100 /* desiredHeight */,
-                0 /* desiredHeightResId */,
-                "title",
-                0 /* taskId */,
-                null /* locus */,
-                true /* isDismissable */,
-                directExecutor(),
-                mock(Bubbles.BubbleMetadataFlagListener.class));
-
-        // Ensure the height is the same as the desired value
-        assertThat(mPositioner.getExpandedViewHeight(bubble)).isEqualTo(
-                bubble.getDesiredHeight(mContext));
-    }
-
-
-    @Test
-    public void testGetExpandedViewHeight_customHeight_tooSmall() {
-        Insets insets = Insets.of(10, 20, 5, 15);
-        Rect screenBounds = new Rect(0, 0, 1800, 2600);
-
-        DeviceConfig deviceConfig = new ConfigBuilder()
-                .setLargeScreen()
-                .setInsets(insets)
-                .setScreenBounds(screenBounds)
-                .build();
-        mPositioner.update(deviceConfig);
-
-        Bubble bubble = new Bubble("key",
-                mock(ShortcutInfo.class),
-                10 /* desiredHeight */,
-                0 /* desiredHeightResId */,
-                "title",
-                0 /* taskId */,
-                null /* locus */,
-                true /* isDismissable */,
-                directExecutor(),
-                mock(Bubbles.BubbleMetadataFlagListener.class));
-
-        // Ensure the height is the same as the minimum value
-        final int minHeight = mContext.getResources().getDimensionPixelSize(
-                R.dimen.bubble_expanded_default_height);
-        assertThat(mPositioner.getExpandedViewHeight(bubble)).isEqualTo(minHeight);
-    }
-
-    @Test
-    public void testGetMaxExpandedViewHeight_onLargeTablet() {
-        Insets insets = Insets.of(10, 20, 5, 15);
-        Rect screenBounds = new Rect(0, 0, 1800, 2600);
-
-        DeviceConfig deviceConfig = new ConfigBuilder()
-                .setLargeScreen()
-                .setInsets(insets)
-                .setScreenBounds(screenBounds)
-                .build();
-        mPositioner.update(deviceConfig);
-
-        int manageButtonHeight =
-                mContext.getResources().getDimensionPixelSize(R.dimen.bubble_manage_button_height);
-        int pointerWidth = mContext.getResources().getDimensionPixelSize(
-                R.dimen.bubble_pointer_width);
-        int expandedViewPadding = mContext.getResources().getDimensionPixelSize(R
-                .dimen.bubble_expanded_view_padding);
-        float expectedHeight = 1800 - 2 * 20 - manageButtonHeight - pointerWidth
-                - expandedViewPadding * 2;
-        assertThat(((float) mPositioner.getMaxExpandedViewHeight(false /* isOverflow */)))
-                .isWithin(0.1f).of(expectedHeight);
-    }
-
-    @Test
-    public void testAreBubblesBottomAligned_largeScreen_true() {
-        Insets insets = Insets.of(10, 20, 5, 15);
-        Rect screenBounds = new Rect(0, 0, 1800, 2600);
-
-        DeviceConfig deviceConfig = new ConfigBuilder()
-                .setLargeScreen()
-                .setInsets(insets)
-                .setScreenBounds(screenBounds)
-                .build();
-        mPositioner.update(deviceConfig);
-
-        assertThat(mPositioner.areBubblesBottomAligned()).isTrue();
-    }
-
-    @Test
-    public void testAreBubblesBottomAligned_largeScreen_false() {
-        Insets insets = Insets.of(10, 20, 5, 15);
-        Rect screenBounds = new Rect(0, 0, 1800, 2600);
-
-        DeviceConfig deviceConfig = new ConfigBuilder()
-                .setLargeScreen()
-                .setLandscape()
-                .setInsets(insets)
-                .setScreenBounds(screenBounds)
-                .build();
-        mPositioner.update(deviceConfig);
-
-        assertThat(mPositioner.areBubblesBottomAligned()).isFalse();
-    }
-
-    @Test
-    public void testAreBubblesBottomAligned_smallTablet_false() {
-        Insets insets = Insets.of(10, 20, 5, 15);
-        Rect screenBounds = new Rect(0, 0, 1800, 2600);
-
-        DeviceConfig deviceConfig = new ConfigBuilder()
-                .setLargeScreen()
-                .setSmallTablet()
-                .setInsets(insets)
-                .setScreenBounds(screenBounds)
-                .build();
-        mPositioner.update(deviceConfig);
-
-        assertThat(mPositioner.areBubblesBottomAligned()).isFalse();
-    }
-
-    @Test
-    public void testAreBubblesBottomAligned_phone_false() {
-        Insets insets = Insets.of(10, 20, 5, 15);
-        Rect screenBounds = new Rect(0, 0, 1800, 2600);
-
-        DeviceConfig deviceConfig = new ConfigBuilder()
-                .setInsets(insets)
-                .setScreenBounds(screenBounds)
-                .build();
-        mPositioner.update(deviceConfig);
-
-        assertThat(mPositioner.areBubblesBottomAligned()).isFalse();
-    }
-
-    @Test
-    public void testExpandedViewY_phoneLandscape() {
-        Insets insets = Insets.of(10, 20, 5, 15);
-        Rect screenBounds = new Rect(0, 0, 1800, 2600);
-
-        DeviceConfig deviceConfig = new ConfigBuilder()
-                .setLandscape()
-                .setInsets(insets)
-                .setScreenBounds(screenBounds)
-                .build();
-        mPositioner.update(deviceConfig);
-
-        Intent intent = new Intent(Intent.ACTION_VIEW).setPackage(mContext.getPackageName());
-        Bubble bubble = Bubble.createAppBubble(intent, new UserHandle(1), null, directExecutor());
-
-        // This bubble will have max height so it'll always be top aligned
-        assertThat(mPositioner.getExpandedViewY(bubble, 0f /* bubblePosition */))
-                .isEqualTo(mPositioner.getExpandedViewYTopAligned());
-    }
-
-    @Test
-    public void testExpandedViewY_phonePortrait() {
-        Insets insets = Insets.of(10, 20, 5, 15);
-        Rect screenBounds = new Rect(0, 0, 1800, 2600);
-
-        DeviceConfig deviceConfig = new ConfigBuilder()
-                .setInsets(insets)
-                .setScreenBounds(screenBounds)
-                .build();
-        mPositioner.update(deviceConfig);
-
-        Intent intent = new Intent(Intent.ACTION_VIEW).setPackage(mContext.getPackageName());
-        Bubble bubble = Bubble.createAppBubble(intent, new UserHandle(1), null, directExecutor());
-
-        // Always top aligned in phone portrait
-        assertThat(mPositioner.getExpandedViewY(bubble, 0f /* bubblePosition */))
-                .isEqualTo(mPositioner.getExpandedViewYTopAligned());
-    }
-
-    @Test
-    public void testExpandedViewY_smallTabletLandscape() {
-        Insets insets = Insets.of(10, 20, 5, 15);
-        Rect screenBounds = new Rect(0, 0, 1800, 2600);
-
-        DeviceConfig deviceConfig = new ConfigBuilder()
-                .setSmallTablet()
-                .setLandscape()
-                .setInsets(insets)
-                .setScreenBounds(screenBounds)
-                .build();
-        mPositioner.update(deviceConfig);
-
-        Intent intent = new Intent(Intent.ACTION_VIEW).setPackage(mContext.getPackageName());
-        Bubble bubble = Bubble.createAppBubble(intent, new UserHandle(1), null, directExecutor());
-
-        // This bubble will have max height which is always top aligned on small tablets
-        assertThat(mPositioner.getExpandedViewY(bubble, 0f /* bubblePosition */))
-                .isEqualTo(mPositioner.getExpandedViewYTopAligned());
-    }
-
-    @Test
-    public void testExpandedViewY_smallTabletPortrait() {
-        Insets insets = Insets.of(10, 20, 5, 15);
-        Rect screenBounds = new Rect(0, 0, 1800, 2600);
-
-        DeviceConfig deviceConfig = new ConfigBuilder()
-                .setSmallTablet()
-                .setInsets(insets)
-                .setScreenBounds(screenBounds)
-                .build();
-        mPositioner.update(deviceConfig);
-
-        Intent intent = new Intent(Intent.ACTION_VIEW).setPackage(mContext.getPackageName());
-        Bubble bubble = Bubble.createAppBubble(intent, new UserHandle(1), null, directExecutor());
-
-        // This bubble will have max height which is always top aligned on small tablets
-        assertThat(mPositioner.getExpandedViewY(bubble, 0f /* bubblePosition */))
-                .isEqualTo(mPositioner.getExpandedViewYTopAligned());
-    }
-
-    @Test
-    public void testExpandedViewY_largeScreenLandscape() {
-        Insets insets = Insets.of(10, 20, 5, 15);
-        Rect screenBounds = new Rect(0, 0, 1800, 2600);
-
-        DeviceConfig deviceConfig = new ConfigBuilder()
-                .setLargeScreen()
-                .setLandscape()
-                .setInsets(insets)
-                .setScreenBounds(screenBounds)
-                .build();
-        mPositioner.update(deviceConfig);
-
-        Intent intent = new Intent(Intent.ACTION_VIEW).setPackage(mContext.getPackageName());
-        Bubble bubble = Bubble.createAppBubble(intent, new UserHandle(1), null, directExecutor());
-
-        // This bubble will have max height which is always top aligned on landscape, large tablet
-        assertThat(mPositioner.getExpandedViewY(bubble, 0f /* bubblePosition */))
-                .isEqualTo(mPositioner.getExpandedViewYTopAligned());
-    }
-
-    @Test
-    public void testExpandedViewY_largeScreenPortrait() {
-        Insets insets = Insets.of(10, 20, 5, 15);
-        Rect screenBounds = new Rect(0, 0, 1800, 2600);
-
-        DeviceConfig deviceConfig = new ConfigBuilder()
-                .setLargeScreen()
-                .setInsets(insets)
-                .setScreenBounds(screenBounds)
-                .build();
-        mPositioner.update(deviceConfig);
-
-        Intent intent = new Intent(Intent.ACTION_VIEW).setPackage(mContext.getPackageName());
-        Bubble bubble = Bubble.createAppBubble(intent, new UserHandle(1), null, directExecutor());
-
-        int manageButtonHeight =
-                mContext.getResources().getDimensionPixelSize(R.dimen.bubble_manage_button_height);
-        int manageButtonPlusMargin = manageButtonHeight + 2
-                * mContext.getResources().getDimensionPixelSize(
-                        R.dimen.bubble_manage_button_margin);
-        int pointerWidth = mContext.getResources().getDimensionPixelSize(
-                R.dimen.bubble_pointer_width);
-
-        final float expectedExpandedViewY = mPositioner.getAvailableRect().bottom
-                - manageButtonPlusMargin
-                - mPositioner.getExpandedViewHeightForLargeScreen()
-                - pointerWidth;
-
-        // Bubbles are bottom aligned on portrait, large tablet
-        assertThat(mPositioner.getExpandedViewY(bubble, 0f /* bubblePosition */))
-                .isEqualTo(expectedExpandedViewY);
-    }
-
-    /**
-     * Calculates the Y position bubbles should be placed based on the config. Based on
-     * the calculations in {@link BubblePositioner#getDefaultStartPosition()} and
-     * {@link BubbleStackView.RelativeStackPosition}.
-     */
-    private float getDefaultYPosition() {
-        final boolean isTablet = mPositioner.isLargeScreen();
-
-        // On tablet the position is centered, on phone it is an offset from the top.
-        final float desiredY = isTablet
-                ? mPositioner.getScreenRect().height() / 2f - (mPositioner.getBubbleSize() / 2f)
-                : mContext.getResources().getDimensionPixelOffset(
-                        R.dimen.bubble_stack_starting_offset_y);
-        // Since we're visually centering the bubbles on tablet, use total screen height rather
-        // than the available height.
-        final float height = isTablet
-                ? mPositioner.getScreenRect().height()
-                : mPositioner.getAvailableRect().height();
-        float offsetPercent = desiredY / height;
-        offsetPercent = Math.max(0f, Math.min(1f, offsetPercent));
-        final RectF allowableStackRegion =
-                mPositioner.getAllowableStackPositionRegion(1 /* bubbleCount */);
-        return allowableStackRegion.top + allowableStackRegion.height() * offsetPercent;
-    }
-
-    /**
-     * Sets up window manager to return config values based on what you need for the test.
-     * By default it sets up a portrait phone without any insets.
-     */
-    private static class ConfigBuilder {
-        private Rect mScreenBounds = new Rect(0, 0, 1000, 2000);
-        private boolean mIsLargeScreen = false;
-        private boolean mIsSmallTablet = false;
-        private boolean mIsLandscape = false;
-        private boolean mIsRtl = false;
-        private Insets mInsets = Insets.of(0, 0, 0, 0);
-
-        public ConfigBuilder setScreenBounds(Rect screenBounds) {
-            mScreenBounds = screenBounds;
-            return this;
-        }
-
-        public ConfigBuilder setLargeScreen() {
-            mIsLargeScreen = true;
-            return this;
-        }
-
-        public ConfigBuilder setSmallTablet() {
-            mIsSmallTablet = true;
-            return this;
-        }
-
-        public ConfigBuilder setLandscape() {
-            mIsLandscape = true;
-            return this;
-        }
-
-        public ConfigBuilder setRtl() {
-            mIsRtl = true;
-            return this;
-        }
-
-        public ConfigBuilder setInsets(Insets insets) {
-            mInsets = insets;
-            return this;
-        }
-
-        private DeviceConfig build() {
-            return new DeviceConfig(mIsLargeScreen, mIsSmallTablet, mIsLandscape, mIsRtl,
-                    mScreenBounds, mInsets);
-        }
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitScreenUtilsTests.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitScreenUtilsTests.kt
new file mode 100644
index 0000000..955660c
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitScreenUtilsTests.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.common.split
+
+import android.content.ComponentName
+import android.content.pm.LauncherApps
+import android.content.pm.ShortcutInfo
+import android.os.UserHandle
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.wm.shell.ShellTestCase
+import org.junit.Assert.assertEquals
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.any
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.`when`
+
+@RunWith(AndroidJUnit4::class)
+class SplitScreenUtilsTests : ShellTestCase() {
+
+    @Test
+    fun getShortcutComponent_nullShortcuts() {
+        val launcherApps = mock(LauncherApps::class.java).also {
+            `when`(it.getShortcuts(any(), any())).thenReturn(null)
+        }
+        assertEquals(null, SplitScreenUtils.getShortcutComponent(TEST_PACKAGE,
+                TEST_SHORTCUT_ID, UserHandle.CURRENT, launcherApps))
+    }
+
+    @Test
+    fun getShortcutComponent_noShortcuts() {
+        val launcherApps = mock(LauncherApps::class.java).also {
+            `when`(it.getShortcuts(any(), any())).thenReturn(ArrayList<ShortcutInfo>())
+        }
+        assertEquals(null, SplitScreenUtils.getShortcutComponent(TEST_PACKAGE,
+                TEST_SHORTCUT_ID, UserHandle.CURRENT, launcherApps))
+    }
+
+    @Test
+    fun getShortcutComponent_validShortcut() {
+        val component = ComponentName(TEST_PACKAGE, TEST_ACTIVITY)
+        val shortcutInfo = ShortcutInfo.Builder(context, "id").setActivity(component).build()
+        val launcherApps = mock(LauncherApps::class.java).also {
+            `when`(it.getShortcuts(any(), any())).thenReturn(arrayListOf(shortcutInfo))
+        }
+        assertEquals(component, SplitScreenUtils.getShortcutComponent(TEST_PACKAGE,
+                TEST_SHORTCUT_ID, UserHandle.CURRENT, launcherApps))
+    }
+
+    companion object {
+        val TEST_PACKAGE = "com.android.wm.shell.common.split"
+        val TEST_ACTIVITY = "TestActivity";
+        val TEST_SHORTCUT_ID = "test_shortcut_1"
+    }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
index 94c862b..9249b0a 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
@@ -393,7 +393,7 @@
     fun moveToFullscreen_displayFullscreen_windowingModeSetToUndefined() {
         val task = setUpFreeformTask()
         task.configuration.windowConfiguration.displayWindowingMode = WINDOWING_MODE_FULLSCREEN
-        controller.moveToFullscreen(task.taskId, desktopModeWindowDecoration)
+        controller.moveToFullscreen(task.taskId)
         val wct = getLatestExitDesktopWct()
         assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
             .isEqualTo(WINDOWING_MODE_UNDEFINED)
@@ -403,7 +403,7 @@
     fun moveToFullscreen_displayFreeform_windowingModeSetToFullscreen() {
         val task = setUpFreeformTask()
         task.configuration.windowConfiguration.displayWindowingMode = WINDOWING_MODE_FREEFORM
-        controller.moveToFullscreen(task.taskId, desktopModeWindowDecoration)
+        controller.moveToFullscreen(task.taskId)
         val wct = getLatestExitDesktopWct()
         assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
                 .isEqualTo(WINDOWING_MODE_FULLSCREEN)
@@ -411,7 +411,7 @@
 
     @Test
     fun moveToFullscreen_nonExistentTask_doesNothing() {
-        controller.moveToFullscreen(999, desktopModeWindowDecoration)
+        controller.moveToFullscreen(999)
         verifyWCTNotExecuted()
     }
 
@@ -420,7 +420,7 @@
         val taskDefaultDisplay = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
         val taskSecondDisplay = setUpFreeformTask(displayId = SECOND_DISPLAY)
 
-        controller.moveToFullscreen(taskDefaultDisplay.taskId, desktopModeWindowDecoration)
+        controller.moveToFullscreen(taskDefaultDisplay.taskId)
 
         with(getLatestExitDesktopWct()) {
             assertThat(changes.keys).contains(taskDefaultDisplay.token.asBinder())
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 855b7ee..12a5594 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
@@ -22,6 +22,7 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
 import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
 import static android.content.Intent.FLAG_ACTIVITY_NO_USER_ACTION;
+import static android.view.WindowManager.PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI;
 
 import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
 import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
@@ -36,6 +37,8 @@
 import static org.mockito.ArgumentMatchers.isNull;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
@@ -46,8 +49,10 @@
 import android.app.ActivityTaskManager;
 import android.app.PendingIntent;
 import android.content.ComponentName;
+import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
 import android.os.Bundle;
 
 import androidx.test.annotation.UiThreadTest;
@@ -55,6 +60,7 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.launcher3.icons.IconProvider;
+import com.android.wm.shell.R;
 import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.ShellTestCase;
@@ -91,6 +97,10 @@
 @RunWith(AndroidJUnit4.class)
 public class SplitScreenControllerTests extends ShellTestCase {
 
+    private static final String TEST_PACKAGE = "com.android.wm.shell.splitscreen";
+    private static final String TEST_NOT_ALLOWED_PACKAGE = "com.android.wm.shell.splitscreen.fake";
+    private static final String TEST_ACTIVITY = "TestActivity";
+
     @Mock ShellInit mShellInit;
     @Mock ShellCommandHandler mShellCommandHandler;
     @Mock ShellTaskOrganizer mTaskOrganizer;
@@ -118,6 +128,8 @@
     public void setup() {
         assumeTrue(ActivityTaskManager.supportsSplitScreenMultiWindow(mContext));
         MockitoAnnotations.initMocks(this);
+        String[] appsSupportingMultiInstance = mContext.getResources()
+                .getStringArray(R.array.config_appsSupportMultiInstancesSplit);
         mShellController = spy(new ShellController(mContext, mShellInit, mShellCommandHandler,
                 mMainExecutor));
         mSplitScreenController = spy(new SplitScreenController(mContext, mShellInit,
@@ -125,7 +137,8 @@
                 mRootTDAOrganizer, mDisplayController, mDisplayImeController,
                 mDisplayInsetsController, mDragAndDropController, mTransitions, mTransactionPool,
                 mIconProvider, mRecentTasks, mLaunchAdjacentController, mWindowDecorViewModel,
-                mDesktopTasksController, mMainExecutor, mStageCoordinator));
+                mDesktopTasksController, mMainExecutor, mStageCoordinator,
+                appsSupportingMultiInstance));
     }
 
     @Test
@@ -200,7 +213,7 @@
 
     @Test
     public void startIntent_multiInstancesSupported_appendsMultipleTaskFag() {
-        doReturn(true).when(mSplitScreenController).supportMultiInstancesSplit(any());
+        doReturn(true).when(mSplitScreenController).supportsMultiInstanceSplit(any());
         Intent startIntent = createStartIntent("startActivity");
         PendingIntent pendingIntent =
                 PendingIntent.getActivity(mContext, 0, startIntent, FLAG_IMMUTABLE);
@@ -237,12 +250,13 @@
 
         verify(mStageCoordinator).startTask(anyInt(), eq(SPLIT_POSITION_TOP_OR_LEFT),
                 isNull());
-        verify(mSplitScreenController, never()).supportMultiInstancesSplit(any());
+        verify(mSplitScreenController, never()).supportsMultiInstanceSplit(any());
         verify(mStageCoordinator, never()).switchSplitPosition(any());
     }
 
     @Test
     public void startIntent_multiInstancesSupported_startTaskInBackgroundAfterSplitActivated() {
+        doReturn(true).when(mSplitScreenController).supportsMultiInstanceSplit(any());
         doNothing().when(mSplitScreenController).startTask(anyInt(), anyInt(), any());
         Intent startIntent = createStartIntent("startActivity");
         PendingIntent pendingIntent =
@@ -259,14 +273,14 @@
 
         mSplitScreenController.startIntent(pendingIntent, mContext.getUserId(), null,
                 SPLIT_POSITION_TOP_OR_LEFT, null);
-        verify(mSplitScreenController, never()).supportMultiInstancesSplit(any());
+        verify(mSplitScreenController, never()).supportsMultiInstanceSplit(any());
         verify(mStageCoordinator).startTask(anyInt(), eq(SPLIT_POSITION_TOP_OR_LEFT),
                 isNull());
     }
 
     @Test
     public void startIntent_multiInstancesNotSupported_switchesPositionAfterSplitActivated() {
-        doReturn(false).when(mSplitScreenController).supportMultiInstancesSplit(any());
+        doReturn(false).when(mSplitScreenController).supportsMultiInstanceSplit(any());
         Intent startIntent = createStartIntent("startActivity");
         PendingIntent pendingIntent =
                 PendingIntent.getActivity(mContext, 0, startIntent, FLAG_IMMUTABLE);
@@ -283,6 +297,130 @@
         verify(mStageCoordinator).switchSplitPosition(anyString());
     }
 
+    @Test
+    public void supportsMultiInstanceSplit_inStaticAllowList() {
+        String[] allowList = { TEST_PACKAGE };
+        SplitScreenController controller = new SplitScreenController(mContext, mShellInit,
+                mShellCommandHandler, mShellController, mTaskOrganizer, mSyncQueue,
+                mRootTDAOrganizer, mDisplayController, mDisplayImeController,
+                mDisplayInsetsController, mDragAndDropController, mTransitions, mTransactionPool,
+                mIconProvider, mRecentTasks, mLaunchAdjacentController, mWindowDecorViewModel,
+                mDesktopTasksController, mMainExecutor, mStageCoordinator,
+                allowList);
+        ComponentName component = new ComponentName(TEST_PACKAGE, TEST_ACTIVITY);
+        assertEquals(true, controller.supportsMultiInstanceSplit(component));
+    }
+
+    @Test
+    public void supportsMultiInstanceSplit_notInStaticAllowList() {
+        String[] allowList = { TEST_PACKAGE };
+        SplitScreenController controller = new SplitScreenController(mContext, mShellInit,
+                mShellCommandHandler, mShellController, mTaskOrganizer, mSyncQueue,
+                mRootTDAOrganizer, mDisplayController, mDisplayImeController,
+                mDisplayInsetsController, mDragAndDropController, mTransitions, mTransactionPool,
+                mIconProvider, mRecentTasks, mLaunchAdjacentController, mWindowDecorViewModel,
+                mDesktopTasksController, mMainExecutor, mStageCoordinator,
+                allowList);
+        ComponentName component = new ComponentName(TEST_NOT_ALLOWED_PACKAGE, TEST_ACTIVITY);
+        assertEquals(false, controller.supportsMultiInstanceSplit(component));
+    }
+
+    @Test
+    public void supportsMultiInstanceSplit_activityPropertyTrue()
+            throws PackageManager.NameNotFoundException {
+        Context context = spy(mContext);
+        ComponentName component = new ComponentName(TEST_PACKAGE, TEST_ACTIVITY);
+        PackageManager pm = mock(PackageManager.class);
+        doReturn(pm).when(context).getPackageManager();
+        PackageManager.Property activityProp = new PackageManager.Property("", true, "", "");
+        doReturn(activityProp).when(pm).getProperty(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
+                eq(component));
+        PackageManager.Property appProp = new PackageManager.Property("", false, "", "");
+        doReturn(appProp).when(pm).getProperty(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
+                eq(component.getPackageName()));
+
+        SplitScreenController controller = new SplitScreenController(context, mShellInit,
+                mShellCommandHandler, mShellController, mTaskOrganizer, mSyncQueue,
+                mRootTDAOrganizer, mDisplayController, mDisplayImeController,
+                mDisplayInsetsController, mDragAndDropController, mTransitions, mTransactionPool,
+                mIconProvider, mRecentTasks, mLaunchAdjacentController, mWindowDecorViewModel,
+                mDesktopTasksController, mMainExecutor, mStageCoordinator,
+                new String[0]);
+        // Expect activity property to override application property
+        assertEquals(true, controller.supportsMultiInstanceSplit(component));
+    }
+
+    @Test
+    public void supportsMultiInstanceSplit_activityPropertyFalseApplicationPropertyTrue()
+            throws PackageManager.NameNotFoundException {
+        Context context = spy(mContext);
+        ComponentName component = new ComponentName(TEST_PACKAGE, TEST_ACTIVITY);
+        PackageManager pm = mock(PackageManager.class);
+        doReturn(pm).when(context).getPackageManager();
+        PackageManager.Property activityProp = new PackageManager.Property("", false, "", "");
+        doReturn(activityProp).when(pm).getProperty(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
+                eq(component));
+        PackageManager.Property appProp = new PackageManager.Property("", true, "", "");
+        doReturn(appProp).when(pm).getProperty(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
+                eq(component.getPackageName()));
+
+        SplitScreenController controller = new SplitScreenController(context, mShellInit,
+                mShellCommandHandler, mShellController, mTaskOrganizer, mSyncQueue,
+                mRootTDAOrganizer, mDisplayController, mDisplayImeController,
+                mDisplayInsetsController, mDragAndDropController, mTransitions, mTransactionPool,
+                mIconProvider, mRecentTasks, mLaunchAdjacentController, mWindowDecorViewModel,
+                mDesktopTasksController, mMainExecutor, mStageCoordinator,
+                new String[0]);
+        // Expect activity property to override application property
+        assertEquals(false, controller.supportsMultiInstanceSplit(component));
+    }
+
+    @Test
+    public void supportsMultiInstanceSplit_noActivityPropertyApplicationPropertyTrue()
+            throws PackageManager.NameNotFoundException {
+        Context context = spy(mContext);
+        ComponentName component = new ComponentName(TEST_PACKAGE, TEST_ACTIVITY);
+        PackageManager pm = mock(PackageManager.class);
+        doReturn(pm).when(context).getPackageManager();
+        doThrow(PackageManager.NameNotFoundException.class).when(pm).getProperty(
+                eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI), eq(component));
+        PackageManager.Property appProp = new PackageManager.Property("", true, "", "");
+        doReturn(appProp).when(pm).getProperty(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
+                eq(component.getPackageName()));
+
+        SplitScreenController controller = new SplitScreenController(context, mShellInit,
+                mShellCommandHandler, mShellController, mTaskOrganizer, mSyncQueue,
+                mRootTDAOrganizer, mDisplayController, mDisplayImeController,
+                mDisplayInsetsController, mDragAndDropController, mTransitions, mTransactionPool,
+                mIconProvider, mRecentTasks, mLaunchAdjacentController, mWindowDecorViewModel,
+                mDesktopTasksController, mMainExecutor, mStageCoordinator,
+                new String[0]);
+        // Expect fall through to app property
+        assertEquals(true, controller.supportsMultiInstanceSplit(component));
+    }
+
+    @Test
+    public void supportsMultiInstanceSplit_noActivityOrAppProperty()
+            throws PackageManager.NameNotFoundException {
+        Context context = spy(mContext);
+        ComponentName component = new ComponentName(TEST_PACKAGE, TEST_ACTIVITY);
+        PackageManager pm = mock(PackageManager.class);
+        doReturn(pm).when(context).getPackageManager();
+        doThrow(PackageManager.NameNotFoundException.class).when(pm).getProperty(
+                eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI), eq(component));
+        doThrow(PackageManager.NameNotFoundException.class).when(pm).getProperty(
+                eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI), eq(component.getPackageName()));
+
+        SplitScreenController controller = new SplitScreenController(context, mShellInit,
+                mShellCommandHandler, mShellController, mTaskOrganizer, mSyncQueue,
+                mRootTDAOrganizer, mDisplayController, mDisplayImeController,
+                mDisplayInsetsController, mDragAndDropController, mTransitions, mTransactionPool,
+                mIconProvider, mRecentTasks, mLaunchAdjacentController, mWindowDecorViewModel,
+                mDesktopTasksController, mMainExecutor, mStageCoordinator,
+                new String[0]);
+        assertEquals(false, controller.supportsMultiInstanceSplit(component));
+    }
+
     private Intent createStartIntent(String activityName) {
         Intent intent = new Intent();
         intent.setComponent(new ComponentName(mContext, activityName));
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
index e22bf3d..e9da258 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
@@ -64,6 +64,7 @@
 import android.app.ActivityManager.RunningTaskInfo;
 import android.app.IApplicationThread;
 import android.app.PendingIntent;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.os.Binder;
@@ -420,6 +421,30 @@
     }
 
     @Test
+    public void testTransitionFilterActivityComponent() {
+        TransitionFilter filter = new TransitionFilter();
+        ComponentName cmpt = new ComponentName("testpak", "testcls");
+        filter.mRequirements =
+                new TransitionFilter.Requirement[]{new TransitionFilter.Requirement()};
+        filter.mRequirements[0].mTopActivity = cmpt;
+        filter.mRequirements[0].mModes = new int[]{TRANSIT_OPEN, TRANSIT_TO_FRONT};
+
+        final RunningTaskInfo taskInf = createTaskInfo(1);
+        final TransitionInfo openTask = new TransitionInfoBuilder(TRANSIT_OPEN)
+                .addChange(TRANSIT_OPEN, taskInf).build();
+        assertFalse(filter.matches(openTask));
+
+        taskInf.topActivity = cmpt;
+        final TransitionInfo openTaskCmpt = new TransitionInfoBuilder(TRANSIT_OPEN)
+                .addChange(TRANSIT_OPEN, taskInf).build();
+        assertTrue(filter.matches(openTaskCmpt));
+
+        final TransitionInfo openAct = new TransitionInfoBuilder(TRANSIT_OPEN)
+                .addChange(TRANSIT_OPEN, cmpt).build();
+        assertTrue(filter.matches(openAct));
+    }
+
+    @Test
     public void testRegisteredRemoteTransition() {
         Transitions transitions = createTestTransitions();
         transitions.replaceDefaultHandlerForTest(mDefaultHandler);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/TransitionInfoBuilder.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/TransitionInfoBuilder.java
index 8343858..b8939e6f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/TransitionInfoBuilder.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/TransitionInfoBuilder.java
@@ -21,6 +21,7 @@
 import static org.mockito.Mockito.mock;
 
 import android.app.ActivityManager;
+import android.content.ComponentName;
 import android.view.SurfaceControl;
 import android.view.WindowManager;
 import android.window.TransitionInfo;
@@ -50,20 +51,34 @@
     }
 
     public TransitionInfoBuilder addChange(@WindowManager.TransitionType int mode,
-            @TransitionInfo.ChangeFlags int flags, ActivityManager.RunningTaskInfo taskInfo) {
+            @TransitionInfo.ChangeFlags int flags, ActivityManager.RunningTaskInfo taskInfo,
+            ComponentName activityComponent) {
         final TransitionInfo.Change change = new TransitionInfo.Change(
                 taskInfo != null ? taskInfo.token : null, createMockSurface(true /* valid */));
         change.setMode(mode);
         change.setFlags(flags);
         change.setTaskInfo(taskInfo);
+        change.setActivityComponent(activityComponent);
         return addChange(change);
     }
 
+    /** Add a change to the TransitionInfo */
+    public TransitionInfoBuilder addChange(@WindowManager.TransitionType int mode,
+            @TransitionInfo.ChangeFlags int flags, ActivityManager.RunningTaskInfo taskInfo) {
+        return addChange(mode, flags, taskInfo, null /* activityComponent */);
+    }
+
     public TransitionInfoBuilder addChange(@WindowManager.TransitionType int mode,
             ActivityManager.RunningTaskInfo taskInfo) {
         return addChange(mode, TransitionInfo.FLAG_NONE, taskInfo);
     }
 
+    /** Add a change to the TransitionInfo */
+    public TransitionInfoBuilder addChange(@WindowManager.TransitionType int mode,
+            ComponentName activityComponent) {
+        return addChange(mode, TransitionInfo.FLAG_NONE, null /* taskinfo */, activityComponent);
+    }
+
     public TransitionInfoBuilder addChange(@WindowManager.TransitionType int mode) {
         return addChange(mode, TransitionInfo.FLAG_NONE, null /* taskInfo */);
     }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
index 883c24e..f84685a 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
@@ -27,7 +27,6 @@
 import android.hardware.display.DisplayManager
 import android.hardware.display.VirtualDisplay
 import android.os.Handler
-import android.os.IBinder
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper.RunWithLooper
 import android.view.Choreographer
@@ -40,8 +39,6 @@
 import android.view.SurfaceView
 import android.view.WindowInsets.Type.navigationBars
 import android.view.WindowInsets.Type.statusBars
-import android.view.WindowManager
-import android.window.TransitionInfo
 import androidx.test.filters.SmallTest
 import com.android.wm.shell.RootTaskDisplayAreaOrganizer
 import com.android.wm.shell.ShellTaskOrganizer
@@ -53,8 +50,6 @@
 import com.android.wm.shell.common.ShellExecutor
 import com.android.wm.shell.common.SyncTransactionQueue
 import com.android.wm.shell.desktopmode.DesktopTasksController
-import com.android.wm.shell.recents.RecentsTransitionHandler
-import com.android.wm.shell.recents.RecentsTransitionStateListener
 import com.android.wm.shell.sysui.KeyguardChangeListener
 import com.android.wm.shell.sysui.ShellCommandHandler
 import com.android.wm.shell.sysui.ShellController
@@ -100,7 +95,6 @@
     @Mock private lateinit var mockShellController: ShellController
     @Mock private lateinit var mockShellExecutor: ShellExecutor
     @Mock private lateinit var mockRootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer
-    @Mock private lateinit var mockRecentsTransitionHandler: RecentsTransitionHandler
     @Mock private lateinit var mockShellCommandHandler: ShellCommandHandler
 
     private val transactionFactory = Supplier<SurfaceControl.Transaction> {
@@ -127,7 +121,6 @@
                 mockSyncQueue,
                 mockTransitions,
                 Optional.of(mockDesktopTasksController),
-                mockRecentsTransitionHandler,
                 mockDesktopModeWindowDecorFactory,
                 mockInputMonitorFactory,
                 transactionFactory,
@@ -275,48 +268,6 @@
     }
 
     @Test
-    fun testRelayoutBlockedDuringRecentsTransition() {
-        val recentsCaptor = argumentCaptor<RecentsTransitionStateListener>()
-        verify(mockRecentsTransitionHandler).addTransitionStateListener(recentsCaptor.capture())
-
-        val transition = mock(IBinder::class.java)
-        val task = createTask(windowingMode = WINDOWING_MODE_FREEFORM)
-        val decoration = setUpMockDecorationForTask(task)
-
-        // Make sure a window decorations exists first by launching a freeform task.
-        onTaskOpening(task)
-        // Now call back when a Recents transition starts.
-        recentsCaptor.firstValue.onTransitionStarted(transition)
-
-        verify(decoration).incrementRelayoutBlock()
-        verify(decoration).addTransitionPausingRelayout(transition)
-    }
-
-    @Test
-    fun testRelayoutBlockedDuringKeyguardTransition() {
-        val transition = mock(IBinder::class.java)
-        val task = createTask(windowingMode = WINDOWING_MODE_FREEFORM)
-        val decoration = setUpMockDecorationForTask(task)
-        val transitionInfo = mock(TransitionInfo::class.java)
-        val transitionChange = mock(TransitionInfo.Change::class.java)
-        val taskInfo = mock(RunningTaskInfo()::class.java)
-
-        // Replicate a keyguard going away transition for a task
-        whenever(transitionInfo.getFlags())
-                .thenReturn(WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY)
-        whenever(transitionChange.getMode()).thenReturn(WindowManager.TRANSIT_TO_FRONT)
-        whenever(transitionChange.getTaskInfo()).thenReturn(taskInfo)
-
-        // Make sure a window decorations exists first by launching a freeform task.
-        onTaskOpening(task)
-        // OnTransition ready is called when a keyguard going away transition happens
-        desktopModeWindowDecorViewModel
-                .onTransitionReady(transition, transitionInfo, transitionChange)
-
-        verify(decoration).incrementRelayoutBlock()
-        verify(decoration).addTransitionPausingRelayout(transition)
-    }
-    @Test
     fun testRelayoutRunsWhenStatusBarsInsetsSourceVisibilityChanges() {
         val task = createTask(windowingMode = WINDOWING_MODE_FREEFORM, focused = true)
         val decoration = setUpMockDecorationForTask(task)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
index 18fcdd0..193f16d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
@@ -16,16 +16,24 @@
 
 package com.android.wm.shell.windowdecor;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 import android.app.ActivityManager;
 import android.content.ComponentName;
 import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
 import android.os.Handler;
+import android.os.SystemProperties;
 import android.testing.AndroidTestingRunner;
+import android.testing.TestableContext;
 import android.view.Choreographer;
 import android.view.Display;
 import android.view.SurfaceControl;
@@ -34,14 +42,17 @@
 
 import androidx.test.filters.SmallTest;
 
+import com.android.internal.R;
 import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.ShellTestCase;
 import com.android.wm.shell.TestRunningTaskInfoBuilder;
 import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.windowdecor.WindowDecoration.RelayoutParams;
 
 import org.junit.Before;
+import org.junit.BeforeClass;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
@@ -57,6 +68,13 @@
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 public class DesktopModeWindowDecorationTests extends ShellTestCase {
+    private static final String USE_WINDOW_SHADOWS_SYSPROP_KEY =
+            "persist.wm.debug.desktop_use_window_shadows";
+    private static final String FOCUSED_USE_WINDOW_SHADOWS_SYSPROP_KEY =
+            "persist.wm.debug.desktop_use_window_shadows_focused_window";
+    private static final String USE_ROUNDED_CORNERS_SYSPROP_KEY =
+            "persist.wm.debug.desktop_use_rounded_corners";
+
     @Mock
     private DisplayController mMockDisplayController;
     @Mock
@@ -79,14 +97,29 @@
     private SurfaceControlViewHost mMockSurfaceControlViewHost;
     @Mock
     private WindowDecoration.SurfaceControlViewHostFactory mMockSurfaceControlViewHostFactory;
+    @Mock
+    private TypedArray mMockRoundedCornersRadiusArray;
 
     private final Configuration mConfiguration = new Configuration();
 
+    private TestableContext mTestableContext;
+
+    /** Set up run before test class. */
+    @BeforeClass
+    public static void setUpClass() {
+        // Reset the sysprop settings before running the test.
+        SystemProperties.set(USE_WINDOW_SHADOWS_SYSPROP_KEY, "");
+        SystemProperties.set(FOCUSED_USE_WINDOW_SHADOWS_SYSPROP_KEY, "");
+        SystemProperties.set(USE_ROUNDED_CORNERS_SYSPROP_KEY, "");
+    }
+
     @Before
     public void setUp() {
         doReturn(mMockSurfaceControlViewHost).when(mMockSurfaceControlViewHostFactory).create(
                 any(), any(), any());
         doReturn(mMockTransaction).when(mMockTransactionSupplier).get();
+        mTestableContext = new TestableContext(mContext);
+        mTestableContext.ensureTestableResources();
     }
 
     @Test
@@ -105,6 +138,52 @@
 
     }
 
+    @Test
+    public void updateRelayoutParams_noSysPropFlagsSet_windowShadowsAreEnabled() {
+        final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
+        RelayoutParams relayoutParams = new RelayoutParams();
+
+        DesktopModeWindowDecoration.updateRelayoutParams(
+                relayoutParams, mContext, taskInfo, /* applyStartTransactionOnDraw= */ true,
+                /* shouldSetTaskPositionAndCrop */ false);
+
+        assertThat(relayoutParams.mShadowRadiusId).isNotEqualTo(Resources.ID_NULL);
+    }
+
+    @Test
+    public void updateRelayoutParams_noSysPropFlagsSet_roundedCornersAreEnabled() {
+        final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
+        fillRoundedCornersResources(/* fillValue= */ 30);
+        RelayoutParams relayoutParams = new RelayoutParams();
+
+        DesktopModeWindowDecoration.updateRelayoutParams(
+                relayoutParams,
+                mTestableContext,
+                taskInfo,
+                /* applyStartTransactionOnDraw= */ true,
+                /* shouldSetTaskPositionAndCrop */ false);
+
+        assertThat(relayoutParams.mCornerRadius).isGreaterThan(0);
+    }
+
+    private void fillRoundedCornersResources(int fillValue) {
+        when(mMockRoundedCornersRadiusArray.getDimensionPixelSize(anyInt(), anyInt()))
+                .thenReturn(fillValue);
+        mTestableContext.getOrCreateTestableResources().addOverride(
+                R.array.config_roundedCornerRadiusArray, mMockRoundedCornersRadiusArray);
+        mTestableContext.getOrCreateTestableResources().addOverride(
+                R.dimen.rounded_corner_radius, fillValue);
+        mTestableContext.getOrCreateTestableResources().addOverride(
+                R.array.config_roundedCornerTopRadiusArray, mMockRoundedCornersRadiusArray);
+        mTestableContext.getOrCreateTestableResources().addOverride(
+                R.dimen.rounded_corner_radius_top, fillValue);
+        mTestableContext.getOrCreateTestableResources().addOverride(
+                R.array.config_roundedCornerBottomRadiusArray, mMockRoundedCornersRadiusArray);
+        mTestableContext.getOrCreateTestableResources().addOverride(
+                R.dimen.rounded_corner_radius_bottom, fillValue);
+    }
+
+
     private DesktopModeWindowDecoration createWindowDecoration(
             ActivityManager.RunningTaskInfo taskInfo) {
         return new DesktopModeWindowDecoration(mContext, mMockDisplayController,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtilityTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtilityTest.kt
index 5c0e04a..e60be71 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtilityTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtilityTest.kt
@@ -181,6 +181,26 @@
     }
 
     @Test
+    fun testDragEndSnapsTaskBoundsWhenOutsideValidDragArea() {
+        val startingPoint = PointF(STARTING_BOUNDS.right.toFloat(), STARTING_BOUNDS.top.toFloat())
+        val repositionTaskBounds = Rect(STARTING_BOUNDS)
+        val validDragArea = Rect(DISPLAY_BOUNDS.left - 100,
+            STABLE_BOUNDS.top,
+            DISPLAY_BOUNDS.right - 100,
+            DISPLAY_BOUNDS.bottom - 100)
+
+        DragPositioningCallbackUtility.onDragEnd(repositionTaskBounds, STARTING_BOUNDS,
+            startingPoint, startingPoint.x - 1000, (DISPLAY_BOUNDS.bottom + 1000).toFloat(),
+            validDragArea)
+        assertThat(repositionTaskBounds.left).isEqualTo(validDragArea.left)
+        assertThat(repositionTaskBounds.top).isEqualTo(validDragArea.bottom)
+        assertThat(repositionTaskBounds.right)
+            .isEqualTo(validDragArea.left + STARTING_BOUNDS.width())
+        assertThat(repositionTaskBounds.bottom)
+            .isEqualTo(validDragArea.bottom + STARTING_BOUNDS.height())
+    }
+
+    @Test
     fun testChangeBounds_toDisallowedBounds_freezesAtLimit() {
         var hasMoved = false
         val startingPoint = PointF(STARTING_BOUNDS.right.toFloat(),
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositionerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositionerTest.kt
index add78b2..de6903d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositionerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositionerTest.kt
@@ -10,6 +10,7 @@
 import android.view.Surface.ROTATION_270
 import android.view.Surface.ROTATION_90
 import android.view.SurfaceControl
+import android.view.WindowManager
 import android.window.WindowContainerToken
 import android.window.WindowContainerTransaction
 import android.window.WindowContainerTransaction.Change.CHANGE_DRAG_RESIZING
@@ -18,13 +19,17 @@
 import com.android.wm.shell.ShellTestCase
 import com.android.wm.shell.common.DisplayController
 import com.android.wm.shell.common.DisplayLayout
+import com.android.wm.shell.transition.Transitions
 import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_BOTTOM
 import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_RIGHT
 import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_TOP
 import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_UNDEFINED
+import junit.framework.Assert.assertFalse
+import junit.framework.Assert.assertTrue
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyInt
 import org.mockito.ArgumentMatchers.eq
 import org.mockito.Mock
 import org.mockito.Mockito
@@ -34,6 +39,7 @@
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.`when`
 import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.doReturn
 import java.util.function.Supplier
 import org.mockito.Mockito.`when` as whenever
 
@@ -50,6 +56,8 @@
     @Mock
     private lateinit var mockShellTaskOrganizer: ShellTaskOrganizer
     @Mock
+    private lateinit var mockTransitions: Transitions
+    @Mock
     private lateinit var mockWindowDecoration: WindowDecoration<*>
     @Mock
     private lateinit var mockDragStartListener: DragPositioningCallbackUtility.DragStartListener
@@ -69,6 +77,8 @@
     private lateinit var mockTransactionFactory: Supplier<SurfaceControl.Transaction>
     @Mock
     private lateinit var mockTransaction: SurfaceControl.Transaction
+    @Mock
+    private lateinit var mockTransitionBinder: IBinder
 
     private lateinit var taskPositioner: FluidResizeTaskPositioner
 
@@ -103,11 +113,15 @@
             configuration.windowConfiguration.setBounds(STARTING_BOUNDS)
             configuration.windowConfiguration.displayRotation = ROTATION_90
         }
+        `when`(mockWindowDecoration.calculateValidDragArea()).thenReturn(VALID_DRAG_AREA)
         mockWindowDecoration.mDisplay = mockDisplay
         whenever(mockDisplay.displayId).thenAnswer { DISPLAY_ID }
+        whenever(mockTransitions.startTransition(anyInt(), any(), any()))
+                .doReturn(mockTransitionBinder)
 
         taskPositioner = FluidResizeTaskPositioner(
                 mockShellTaskOrganizer,
+                mockTransitions,
                 mockWindowDecoration,
                 mockDisplayController,
                 mockDragStartListener,
@@ -117,7 +131,7 @@
     }
 
     @Test
-    fun testDragResize_notMove_skipsTransactionOnEnd() {
+    fun testDragResize_notMove_skipsTransitionOnEnd() {
         taskPositioner.onDragPositioningStart(
                 CTRL_TYPE_TOP or CTRL_TYPE_RIGHT,
                 STARTING_BOUNDS.left.toFloat(),
@@ -129,16 +143,16 @@
                 STARTING_BOUNDS.top.toFloat() + 10
         )
 
-        verify(mockShellTaskOrganizer, never()).applyTransaction(argThat { wct ->
+        verify(mockTransitions, never()).startTransition(
+                eq(WindowManager.TRANSIT_CHANGE), argThat { wct ->
             return@argThat wct.changes.any { (token, change) ->
                 token == taskBinder &&
                         ((change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0)
-            }
-        })
+            }}, eq(taskPositioner))
     }
 
     @Test
-    fun testDragResize_noEffectiveMove_skipsTransactionOnMoveAndEnd() {
+    fun testDragResize_noEffectiveMove_skipsTransitionOnMoveAndEnd() {
         taskPositioner.onDragPositioningStart(
                 CTRL_TYPE_TOP or CTRL_TYPE_RIGHT,
                 STARTING_BOUNDS.left.toFloat(),
@@ -150,21 +164,28 @@
                 STARTING_BOUNDS.top.toFloat()
         )
 
-        taskPositioner.onDragPositioningEnd(
-                STARTING_BOUNDS.left.toFloat() + 10,
-                STARTING_BOUNDS.top.toFloat() + 10
-        )
-
         verify(mockShellTaskOrganizer, never()).applyTransaction(argThat { wct ->
             return@argThat wct.changes.any { (token, change) ->
                 token == taskBinder &&
                         ((change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0)
             }
         })
+
+        taskPositioner.onDragPositioningEnd(
+                STARTING_BOUNDS.left.toFloat() + 10,
+                STARTING_BOUNDS.top.toFloat() + 10
+        )
+
+        verify(mockTransitions, never()).startTransition(
+                eq(WindowManager.TRANSIT_CHANGE), argThat { wct ->
+            return@argThat wct.changes.any { (token, change) ->
+                token == taskBinder &&
+                        ((change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0)
+            }}, eq(taskPositioner))
     }
 
     @Test
-    fun testDragResize_hasEffectiveMove_issuesTransactionOnMoveAndEnd() {
+    fun testDragResize_hasEffectiveMove_issuesTransitionOnMoveAndEnd() {
         taskPositioner.onDragPositioningStart(
                 CTRL_TYPE_TOP or CTRL_TYPE_RIGHT,
                 STARTING_BOUNDS.left.toFloat(),
@@ -191,13 +212,13 @@
         )
         val rectAfterEnd = Rect(rectAfterMove)
         rectAfterEnd.top += 10
-        verify(mockShellTaskOrganizer).applyTransaction(argThat { wct ->
-            return@argThat wct.changes.any { (token, change) ->
-                token == taskBinder &&
-                        (change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0 &&
-                        change.configuration.windowConfiguration.bounds == rectAfterEnd
-            }
-        })
+        verify(mockTransitions).startTransition(
+                eq(WindowManager.TRANSIT_CHANGE), argThat { wct ->
+        return@argThat wct.changes.any { (token, change) ->
+            token == taskBinder &&
+                    (change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0 &&
+                    change.configuration.windowConfiguration.bounds == rectAfterEnd
+            }}, eq(taskPositioner))
     }
 
     @Test
@@ -225,6 +246,13 @@
                         change.dragResizing
             }
         })
+        verify(mockTransitions, never()).startTransition(
+                eq(WindowManager.TRANSIT_CHANGE), argThat { wct ->
+            return@argThat wct.changes.any { (token, change) ->
+                token == taskBinder &&
+                        ((change.changeMask and CHANGE_DRAG_RESIZING) != 0) &&
+                        change.dragResizing
+            }}, eq(taskPositioner))
     }
 
     @Test
@@ -252,13 +280,13 @@
                         change.dragResizing
             }
         })
-        verify(mockShellTaskOrganizer).applyTransaction(argThat { wct ->
+        verify(mockTransitions).startTransition(
+                eq(WindowManager.TRANSIT_CHANGE), argThat { wct ->
             return@argThat wct.changes.any { (token, change) ->
                 token == taskBinder &&
                         ((change.changeMask and CHANGE_DRAG_RESIZING) != 0) &&
                         !change.dragResizing
-            }
-        })
+            }}, eq(taskPositioner))
     }
 
     @Test
@@ -269,7 +297,7 @@
                 STARTING_BOUNDS.top.toFloat()
         )
 
-        // Resize to width of 95px and height of 5px with min width of 10px
+        // Resize to width of 95px and height of 5px with min height of 10px
         val newX = STARTING_BOUNDS.right.toFloat() - 5
         val newY = STARTING_BOUNDS.top.toFloat() + 95
         taskPositioner.onDragPositioningMove(
@@ -565,12 +593,12 @@
 
         taskPositioner.onDragPositioningEnd(newX, newY)
 
-        verify(mockShellTaskOrganizer, never()).applyTransaction(argThat { wct ->
+        verify(mockTransitions, never()).startTransition(
+                eq(WindowManager.TRANSIT_CHANGE), argThat { wct ->
             return@argThat wct.changes.any { (token, change) ->
                 token == taskBinder &&
                         ((change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0)
-            }
-        })
+            }}, eq(taskPositioner))
     }
 
     private fun WindowContainerTransaction.Change.ofBounds(bounds: Rect): Boolean {
@@ -649,14 +677,46 @@
         )
         // Verify task's top bound is set to stable bounds top since dragged outside stable bounds
         // but not in disallowed end bounds area.
-        verify(mockShellTaskOrganizer).applyTransaction(argThat { wct ->
+        verify(mockTransitions).startTransition(
+                eq(WindowManager.TRANSIT_CHANGE), argThat { wct ->
             return@argThat wct.changes.any { (token, change) ->
                 token == taskBinder &&
                         (change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0 &&
                         change.configuration.windowConfiguration.bounds.top ==
                         STABLE_BOUNDS_LANDSCAPE.top
-            }
-        })
+            }}, eq(taskPositioner))
+    }
+
+    @Test
+    fun testDragResize_drag_taskPositionedInValidDragArea() {
+        taskPositioner.onDragPositioningStart(
+            CTRL_TYPE_UNDEFINED, // drag
+            STARTING_BOUNDS.left.toFloat(),
+            STARTING_BOUNDS.top.toFloat()
+        )
+
+        val newX = VALID_DRAG_AREA.left - 500f
+        val newY = VALID_DRAG_AREA.bottom + 500f
+        taskPositioner.onDragPositioningMove(
+            newX,
+            newY
+        )
+        verify(mockTransaction).setPosition(any(), eq(newX), eq(newY))
+
+        taskPositioner.onDragPositioningEnd(
+            newX,
+            newY
+        )
+        verify(mockTransitions).startTransition(
+                eq(WindowManager.TRANSIT_CHANGE), argThat { wct ->
+            return@argThat wct.changes.any { (token, change) ->
+                token == taskBinder &&
+                        (change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0 &&
+                        change.configuration.windowConfiguration.bounds.top ==
+                        VALID_DRAG_AREA.bottom &&
+                        change.configuration.windowConfiguration.bounds.left ==
+                        VALID_DRAG_AREA.left
+            }}, eq(taskPositioner))
     }
 
     @Test
@@ -708,6 +768,59 @@
         verify(mockDisplayLayout, Mockito.times(2)).getStableBounds(any())
     }
 
+    @Test
+    fun testIsResizingOrAnimatingResizeSet() {
+        assertFalse(taskPositioner.isResizingOrAnimating)
+
+        taskPositioner.onDragPositioningStart(
+                CTRL_TYPE_TOP or CTRL_TYPE_RIGHT,
+                STARTING_BOUNDS.left.toFloat(),
+                STARTING_BOUNDS.top.toFloat()
+        )
+
+        taskPositioner.onDragPositioningMove(
+                STARTING_BOUNDS.left.toFloat() - 20,
+                STARTING_BOUNDS.top.toFloat() - 20
+        )
+
+        // isResizingOrAnimating should be set to true after move during a resize
+        assertTrue(taskPositioner.isResizingOrAnimating)
+
+        taskPositioner.onDragPositioningEnd(
+                STARTING_BOUNDS.left.toFloat(),
+                STARTING_BOUNDS.top.toFloat()
+        )
+
+        // isResizingOrAnimating should be not be set till false until after transition animation
+        assertTrue(taskPositioner.isResizingOrAnimating)
+    }
+
+    @Test
+    fun testIsResizingOrAnimatingResizeResetAfterAbortedTransition() {
+        performDrag(STARTING_BOUNDS.left.toFloat(),
+                STARTING_BOUNDS.top.toFloat(), STARTING_BOUNDS.left.toFloat() - 20,
+                STARTING_BOUNDS.top.toFloat() - 20, CTRL_TYPE_TOP or CTRL_TYPE_RIGHT)
+
+        taskPositioner.onTransitionConsumed(mockTransitionBinder, true /* aborted */,
+                mockTransaction)
+
+        // isResizingOrAnimating should be set to false until after transition successfully consumed
+        assertFalse(taskPositioner.isResizingOrAnimating)
+    }
+
+    @Test
+    fun testIsResizingOrAnimatingResizeResetAfterNonAbortedTransition() {
+        performDrag(STARTING_BOUNDS.left.toFloat(),
+                STARTING_BOUNDS.top.toFloat(), STARTING_BOUNDS.left.toFloat() - 20,
+                STARTING_BOUNDS.top.toFloat() - 20, CTRL_TYPE_TOP or CTRL_TYPE_RIGHT)
+
+        taskPositioner.onTransitionConsumed(mockTransitionBinder, false /* aborted */,
+                mockTransaction)
+
+        // isResizingOrAnimating should be set to false until after transition successfully consumed
+        assertFalse(taskPositioner.isResizingOrAnimating)
+    }
+
     private fun performDrag(
         startX: Float,
         startY: Float,
@@ -761,5 +874,11 @@
             DISPLAY_BOUNDS.bottom,
             DISPLAY_BOUNDS.right - NAVBAR_HEIGHT
         )
+        private val VALID_DRAG_AREA = Rect(
+            DISPLAY_BOUNDS.left - 100,
+            STABLE_BOUNDS_LANDSCAPE.top,
+            DISPLAY_BOUNDS.right - 100,
+            DISPLAY_BOUNDS.bottom - 100
+        )
     }
 }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositionerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositionerTest.kt
index a70ebf1..0841210 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositionerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositionerTest.kt
@@ -26,6 +26,7 @@
 import android.view.Surface.ROTATION_90
 import android.view.SurfaceControl
 import android.view.WindowManager.TRANSIT_CHANGE
+import android.window.TransitionInfo
 import android.window.WindowContainerToken
 import androidx.test.filters.SmallTest
 import com.android.wm.shell.ShellTaskOrganizer
@@ -33,10 +34,12 @@
 import com.android.wm.shell.common.DisplayController
 import com.android.wm.shell.common.DisplayLayout
 import com.android.wm.shell.transition.Transitions
+import com.android.wm.shell.transition.Transitions.TransitionFinishCallback
 import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_BOTTOM
 import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_RIGHT
 import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_TOP
 import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_UNDEFINED
+import junit.framework.Assert
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -85,6 +88,12 @@
     @Mock
     private lateinit var mockTransaction: SurfaceControl.Transaction
     @Mock
+    private lateinit var mockTransitionBinder: IBinder
+    @Mock
+    private lateinit var mockTransitionInfo: TransitionInfo
+    @Mock
+    private lateinit var mockFinishCallback: TransitionFinishCallback
+    @Mock
     private lateinit var mockTransitions: Transitions
 
     private lateinit var taskPositioner: VeiledResizeTaskPositioner
@@ -118,6 +127,7 @@
             configuration.windowConfiguration.setBounds(STARTING_BOUNDS)
             configuration.windowConfiguration.displayRotation = ROTATION_90
         }
+        `when`(mockDesktopWindowDecoration.calculateValidDragArea()).thenReturn(VALID_DRAG_AREA)
         mockDesktopWindowDecoration.mDisplay = mockDisplay
         whenever(mockDisplay.displayId).thenAnswer { DISPLAY_ID }
 
@@ -187,13 +197,12 @@
 
         verify(mockDesktopWindowDecoration, never()).createResizeVeil()
         verify(mockDesktopWindowDecoration, never()).hideResizeVeil()
-        verify(mockShellTaskOrganizer).applyTransaction(argThat { wct ->
+        verify(mockTransitions).startTransition(eq(TRANSIT_CHANGE), argThat { wct ->
             return@argThat wct.changes.any { (token, change) ->
                 token == taskBinder &&
                         (change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0 &&
-                        change.configuration.windowConfiguration.bounds == rectAfterEnd
-            }
-        })
+                        change.configuration.windowConfiguration.bounds == rectAfterEnd }},
+                eq(taskPositioner))
     }
 
     @Test
@@ -368,14 +377,44 @@
         )
         // Verify task's top bound is set to stable bounds top since dragged outside stable bounds
         // but not in disallowed end bounds area.
-        verify(mockShellTaskOrganizer).applyTransaction(argThat { wct ->
+        verify(mockTransitions).startTransition(eq(TRANSIT_CHANGE), argThat { wct ->
             return@argThat wct.changes.any { (token, change) ->
                 token == taskBinder &&
                         (change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0 &&
                         change.configuration.windowConfiguration.bounds.top ==
-                        STABLE_BOUNDS_LANDSCAPE.top
-            }
-        })
+                        STABLE_BOUNDS_LANDSCAPE.top }},
+                eq(taskPositioner))
+    }
+
+    @Test
+    fun testDragResize_drag_taskPositionedInValidDragArea() {
+        taskPositioner.onDragPositioningStart(
+            CTRL_TYPE_UNDEFINED, // drag
+            STARTING_BOUNDS.left.toFloat(),
+            STARTING_BOUNDS.top.toFloat()
+        )
+
+        val newX = VALID_DRAG_AREA.left - 500f
+        val newY = VALID_DRAG_AREA.bottom + 500f
+        taskPositioner.onDragPositioningMove(
+            newX,
+            newY
+        )
+        verify(mockTransaction).setPosition(any(), eq(newX), eq(newY))
+
+        taskPositioner.onDragPositioningEnd(
+            newX,
+            newY
+        )
+        verify(mockTransitions).startTransition(eq(TRANSIT_CHANGE), argThat { wct ->
+            return@argThat wct.changes.any { (token, change) ->
+                token == taskBinder &&
+                        (change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0 &&
+                        change.configuration.windowConfiguration.bounds.top ==
+                        VALID_DRAG_AREA.bottom &&
+                        change.configuration.windowConfiguration.bounds.left ==
+                        VALID_DRAG_AREA.left }},
+                eq(taskPositioner))
     }
 
     @Test
@@ -423,6 +462,47 @@
         verify(mockDisplayLayout, times(2)).getStableBounds(any())
     }
 
+    @Test
+    fun testIsResizingOrAnimatingResizeSet() {
+        Assert.assertFalse(taskPositioner.isResizingOrAnimating)
+
+        taskPositioner.onDragPositioningStart(
+                CTRL_TYPE_TOP or CTRL_TYPE_RIGHT,
+                STARTING_BOUNDS.left.toFloat(),
+                STARTING_BOUNDS.top.toFloat()
+        )
+
+        taskPositioner.onDragPositioningMove(
+                STARTING_BOUNDS.left.toFloat() - 20,
+                STARTING_BOUNDS.top.toFloat() - 20
+        )
+
+        // isResizingOrAnimating should be set to true after move during a resize
+        Assert.assertTrue(taskPositioner.isResizingOrAnimating)
+
+        taskPositioner.onDragPositioningEnd(
+                STARTING_BOUNDS.left.toFloat(),
+                STARTING_BOUNDS.top.toFloat()
+        )
+
+        // isResizingOrAnimating should be not be set till false until after transition animation
+        Assert.assertTrue(taskPositioner.isResizingOrAnimating)
+    }
+
+    @Test
+    fun testIsResizingOrAnimatingResizeResetAfterStartAnimation() {
+        performDrag(
+                STARTING_BOUNDS.left.toFloat(), STARTING_BOUNDS.top.toFloat(),
+                STARTING_BOUNDS.left.toFloat() - 20, STARTING_BOUNDS.top.toFloat() - 20,
+                CTRL_TYPE_TOP or CTRL_TYPE_RIGHT)
+
+        taskPositioner.startAnimation(mockTransitionBinder, mockTransitionInfo, mockTransaction,
+                mockTransaction, mockFinishCallback)
+
+        // isResizingOrAnimating should be set to false until after transition successfully consumed
+        Assert.assertFalse(taskPositioner.isResizingOrAnimating)
+    }
+
     private fun performDrag(
         startX: Float,
         startY: Float,
@@ -470,5 +550,11 @@
             DISPLAY_BOUNDS.bottom,
             DISPLAY_BOUNDS.right - NAVBAR_HEIGHT
         )
+        private val VALID_DRAG_AREA = Rect(
+            DISPLAY_BOUNDS.left - 100,
+            STABLE_BOUNDS_LANDSCAPE.top,
+            DISPLAY_BOUNDS.right - 100,
+            DISPLAY_BOUNDS.bottom - 100
+        )
     }
 }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
index 8e42f74..7b53f70 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
@@ -32,6 +32,7 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNull;
+import static org.mockito.ArgumentMatchers.anyFloat;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.argThat;
@@ -261,11 +262,6 @@
                     eq(new Rect(100, 300, 400, 364)));
         }
 
-        verify(mMockSurfaceControlFinishT)
-                .setPosition(mMockTaskSurface, TASK_POSITION_IN_PARENT.x,
-                        TASK_POSITION_IN_PARENT.y);
-        verify(mMockSurfaceControlFinishT)
-                .setWindowCrop(mMockTaskSurface, 300, 100);
         verify(mMockSurfaceControlStartT).setCornerRadius(mMockTaskSurface, CORNER_RADIUS);
         verify(mMockSurfaceControlFinishT).setCornerRadius(mMockTaskSurface, CORNER_RADIUS);
         verify(mMockSurfaceControlStartT)
@@ -642,6 +638,66 @@
                 eq(0) /* index */, eq(mandatorySystemGestures()));
     }
 
+    @Test
+    public void testTaskPositionAndCropNotSetWhenFalse() {
+        final Display defaultDisplay = mock(Display.class);
+        doReturn(defaultDisplay).when(mMockDisplayController)
+                .getDisplay(Display.DEFAULT_DISPLAY);
+
+        final ActivityManager.RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder()
+                .setDisplayId(Display.DEFAULT_DISPLAY)
+                .setBounds(TASK_BOUNDS)
+                .setPositionInParent(TASK_POSITION_IN_PARENT.x, TASK_POSITION_IN_PARENT.y)
+                .setVisible(true)
+                .setWindowingMode(WINDOWING_MODE_FREEFORM)
+                .build();
+        taskInfo.isFocused = true;
+        // Density is 2. Shadow radius is 10px. Caption height is 64px.
+        taskInfo.configuration.densityDpi = DisplayMetrics.DENSITY_DEFAULT * 2;
+        final TestWindowDecoration windowDecor = createWindowDecoration(taskInfo);
+
+
+        mRelayoutParams.mSetTaskPositionAndCrop = false;
+        windowDecor.relayout(taskInfo);
+
+        verify(mMockSurfaceControlStartT, never()).setWindowCrop(
+                eq(mMockTaskSurface), anyInt(), anyInt());
+        verify(mMockSurfaceControlFinishT, never()).setPosition(
+                eq(mMockTaskSurface), anyFloat(), anyFloat());
+        verify(mMockSurfaceControlFinishT, never()).setWindowCrop(
+                eq(mMockTaskSurface), anyInt(), anyInt());
+    }
+
+    @Test
+    public void testTaskPositionAndCropSetWhenSetTrue() {
+        final Display defaultDisplay = mock(Display.class);
+        doReturn(defaultDisplay).when(mMockDisplayController)
+                .getDisplay(Display.DEFAULT_DISPLAY);
+
+        final ActivityManager.RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder()
+                .setDisplayId(Display.DEFAULT_DISPLAY)
+                .setBounds(TASK_BOUNDS)
+                .setPositionInParent(TASK_POSITION_IN_PARENT.x, TASK_POSITION_IN_PARENT.y)
+                .setVisible(true)
+                .setWindowingMode(WINDOWING_MODE_FREEFORM)
+                .build();
+        taskInfo.isFocused = true;
+        // Density is 2. Shadow radius is 10px. Caption height is 64px.
+        taskInfo.configuration.densityDpi = DisplayMetrics.DENSITY_DEFAULT * 2;
+        final TestWindowDecoration windowDecor = createWindowDecoration(taskInfo);
+
+        mRelayoutParams.mSetTaskPositionAndCrop = true;
+        windowDecor.relayout(taskInfo);
+
+        verify(mMockSurfaceControlStartT).setWindowCrop(
+                eq(mMockTaskSurface), anyInt(), anyInt());
+        verify(mMockSurfaceControlFinishT).setPosition(
+                eq(mMockTaskSurface), anyFloat(), anyFloat());
+        verify(mMockSurfaceControlFinishT).setWindowCrop(
+                eq(mMockTaskSurface), anyInt(), anyInt());
+    }
+
+
     private TestWindowDecoration createWindowDecoration(ActivityManager.RunningTaskInfo taskInfo) {
         return new TestWindowDecoration(mContext, mMockDisplayController, mMockShellTaskOrganizer,
                 taskInfo, mMockTaskSurface, mWindowConfiguration,
@@ -702,6 +758,11 @@
             relayout(taskInfo, false /* applyStartTransactionOnDraw */);
         }
 
+        @Override
+        Rect calculateValidDragArea() {
+            return null;
+        }
+
         void relayout(ActivityManager.RunningTaskInfo taskInfo,
                 boolean applyStartTransactionOnDraw) {
             mRelayoutParams.mApplyStartTransactionOnDraw = applyStartTransactionOnDraw;
@@ -711,15 +772,13 @@
 
         private WindowDecoration.AdditionalWindow addTestWindow() {
             final Resources resources = mDecorWindowContext.getResources();
-            int x = mRelayoutParams.mCaptionX;
-            int y = mRelayoutParams.mCaptionY;
             int width = loadDimensionPixelSize(resources, mCaptionMenuWidthId);
             int height = loadDimensionPixelSize(resources, mRelayoutParams.mCaptionHeightId);
             String name = "Test Window";
             WindowDecoration.AdditionalWindow additionalWindow =
                     addWindow(R.layout.desktop_mode_window_decor_handle_menu, name,
-                            mMockSurfaceControlAddWindowT, mMockSurfaceSyncGroup, x, y,
-                            width, height);
+                            mMockSurfaceControlAddWindowT, mMockSurfaceSyncGroup, 0 /* x */,
+                            0 /* y */, width, height);
             return additionalWindow;
         }
     }
diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp
index 2f28363..77800a3 100644
--- a/libs/androidfw/Android.bp
+++ b/libs/androidfw/Android.bp
@@ -31,6 +31,12 @@
     ],
 }
 
+cc_aconfig_library {
+    name: "backup_flags_cc_lib",
+    host_supported: true,
+    aconfig_declarations: "backup_flags",
+}
+
 cc_defaults {
     name: "libandroidfw_defaults",
     cpp_std: "gnu++2b",
@@ -115,7 +121,10 @@
                 "libutils",
                 "libz",
             ],
-            static_libs: ["libziparchive_for_incfs"],
+            static_libs: [
+                "libziparchive_for_incfs",
+                "backup_flags_cc_lib",
+            ],
             static: {
                 enabled: false,
             },
diff --git a/libs/androidfw/BackupHelpers.cpp b/libs/androidfw/BackupHelpers.cpp
index 1a6a952..a1e7c2f 100644
--- a/libs/androidfw/BackupHelpers.cpp
+++ b/libs/androidfw/BackupHelpers.cpp
@@ -36,6 +36,9 @@
 #include <utils/KeyedVector.h>
 #include <utils/String8.h>
 
+#include <com_android_server_backup.h>
+namespace backup_flags = com::android::server::backup;
+
 namespace android {
 
 #define MAGIC0 0x70616e53 // Snap
@@ -214,7 +217,7 @@
 {
     LOGP("write_update_file %s (%s) : mode 0%o\n", realFilename, key.c_str(), mode);
 
-    const int bufsize = 4*1024;
+    const int bufsize = backup_flags::enable_max_size_writes_to_pipes() ? (64*1024) : (4*1024);
     int err;
     int amt;
     int fileSize;
@@ -550,7 +553,8 @@
     }
 
     // read/write up to this much at a time.
-    const size_t BUFSIZE = 32 * 1024;
+    const size_t BUFSIZE = backup_flags::enable_max_size_writes_to_pipes() ? (64*1024) : (32*1024);
+
     char* buf = (char *)calloc(1,BUFSIZE);
     const size_t PAXHEADER_OFFSET = 512;
     const size_t PAXHEADER_SIZE = 512;
@@ -726,7 +730,7 @@
 
 
 
-#define RESTORE_BUF_SIZE (8*1024)
+const size_t RESTORE_BUF_SIZE = backup_flags::enable_max_size_writes_to_pipes() ? 64*1024 : 8*1024;
 
 RestoreHelperBase::RestoreHelperBase()
 {
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 4741170..eebf8aa 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -38,6 +38,7 @@
 
 cc_aconfig_library {
     name: "hwui_flags_cc_lib",
+    host_supported: true,
     aconfig_declarations: "hwui_flags",
 }
 
@@ -109,12 +110,15 @@
         "libbase",
         "libharfbuzz_ng",
         "libminikin",
+        "server_configurable_flags",
     ],
 
     static_libs: [
         "libui-types",
     ],
 
+    whole_static_libs: ["hwui_flags_cc_lib"],
+
     target: {
         android: {
             shared_libs: [
@@ -146,7 +150,6 @@
                 "libstatspull_lazy",
                 "libstatssocket_lazy",
                 "libtonemap",
-                "hwui_flags_cc_lib",
             ],
         },
         host: {
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index 6c3172a..d58c872 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -56,6 +56,7 @@
 
 bool Properties::debugLayersUpdates = false;
 bool Properties::debugOverdraw = false;
+bool Properties::debugTraceGpuResourceCategories = false;
 bool Properties::showDirtyRegions = false;
 bool Properties::skipEmptyFrames = true;
 bool Properties::useBufferAge = true;
@@ -151,10 +152,12 @@
 
     skpCaptureEnabled = debuggingEnabled && base::GetBoolProperty(PROPERTY_CAPTURE_SKP_ENABLED, false);
 
-    SkAndroidFrameworkTraceUtil::setEnableTracing(
-            base::GetBoolProperty(PROPERTY_SKIA_TRACING_ENABLED, false));
+    bool skiaBroadTracing = base::GetBoolProperty(PROPERTY_SKIA_TRACING_ENABLED, false);
+    SkAndroidFrameworkTraceUtil::setEnableTracing(skiaBroadTracing);
     SkAndroidFrameworkTraceUtil::setUsePerfettoTrackEvents(
             base::GetBoolProperty(PROPERTY_SKIA_USE_PERFETTO_TRACK_EVENTS, false));
+    debugTraceGpuResourceCategories =
+            base::GetBoolProperty(PROPERTY_TRACE_GPU_RESOURCES, skiaBroadTracing);
 
     runningInEmulator = base::GetBoolProperty(PROPERTY_IS_EMULATOR, false);
 
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index bca57e9..b956fac 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -143,6 +143,15 @@
 #define PROPERTY_CAPTURE_SKP_ENABLED "debug.hwui.capture_skp_enabled"
 
 /**
+ * Might split Skia's GPU resource utilization into separate tracing tracks (slow).
+ *
+ * Aggregate total and purgeable numbers will still be reported under a "misc" track when this is
+ * disabled, they just won't be split into distinct categories. Results may vary depending on GPU
+ * backend/API, and the category mappings defined in ATraceMemoryDump's hardcoded sResourceMap.
+ */
+#define PROPERTY_TRACE_GPU_RESOURCES "debug.hwui.trace_gpu_resources"
+
+/**
  * Allows broad recording of Skia drawing commands.
  *
  * If disabled, a very minimal set of trace events *may* be recorded.
@@ -254,6 +263,7 @@
 
     static bool debugLayersUpdates;
     static bool debugOverdraw;
+    static bool debugTraceGpuResourceCategories;
     static bool showDirtyRegions;
     // TODO: Remove after stabilization period
     static bool skipEmptyFrames;
diff --git a/libs/hwui/aconfig/hwui_flags.aconfig b/libs/hwui/aconfig/hwui_flags.aconfig
index ca11975..72ddecc 100644
--- a/libs/hwui/aconfig/hwui_flags.aconfig
+++ b/libs/hwui/aconfig/hwui_flags.aconfig
@@ -15,6 +15,20 @@
 }
 
 flag {
+  name: "high_contrast_text_luminance"
+  namespace: "accessibility"
+  description: "Use luminance to determine how to make text more high contrast, instead of RGB heuristic"
+  bug: "186567103"
+}
+
+flag {
+  name: "high_contrast_text_small_text_rect"
+  namespace: "accessibility"
+  description: "Draw a solid rectangle background behind text instead of a stroke outline"
+  bug: "186567103"
+}
+
+flag {
   name: "hdr_10bit_plus"
   namespace: "core_graphics"
   description: "Use 10101010 and FP16 formats for HDR-UI when available"
diff --git a/libs/hwui/hwui/Canvas.cpp b/libs/hwui/hwui/Canvas.cpp
index 80b6c03..e9f4b81c 100644
--- a/libs/hwui/hwui/Canvas.cpp
+++ b/libs/hwui/hwui/Canvas.cpp
@@ -18,6 +18,7 @@
 
 #include <SkFontMetrics.h>
 #include <SkRRect.h>
+#include <minikin/MinikinRect.h>
 
 #include "FeatureFlags.h"
 #include "MinikinUtils.h"
@@ -107,7 +108,13 @@
     // care of all alignment.
     paint.setTextAlign(Paint::kLeft_Align);
 
-    DrawTextFunctor f(layout, this, paint, x, y, layout.getAdvance());
+    minikin::MinikinRect bounds;
+    // We only need the bounds to draw a rectangular background in high contrast mode. Let's save
+    // the cycles otherwise.
+    if (flags::high_contrast_text_small_text_rect() && isHighContrastText()) {
+        MinikinUtils::getBounds(&paint, bidiFlags, typeface, text, textSize, &bounds);
+    }
+    DrawTextFunctor f(layout, this, paint, x, y, layout.getAdvance(), bounds);
     MinikinUtils::forFontRun(layout, &paint, f);
 
     if (text_feature::fix_double_underline()) {
diff --git a/libs/hwui/hwui/DrawTextFunctor.h b/libs/hwui/hwui/DrawTextFunctor.h
index 2e6e976..ba65439 100644
--- a/libs/hwui/hwui/DrawTextFunctor.h
+++ b/libs/hwui/hwui/DrawTextFunctor.h
@@ -16,7 +16,9 @@
 
 #include <SkFontMetrics.h>
 #include <SkRRect.h>
+#include <com_android_graphics_hwui_flags.h>
 
+#include "../utils/Color.h"
 #include "Canvas.h"
 #include "FeatureFlags.h"
 #include "MinikinUtils.h"
@@ -27,8 +29,12 @@
 #include "hwui/PaintFilter.h"
 #include "pipeline/skia/SkiaRecordingCanvas.h"
 
+namespace flags = com::android::graphics::hwui::flags;
+
 namespace android {
 
+inline constexpr int kHighContrastTextBorderWidth = 4;
+
 static inline void drawStroke(SkScalar left, SkScalar right, SkScalar top, SkScalar thickness,
                               const Paint& paint, Canvas* canvas) {
     const SkScalar strokeWidth = fmax(thickness, 1.0f);
@@ -41,15 +47,26 @@
     paint->setShader(nullptr);
     paint->setColorFilter(nullptr);
     paint->setLooper(nullptr);
-    paint->setStrokeWidth(4 + 0.04 * paint->getSkFont().getSize());
+    paint->setStrokeWidth(kHighContrastTextBorderWidth + 0.04 * paint->getSkFont().getSize());
     paint->setStrokeJoin(SkPaint::kRound_Join);
     paint->setLooper(nullptr);
 }
 
 class DrawTextFunctor {
 public:
+    /**
+     * Creates a Functor to draw the given text layout.
+     *
+     * @param layout
+     * @param canvas
+     * @param paint
+     * @param x
+     * @param y
+     * @param totalAdvance
+     * @param bounds bounds of the text. Only required if high contrast text mode is enabled.
+     */
     DrawTextFunctor(const minikin::Layout& layout, Canvas* canvas, const Paint& paint, float x,
-                    float y, float totalAdvance)
+                    float y, float totalAdvance, const minikin::MinikinRect& bounds)
             : layout(layout)
             , canvas(canvas)
             , paint(paint)
@@ -57,7 +74,8 @@
             , y(y)
             , totalAdvance(totalAdvance)
             , underlinePosition(0)
-            , underlineThickness(0) {}
+            , underlineThickness(0)
+            , bounds(bounds) {}
 
     void operator()(size_t start, size_t end) {
         auto glyphFunc = [&](uint16_t* text, float* positions) {
@@ -73,15 +91,30 @@
         if (CC_UNLIKELY(canvas->isHighContrastText() && paint.getAlpha() != 0)) {
             // high contrast draw path
             int color = paint.getColor();
-            int channelSum = SkColorGetR(color) + SkColorGetG(color) + SkColorGetB(color);
-            bool darken = channelSum < (128 * 3);
+            bool darken;
+            if (flags::high_contrast_text_luminance()) {
+                uirenderer::Lab lab = uirenderer::sRGBToLab(color);
+                darken = lab.L <= 50;
+            } else {
+                int channelSum = SkColorGetR(color) + SkColorGetG(color) + SkColorGetB(color);
+                darken = channelSum < (128 * 3);
+            }
 
             // outline
             gDrawTextBlobMode = DrawTextBlobMode::HctOutline;
             Paint outlinePaint(paint);
             simplifyPaint(darken ? SK_ColorWHITE : SK_ColorBLACK, &outlinePaint);
             outlinePaint.setStyle(SkPaint::kStrokeAndFill_Style);
-            canvas->drawGlyphs(glyphFunc, glyphCount, outlinePaint, x, y, totalAdvance);
+            if (flags::high_contrast_text_small_text_rect()) {
+                auto bgBounds(bounds);
+                auto padding = kHighContrastTextBorderWidth + 0.1f * paint.getSkFont().getSize();
+                bgBounds.offset(x, y);
+                canvas->drawRect(bgBounds.mLeft - padding, bgBounds.mTop - padding,
+                                 bgBounds.mRight + padding, bgBounds.mBottom + padding,
+                                 outlinePaint);
+            } else {
+                canvas->drawGlyphs(glyphFunc, glyphCount, outlinePaint, x, y, totalAdvance);
+            }
 
             // inner
             gDrawTextBlobMode = DrawTextBlobMode::HctInner;
@@ -136,6 +169,7 @@
     float totalAdvance;
     float underlinePosition;
     float underlineThickness;
+    const minikin::MinikinRect& bounds;
 };
 
 }  // namespace android
diff --git a/libs/hwui/hwui/MinikinUtils.cpp b/libs/hwui/hwui/MinikinUtils.cpp
index 7552b56d..833069f 100644
--- a/libs/hwui/hwui/MinikinUtils.cpp
+++ b/libs/hwui/hwui/MinikinUtils.cpp
@@ -96,7 +96,7 @@
 float MinikinUtils::measureText(const Paint* paint, minikin::Bidi bidiFlags,
                                 const Typeface* typeface, const uint16_t* buf, size_t start,
                                 size_t count, size_t bufSize, float* advances,
-                                minikin::MinikinRect* bounds) {
+                                minikin::MinikinRect* bounds, uint32_t* clusterCount) {
     minikin::MinikinPaint minikinPaint = prepareMinikinPaint(paint, typeface);
     const minikin::U16StringPiece textBuf(buf, bufSize);
     const minikin::Range range(start, start + count);
@@ -104,7 +104,7 @@
     const minikin::EndHyphenEdit endHyphen = paint->getEndHyphenEdit();
 
     return minikin::Layout::measureText(textBuf, range, bidiFlags, minikinPaint, startHyphen,
-                                        endHyphen, advances, bounds);
+                                        endHyphen, advances, bounds, clusterCount);
 }
 
 minikin::MinikinExtent MinikinUtils::getFontExtent(const Paint* paint, minikin::Bidi bidiFlags,
diff --git a/libs/hwui/hwui/MinikinUtils.h b/libs/hwui/hwui/MinikinUtils.h
index 61bc881..f8574ee 100644
--- a/libs/hwui/hwui/MinikinUtils.h
+++ b/libs/hwui/hwui/MinikinUtils.h
@@ -53,7 +53,7 @@
 
     static float measureText(const Paint* paint, minikin::Bidi bidiFlags, const Typeface* typeface,
                              const uint16_t* buf, size_t start, size_t count, size_t bufSize,
-                             float* advances, minikin::MinikinRect* bounds);
+                             float* advances, minikin::MinikinRect* bounds, uint32_t* clusterCount);
 
     static minikin::MinikinExtent getFontExtent(const Paint* paint, minikin::Bidi bidiFlags,
                                                 const Typeface* typeface, const uint16_t* buf,
diff --git a/libs/hwui/jni/Graphics.cpp b/libs/hwui/jni/Graphics.cpp
index 7cc4866..8315c4c 100644
--- a/libs/hwui/jni/Graphics.cpp
+++ b/libs/hwui/jni/Graphics.cpp
@@ -247,6 +247,9 @@
 static jfieldID gFontMetricsInt_bottom;
 static jfieldID gFontMetricsInt_leading;
 
+static jclass gRunInfo_class;
+static jfieldID gRunInfo_clusterCount;
+
 ///////////////////////////////////////////////////////////////////////////////
 
 void GraphicsJNI::get_jrect(JNIEnv* env, jobject obj, int* L, int* T, int* R, int* B)
@@ -511,6 +514,10 @@
     return descent - ascent + leading;
 }
 
+void GraphicsJNI::set_cluster_count_to_run_info(JNIEnv* env, jobject runInfo, jint clusterCount) {
+    env->SetIntField(runInfo, gRunInfo_clusterCount, clusterCount);
+}
+
 ///////////////////////////////////////////////////////////////////////////////////////////
 
 jobject GraphicsJNI::createBitmapRegionDecoder(JNIEnv* env, BitmapRegionDecoderWrapper* bitmap) {
@@ -834,5 +841,10 @@
     gFontMetricsInt_bottom = GetFieldIDOrDie(env, gFontMetricsInt_class, "bottom", "I");
     gFontMetricsInt_leading = GetFieldIDOrDie(env, gFontMetricsInt_class, "leading", "I");
 
+    gRunInfo_class = FindClassOrDie(env, "android/graphics/Paint$RunInfo");
+    gRunInfo_class = MakeGlobalRefOrDie(env, gRunInfo_class);
+
+    gRunInfo_clusterCount = GetFieldIDOrDie(env, gRunInfo_class, "mClusterCount", "I");
+
     return 0;
 }
diff --git a/libs/hwui/jni/GraphicsJNI.h b/libs/hwui/jni/GraphicsJNI.h
index b9fff36..b0a1074 100644
--- a/libs/hwui/jni/GraphicsJNI.h
+++ b/libs/hwui/jni/GraphicsJNI.h
@@ -77,6 +77,8 @@
     static SkRect* jrect_to_rect(JNIEnv*, jobject jrect, SkRect*);
     static void rect_to_jrectf(const SkRect&, JNIEnv*, jobject jrectf);
 
+    static void set_cluster_count_to_run_info(JNIEnv* env, jobject runInfo, jint clusterCount);
+
     static void set_jpoint(JNIEnv*, jobject jrect, int x, int y);
 
     static SkIPoint* jpoint_to_ipoint(JNIEnv*, jobject jpoint, SkIPoint* point);
diff --git a/libs/hwui/jni/Paint.cpp b/libs/hwui/jni/Paint.cpp
index d84b73d..58d9d8b 100644
--- a/libs/hwui/jni/Paint.cpp
+++ b/libs/hwui/jni/Paint.cpp
@@ -114,7 +114,7 @@
 
         std::unique_ptr<float[]> advancesArray(new float[count]);
         MinikinUtils::measureText(&paint, static_cast<minikin::Bidi>(bidiFlags), typeface, text, 0,
-                                  count, count, advancesArray.get(), nullptr);
+                                  count, count, advancesArray.get(), nullptr, nullptr);
 
         for (int i = 0; i < count; i++) {
             // traverse in the given direction
@@ -206,7 +206,7 @@
         }
         const float advance = MinikinUtils::measureText(
                 paint, static_cast<minikin::Bidi>(bidiFlags), typeface, text, start, count,
-                contextCount, advancesArray.get(), nullptr);
+                contextCount, advancesArray.get(), nullptr, nullptr);
         if (advances) {
             env->SetFloatArrayRegion(advances, advancesIndex, count, advancesArray.get());
         }
@@ -244,7 +244,7 @@
         minikin::Bidi bidiFlags = dir == 1 ? minikin::Bidi::FORCE_RTL : minikin::Bidi::FORCE_LTR;
         std::unique_ptr<float[]> advancesArray(new float[count]);
         MinikinUtils::measureText(paint, bidiFlags, typeface, text, start, count, start + count,
-                                  advancesArray.get(), nullptr);
+                                  advancesArray.get(), nullptr, nullptr);
         size_t result = minikin::GraphemeBreak::getTextRunCursor(advancesArray.get(), text,
                 start, count, offset, moveOpt);
         return static_cast<jint>(result);
@@ -508,7 +508,7 @@
     static jfloat doRunAdvance(JNIEnv* env, const Paint* paint, const Typeface* typeface,
                                const jchar buf[], jint start, jint count, jint bufSize,
                                jboolean isRtl, jint offset, jfloatArray advances,
-                               jint advancesIndex, SkRect* drawBounds) {
+                               jint advancesIndex, SkRect* drawBounds, uint32_t* clusterCount) {
         if (advances) {
             size_t advancesLength = env->GetArrayLength(advances);
             if ((size_t)(count + advancesIndex) > advancesLength) {
@@ -519,9 +519,9 @@
         minikin::Bidi bidiFlags = isRtl ? minikin::Bidi::FORCE_RTL : minikin::Bidi::FORCE_LTR;
         minikin::MinikinRect bounds;
         if (offset == start + count && advances == nullptr) {
-            float result =
-                    MinikinUtils::measureText(paint, bidiFlags, typeface, buf, start, count,
-                                              bufSize, nullptr, drawBounds ? &bounds : nullptr);
+            float result = MinikinUtils::measureText(paint, bidiFlags, typeface, buf, start, count,
+                                                     bufSize, nullptr,
+                                                     drawBounds ? &bounds : nullptr, clusterCount);
             if (drawBounds) {
                 copyMinikinRectToSkRect(bounds, drawBounds);
             }
@@ -529,7 +529,8 @@
         }
         std::unique_ptr<float[]> advancesArray(new float[count]);
         MinikinUtils::measureText(paint, bidiFlags, typeface, buf, start, count, bufSize,
-                                  advancesArray.get(), drawBounds ? &bounds : nullptr);
+                                  advancesArray.get(), drawBounds ? &bounds : nullptr,
+                                  clusterCount);
 
         if (drawBounds) {
             copyMinikinRectToSkRect(bounds, drawBounds);
@@ -549,7 +550,7 @@
         ScopedCharArrayRO textArray(env, text);
         jfloat result = doRunAdvance(env, paint, typeface, textArray.get() + contextStart,
                                      start - contextStart, end - start, contextEnd - contextStart,
-                                     isRtl, offset - contextStart, nullptr, 0, nullptr);
+                                     isRtl, offset - contextStart, nullptr, 0, nullptr, nullptr);
         return result;
     }
 
@@ -558,27 +559,41 @@
                                                         jint contextStart, jint contextEnd,
                                                         jboolean isRtl, jint offset,
                                                         jfloatArray advances, jint advancesIndex,
-                                                        jobject drawBounds) {
+                                                        jobject drawBounds, jobject runInfo) {
         const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
         const Typeface* typeface = paint->getAndroidTypeface();
         ScopedCharArrayRO textArray(env, text);
         SkRect skDrawBounds;
+        uint32_t clusterCount = 0;
         jfloat result = doRunAdvance(env, paint, typeface, textArray.get() + contextStart,
                                      start - contextStart, end - start, contextEnd - contextStart,
                                      isRtl, offset - contextStart, advances, advancesIndex,
-                                     drawBounds ? &skDrawBounds : nullptr);
+                                     drawBounds ? &skDrawBounds : nullptr, &clusterCount);
         if (drawBounds != nullptr) {
             GraphicsJNI::rect_to_jrectf(skDrawBounds, env, drawBounds);
         }
+        if (runInfo) {
+            GraphicsJNI::set_cluster_count_to_run_info(env, runInfo, clusterCount);
+        }
         return result;
     }
 
+    // This method is kept for old Robolectric JNI signature used by SystemUIGoogleRoboRNGTests.
+    static jfloat getRunCharacterAdvance___CIIIIZI_FI_F_ForRobolectric(
+            JNIEnv* env, jclass cls, jlong paintHandle, jcharArray text, jint start, jint end,
+            jint contextStart, jint contextEnd, jboolean isRtl, jint offset, jfloatArray advances,
+            jint advancesIndex, jobject drawBounds) {
+        return getRunCharacterAdvance___CIIIIZI_FI_F(env, cls, paintHandle, text, start, end,
+                                                     contextStart, contextEnd, isRtl, offset,
+                                                     advances, advancesIndex, drawBounds, nullptr);
+    }
+
     static jint doOffsetForAdvance(const Paint* paint, const Typeface* typeface, const jchar buf[],
             jint start, jint count, jint bufSize, jboolean isRtl, jfloat advance) {
         minikin::Bidi bidiFlags = isRtl ? minikin::Bidi::FORCE_RTL : minikin::Bidi::FORCE_LTR;
         std::unique_ptr<float[]> advancesArray(new float[count]);
         MinikinUtils::measureText(paint, bidiFlags, typeface, buf, start, count, bufSize,
-                                  advancesArray.get(), nullptr);
+                                  advancesArray.get(), nullptr, nullptr);
         return minikin::getOffsetForAdvance(advancesArray.get(), buf, start, count, advance);
     }
 
@@ -1145,8 +1160,11 @@
          (void*)PaintGlue::getCharArrayBounds},
         {"nHasGlyph", "(JILjava/lang/String;)Z", (void*)PaintGlue::hasGlyph},
         {"nGetRunAdvance", "(J[CIIIIZI)F", (void*)PaintGlue::getRunAdvance___CIIIIZI_F},
-        {"nGetRunCharacterAdvance", "(J[CIIIIZI[FILandroid/graphics/RectF;)F",
+        {"nGetRunCharacterAdvance",
+         "(J[CIIIIZI[FILandroid/graphics/RectF;Landroid/graphics/Paint$RunInfo;)F",
          (void*)PaintGlue::getRunCharacterAdvance___CIIIIZI_FI_F},
+        {"nGetRunCharacterAdvance", "(J[CIIIIZI[FILandroid/graphics/RectF;)F",
+         (void*)PaintGlue::getRunCharacterAdvance___CIIIIZI_FI_F_ForRobolectric},
         {"nGetOffsetForAdvance", "(J[CIIIIZF)I", (void*)PaintGlue::getOffsetForAdvance___CIIIIZF_I},
         {"nGetFontMetricsIntForText", "(J[CIIIIZLandroid/graphics/Paint$FontMetricsInt;)V",
          (void*)PaintGlue::getFontMetricsIntForText___C},
diff --git a/libs/hwui/pipeline/skia/ATraceMemoryDump.cpp b/libs/hwui/pipeline/skia/ATraceMemoryDump.cpp
index 234f42d..756b937 100644
--- a/libs/hwui/pipeline/skia/ATraceMemoryDump.cpp
+++ b/libs/hwui/pipeline/skia/ATraceMemoryDump.cpp
@@ -20,6 +20,8 @@
 
 #include <cstring>
 
+#include "GrDirectContext.h"
+
 namespace android {
 namespace uirenderer {
 namespace skiapipeline {
@@ -114,8 +116,16 @@
 
 /**
  * logTraces reads from mCurrentValues and logs the counters with ATRACE.
+ *
+ * gpuMemoryIsAlreadyInDump must be true if GrDirectContext::dumpMemoryStatistics(...) was called
+ * with this tracer, false otherwise. Leaving this false allows this function to quickly query total
+ * and purgable GPU memory without the caller having to spend time in
+ * GrDirectContext::dumpMemoryStatistics(...) first, which iterates over every resource in the GPU
+ * cache. This can save significant time, but buckets all GPU memory into a single "misc" track,
+ * which may be a loss of granularity depending on the GPU backend and the categories defined in
+ * sResourceMap.
  */
-void ATraceMemoryDump::logTraces() {
+void ATraceMemoryDump::logTraces(bool gpuMemoryIsAlreadyInDump, GrDirectContext* grContext) {
     // Accumulate data from last dumpName
     recordAndResetCountersIfNeeded("");
     uint64_t hwui_all_frame_memory = 0;
@@ -126,6 +136,20 @@
             ATRACE_INT64((std::string("Purgeable ") + it.first).c_str(), it.second.purgeableMemory);
         }
     }
+
+    if (!gpuMemoryIsAlreadyInDump && grContext) {
+        // Total GPU memory
+        int gpuResourceCount;
+        size_t gpuResourceBytes;
+        grContext->getResourceCacheUsage(&gpuResourceCount, &gpuResourceBytes);
+        hwui_all_frame_memory += (uint64_t)gpuResourceBytes;
+        ATRACE_INT64("HWUI Misc Memory", gpuResourceBytes);
+
+        // Purgable subset of GPU memory
+        size_t purgeableGpuResourceBytes = grContext->getResourceCachePurgeableBytes();
+        ATRACE_INT64("Purgeable HWUI Misc Memory", purgeableGpuResourceBytes);
+    }
+
     ATRACE_INT64("HWUI All Memory", hwui_all_frame_memory);
 }
 
diff --git a/libs/hwui/pipeline/skia/ATraceMemoryDump.h b/libs/hwui/pipeline/skia/ATraceMemoryDump.h
index 4592711..777d1a2 100644
--- a/libs/hwui/pipeline/skia/ATraceMemoryDump.h
+++ b/libs/hwui/pipeline/skia/ATraceMemoryDump.h
@@ -16,6 +16,7 @@
 
 #pragma once
 
+#include <GrDirectContext.h>
 #include <SkString.h>
 #include <SkTraceMemoryDump.h>
 
@@ -50,7 +51,7 @@
 
     void startFrame();
 
-    void logTraces();
+    void logTraces(bool gpuMemoryIsAlreadyInDump, GrDirectContext* grContext);
 
 private:
     std::string mLastDumpName;
diff --git a/libs/hwui/pipeline/skia/DumpOpsCanvas.h b/libs/hwui/pipeline/skia/DumpOpsCanvas.h
index 6a052db..260547c 100644
--- a/libs/hwui/pipeline/skia/DumpOpsCanvas.h
+++ b/libs/hwui/pipeline/skia/DumpOpsCanvas.h
@@ -90,11 +90,6 @@
         mOutput << mIdent << "drawTextBlob" << std::endl;
     }
 
-    void onDrawImage2(const SkImage*, SkScalar dx, SkScalar dy, const SkSamplingOptions&,
-                      const SkPaint*) override {
-        mOutput << mIdent << "drawImage" << std::endl;
-    }
-
     void onDrawImageRect2(const SkImage*, const SkRect&, const SkRect&, const SkSamplingOptions&,
                           const SkPaint*, SrcRectConstraint) override {
         mOutput << mIdent << "drawImageRect" << std::endl;
diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp
index 30d4612..eb4d494 100644
--- a/libs/hwui/renderthread/CacheManager.cpp
+++ b/libs/hwui/renderthread/CacheManager.cpp
@@ -269,13 +269,14 @@
     cancelDestroyContext();
     mFrameCompletions.next() = systemTime(CLOCK_MONOTONIC);
     if (ATRACE_ENABLED()) {
+        ATRACE_NAME("dumpingMemoryStatistics");
         static skiapipeline::ATraceMemoryDump tracer;
         tracer.startFrame();
         SkGraphics::DumpMemoryStatistics(&tracer);
-        if (mGrContext) {
+        if (mGrContext && Properties::debugTraceGpuResourceCategories) {
             mGrContext->dumpMemoryStatistics(&tracer);
         }
-        tracer.logTraces();
+        tracer.logTraces(Properties::debugTraceGpuResourceCategories, mGrContext.get());
     }
 }
 
diff --git a/libs/hwui/tests/common/CallCountingCanvas.h b/libs/hwui/tests/common/CallCountingCanvas.h
index dc36a2e..df5f04f99 100644
--- a/libs/hwui/tests/common/CallCountingCanvas.h
+++ b/libs/hwui/tests/common/CallCountingCanvas.h
@@ -109,12 +109,6 @@
         drawPoints++;
     }
 
-    int drawImageCount = 0;
-    void onDrawImage2(const SkImage* image, SkScalar dx, SkScalar dy, const SkSamplingOptions&,
-                     const SkPaint* paint) override {
-        drawImageCount++;
-    }
-
     int drawImageRectCount = 0;
     void onDrawImageRect2(const SkImage*, const SkRect&, const SkRect&, const SkSamplingOptions&,
                           const SkPaint*, SkCanvas::SrcRectConstraint) override {
diff --git a/libs/hwui/tests/unit/CanvasOpTests.cpp b/libs/hwui/tests/unit/CanvasOpTests.cpp
index 18c5047..4ae76e2 100644
--- a/libs/hwui/tests/unit/CanvasOpTests.cpp
+++ b/libs/hwui/tests/unit/CanvasOpTests.cpp
@@ -492,7 +492,7 @@
     CallCountingCanvas canvas;
     EXPECT_EQ(0, canvas.sumTotalDrawCalls());
     rasterizeCanvasBuffer(buffer, &canvas);
-    EXPECT_EQ(1, canvas.drawImageCount);
+    EXPECT_EQ(1, canvas.drawImageRectCount);
     EXPECT_EQ(1, canvas.sumTotalDrawCalls());
 }
 
diff --git a/libs/hwui/tests/unit/FatalTestCanvas.h b/libs/hwui/tests/unit/FatalTestCanvas.h
index 96a0c61..8b95e0c 100644
--- a/libs/hwui/tests/unit/FatalTestCanvas.h
+++ b/libs/hwui/tests/unit/FatalTestCanvas.h
@@ -69,10 +69,6 @@
     void onDrawPath(const SkPath&, const SkPaint&) {
         ADD_FAILURE() << "onDrawPath not expected in this test";
     }
-    void onDrawImage2(const SkImage*, SkScalar dx, SkScalar dy, const SkSamplingOptions&,
-                      const SkPaint*) {
-        ADD_FAILURE() << "onDrawImage not expected in this test";
-    }
     void onDrawImageRect2(const SkImage*, const SkRect&, const SkRect&, const SkSamplingOptions&,
                           const SkPaint*, SrcRectConstraint) {
         ADD_FAILURE() << "onDrawImageRect not expected in this test";
diff --git a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
index 073a835..ca54087 100644
--- a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
+++ b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
@@ -941,8 +941,9 @@
         void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
             EXPECT_EQ(0, mDrawCounter++);
         }
-        void onDrawImage2(const SkImage*, SkScalar dx, SkScalar dy, const SkSamplingOptions&,
-                          const SkPaint*) override {
+        void onDrawImageRect2(const SkImage*, const SkRect&, const SkRect&,
+                              const SkSamplingOptions&, const SkPaint*,
+                              SrcRectConstraint) override {
             EXPECT_EQ(1, mDrawCounter++);
         }
     };
diff --git a/libs/hwui/tests/unit/SkiaPipelineTests.cpp b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
index 3ded540..785e286 100644
--- a/libs/hwui/tests/unit/SkiaPipelineTests.cpp
+++ b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
@@ -303,8 +303,9 @@
     class ClippedTestCanvas : public SkCanvas {
     public:
         ClippedTestCanvas() : SkCanvas(CANVAS_WIDTH, CANVAS_HEIGHT) {}
-        void onDrawImage2(const SkImage*, SkScalar dx, SkScalar dy, const SkSamplingOptions&,
-                          const SkPaint*) override {
+        void onDrawImageRect2(const SkImage*, const SkRect&, const SkRect&,
+                              const SkSamplingOptions&, const SkPaint*,
+                              SrcRectConstraint) override {
             EXPECT_EQ(0, mDrawCounter++);
             EXPECT_EQ(SkRect::MakeLTRB(10, 20, 30, 40), TestUtils::getClipBounds(this));
             EXPECT_TRUE(getTotalMatrix().isIdentity());
@@ -338,8 +339,9 @@
     class ClippedTestCanvas : public SkCanvas {
     public:
         ClippedTestCanvas() : SkCanvas(CANVAS_WIDTH, CANVAS_HEIGHT) {}
-        void onDrawImage2(const SkImage*, SkScalar dx, SkScalar dy, const SkSamplingOptions&,
-                          const SkPaint*) override {
+        void onDrawImageRect2(const SkImage*, const SkRect&, const SkRect&,
+                              const SkSamplingOptions&, const SkPaint*,
+                              SrcRectConstraint) override {
             EXPECT_EQ(0, mDrawCounter++);
             // Expect clip to be rotated.
             EXPECT_EQ(SkRect::MakeLTRB(CANVAS_HEIGHT - dirty.fTop - dirty.height(), dirty.fLeft,
diff --git a/libs/hwui/tests/unit/UnderlineTest.cpp b/libs/hwui/tests/unit/UnderlineTest.cpp
index c70a304..9911bfa 100644
--- a/libs/hwui/tests/unit/UnderlineTest.cpp
+++ b/libs/hwui/tests/unit/UnderlineTest.cpp
@@ -103,8 +103,9 @@
     // Create minikin::Layout
     std::unique_ptr<Typeface> typeface(makeTypeface());
     minikin::Layout layout = doLayout(text, *paint, typeface.get());
+    minikin::MinikinRect bounds;
 
-    DrawTextFunctor f(layout, &canvas, *paint, 0, 0, layout.getAdvance());
+    DrawTextFunctor f(layout, &canvas, *paint, 0, 0, layout.getAdvance(), bounds);
     MinikinUtils::forFontRun(layout, paint, f);
     return f;
 }
diff --git a/libs/hwui/utils/ForceDark.h b/libs/hwui/utils/ForceDark.h
index 28538c4b..ecfe41f 100644
--- a/libs/hwui/utils/ForceDark.h
+++ b/libs/hwui/utils/ForceDark.h
@@ -17,6 +17,8 @@
 #ifndef FORCEDARKUTILS_H
 #define FORCEDARKUTILS_H
 
+#include <stdint.h>
+
 namespace android {
 namespace uirenderer {
 
@@ -26,9 +28,9 @@
  * This should stay in sync with the java @IntDef in
  * frameworks/base/graphics/java/android/graphics/ForceDarkType.java
  */
-enum class ForceDarkType : __uint8_t { NONE = 0, FORCE_DARK = 1, FORCE_INVERT_COLOR_DARK = 2 };
+enum class ForceDarkType : uint8_t { NONE = 0, FORCE_DARK = 1, FORCE_INVERT_COLOR_DARK = 2 };
 
 } /* namespace uirenderer */
 } /* namespace android */
 
-#endif  // FORCEDARKUTILS_H
\ No newline at end of file
+#endif  // FORCEDARKUTILS_H
diff --git a/libs/incident/libincident.map.txt b/libs/incident/libincident.map.txt
index f75ccea..d8650e1 100644
--- a/libs/incident/libincident.map.txt
+++ b/libs/incident/libincident.map.txt
@@ -1,15 +1,15 @@
 LIBINCIDENT {
     global:
-        AIncidentReportArgs_init; # systemapi # introduced=30
-        AIncidentReportArgs_clone; # systemapi # introduced=30
-        AIncidentReportArgs_delete; # systemapi # introduced=30
-        AIncidentReportArgs_setAll; # systemapi # introduced=30
-        AIncidentReportArgs_setPrivacyPolicy; # systemapi # introduced=30
-        AIncidentReportArgs_addSection; # systemapi # introduced=30
-        AIncidentReportArgs_setReceiverPackage; # systemapi # introduced=30
-        AIncidentReportArgs_setReceiverClass; # systemapi # introduced=30
-        AIncidentReportArgs_addHeader; # systemapi # introduced=30
-        AIncidentReportArgs_takeReport; # systemapi # introduced=30
+        AIncidentReportArgs_init; # systemapi introduced=30
+        AIncidentReportArgs_clone; # systemapi introduced=30
+        AIncidentReportArgs_delete; # systemapi introduced=30
+        AIncidentReportArgs_setAll; # systemapi introduced=30
+        AIncidentReportArgs_setPrivacyPolicy; # systemapi introduced=30
+        AIncidentReportArgs_addSection; # systemapi introduced=30
+        AIncidentReportArgs_setReceiverPackage; # systemapi introduced=30
+        AIncidentReportArgs_setReceiverClass; # systemapi introduced=30
+        AIncidentReportArgs_addHeader; # systemapi introduced=30
+        AIncidentReportArgs_takeReport; # systemapi introduced=30
     local:
         *;
 };
diff --git a/lint-baseline.xml b/lint-baseline.xml
index 79b2155..660884a 100644
--- a/lint-baseline.xml
+++ b/lint-baseline.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 7.1.0-dev" type="baseline" client="" dependencies="true" name="" variant="all" version="7.1.0-dev">
+<issues format="6" by="lint 8.4.0-alpha01" type="baseline" client="" dependencies="true" name="" variant="all" version="8.4.0-alpha01">
 
     <issue
         id="NonUserGetterCalled"
@@ -433,7 +433,7 @@
     <issue
         id="NonUserGetterCalled"
         message="`android.provider.Settings.System#getInt()` called from system process. Please call `android.provider.Settings.System#getIntForUser()` instead. "
-        errorLine1="        boolean cap = System.getInt(resolver, System.TEXT_AUTO_CAPS, 1) > 0;"
+        errorLine1="        boolean cap = System.getInt(resolver, System.TEXT_AUTO_CAPS, 1) &gt; 0;"
         errorLine2="                             ~~~~~~">
         <location
             file="frameworks/base/core/java/android/text/method/TextKeyListener.java"
@@ -444,7 +444,7 @@
     <issue
         id="NonUserGetterCalled"
         message="`android.provider.Settings.System#getInt()` called from system process. Please call `android.provider.Settings.System#getIntForUser()` instead. "
-        errorLine1="        boolean text = System.getInt(resolver, System.TEXT_AUTO_REPLACE, 1) > 0;"
+        errorLine1="        boolean text = System.getInt(resolver, System.TEXT_AUTO_REPLACE, 1) &gt; 0;"
         errorLine2="                              ~~~~~~">
         <location
             file="frameworks/base/core/java/android/text/method/TextKeyListener.java"
@@ -455,7 +455,7 @@
     <issue
         id="NonUserGetterCalled"
         message="`android.provider.Settings.System#getInt()` called from system process. Please call `android.provider.Settings.System#getIntForUser()` instead. "
-        errorLine1="        boolean period = System.getInt(resolver, System.TEXT_AUTO_PUNCTUATE, 1) > 0;"
+        errorLine1="        boolean period = System.getInt(resolver, System.TEXT_AUTO_PUNCTUATE, 1) &gt; 0;"
         errorLine2="                                ~~~~~~">
         <location
             file="frameworks/base/core/java/android/text/method/TextKeyListener.java"
@@ -466,7 +466,7 @@
     <issue
         id="NonUserGetterCalled"
         message="`android.provider.Settings.System#getInt()` called from system process. Please call `android.provider.Settings.System#getIntForUser()` instead. "
-        errorLine1="        boolean pw = System.getInt(resolver, System.TEXT_SHOW_PASSWORD, 1) > 0;"
+        errorLine1="        boolean pw = System.getInt(resolver, System.TEXT_SHOW_PASSWORD, 1) &gt; 0;"
         errorLine2="                            ~~~~~~">
         <location
             file="frameworks/base/core/java/android/text/method/TextKeyListener.java"
@@ -562,4 +562,4 @@
             column="74"/>
     </issue>
 
-</issues>
+</issues>
\ No newline at end of file
diff --git a/location/api/current.txt b/location/api/current.txt
index 0c23d8c..c55676b 100644
--- a/location/api/current.txt
+++ b/location/api/current.txt
@@ -414,7 +414,7 @@
     field public static final int TYPE_GPS_L5CNAV = 259; // 0x103
     field @FlaggedApi(Flags.FLAG_GNSS_API_NAVIC_L1) public static final int TYPE_IRN_L1 = 1795; // 0x703
     field @FlaggedApi(Flags.FLAG_GNSS_API_NAVIC_L1) public static final int TYPE_IRN_L5 = 1794; // 0x702
-    field @Deprecated public static final int TYPE_IRN_L5CA = 1793; // 0x701
+    field public static final int TYPE_IRN_L5CA = 1793; // 0x701
     field public static final int TYPE_QZS_L1CA = 1025; // 0x401
     field public static final int TYPE_SBS = 513; // 0x201
     field public static final int TYPE_UNKNOWN = 0; // 0x0
diff --git a/location/java/android/location/GnssNavigationMessage.java b/location/java/android/location/GnssNavigationMessage.java
index 5e3f803..7a667ae 100644
--- a/location/java/android/location/GnssNavigationMessage.java
+++ b/location/java/android/location/GnssNavigationMessage.java
@@ -78,9 +78,7 @@
     public static final int TYPE_GAL_F = 0x0602;
     /**
      * NavIC L5 C/A message contained in the structure.
-     * @deprecated deprecated.
      */
-    @Deprecated
     public static final int TYPE_IRN_L5CA = 0x0701;
     /** NavIC L5 message contained in the structure. */
     @FlaggedApi(Flags.FLAG_GNSS_API_NAVIC_L1)
diff --git a/location/java/android/location/LocationResult.java b/location/java/android/location/LocationResult.java
index 8423000..67f4775 100644
--- a/location/java/android/location/LocationResult.java
+++ b/location/java/android/location/LocationResult.java
@@ -19,8 +19,11 @@
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.location.flags.Flags;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.SystemClock;
+import android.util.Log;
 
 import com.android.internal.util.Preconditions;
 
@@ -37,6 +40,23 @@
  * @hide
  */
 public final class LocationResult implements Parcelable {
+    private static final String TAG = "LocationResult";
+
+    // maximum reasonable accuracy, somewhat arbitrarily chosen. this is a very high upper limit, it
+    // could likely be lower, but we only want to throw out really absurd values.
+    private static final float MAX_ACCURACY_M = 1000000;
+
+    // maximum reasonable speed we expect a device to travel at is currently mach 1 (top speed of
+    // current fastest private jet). Higher speed than the value is considered as a malfunction
+    // than a correct reading.
+    private static final float MAX_SPEED_MPS = 343;
+
+    /** Exception representing an invalid location within a {@link LocationResult}. */
+    public static class BadLocationException extends Exception {
+        public BadLocationException(String message) {
+            super(message);
+        }
+    }
 
     /**
      * Creates a new LocationResult from the given locations, making a copy of each location.
@@ -101,18 +121,60 @@
      *
      * @hide
      */
-    public @NonNull LocationResult validate() {
+    public @NonNull LocationResult validate() throws BadLocationException {
         long prevElapsedRealtimeNs = 0;
         final int size = mLocations.size();
         for (int i = 0; i < size; ++i) {
             Location location = mLocations.get(i);
-            if (!location.isComplete()) {
-                throw new IllegalArgumentException(
-                        "incomplete location at index " + i + ": " + mLocations);
-            }
-            if (location.getElapsedRealtimeNanos() < prevElapsedRealtimeNs) {
-                throw new IllegalArgumentException(
-                        "incorrectly ordered location at index " + i + ": " + mLocations);
+            if (Flags.locationValidation()) {
+                if (location.getLatitude() < -90.0
+                        || location.getLatitude() > 90.0
+                        || location.getLongitude() < -180.0
+                        || location.getLongitude() > 180.0
+                        || Double.isNaN(location.getLatitude())
+                        || Double.isNaN(location.getLongitude())) {
+                    throw new BadLocationException("location must have valid lat/lng");
+                }
+                if (!location.hasAccuracy()) {
+                    throw new BadLocationException("location must have accuracy");
+                }
+                if (location.getAccuracy() < 0 || location.getAccuracy() > MAX_ACCURACY_M) {
+                    throw new BadLocationException("location must have reasonable accuracy");
+                }
+                if (location.getTime() < 0) {
+                    throw new BadLocationException("location must have valid time");
+                }
+                if (prevElapsedRealtimeNs > location.getElapsedRealtimeNanos()) {
+                    throw new BadLocationException(
+                            "location must have valid monotonically increasing realtime");
+                }
+                if (location.getElapsedRealtimeNanos()
+                        > SystemClock.elapsedRealtimeNanos()) {
+                    throw new BadLocationException("location must not have realtime in the future");
+                }
+                if (!location.isMock()) {
+                    if (location.getProvider() == null) {
+                        throw new BadLocationException("location must have valid provider");
+                    }
+                    if (location.getLatitude() == 0 && location.getLongitude() == 0) {
+                        throw new BadLocationException("location must not be at 0,0");
+                    }
+                }
+
+                if (location.hasSpeed() && (location.getSpeed() < 0
+                        || location.getSpeed() > MAX_SPEED_MPS)) {
+                    Log.w(TAG, "removed bad location speed: " + location.getSpeed());
+                    location.removeSpeed();
+                }
+            } else {
+                if (!location.isComplete()) {
+                    throw new IllegalArgumentException(
+                            "incomplete location at index " + i + ": " + mLocations);
+                }
+                if (location.getElapsedRealtimeNanos() < prevElapsedRealtimeNs) {
+                    throw new IllegalArgumentException(
+                            "incorrectly ordered location at index " + i + ": " + mLocations);
+                }
             }
             prevElapsedRealtimeNs = location.getElapsedRealtimeNanos();
         }
diff --git a/location/java/android/location/flags/gnss.aconfig b/location/java/android/location/flags/gnss.aconfig
index f4b1056..5f1279e 100644
--- a/location/java/android/location/flags/gnss.aconfig
+++ b/location/java/android/location/flags/gnss.aconfig
@@ -27,3 +27,24 @@
     description: "Flag for releasing SUPL connection on timeout"
     bug: "315024652"
 }
+
+flag {
+    name: "location_validation"
+    namespace: "location"
+    description: "Flag for location validation"
+    bug: "314328533"
+}
+
+flag {
+    name: "gnss_configuration_from_resource"
+    namespace: "location"
+    description: "Flag for GNSS configuration from resource"
+    bug: "317734846"
+}
+
+flag {
+    name: "replace_future_elapsed_realtime_jni"
+    namespace: "location"
+    description: "Flag for replacing future elapsedRealtime in JNI"
+    bug: "314328533"
+}
diff --git a/location/java/com/android/internal/location/GpsNetInitiatedHandler.java b/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
index ee2510f..0d5af50 100644
--- a/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
+++ b/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
@@ -19,6 +19,7 @@
 import android.Manifest;
 import android.annotation.RequiresPermission;
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.location.LocationManager;
 import android.os.SystemClock;
 import android.telephony.TelephonyCallback;
@@ -26,6 +27,8 @@
 import android.telephony.emergency.EmergencyNumber;
 import android.util.Log;
 
+import com.android.internal.telephony.flags.Flags;
+
 import java.util.concurrent.TimeUnit;
 
 /**
@@ -139,8 +142,20 @@
                 (mCallEndElapsedRealtimeMillis > 0)
                         && ((SystemClock.elapsedRealtime() - mCallEndElapsedRealtimeMillis)
                         < emergencyExtensionMillis);
-        boolean isInEmergencyCallback = mTelephonyManager.getEmergencyCallbackMode();
-        boolean isInEmergencySmsMode = mTelephonyManager.isInEmergencySmsMode();
+        boolean isInEmergencyCallback = false;
+        boolean isInEmergencySmsMode = false;
+        if (!Flags.enforceTelephonyFeatureMappingForPublicApis()) {
+            isInEmergencyCallback = mTelephonyManager.getEmergencyCallbackMode();
+            isInEmergencySmsMode = mTelephonyManager.isInEmergencySmsMode();
+        } else {
+            PackageManager pm = mContext.getPackageManager();
+            if (pm != null && pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY_CALLING)) {
+                isInEmergencyCallback = mTelephonyManager.getEmergencyCallbackMode();
+            }
+            if (pm != null && pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)) {
+                isInEmergencySmsMode = mTelephonyManager.isInEmergencySmsMode();
+            }
+        }
         return mIsInEmergencyCall || isInEmergencyCallback || isInEmergencyExtension
                 || isInEmergencySmsMode;
     }
diff --git a/media/TEST_MAPPING b/media/TEST_MAPPING
index 8f5f1f6..4fbe9ee 100644
--- a/media/TEST_MAPPING
+++ b/media/TEST_MAPPING
@@ -48,9 +48,7 @@
         {"exclude-annotation": "androidx.test.filters.FlakyTest"},
         {"exclude-annotation": "org.junit.Ignore"}
       ]
-    }
-  ],
-  "postsubmit": [
+    },
     {
       "file_patterns": [
         "[^/]*(LoudnessCodec)[^/]*\\.java"
diff --git a/media/java/android/media/AudioHalVersionInfo.java b/media/java/android/media/AudioHalVersionInfo.java
index 0f48abeb..25b1404 100644
--- a/media/java/android/media/AudioHalVersionInfo.java
+++ b/media/java/android/media/AudioHalVersionInfo.java
@@ -78,11 +78,10 @@
 
     /**
      * List of all valid Audio HAL versions. This list need to be in sync with sAudioHALVersions
-     * defined in frameworks/av/media/libaudiohal/FactoryHalHidl.cpp.
+     * defined in frameworks/av/media/libaudiohal/FactoryHal.cpp.
      */
-    // TODO: add AIDL_1_0 with sAudioHALVersions.
     public static final @NonNull List<AudioHalVersionInfo> VERSIONS =
-            List.of(HIDL_7_1, HIDL_7_0, HIDL_6_0, HIDL_5_0, HIDL_4_0);
+            List.of(AIDL_1_0, HIDL_7_1, HIDL_7_0, HIDL_6_0, HIDL_5_0, HIDL_4_0);
 
     private static final String TAG = "AudioHalVersionInfo";
     private AudioHalVersion mHalVersion = new AudioHalVersion();
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index a5a69f9..4918289 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -21,6 +21,7 @@
 import static android.content.Context.DEVICE_ID_DEFAULT;
 import static android.media.audio.Flags.autoPublicVolumeApiHardening;
 import static android.media.audio.Flags.automaticBtDeviceType;
+import static android.media.audio.Flags.FLAG_FOCUS_EXCLUSIVE_WITH_RECORDING;
 import static android.media.audio.Flags.FLAG_FOCUS_FREEZE_TEST_API;
 import static android.media.audiopolicy.Flags.FLAG_ENABLE_FADE_MANAGER_CONFIGURATION;
 
@@ -10081,6 +10082,28 @@
         }
     }
 
+    /**
+     * @hide
+     * Checks whether a notification sound should be played or not, as reported by the state
+     * of the audio framework. Querying whether playback should proceed is favored over
+     * playing and letting the sound be muted or not.
+     * @param aa the {@link AudioAttributes} of the notification about to maybe play
+     * @return true if the audio framework state is such that the notification should be played
+     *    because at time of checking, and the notification will be heard,
+     *    false otherwise
+     */
+    @TestApi
+    @FlaggedApi(FLAG_FOCUS_EXCLUSIVE_WITH_RECORDING)
+    @RequiresPermission(android.Manifest.permission.QUERY_AUDIO_STATE)
+    public boolean shouldNotificationSoundPlay(@NonNull final AudioAttributes aa) {
+        final IAudioService service = getService();
+        try {
+            return service.shouldNotificationSoundPlay(Objects.requireNonNull(aa));
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
     //====================================================================
     // Mute await connection
 
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 46a0b99..0f6cbff 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -2499,14 +2499,12 @@
      * </ul>
      */
     public static int getPlatformType(Context context) {
-        if (((TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE))
-                .isVoiceCapable()) {
+        if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
+            return PLATFORM_AUTOMOTIVE;
+        } else if ((context.getSystemService(TelephonyManager.class)).isVoiceCapable()) {
             return PLATFORM_VOICE;
         } else if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK)) {
             return PLATFORM_TELEVISION;
-        } else if (context.getPackageManager().hasSystemFeature(
-                PackageManager.FEATURE_AUTOMOTIVE)) {
-            return PLATFORM_AUTOMOTIVE;
         } else {
             return PLATFORM_DEFAULT;
         }
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 5c268d4..2eec9b3 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -775,4 +775,8 @@
     @EnforcePermission("MODIFY_AUDIO_SETTINGS_PRIVILEGED")
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED)")
     FadeManagerConfiguration getFadeManagerConfigurationForFocusLoss();
+
+    @EnforcePermission("QUERY_AUDIO_STATE")
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.QUERY_AUDIO_STATE)")
+    boolean shouldNotificationSoundPlay(in AudioAttributes aa);
 }
diff --git a/media/java/android/media/LoudnessCodecConfigurator.java b/media/java/android/media/LoudnessCodecConfigurator.java
index de9d87c0..aadd783 100644
--- a/media/java/android/media/LoudnessCodecConfigurator.java
+++ b/media/java/android/media/LoudnessCodecConfigurator.java
@@ -234,19 +234,21 @@
      * @param mediaCodec the codec to start receiving asynchronous loudness
      *                   updates. The codec has to be in a configured or started
      *                   state in order to add it for loudness updates.
-     * @throws IllegalArgumentException if the {@code mediaCodec} was not configured,
-     *                                  does not contain loudness metadata or if it
-     *                                  was already added before
+     * @throws IllegalArgumentException if the same {@code mediaCodec} was already
+     *                                  added before.
+     * @return {@code false} if the {@code mediaCodec} was not configured or does
+     *         not contain loudness metadata, {@code true} otherwise.
      */
     @FlaggedApi(FLAG_LOUDNESS_CONFIGURATOR_API)
-    public void addMediaCodec(@NonNull MediaCodec mediaCodec) {
+    public boolean addMediaCodec(@NonNull MediaCodec mediaCodec) {
         final MediaCodec mc = Objects.requireNonNull(mediaCodec,
                 "MediaCodec for addMediaCodec cannot be null");
         int piid = PLAYER_PIID_INVALID;
         final LoudnessCodecInfo mcInfo = getCodecInfo(mc);
 
         if (mcInfo == null) {
-            throw new IllegalArgumentException("Could not extract codec loudness information");
+            Log.v(TAG, "Could not extract codec loudness information");
+            return false;
         }
         synchronized (mConfiguratorLock) {
             final AtomicBoolean containsCodec = new AtomicBoolean(false);
@@ -271,6 +273,8 @@
         if (piid != PLAYER_PIID_INVALID) {
             mLcDispatcher.addLoudnessCodecInfo(piid, mediaCodec.hashCode(), mcInfo);
         }
+
+        return true;
     }
 
     /**
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index b7c97208..470a8ac 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -3401,13 +3401,15 @@
         }
 
         /**
-         * Set a harware graphic buffer to this queue request. Exactly one buffer must
+         * Set a hardware graphic buffer to this queue request. Exactly one buffer must
          * be set for a queue request before calling {@link #queue}.
          * <p>
          * Note: buffers should have format {@link HardwareBuffer#YCBCR_420_888},
          * a single layer, and an appropriate usage ({@link HardwareBuffer#USAGE_CPU_READ_OFTEN}
          * for software codecs and {@link HardwareBuffer#USAGE_VIDEO_ENCODE} for hardware)
-         * for codecs to recognize.  Codecs may throw exception if the buffer is not recognizable.
+         * for codecs to recognize. Format {@link ImageFormat#PRIVATE} together with
+         * usage {@link HardwareBuffer#USAGE_VIDEO_ENCODE} will also work for hardware codecs.
+         * Codecs may throw exception if the buffer is not recognizable.
          *
          * @param buffer The hardware graphic buffer object
          * @return this object
diff --git a/media/java/android/media/MediaRoute2Info.java b/media/java/android/media/MediaRoute2Info.java
index 0eabe66..838630f 100644
--- a/media/java/android/media/MediaRoute2Info.java
+++ b/media/java/android/media/MediaRoute2Info.java
@@ -943,6 +943,10 @@
                         .append(getId())
                         .append(", name=")
                         .append(getName())
+                        .append(", type=")
+                        .append(getDeviceTypeString(getType()))
+                        .append(", isSystem=")
+                        .append(isSystemRoute())
                         .append(", features=")
                         .append(getFeatures())
                         .append(", iconUri=")
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index 5e23551..9616b5d 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -18,8 +18,8 @@
 
 import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
 import static com.android.media.flags.Flags.FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES;
-import static com.android.media.flags.Flags.FLAG_ENABLE_RLP_CALLBACKS_IN_MEDIA_ROUTER2;
 import static com.android.media.flags.Flags.FLAG_ENABLE_CROSS_USER_ROUTING_IN_MEDIA_ROUTER2;
+import static com.android.media.flags.Flags.FLAG_ENABLE_RLP_CALLBACKS_IN_MEDIA_ROUTER2;
 
 import android.Manifest;
 import android.annotation.CallbackExecutor;
@@ -48,6 +48,7 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
@@ -141,7 +142,9 @@
      * dispatch. This is only used to determine what callback a route should be assigned to (added,
      * removed, changed) in {@link #dispatchFilteredRoutesUpdatedOnHandler(List)}.
      */
-    private volatile ArrayMap<String, MediaRoute2Info> mPreviousRoutes = new ArrayMap<>();
+    private volatile ArrayMap<String, MediaRoute2Info> mPreviousFilteredRoutes = new ArrayMap<>();
+
+    private final Map<String, MediaRoute2Info> mPreviousUnfilteredRoutes = new ArrayMap<>();
 
     /**
      * Stores the latest copy of exposed routes after filtering, sorting, and deduplication. Can be
@@ -282,6 +285,8 @@
             MediaRouter2 instance = sAppToProxyRouterMap.get(key);
             if (instance == null) {
                 instance = new MediaRouter2(context, looper, clientPackageName, user);
+                // Register proxy router after instantiation to avoid race condition.
+                ((ProxyMediaRouter2Impl) instance.mImpl).registerProxyRouter();
                 sAppToProxyRouterMap.put(key, instance);
             }
             return instance;
@@ -344,25 +349,13 @@
         mImpl = new LocalMediaRouter2Impl(mContext.getPackageName());
         mHandler = new Handler(Looper.getMainLooper());
 
-        List<MediaRoute2Info> currentSystemRoutes = null;
-        try {
-            currentSystemRoutes = mMediaRouterService.getSystemRoutes();
-        } catch (RemoteException ex) {
-            ex.rethrowFromSystemServer();
-        }
-
-        if (currentSystemRoutes == null || currentSystemRoutes.isEmpty()) {
-            throw new RuntimeException("Null or empty currentSystemRoutes. Something is wrong.");
-        }
+        loadSystemRoutes();
 
         RoutingSessionInfo currentSystemSessionInfo = mImpl.getSystemSessionInfo();
         if (currentSystemSessionInfo == null) {
             throw new RuntimeException("Null currentSystemSessionInfo. Something is wrong.");
         }
 
-        for (MediaRoute2Info route : currentSystemRoutes) {
-            mRoutes.put(route.getId(), route);
-        }
         mSystemController = new SystemRoutingController(currentSystemSessionInfo);
     }
 
@@ -374,13 +367,34 @@
                 IMediaRouterService.Stub.asInterface(
                         ServiceManager.getService(Context.MEDIA_ROUTER_SERVICE));
 
+        loadSystemRoutes();
+
         mSystemController =
                 new SystemRoutingController(
                         ProxyMediaRouter2Impl.getSystemSessionInfoImpl(
                                 mMediaRouterService, clientPackageName));
+
         mImpl = new ProxyMediaRouter2Impl(context, clientPackageName, user);
     }
 
+    @GuardedBy("mLock")
+    private void loadSystemRoutes() {
+        List<MediaRoute2Info> currentSystemRoutes = null;
+        try {
+            currentSystemRoutes = mMediaRouterService.getSystemRoutes();
+        } catch (RemoteException ex) {
+            ex.rethrowFromSystemServer();
+        }
+
+        if (currentSystemRoutes == null || currentSystemRoutes.isEmpty()) {
+            throw new RuntimeException("Null or empty currentSystemRoutes. Something is wrong.");
+        }
+
+        for (MediaRoute2Info route : currentSystemRoutes) {
+            mRoutes.put(route.getId(), route);
+        }
+    }
+
     /**
      * Gets the client package name of the app which this media router controls.
      *
@@ -705,7 +719,7 @@
         mImpl.transfer(
                 controller.getRoutingSessionInfo(),
                 route,
-                android.os.Process.myUserHandle(),
+                Process.myUserHandle(),
                 mContext.getPackageName());
     }
 
@@ -875,7 +889,7 @@
                 newRoutes.stream().map(MediaRoute2Info::getId).collect(Collectors.toSet());
 
         for (MediaRoute2Info route : newRoutes) {
-            MediaRoute2Info prevRoute = mPreviousRoutes.get(route.getId());
+            MediaRoute2Info prevRoute = mPreviousFilteredRoutes.get(route.getId());
             if (prevRoute == null) {
                 addedRoutes.add(route);
             } else if (!prevRoute.equals(route)) {
@@ -883,21 +897,21 @@
             }
         }
 
-        for (int i = 0; i < mPreviousRoutes.size(); i++) {
-            if (!newRouteIds.contains(mPreviousRoutes.keyAt(i))) {
-                removedRoutes.add(mPreviousRoutes.valueAt(i));
+        for (int i = 0; i < mPreviousFilteredRoutes.size(); i++) {
+            if (!newRouteIds.contains(mPreviousFilteredRoutes.keyAt(i))) {
+                removedRoutes.add(mPreviousFilteredRoutes.valueAt(i));
             }
         }
 
         // update previous routes
         for (MediaRoute2Info route : removedRoutes) {
-            mPreviousRoutes.remove(route.getId());
+            mPreviousFilteredRoutes.remove(route.getId());
         }
         for (MediaRoute2Info route : addedRoutes) {
-            mPreviousRoutes.put(route.getId(), route);
+            mPreviousFilteredRoutes.put(route.getId(), route);
         }
         for (MediaRoute2Info route : changedRoutes) {
-            mPreviousRoutes.put(route.getId(), route);
+            mPreviousFilteredRoutes.put(route.getId(), route);
         }
 
         if (!addedRoutes.isEmpty()) {
@@ -916,6 +930,27 @@
         }
     }
 
+    void dispatchControllerUpdatedIfNeededOnHandler(Map<String, MediaRoute2Info> routesMap) {
+        List<RoutingController> controllers = getControllers();
+        for (RoutingController controller : controllers) {
+
+            for (String selectedRoute : controller.getRoutingSessionInfo().getSelectedRoutes()) {
+                if (routesMap.containsKey(selectedRoute)
+                        && mPreviousUnfilteredRoutes.containsKey(selectedRoute)) {
+                    MediaRoute2Info currentRoute = routesMap.get(selectedRoute);
+                    MediaRoute2Info oldRoute = mPreviousUnfilteredRoutes.get(selectedRoute);
+                    if (!currentRoute.equals(oldRoute)) {
+                        notifyControllerUpdated(controller);
+                        break;
+                    }
+                }
+            }
+        }
+
+        mPreviousUnfilteredRoutes.clear();
+        mPreviousUnfilteredRoutes.putAll(routesMap);
+    }
+
     void updateRoutesOnHandler(List<MediaRoute2Info> newRoutes) {
         synchronized (mLock) {
             mRoutes.clear();
@@ -937,6 +972,11 @@
                         MediaRouter2::dispatchFilteredRoutesUpdatedOnHandler,
                         this,
                         mFilteredRoutes));
+        mHandler.sendMessage(
+                obtainMessage(
+                        MediaRouter2::dispatchControllerUpdatedIfNeededOnHandler,
+                        this,
+                        new HashMap<>(mRoutes)));
     }
 
     /**
@@ -1520,7 +1560,7 @@
             UserHandle transferInitiatorUserHandle = sessionInfo.getTransferInitiatorUserHandle();
             String transferInitiatorPackageName = sessionInfo.getTransferInitiatorPackageName();
 
-            return Objects.equals(android.os.Process.myUserHandle(), transferInitiatorUserHandle)
+            return Objects.equals(Process.myUserHandle(), transferInitiatorUserHandle)
                     && Objects.equals(mContext.getPackageName(), transferInitiatorPackageName);
         }
 
@@ -2145,18 +2185,19 @@
             mClientUser = user;
             mClientPackageName = clientPackageName;
             mClient = new Client();
+            mDiscoveryPreference = RouteDiscoveryPreference.EMPTY;
+        }
 
+        public void registerProxyRouter() {
             try {
                 mMediaRouterService.registerProxyRouter(
                         mClient,
-                        context.getApplicationContext().getPackageName(),
-                        clientPackageName,
-                        user);
+                        mContext.getApplicationContext().getPackageName(),
+                        mClientPackageName,
+                        mClientUser);
             } catch (RemoteException ex) {
                 throw ex.rethrowFromSystemServer();
             }
-
-            mDiscoveryPreference = RouteDiscoveryPreference.EMPTY;
         }
 
         @Override
@@ -2286,11 +2327,7 @@
 
             List<RoutingSessionInfo> sessionInfos = getRoutingSessions();
             RoutingSessionInfo targetSession = sessionInfos.get(sessionInfos.size() - 1);
-            transfer(
-                    targetSession,
-                    route,
-                    android.os.Process.myUserHandle(),
-                    mContext.getPackageName());
+            transfer(targetSession, route, Process.myUserHandle(), mContext.getPackageName());
         }
 
         @Override
@@ -3157,8 +3194,12 @@
                 return;
             }
 
-            requestCreateController(controller, route, MANAGER_REQUEST_ID_NONE,
-                    android.os.Process.myUserHandle(), mContext.getPackageName());
+            requestCreateController(
+                    controller,
+                    route,
+                    MANAGER_REQUEST_ID_NONE,
+                    Process.myUserHandle(),
+                    mContext.getPackageName());
         }
 
         @Override
diff --git a/media/java/android/media/flags/media_better_together.aconfig b/media/java/android/media/flags/media_better_together.aconfig
index 3da52cc..7f95886 100644
--- a/media/java/android/media/flags/media_better_together.aconfig
+++ b/media/java/android/media/flags/media_better_together.aconfig
@@ -77,3 +77,9 @@
      bug: "279555229"
 }
 
+flag {
+    name: "enable_notifying_activity_manager_with_media_session_status_change"
+    namespace: "media_solutions"
+    description: "Notify ActivityManager with the changes in playback state of the media session."
+    bug: "295518668"
+}
diff --git a/media/java/android/media/projection/IMediaProjectionManager.aidl b/media/java/android/media/projection/IMediaProjectionManager.aidl
index a7ec6c6..8ce1b6d 100644
--- a/media/java/android/media/projection/IMediaProjectionManager.aidl
+++ b/media/java/android/media/projection/IMediaProjectionManager.aidl
@@ -37,31 +37,41 @@
     const String EXTRA_PACKAGE_REUSING_GRANTED_CONSENT =
             "extra_media_projection_package_reusing_consent";
 
+    /**
+     * Returns whether a combination of process UID and package has the projection permission.
+     *
+     * @param processUid the process UID as returned by {@link android.os.Process.myUid()}.
+     */
     @UnsupportedAppUsage
-    boolean hasProjectionPermission(int uid, String packageName);
+    boolean hasProjectionPermission(int processUid, String packageName);
 
     /**
      * Returns a new {@link IMediaProjection} instance associated with the given package.
+     *
+     * @param processUid the process UID as returned by {@link android.os.Process.myUid()}.
      */
     @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest"
             + ".permission.MANAGE_MEDIA_PROJECTION)")
-    IMediaProjection createProjection(int uid, String packageName, int type,
+    IMediaProjection createProjection(int processUid, String packageName, int type,
             boolean permanentGrant);
 
     /**
      * Returns the current {@link IMediaProjection} instance associated with the given
-     * package, or {@code null} if it is not possible to re-use the current projection.
+     * package and process UID, or {@code null} if it is not possible to re-use the current
+     * projection.
      *
      * <p>Should only be invoked when the user has reviewed consent for a re-used projection token.
      * Requires that there is a prior session waiting for the user to review consent, and the given
      * package details match those on the current projection.
      *
      * @see {@link #isCurrentProjection}
+     *
+     * @param processUid the process UID as returned by {@link android.os.Process.myUid()}.
      */
     @EnforcePermission("android.Manifest.permission.MANAGE_MEDIA_PROJECTION")
     @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest"
             + ".permission.MANAGE_MEDIA_PROJECTION)")
-    IMediaProjection getProjection(int uid, String packageName);
+    IMediaProjection getProjection(int processUid, String packageName);
 
     /**
      * Returns {@code true} if the given {@link IMediaProjection} corresponds to the current
@@ -162,8 +172,8 @@
      *
      * <p>Only used for emitting atoms.
      *
-     * @param hostUid               The uid of the process requesting consent to capture, may be an app or
-     *                              SystemUI.
+     * @param hostProcessUid        The uid of the process requesting consent to capture, may be an
+     *                              app or SystemUI.
      * @param sessionCreationSource Only set if the state is MEDIA_PROJECTION_STATE_INITIATED.
      *                              Indicates the entry point for requesting the permission. Must be
      *                              a valid state defined
@@ -172,49 +182,49 @@
     @EnforcePermission("android.Manifest.permission.MANAGE_MEDIA_PROJECTION")
     @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest"
             + ".permission.MANAGE_MEDIA_PROJECTION)")
-    oneway void notifyPermissionRequestInitiated(int hostUid, int sessionCreationSource);
+    oneway void notifyPermissionRequestInitiated(int hostProcessUid, int sessionCreationSource);
 
     /**
      * Notifies system server that the permission request was displayed.
      *
      * <p>Only used for emitting atoms.
      *
-     * @param hostUid The uid of the process requesting consent to capture, may be an app or
-     *                SystemUI.
+     * @param hostProcessUid The uid of the process requesting consent to capture, may be an app or
+     *                       SystemUI.
      */
     @EnforcePermission("android.Manifest.permission.MANAGE_MEDIA_PROJECTION")
     @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest"
             + ".permission.MANAGE_MEDIA_PROJECTION)")
-    oneway void notifyPermissionRequestDisplayed(int hostUid);
+    oneway void notifyPermissionRequestDisplayed(int hostProcessUid);
 
     /**
      * Notifies system server that the permission request was cancelled.
      *
      * <p>Only used for emitting atoms.
      *
-     * @param hostUid The uid of the process requesting consent to capture, may be an app or
-     *                SystemUI.
+     * @param hostProcessUid The uid of the process requesting consent to capture, may be an app or
+     *                       SystemUI.
      */
     @EnforcePermission("android.Manifest.permission.MANAGE_MEDIA_PROJECTION")
     @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest"
             + ".permission.MANAGE_MEDIA_PROJECTION)")
-    oneway void notifyPermissionRequestCancelled(int hostUid);
+    oneway void notifyPermissionRequestCancelled(int hostProcessUid);
 
     /**
      * Notifies system server that the app selector was displayed.
      *
      * <p>Only used for emitting atoms.
      *
-     * @param hostUid The uid of the process requesting consent to capture, may be an app or
-     *                SystemUI.
+     * @param hostProcessUid The uid of the process requesting consent to capture, may be an app or
+     *                       SystemUI.
      */
     @EnforcePermission("android.Manifest.permission.MANAGE_MEDIA_PROJECTION")
     @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest"
             + ".permission.MANAGE_MEDIA_PROJECTION)")
-    oneway void notifyAppSelectorDisplayed(int hostUid);
+    oneway void notifyAppSelectorDisplayed(int hostProcessUid);
 
     @EnforcePermission("MANAGE_MEDIA_PROJECTION")
     @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest"
             + ".permission.MANAGE_MEDIA_PROJECTION)")
-    void notifyWindowingModeChanged(int contentToRecord, int targetUid, int windowingMode);
+    void notifyWindowingModeChanged(int contentToRecord, int targetProcessUid, int windowingMode);
 }
diff --git a/media/java/android/media/session/PlaybackState.java b/media/java/android/media/session/PlaybackState.java
index 7891ee6..47637b8 100644
--- a/media/java/android/media/session/PlaybackState.java
+++ b/media/java/android/media/session/PlaybackState.java
@@ -15,7 +15,10 @@
  */
 package android.media.session;
 
+import static com.android.media.flags.Flags.FLAG_ENABLE_NOTIFYING_ACTIVITY_MANAGER_WITH_MEDIA_SESSION_STATUS_CHANGE;
+
 import android.annotation.DrawableRes;
+import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.LongDef;
 import android.annotation.Nullable;
@@ -189,7 +192,8 @@
      */
     @IntDef({STATE_NONE, STATE_STOPPED, STATE_PAUSED, STATE_PLAYING, STATE_FAST_FORWARDING,
             STATE_REWINDING, STATE_BUFFERING, STATE_ERROR, STATE_CONNECTING,
-            STATE_SKIPPING_TO_PREVIOUS, STATE_SKIPPING_TO_NEXT, STATE_SKIPPING_TO_QUEUE_ITEM})
+            STATE_SKIPPING_TO_PREVIOUS, STATE_SKIPPING_TO_NEXT, STATE_SKIPPING_TO_QUEUE_ITEM,
+            STATE_PLAYBACK_SUPPRESSED})
     @Retention(RetentionPolicy.SOURCE)
     public @interface State {}
 
@@ -286,6 +290,19 @@
     public static final int STATE_SKIPPING_TO_QUEUE_ITEM = 11;
 
     /**
+     * State indicating that playback is paused due to an external transient interruption, like a
+     * phone call.
+     *
+     * <p>This state is different from {@link #STATE_PAUSED} in that it is deemed transitory,
+     * possibly allowing the service associated to the session in this state to run in the
+     * foreground.
+     *
+     * @see Builder#setState
+     */
+    @FlaggedApi(FLAG_ENABLE_NOTIFYING_ACTIVITY_MANAGER_WITH_MEDIA_SESSION_STATUS_CHANGE)
+    public static final int STATE_PLAYBACK_SUPPRESSED = 12;
+
+    /**
      * Use this value for the position to indicate the position is not known.
      */
     public static final long PLAYBACK_POSITION_UNKNOWN = -1;
@@ -384,6 +401,7 @@
      * <li> {@link PlaybackState#STATE_SKIPPING_TO_PREVIOUS}</li>
      * <li> {@link PlaybackState#STATE_SKIPPING_TO_NEXT}</li>
      * <li> {@link PlaybackState#STATE_SKIPPING_TO_QUEUE_ITEM}</li>
+     * <li> {@link PlaybackState#STATE_PLAYBACK_SUPPRESSED}</li>
      * </ul>
      */
     @State
@@ -507,6 +525,7 @@
      * <li>{@link #STATE_SKIPPING_TO_NEXT}</li>
      * <li>{@link #STATE_SKIPPING_TO_PREVIOUS}</li>
      * <li>{@link #STATE_SKIPPING_TO_QUEUE_ITEM}</li>
+     * <li>{@link #STATE_PLAYBACK_SUPPRESSED}</li>
      * </ul>
      */
     public boolean isActive() {
@@ -519,6 +538,7 @@
             case PlaybackState.STATE_BUFFERING:
             case PlaybackState.STATE_CONNECTING:
             case PlaybackState.STATE_PLAYING:
+            case PlaybackState.STATE_PLAYBACK_SUPPRESSED:
                 return true;
         }
         return false;
@@ -564,6 +584,8 @@
                 return "SKIPPING_TO_NEXT";
             case STATE_SKIPPING_TO_QUEUE_ITEM:
                 return "SKIPPING_TO_QUEUE_ITEM";
+            case STATE_PLAYBACK_SUPPRESSED:
+                return "STATE_PLAYBACK_SUPPRESSED";
             default:
                 return "UNKNOWN";
         }
@@ -801,6 +823,7 @@
          * <li> {@link PlaybackState#STATE_SKIPPING_TO_PREVIOUS}</li>
          * <li> {@link PlaybackState#STATE_SKIPPING_TO_NEXT}</li>
          * <li> {@link PlaybackState#STATE_SKIPPING_TO_QUEUE_ITEM}</li>
+         * <li> {@link PlaybackState#STATE_PLAYBACK_SUPPRESSED}</li>
          * </ul>
          *
          * @param state The current state of playback.
@@ -845,6 +868,7 @@
          * <li> {@link PlaybackState#STATE_SKIPPING_TO_PREVIOUS}</li>
          * <li> {@link PlaybackState#STATE_SKIPPING_TO_NEXT}</li>
          * <li> {@link PlaybackState#STATE_SKIPPING_TO_QUEUE_ITEM}</li>
+         * <li> {@link PlaybackState#STATE_PLAYBACK_SUPPRESSED}</li>
          * </ul>
          *
          * @param state The current state of playback.
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index 96e95fd..3fcb871 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -693,6 +693,8 @@
                                            mpuSequenceNumber, isPesPrivateData, sc,
                                            audioDescriptor.get(), presentationsJObj.get()));
 
+    // Protect mFilterClient from being set to null.
+    android::Mutex::Autolock autoLock(mLock);
     uint64_t avSharedMemSize = mFilterClient->getAvSharedHandleInfo().size;
     if (mediaEvent.avMemory.fds.size() > 0 || mediaEvent.avDataId != 0 ||
         (dataLength > 0 && (dataLength + offset) < avSharedMemSize)) {
@@ -939,38 +941,52 @@
             }
         }
     }
-    ScopedLocalRef filter(env, env->NewLocalRef(mFilterObj));
-    if (!env->IsSameObject(filter.get(), nullptr)) {
-        jmethodID methodID = gFields.onFilterEventID;
-        if (mSharedFilter) {
-            methodID = gFields.onSharedFilterEventID;
+
+    ScopedLocalRef<jobject> filter(env);
+    {
+        android::Mutex::Autolock autoLock(mLock);
+        if (env->IsSameObject(mFilterObj, nullptr)) {
+            ALOGE("FilterClientCallbackImpl::onFilterEvent:"
+                  "Filter object has been freed. Ignoring callback.");
+            return;
+        } else {
+            filter.reset(env->NewLocalRef(mFilterObj));
         }
-        env->CallVoidMethod(filter.get(), methodID, array.get());
-    } else {
-        ALOGE("FilterClientCallbackImpl::onFilterEvent:"
-              "Filter object has been freed. Ignoring callback.");
     }
+
+    jmethodID methodID = gFields.onFilterEventID;
+    if (mSharedFilter) {
+        methodID = gFields.onSharedFilterEventID;
+    }
+    env->CallVoidMethod(filter.get(), methodID, array.get());
 }
 
 void FilterClientCallbackImpl::onFilterStatus(const DemuxFilterStatus status) {
     ALOGV("FilterClientCallbackImpl::onFilterStatus");
     JNIEnv *env = AndroidRuntime::getJNIEnv();
-    ScopedLocalRef filter(env, env->NewLocalRef(mFilterObj));
-    if (!env->IsSameObject(filter.get(), nullptr)) {
-        jmethodID methodID = gFields.onFilterStatusID;
-        if (mSharedFilter) {
-            methodID = gFields.onSharedFilterStatusID;
+    ScopedLocalRef<jobject> filter(env);
+    {
+        android::Mutex::Autolock autoLock(mLock);
+        if (env->IsSameObject(filter.get(), nullptr)) {
+            ALOGE("FilterClientCallbackImpl::onFilterStatus:"
+                  "Filter object has been freed. Ignoring callback.");
+            return;
+        } else {
+            filter.reset(env->NewLocalRef(mFilterObj));
         }
-        env->CallVoidMethod(filter.get(), methodID, (jint)static_cast<uint8_t>(status));
-    } else {
-        ALOGE("FilterClientCallbackImpl::onFilterStatus:"
-              "Filter object has been freed. Ignoring callback.");
     }
+
+    jmethodID methodID = gFields.onFilterStatusID;
+    if (mSharedFilter) {
+        methodID = gFields.onSharedFilterStatusID;
+    }
+    env->CallVoidMethod(filter.get(), methodID, (jint)static_cast<uint8_t>(status));
 }
 
 void FilterClientCallbackImpl::setFilter(jweak filterObj, sp<FilterClient> filterClient) {
     ALOGV("FilterClientCallbackImpl::setFilter");
     // Java Object
+    android::Mutex::Autolock autoLock(mLock);
     mFilterObj = filterObj;
     mFilterClient = filterClient;
     mSharedFilter = false;
@@ -979,6 +995,7 @@
 void FilterClientCallbackImpl::setSharedFilter(jweak filterObj, sp<FilterClient> filterClient) {
     ALOGV("FilterClientCallbackImpl::setFilter");
     // Java Object
+    android::Mutex::Autolock autoLock(mLock);
     mFilterObj = filterObj;
     mFilterClient = filterClient;
     mSharedFilter = true;
@@ -1047,11 +1064,14 @@
 
 FilterClientCallbackImpl::~FilterClientCallbackImpl() {
     JNIEnv *env = AndroidRuntime::getJNIEnv();
-    if (mFilterObj != nullptr) {
-        env->DeleteWeakGlobalRef(mFilterObj);
-        mFilterObj = nullptr;
+    {
+        android::Mutex::Autolock autoLock(mLock);
+        if (mFilterObj != nullptr) {
+            env->DeleteWeakGlobalRef(mFilterObj);
+            mFilterObj = nullptr;
+        }
+        mFilterClient = nullptr;
     }
-    mFilterClient = nullptr;
     env->DeleteGlobalRef(mEventClass);
     env->DeleteGlobalRef(mSectionEventClass);
     env->DeleteGlobalRef(mMediaEventClass);
diff --git a/media/jni/android_media_tv_Tuner.h b/media/jni/android_media_tv_Tuner.h
index 01c998d..3de3ab9 100644
--- a/media/jni/android_media_tv_Tuner.h
+++ b/media/jni/android_media_tv_Tuner.h
@@ -136,6 +136,7 @@
 private:
     jweak mFilterObj;
     sp<FilterClient> mFilterClient;
+    android::Mutex mLock;
     jclass mEventClass;
     jclass mSectionEventClass;
     jclass mMediaEventClass;
diff --git a/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java b/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java
index 262f5f1..096e8ad 100644
--- a/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java
+++ b/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java
@@ -50,8 +50,16 @@
     private static final String TAG = "BluetoothMidiDevice";
     private static final boolean DEBUG = false;
 
-    private static final int DEFAULT_PACKET_SIZE = 20;
-    private static final int MAX_PACKET_SIZE = 512;
+    // Bluetooth services should subtract 5 bytes from the MTU for headers.
+    private static final int HEADER_SIZE = 5;
+    // Min MTU size for BLE
+    private static final int MIN_L2CAP_MTU = 23;
+    // 23 (min L2CAP MTU) - 5 (header size)
+    private static final int DEFAULT_PACKET_SIZE = MIN_L2CAP_MTU - HEADER_SIZE;
+    // Max MTU size on Android
+    private static final int MAX_ANDROID_MTU = 517;
+    // 517 (max Android MTU) - 5 (header size)
+    private static final int MAX_PACKET_SIZE = MAX_ANDROID_MTU - HEADER_SIZE;
 
     //  Bluetooth MIDI Gatt service UUID
     private static final UUID MIDI_SERVICE = UUID.fromString(
@@ -135,8 +143,8 @@
                         // switch to receiving notifications
                         mBluetoothGatt.readCharacteristic(characteristic);
 
-                        // Request higher MTU size
-                        if (!gatt.requestMtu(MAX_PACKET_SIZE)) {
+                        // Request max MTU size
+                        if (!gatt.requestMtu(MAX_ANDROID_MTU)) {
                             Log.e(TAG, "request mtu failed");
                             mPacketEncoder.setMaxPacketSize(DEFAULT_PACKET_SIZE);
                             mPacketDecoder.setMaxPacketSize(DEFAULT_PACKET_SIZE);
@@ -204,8 +212,15 @@
         public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
             Log.d(TAG, "onMtuChanged callback received. mtu: " + mtu + ", status: " + status);
             if (status == BluetoothGatt.GATT_SUCCESS) {
-                mPacketEncoder.setMaxPacketSize(Math.min(mtu, MAX_PACKET_SIZE));
-                mPacketDecoder.setMaxPacketSize(Math.min(mtu, MAX_PACKET_SIZE));
+                int packetSize = Math.min(mtu - HEADER_SIZE, MAX_PACKET_SIZE);
+                if (packetSize <= 0) {
+                    Log.e(TAG, "onMtuChanged non-positive packet size: " + packetSize);
+                    packetSize = DEFAULT_PACKET_SIZE;
+                } else if (packetSize < DEFAULT_PACKET_SIZE) {
+                    Log.w(TAG, "onMtuChanged small packet size: " + packetSize);
+                }
+                mPacketEncoder.setMaxPacketSize(packetSize);
+                mPacketDecoder.setMaxPacketSize(packetSize);
             } else {
                 mPacketEncoder.setMaxPacketSize(DEFAULT_PACKET_SIZE);
                 mPacketDecoder.setMaxPacketSize(DEFAULT_PACKET_SIZE);
diff --git a/media/tests/LoudnessCodecApiTest/src/com/android/loudnesscodecapitest/LoudnessCodecConfiguratorTest.java b/media/tests/LoudnessCodecApiTest/src/com/android/loudnesscodecapitest/LoudnessCodecConfiguratorTest.java
index 3b15632..74e5612 100644
--- a/media/tests/LoudnessCodecApiTest/src/com/android/loudnesscodecapitest/LoudnessCodecConfiguratorTest.java
+++ b/media/tests/LoudnessCodecApiTest/src/com/android/loudnesscodecapitest/LoudnessCodecConfiguratorTest.java
@@ -19,6 +19,7 @@
 import static android.media.audio.Flags.FLAG_LOUDNESS_CONFIGURATOR_API;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertThrows;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
@@ -95,12 +96,17 @@
     @RequiresFlagsEnabled(FLAG_LOUDNESS_CONFIGURATOR_API)
     public void setAudioTrack_callsAudioServiceStart() throws Exception {
         final AudioTrack track = createAudioTrack();
+        final MediaCodec mediaCodec = createAndConfigureMediaCodec();
 
-        mLcc.addMediaCodec(createAndConfigureMediaCodec());
-        mLcc.setAudioTrack(track);
+        try {
+            mLcc.addMediaCodec(mediaCodec);
+            mLcc.setAudioTrack(track);
 
-        verify(mAudioService).startLoudnessCodecUpdates(eq(track.getPlayerIId()),
-                anyList());
+            verify(mAudioService).startLoudnessCodecUpdates(eq(track.getPlayerIId()),
+                    anyList());
+        } finally {
+            mediaCodec.release();
+        }
     }
 
     @Test
@@ -108,10 +114,15 @@
     public void getLoudnessCodecParams_callsAudioServiceGetLoudness() throws Exception {
         when(mAudioService.getLoudnessParams(anyInt(), any())).thenReturn(new PersistableBundle());
         final AudioTrack track = createAudioTrack();
+        final MediaCodec mediaCodec = createAndConfigureMediaCodec();
 
-        mLcc.getLoudnessCodecParams(track, createAndConfigureMediaCodec());
+        try {
+            mLcc.getLoudnessCodecParams(track, mediaCodec);
 
-        verify(mAudioService).getLoudnessParams(eq(track.getPlayerIId()), any());
+            verify(mAudioService).getLoudnessParams(eq(track.getPlayerIId()), any());
+        } finally {
+            mediaCodec.release();
+        }
     }
 
     @Test
@@ -120,10 +131,14 @@
         final AudioTrack track = createAudioTrack();
         final MediaCodec mediaCodec = createAndConfigureMediaCodec();
 
-        mLcc.addMediaCodec(mediaCodec);
-        mLcc.setAudioTrack(track);
+        try {
+            mLcc.addMediaCodec(mediaCodec);
+            mLcc.setAudioTrack(track);
 
-        verify(mAudioService).startLoudnessCodecUpdates(eq(track.getPlayerIId()), anyList());
+            verify(mAudioService).startLoudnessCodecUpdates(eq(track.getPlayerIId()), anyList());
+        } finally {
+            mediaCodec.release();
+        }
     }
 
     @Test
@@ -132,24 +147,33 @@
         final AudioTrack track = createAudioTrack();
         final MediaCodec mediaCodec = createAndConfigureMediaCodec();
 
-        mLcc.addMediaCodec(mediaCodec);
-        mLcc.setAudioTrack(track);
-        mLcc.setAudioTrack(track);
+        try {
+            mLcc.addMediaCodec(mediaCodec);
+            mLcc.setAudioTrack(track);
+            mLcc.setAudioTrack(track);
 
-        verify(mAudioService, times(1)).startLoudnessCodecUpdates(eq(track.getPlayerIId()),
-                anyList());
+            verify(mAudioService, times(1)).startLoudnessCodecUpdates(eq(track.getPlayerIId()),
+                    anyList());
+        } finally {
+            mediaCodec.release();
+        }
     }
 
     @Test
     @RequiresFlagsEnabled(FLAG_LOUDNESS_CONFIGURATOR_API)
     public void setTrackNull_stopCodecUpdates() throws Exception {
         final AudioTrack track = createAudioTrack();
+        final MediaCodec mediaCodec = createAndConfigureMediaCodec();
 
-        mLcc.addMediaCodec(createAndConfigureMediaCodec());
-        mLcc.setAudioTrack(track);
+        try {
+            mLcc.addMediaCodec(mediaCodec);
+            mLcc.setAudioTrack(track);
 
-        mLcc.setAudioTrack(null);  // stops updates
-        verify(mAudioService).stopLoudnessCodecUpdates(eq(track.getPlayerIId()));
+            mLcc.setAudioTrack(null);  // stops updates
+            verify(mAudioService).stopLoudnessCodecUpdates(eq(track.getPlayerIId()));
+        } finally {
+            mediaCodec.release();
+        }
     }
 
     @Test
@@ -157,27 +181,49 @@
     public void addMediaCodecTwice_triggersIAE() throws Exception {
         final MediaCodec mediaCodec = createAndConfigureMediaCodec();
 
-        mLcc.addMediaCodec(mediaCodec);
+        try {
+            mLcc.addMediaCodec(mediaCodec);
 
-        assertThrows(IllegalArgumentException.class, () -> mLcc.addMediaCodec(mediaCodec));
+            assertThrows(IllegalArgumentException.class, () -> mLcc.addMediaCodec(mediaCodec));
+        } finally {
+            mediaCodec.release();
+        }
+    }
+
+    @Test
+    @RequiresFlagsEnabled(FLAG_LOUDNESS_CONFIGURATOR_API)
+    public void addUnconfiguredMediaCodec_returnsFalse() throws Exception {
+        final MediaCodec mediaCodec = MediaCodec.createDecoderByType("audio/mpeg");
+
+        try {
+            assertFalse(mLcc.addMediaCodec(mediaCodec));
+        } finally {
+            mediaCodec.release();
+        }
     }
 
     @Test
     @RequiresFlagsEnabled(FLAG_LOUDNESS_CONFIGURATOR_API)
     public void setClearTrack_removeAllAudioServicePiidCodecs() throws Exception {
         final ArgumentCaptor<List> argument = ArgumentCaptor.forClass(List.class);
-
         final AudioTrack track = createAudioTrack();
+        final MediaCodec mediaCodec1 = createAndConfigureMediaCodec();
+        final MediaCodec mediaCodec2 = createAndConfigureMediaCodec();
 
-        mLcc.addMediaCodec(createAndConfigureMediaCodec());
-        mLcc.setAudioTrack(track);
-        verify(mAudioService).startLoudnessCodecUpdates(eq(track.getPlayerIId()),
-                argument.capture());
-        assertEquals(argument.getValue().size(), 1);
+        try {
+            mLcc.addMediaCodec(mediaCodec1);
+            mLcc.setAudioTrack(track);
+            verify(mAudioService).startLoudnessCodecUpdates(eq(track.getPlayerIId()),
+                    argument.capture());
+            assertEquals(argument.getValue().size(), 1);
 
-        mLcc.addMediaCodec(createAndConfigureMediaCodec());
-        mLcc.setAudioTrack(null);
-        verify(mAudioService).stopLoudnessCodecUpdates(eq(track.getPlayerIId()));
+            mLcc.addMediaCodec(mediaCodec2);
+            mLcc.setAudioTrack(null);
+            verify(mAudioService).stopLoudnessCodecUpdates(eq(track.getPlayerIId()));
+        } finally {
+            mediaCodec1.release();
+            mediaCodec2.release();
+        }
     }
 
     @Test
@@ -186,24 +232,35 @@
         final AudioTrack track = createAudioTrack();
         final MediaCodec mediaCodec = createAndConfigureMediaCodec();
 
-        mLcc.addMediaCodec(mediaCodec);
-        mLcc.setAudioTrack(track);
-        mLcc.removeMediaCodec(mediaCodec);
+        try {
+            mLcc.addMediaCodec(mediaCodec);
+            mLcc.setAudioTrack(track);
+            mLcc.removeMediaCodec(mediaCodec);
 
-        verify(mAudioService).removeLoudnessCodecInfo(eq(track.getPlayerIId()), any());
+            verify(mAudioService).removeLoudnessCodecInfo(eq(track.getPlayerIId()), any());
+        } finally {
+            mediaCodec.release();
+        }
     }
 
     @Test
     @RequiresFlagsEnabled(FLAG_LOUDNESS_CONFIGURATOR_API)
     public void addMediaCodecAfterSetTrack_callsAudioServiceAdd() throws Exception {
         final AudioTrack track = createAudioTrack();
+        final MediaCodec mediaCodec1 = createAndConfigureMediaCodec();
+        final MediaCodec mediaCodec2 = createAndConfigureMediaCodec();
 
-        mLcc.addMediaCodec(createAndConfigureMediaCodec());
-        mLcc.setAudioTrack(track);
-        verify(mAudioService).startLoudnessCodecUpdates(eq(track.getPlayerIId()), anyList());
+        try {
+            mLcc.addMediaCodec(mediaCodec1);
+            mLcc.setAudioTrack(track);
+            verify(mAudioService).startLoudnessCodecUpdates(eq(track.getPlayerIId()), anyList());
 
-        mLcc.addMediaCodec(createAndConfigureMediaCodec());
-        verify(mAudioService).addLoudnessCodecInfo(eq(track.getPlayerIId()), anyInt(), any());
+            mLcc.addMediaCodec(mediaCodec2);
+            verify(mAudioService).addLoudnessCodecInfo(eq(track.getPlayerIId()), anyInt(), any());
+        } finally {
+            mediaCodec1.release();
+            mediaCodec2.release();
+        }
     }
 
     @Test
@@ -212,25 +269,36 @@
         final AudioTrack track = createAudioTrack();
         final MediaCodec mediaCodec = createAndConfigureMediaCodec();
 
-        mLcc.addMediaCodec(mediaCodec);
-        mLcc.setAudioTrack(track);
-        verify(mAudioService).startLoudnessCodecUpdates(eq(track.getPlayerIId()), anyList());
+        try {
+            mLcc.addMediaCodec(mediaCodec);
+            mLcc.setAudioTrack(track);
+            verify(mAudioService).startLoudnessCodecUpdates(eq(track.getPlayerIId()), anyList());
 
-        mLcc.removeMediaCodec(mediaCodec);
-        verify(mAudioService).removeLoudnessCodecInfo(eq(track.getPlayerIId()), any());
+            mLcc.removeMediaCodec(mediaCodec);
+            verify(mAudioService).removeLoudnessCodecInfo(eq(track.getPlayerIId()), any());
+        } finally {
+            mediaCodec.release();
+        }
     }
 
     @Test
     @RequiresFlagsEnabled(FLAG_LOUDNESS_CONFIGURATOR_API)
     public void removeWrongMediaCodecAfterSetTrack_triggersIAE() throws Exception {
         final AudioTrack track = createAudioTrack();
+        final MediaCodec mediaCodec1 = createAndConfigureMediaCodec();
+        final MediaCodec mediaCodec2 = createAndConfigureMediaCodec();
 
-        mLcc.addMediaCodec(createAndConfigureMediaCodec());
-        mLcc.setAudioTrack(track);
-        verify(mAudioService).startLoudnessCodecUpdates(eq(track.getPlayerIId()), anyList());
+        try {
+            mLcc.addMediaCodec(mediaCodec1);
+            mLcc.setAudioTrack(track);
+            verify(mAudioService).startLoudnessCodecUpdates(eq(track.getPlayerIId()), anyList());
 
-        assertThrows(IllegalArgumentException.class,
-                () -> mLcc.removeMediaCodec(createAndConfigureMediaCodec()));
+            assertThrows(IllegalArgumentException.class,
+                    () -> mLcc.removeMediaCodec(mediaCodec2));
+        } finally {
+            mediaCodec1.release();
+            mediaCodec2.release();
+        }
     }
 
     private static AudioTrack createAudioTrack() {
@@ -250,19 +318,21 @@
 
         MediaExtractor extractor;
         extractor = new MediaExtractor();
-        extractor.setDataSource(testFd.getFileDescriptor(), testFd.getStartOffset(),
+        try {
+            extractor.setDataSource(testFd.getFileDescriptor(), testFd.getStartOffset(),
                 testFd.getLength());
-        testFd.close();
+            assertEquals("wrong number of tracks", 1, extractor.getTrackCount());
+            MediaFormat format = extractor.getTrackFormat(0);
+            String mime = format.getString(MediaFormat.KEY_MIME);
+            assertTrue("not an audio file", mime.startsWith(TEST_MEDIA_AUDIO_CODEC_PREFIX));
+            final MediaCodec mediaCodec = MediaCodec.createDecoderByType(mime);
 
-        assertEquals("wrong number of tracks", 1, extractor.getTrackCount());
-        MediaFormat format = extractor.getTrackFormat(0);
-        String mime = format.getString(MediaFormat.KEY_MIME);
-        assertTrue("not an audio file", mime.startsWith(TEST_MEDIA_AUDIO_CODEC_PREFIX));
-        final MediaCodec mediaCodec = MediaCodec.createDecoderByType(mime);
-
-        Log.v(TAG, "configuring with " + format);
-        mediaCodec.configure(format, null /* surface */, null /* crypto */, 0 /* flags */);
-
-        return mediaCodec;
+            Log.v(TAG, "configuring with " + format);
+            mediaCodec.configure(format, null /* surface */, null /* crypto */, 0 /* flags */);
+            return mediaCodec;
+        } finally {
+            testFd.close();
+            extractor.release();
+        }
     }
 }
diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt
index 9f2a9ac..9605108 100644
--- a/native/android/libandroid.map.txt
+++ b/native/android/libandroid.map.txt
@@ -1,9 +1,9 @@
 LIBANDROID {
   global:
-    AActivityManager_addUidImportanceListener; # systemapi # introduced=31
-    AActivityManager_removeUidImportanceListener; # systemapi # introduced=31
-    AActivityManager_isUidActive; # systemapi # introduced=31
-    AActivityManager_getUidImportance; # systemapi # introduced=31
+    AActivityManager_addUidImportanceListener; # systemapi introduced=31
+    AActivityManager_removeUidImportanceListener; # systemapi introduced=31
+    AActivityManager_isUidActive; # systemapi introduced=31
+    AActivityManager_getUidImportance; # systemapi introduced=31
     AAssetDir_close;
     AAssetDir_getNextFileName;
     AAssetDir_rewind;
diff --git a/native/graphics/jni/libjnigraphics.map.txt b/native/graphics/jni/libjnigraphics.map.txt
index e0df794..193728a 100644
--- a/native/graphics/jni/libjnigraphics.map.txt
+++ b/native/graphics/jni/libjnigraphics.map.txt
@@ -18,21 +18,21 @@
     AImageDecoder_getRepeatCount; # introduced=31
     AImageDecoder_advanceFrame; # introduced=31
     AImageDecoder_rewind; # introduced=31
-    AImageDecoder_getFrameInfo; # introduced = 31
-    AImageDecoder_setInternallyHandleDisposePrevious; # introduced = 31
+    AImageDecoder_getFrameInfo; # introduced=31
+    AImageDecoder_setInternallyHandleDisposePrevious; # introduced=31
     AImageDecoderHeaderInfo_getWidth; # introduced=30
     AImageDecoderHeaderInfo_getHeight; # introduced=30
     AImageDecoderHeaderInfo_getMimeType; # introduced=30
     AImageDecoderHeaderInfo_getAlphaFlags; # introduced=30
     AImageDecoderHeaderInfo_getAndroidBitmapFormat; # introduced=30
     AImageDecoderHeaderInfo_getDataSpace; # introduced=30
-    AImageDecoderFrameInfo_create; # introduced = 31
-    AImageDecoderFrameInfo_delete; # introduced = 31
-    AImageDecoderFrameInfo_getDuration; # introduced = 31
-    AImageDecoderFrameInfo_getFrameRect; # introduced = 31
-    AImageDecoderFrameInfo_hasAlphaWithinBounds; # introduced = 31
-    AImageDecoderFrameInfo_getDisposeOp; # introduced = 31
-    AImageDecoderFrameInfo_getBlendOp; # introduced = 31
+    AImageDecoderFrameInfo_create; # introduced=31
+    AImageDecoderFrameInfo_delete; # introduced=31
+    AImageDecoderFrameInfo_getDuration; # introduced=31
+    AImageDecoderFrameInfo_getFrameRect; # introduced=31
+    AImageDecoderFrameInfo_hasAlphaWithinBounds; # introduced=31
+    AImageDecoderFrameInfo_getDisposeOp; # introduced=31
+    AImageDecoderFrameInfo_getBlendOp; # introduced=31
     AndroidBitmap_getInfo;
     AndroidBitmap_getDataSpace;
     AndroidBitmap_lockPixels;
diff --git a/packages/CarrierDefaultApp/res/values-fr/strings.xml b/packages/CarrierDefaultApp/res/values-fr/strings.xml
index 4bc03ffa..9e85969 100644
--- a/packages/CarrierDefaultApp/res/values-fr/strings.xml
+++ b/packages/CarrierDefaultApp/res/values-fr/strings.xml
@@ -16,7 +16,7 @@
     <string name="ssl_error_continue" msgid="1138548463994095584">"Continuer quand même dans le navigateur"</string>
     <string name="performance_boost_notification_channel" msgid="3475440855635538592">"Boost de performances"</string>
     <string name="performance_boost_notification_title" msgid="3126203390685781861">"Options 5G de votre opérateur"</string>
-    <string name="performance_boost_notification_detail" msgid="216569851036236346">"Accédez au site Web de %s pour consulter les options pour l\'expérience de votre appli"</string>
+    <string name="performance_boost_notification_detail" msgid="216569851036236346">"Accédez au site Web de %s pour consulter les options de votre appli"</string>
     <string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"Pas maintenant"</string>
     <string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"Gérer"</string>
     <string name="slice_purchase_app_label" msgid="7170191659233241166">"Achetez un boost de performances."</string>
diff --git a/packages/CompanionDeviceManager/res/layout/data_transfer_confirmation.xml b/packages/CompanionDeviceManager/res/layout/data_transfer_confirmation.xml
index db8ebb4..1ac5db6 100644
--- a/packages/CompanionDeviceManager/res/layout/data_transfer_confirmation.xml
+++ b/packages/CompanionDeviceManager/res/layout/data_transfer_confirmation.xml
@@ -18,54 +18,63 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    style="@style/ScrollViewStyle">
+    style="@style/ScrollViewStyle"
+    android:importantForAccessibility="no">
 
     <LinearLayout
-        android:id="@+id/data_transfer_confirmation"
-        style="@style/ContainerLayout">
-
-        <!-- Do NOT change the ID of the root LinearLayout above: it's referenced in CTS tests. -->
-
-        <ImageView
-            android:id="@+id/header_icon"
-            android:layout_width="match_parent"
-            android:layout_height="32dp"
-            android:gravity="center"
-            android:layout_marginTop="18dp"
-            android:src="@drawable/ic_warning"
-            android:contentDescription="@null" />
-
-        <LinearLayout style="@style/Description">
-
-            <TextView
-                android:id="@+id/title"
-                style="@style/DescriptionTitle" />
-
-            <TextView
-                android:id="@+id/summary"
-                style="@style/DescriptionSummary" />
-
-        </LinearLayout>
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:baselineAligned="false"
+        android:importantForAccessibility="no">
 
         <LinearLayout
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:gravity="center"
-            android:orientation="vertical"
-            android:layout_marginTop="12dp"
-            android:layout_marginBottom="18dp">
+            android:id="@+id/data_transfer_confirmation"
+            style="@style/ContainerLayout">
 
-            <!-- Do NOT change the IDs of the buttons: they are referenced in CTS tests. -->
+            <!-- Do NOT change the ID of the root LinearLayout above: it's referenced in CTS tests. -->
 
-            <Button
-                android:id="@+id/btn_positive"
-                style="@style/PositiveButton"
-                android:text="@string/consent_yes" />
+            <ImageView
+                android:id="@+id/header_icon"
+                android:layout_width="match_parent"
+                android:layout_height="32dp"
+                android:gravity="center"
+                android:layout_marginTop="18dp"
+                android:src="@drawable/ic_warning"
+                android:contentDescription="@null" />
 
-            <Button
-                android:id="@+id/btn_negative"
-                style="@style/NegativeButton"
-                android:text="@string/consent_no" />
+            <LinearLayout style="@style/Description">
+
+                <TextView
+                    android:id="@+id/title"
+                    style="@style/DescriptionTitle" />
+
+                <TextView
+                    android:id="@+id/summary"
+                    style="@style/DescriptionSummary" />
+
+            </LinearLayout>
+
+            <LinearLayout
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:gravity="center"
+                android:orientation="vertical"
+                android:layout_marginTop="12dp"
+                android:layout_marginBottom="18dp">
+
+                <!-- Do NOT change the IDs of the buttons: they are referenced in CTS tests. -->
+
+                <Button
+                    android:id="@+id/btn_positive"
+                    style="@style/PositiveButton"
+                    android:text="@string/consent_yes" />
+
+                <Button
+                    android:id="@+id/btn_negative"
+                    style="@style/NegativeButton"
+                    android:text="@string/consent_no" />
+
+            </LinearLayout>
 
         </LinearLayout>
 
diff --git a/packages/CompanionDeviceManager/res/values-af/strings.xml b/packages/CompanionDeviceManager/res/values-af/strings.xml
index 285d2d1..1f39509 100644
--- a/packages/CompanionDeviceManager/res/values-af/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-af/strings.xml
@@ -56,28 +56,18 @@
     <string name="permission_nearby_devices" msgid="7530973297737123481">"Toestelle in die omtrek"</string>
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Verander media-uitset"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Foto\'s en media"</string>
-    <!-- no translation found for permission_notifications (4099418516590632909) -->
-    <skip />
+    <string name="permission_notifications" msgid="4099418516590632909">"Kennisgewings"</string>
     <string name="permission_app_streaming" msgid="6009695219091526422">"Apps"</string>
     <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Stroming"</string>
-    <!-- no translation found for permission_phone_summary (8246321093970051702) -->
-    <skip />
-    <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
-    <skip />
-    <!-- no translation found for permission_sms_summary (8499509535410068616) -->
-    <skip />
-    <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
-    <skip />
-    <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
-    <skip />
-    <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
-    <skip />
-    <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
-    <skip />
-    <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
-    <skip />
-    <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
-    <skip />
+    <string name="permission_phone_summary" msgid="8246321093970051702">"Maak en bestuur foonoproepe"</string>
+    <string name="permission_call_logs_summary" msgid="7545243592757693321">"Lees en skryf foonoproeprekord neer"</string>
+    <string name="permission_sms_summary" msgid="8499509535410068616">"Stuur en bekyk SMS-boodskappe"</string>
+    <string name="permission_contacts_summary" msgid="2840800622763086808">"Verkry toegang tot jou kontakte"</string>
+    <string name="permission_calendar_summary" msgid="8430353935747336165">"Verkry toegang tot jou kalender"</string>
+    <string name="permission_microphone_summary" msgid="4862628553869973259">"Neem oudio op"</string>
+    <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Vind, koppel aan en bepaal die relatiewe posisie van toestelle in die omtrek"</string>
+    <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Lees alle kennisgewings, insluitend inligting soos kontakte, boodskappe en foto’s"</string>
+    <string name="permission_notifications_summary" msgid="2272810466047367030">"• Lees alle kennisgewings, insluitend inligting soos kontakte, boodskappe en foto’s&lt;br/&gt;• Stuur kennisgewings&lt;br/&gt;&lt;br/&gt;Jy kan hierdie app se vermoë om kennisgewings te lees en te stuur, enige tyd in Instellings bestuur &gt; Kennisgewings."</string>
     <string name="permission_app_streaming_summary" msgid="606923325679670624">"Stroom jou foon se apps"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
     <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Stroom apps en ander stelselkenmerke van jou foon af"</string>
diff --git a/packages/CompanionDeviceManager/res/values-am/strings.xml b/packages/CompanionDeviceManager/res/values-am/strings.xml
index 996d2ea..007173c 100644
--- a/packages/CompanionDeviceManager/res/values-am/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-am/strings.xml
@@ -56,28 +56,18 @@
     <string name="permission_nearby_devices" msgid="7530973297737123481">"á‰ áŠ á‰…áˆ«á‰ąá‹« ያሉ መሣáˆȘያዎቜ"</string>
     <string name="permission_media_routing_control" msgid="5498639511586715253">"ዹሚá‹Čያ ውጀቔን ይቀይሩ"</string>
     <string name="permission_storage" msgid="6831099350839392343">"ፎቶዎቜ ኄና ሚá‹Čያ"</string>
-    <!-- no translation found for permission_notifications (4099418516590632909) -->
-    <skip />
+    <string name="permission_notifications" msgid="4099418516590632909">"áˆ›áˆłá‹ˆá‰‚á‹«á‹Žá‰œ"</string>
     <string name="permission_app_streaming" msgid="6009695219091526422">"መተግበáˆȘያዎቜ"</string>
     <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"በዄሚቔ መልቀቅ"</string>
-    <!-- no translation found for permission_phone_summary (8246321093970051702) -->
-    <skip />
-    <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
-    <skip />
-    <!-- no translation found for permission_sms_summary (8499509535410068616) -->
-    <skip />
-    <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
-    <skip />
-    <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
-    <skip />
-    <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
-    <skip />
-    <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
-    <skip />
-    <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
-    <skip />
-    <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
-    <skip />
+    <string name="permission_phone_summary" msgid="8246321093970051702">"ዚሔልክ ጄáˆȘዎቜ ያዔርጉ ኄና á‹«áˆ”á‰°á‹łá‹”áˆ©"</string>
+    <string name="permission_call_logs_summary" msgid="7545243592757693321">"ዚሔልክ ጄáˆȘ ምዝግቄ áˆ›áˆ”á‰łá‹ˆáˆ»áŠ• ያንቄብ ኄና ይጻፉ"</string>
+    <string name="permission_sms_summary" msgid="8499509535410068616">"ዚኀሔኀምኀሔ መልዕክቶቜን ይላኩና ይመልኚቱ"</string>
+    <string name="permission_contacts_summary" msgid="2840800622763086808">"ዚኄርሔዎን ኄውቂያዎቜ á‹­á‹”áˆšáˆ±á‰Łá‰žá‹"</string>
+    <string name="permission_calendar_summary" msgid="8430353935747336165">"ዚኄርሔዎን ቀን መቁጠáˆȘያ ይዔሚሱበቔ"</string>
+    <string name="permission_microphone_summary" msgid="4862628553869973259">"ኩá‹Čዼ ቅሚጜ"</string>
+    <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"á‰ áŠ á‰…áˆ«á‰ąá‹« ያሉ መሣáˆȘያዎቜን áˆ›áŒáŠ˜á‰”áŁ ኚኄነሱ ጋር መገናኘቔ ኄና አንጻራዊ ቩታቾውን መወሰን"</string>
+    <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"ኄንደ áŠ„á‹á‰‚á‹«á‹Žá‰œáŁ መልዕክቶቜ ኄና ፎቶዎቜ ያሉ መሚጃዎቜን ጹምሼ ሁሉንም áˆ›áˆłá‹ˆá‰‚á‹«á‹Žá‰œ ማንበቄ"</string>
+    <string name="permission_notifications_summary" msgid="2272810466047367030">"• ኄንደ áŠ„á‹á‰‚á‹«á‹Žá‰œáŁ መልዕክቶቜ ኄና ፎቶዎቜ ያሉ መሚጃዎቜን ጹምሼ ሁሉንም áˆ›áˆłá‹ˆá‰‚á‹«á‹Žá‰œ ያንቄብ&lt;br/&gt;• áˆ›áˆłá‹ˆá‰‚á‹«á‹Žá‰œáŠ• መላክ&lt;br/&gt;&lt;br/&gt;ዹዚህን መተግበáˆȘያ áˆ›áˆłá‹ˆá‰‚á‹«á‹Žá‰œáŠ• ዚማንበቄ ኄና ዹመላክ á‰œáˆŽá‰łáŠ• á‰ á‰…áŠ•á‰„áˆźá‰œ &gt; áˆ›áˆłá‹ˆá‰‚á‹«á‹Žá‰œ ውሔጄ áˆ›áˆ”á‰°á‹łá‹°áˆ­ á‹­á‰œáˆ‹áˆ‰áą"</string>
     <string name="permission_app_streaming_summary" msgid="606923325679670624">"ዚሔልክዎን መተግበáˆȘያዎቜ በዄሚቔ ይልቀቁ"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
     <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"ኚሔልክዎ ሆነው መተግበáˆȘያዎቜ ኄና ሌሎቜ ዚሔርዓቔ ባህáˆȘያቔን በዄሚቔ ይልቀቁ"</string>
diff --git a/packages/CompanionDeviceManager/res/values-ar/strings.xml b/packages/CompanionDeviceManager/res/values-ar/strings.xml
index 58cf926..6ddd5d3 100644
--- a/packages/CompanionDeviceManager/res/values-ar/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ar/strings.xml
@@ -54,36 +54,24 @@
     <string name="permission_microphone" msgid="2152206421428732949">"Ű§Ù„Ù…ÙŠÙƒŰ±ÙˆÙÙˆÙ†"</string>
     <string name="permission_call_logs" msgid="5546761417694586041">"ŰłŰŹÙ„Ù‘ Ű§Ù„Ù…ÙƒŰ§Ù„Ù…Ű§ŰȘ"</string>
     <string name="permission_nearby_devices" msgid="7530973297737123481">"Ű§Ù„ŰŁŰŹÙ‡ŰČŰ© Ű§Ù„Ù…ŰŹŰ§ÙˆŰ±Ű©"</string>
-    <!-- no translation found for permission_media_routing_control (5498639511586715253) -->
-    <skip />
+    <string name="permission_media_routing_control" msgid="5498639511586715253">"ŰȘŰșÙŠÙŠŰ± ŰŹÙ‡Ű§ŰČ Ű„ŰźŰ±Ű§ŰŹ Ű§Ù„ÙˆŰłŰ§ŰŠŰ·"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Ű§Ù„Ű”ÙˆŰ± ÙˆŰ§Ù„ÙˆŰłŰ§ŰŠŰ·"</string>
-    <!-- no translation found for permission_notifications (4099418516590632909) -->
-    <skip />
+    <string name="permission_notifications" msgid="4099418516590632909">"Ű§Ù„Ű„ŰŽŰčۧ۱ۧŰȘ"</string>
     <string name="permission_app_streaming" msgid="6009695219091526422">"Ű§Ù„ŰȘŰ·ŰšÙŠÙ‚Ű§ŰȘ"</string>
     <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Ű§Ù„ŰšŰ«Ù‘"</string>
-    <!-- no translation found for permission_phone_summary (8246321093970051702) -->
-    <skip />
-    <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
-    <skip />
-    <!-- no translation found for permission_sms_summary (8499509535410068616) -->
-    <skip />
-    <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
-    <skip />
-    <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
-    <skip />
-    <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
-    <skip />
-    <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
-    <skip />
-    <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
-    <skip />
-    <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
-    <skip />
+    <string name="permission_phone_summary" msgid="8246321093970051702">"ۄۏ۱ۧۥ Ű§Ù„Ù…ÙƒŰ§Ù„Ù…Ű§ŰȘ Ű§Ù„Ù‡Ű§ŰȘÙÙŠŰ© ÙˆŰ„ŰŻŰ§Ű±ŰȘÙ‡Ű§"</string>
+    <string name="permission_call_logs_summary" msgid="7545243592757693321">"Ù‚Ű±Ű§ŰĄŰ© ŰłŰŹÙ„Ù‘ Ű§Ù„Ù…ÙƒŰ§Ù„Ù…Ű§ŰȘ Ű§Ù„Ù‡Ű§ŰȘÙÙŠŰ© ÙˆŰ§Ù„ÙƒŰȘۧۚ۩ Ű„Ù„ÙŠÙ‡"</string>
+    <string name="permission_sms_summary" msgid="8499509535410068616">"Ű„Ű±ŰłŰ§Ù„ Ű§Ù„Ű±ŰłŰ§ŰŠÙ„ Ű§Ù„Ù‚Ű”ÙŠŰ±Ű© وŰčŰ±Ű¶Ù‡Ű§"</string>
+    <string name="permission_contacts_summary" msgid="2840800622763086808">"Ű§Ù„ÙˆŰ”ÙˆÙ„ Ű„Ù„Ù‰ ŰŹÙ‡Ű§ŰȘ ۧŰȘŰ”Ű§Ù„Ùƒ"</string>
+    <string name="permission_calendar_summary" msgid="8430353935747336165">"Ű§Ù„ÙˆŰ”ÙˆÙ„ Ű„Ù„Ù‰ ŰȘقويمك"</string>
+    <string name="permission_microphone_summary" msgid="4862628553869973259">"ŰȘŰłŰŹÙŠÙ„ Ű§Ù„Ű”ÙˆŰȘ"</string>
+    <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"يمكن Ű§Ù„ŰčŰ«ÙˆŰ± Űčلى Ű§Ù„Ù…ÙˆŰ¶Űč Ű§Ù„Ù†ŰłŰšÙŠ Ù„Ù„ŰŁŰŹÙ‡ŰČŰ© Ű§Ù„Ù…ŰŹŰ§ÙˆŰ±Ű© ÙˆŰ§Ù„Ű±ŰšŰ· ŰšÙ‡Ű§ وŰȘŰ­ŰŻÙŠŰŻÙ‡Ű§."</string>
+    <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"يمكن Ù„Ù‡Ű°Ű§ Ű§Ù„Ù…Ù„Ù Ű§Ù„ŰŽŰźŰ”ÙŠ Ù‚Ű±Ű§ŰĄŰ© ŰŹÙ…ÙŠŰč Ű§Ù„Ű„ŰŽŰčۧ۱ۧŰȘی ŰšÙ…Ű§ في Ű°Ù„Ùƒ Ű§Ù„Ù…ŰčÙ„ÙˆÙ…Ű§ŰȘی Ù…Ű«Ù„ ŰŹÙ‡Ű§ŰȘ Ű§Ù„Ű§ŰȘŰ”Ű§Ù„ ÙˆŰ§Ù„Ű±ŰłŰ§ŰŠÙ„ ÙˆŰ§Ù„Ű”ÙˆŰ±."</string>
+    <string name="permission_notifications_summary" msgid="2272810466047367030">"‏• Ù‚Ű±Ű§ŰĄŰ© كل Ű§Ù„Ű„ŰŽŰčۧ۱ۧŰȘ ŰšÙ…Ű§ ÙÙŠÙ‡Ű§ Ű§Ù„Ù…ŰčÙ„ÙˆÙ…Ű§ŰȘی Ù…Ű«Ù„ ŰŹÙ‡Ű§ŰȘ Ű§Ù„Ű§ŰȘŰ”Ű§Ù„ ÙˆŰ§Ù„Ű±ŰłŰ§ŰŠÙ„ ÙˆŰ§Ù„Ű”ÙˆŰ±&lt;br/&gt;• Ű„Ű±ŰłŰ§Ù„ Ű§Ù„Ű„ŰŽŰčۧ۱ۧŰȘ&lt;br/&gt;&lt;br/&gt;يمكنك ۄۯۧ۱۩ Ű§Ù„Ű„Ű°Ù† Ű§Ù„Ù…Ù…Ù†ÙˆŰ­ Ù„Ù‡Ű°Ű§ Ű§Ù„ŰȘŰ·ŰšÙŠÙ‚ ŰšÙ‚Ű±Ű§ŰĄŰ© Ű§Ù„Ű„ŰŽŰčۧ۱ۧŰȘ ÙˆŰ„Ű±ŰłŰ§Ù„Ù‡Ű§ في ŰŁÙŠ وقŰȘ من ŰźÙ„Ű§Ù„ Ű§Ù„Ű„ŰčۯۧۯۧŰȘ &gt; Ű§Ù„Ű„ŰŽŰčۧ۱ۧŰȘ."</string>
     <string name="permission_app_streaming_summary" msgid="606923325679670624">"ۚ۫ ŰȘŰ·ŰšÙŠÙ‚Ű§ŰȘ Ù‡Ű§ŰȘفك"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
     <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"ŰšŰ«Ù‘ Ű§Ù„ŰȘŰ·ŰšÙŠÙ‚Ű§ŰȘ وميŰČۧŰȘ Ű§Ù„Ù†ŰžŰ§Ù… Ű§Ù„ŰŁŰźŰ±Ù‰ من Ù‡Ű§ŰȘفك"</string>
-    <!-- no translation found for permission_media_routing_control_summary (2714631092321412250) -->
-    <skip />
+    <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"يŰȘÙŠŰ­ لك Ù‡Ű°Ű§ Ű§Ù„Ű„Ű°Ù† Ű§Ù„Ű§Ű·Ù‘Ù„Ű§Űč Űčلى Ù…ŰŹÙ…ÙˆŰčŰ© من Ű§Ù„ŰŁŰŹÙ‡ŰČŰ© Ű§Ù„Ù…ŰȘۭۧ۩ وŰȘŰ­ŰŻÙŠŰŻ Ű§Ù„ŰŹÙ‡Ű§ŰČ Ű§Ù„Ù…ŰłÙ…ÙˆŰ­ له ŰšŰȘŰŽŰșيل Ű§Ù„Ű”ÙˆŰȘ ŰŁÙˆ Ű§Ù„ÙÙŠŰŻÙŠÙˆ ŰŁÙˆ ŰšŰ«Ù‘Ù‡Ù…Ű§ من Ű§Ù„ŰȘŰ·ŰšÙŠÙ‚Ű§ŰȘ Ű§Ù„ŰŁŰźŰ±Ù‰."</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"Ù‡Ű§ŰȘف"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"ŰŹÙ‡Ű§ŰČ Ù„ÙˆŰ­ÙŠ"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-as/strings.xml b/packages/CompanionDeviceManager/res/values-as/strings.xml
index fddc24c..623b212 100644
--- a/packages/CompanionDeviceManager/res/values-as/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-as/strings.xml
@@ -56,28 +56,18 @@
     <string name="permission_nearby_devices" msgid="7530973297737123481">"àŠšàŠżàŠ•àŠŸà§±à§°à§àŠ€à§€ àŠĄàŠżàŠ­àŠŸàŠ‡àŠš"</string>
     <string name="permission_media_routing_control" msgid="5498639511586715253">"àŠźàŠżàŠĄàŠżàŠŻàŠŒàŠŸ àŠ†àŠ‰àŠŸàŠȘà§àŠŸ àŠžàŠČàŠšàŠż àŠ•à§°àŠ•"</string>
     <string name="permission_storage" msgid="6831099350839392343">"àŠ«àŠŸ’ àŠ†à§°à§ àŠźàŠżàŠĄàŠżàŠŻàŠŒàŠŸ"</string>
-    <!-- no translation found for permission_notifications (4099418516590632909) -->
-    <skip />
+    <string name="permission_notifications" msgid="4099418516590632909">"àŠœàŠŸàŠšàŠšà§€"</string>
     <string name="permission_app_streaming" msgid="6009695219091526422">"àŠàŠȘ্"</string>
     <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"àŠ·à§àŠŸà§à§°à§€àŠź àŠ•à§°àŠż àŠ„àŠ•àŠŸ àŠčà§ˆàŠ›à§‡"</string>
-    <!-- no translation found for permission_phone_summary (8246321093970051702) -->
-    <skip />
-    <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
-    <skip />
-    <!-- no translation found for permission_sms_summary (8499509535410068616) -->
-    <skip />
-    <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
-    <skip />
-    <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
-    <skip />
-    <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
-    <skip />
-    <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
-    <skip />
-    <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
-    <skip />
-    <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
-    <skip />
+    <string name="permission_phone_summary" msgid="8246321093970051702">"àŠ«’àŠš àŠ•àŠČ àŠ•à§°àŠ• àŠ†à§°à§ àŠžà§‡àŠŻàŠŒàŠŸ àŠȘà§°àŠżàŠšàŠČàŠšàŠŸ àŠ•à§°àŠ•"</string>
+    <string name="permission_call_logs_summary" msgid="7545243592757693321">"àŠ«’àŠšà§° àŠ•àŠČ àŠČàŠ— àŠȘàŠąàŠŒàŠ• àŠ†à§°à§ àŠČàŠżàŠ–àŠ•"</string>
+    <string name="permission_sms_summary" msgid="8499509535410068616">"àŠàŠ›àŠàŠźàŠàŠ› àŠŹàŠŸàŠ°à§àŠ€àŠŸàŠŹà§‹à§° àŠȘà§à§°à§‡à§°àŠŁ àŠ•à§°àŠ• àŠ†à§°à§ àŠšàŠŸàŠ“àŠ•"</string>
+    <string name="permission_contacts_summary" msgid="2840800622763086808">"àŠ†àŠȘà§‹àŠšàŠŸà§° àŠžàŠźà§àŠȘà§°à§àŠ•àŠžà§‚àŠšà§€ àŠàŠ•à§àŠžà§‡àŠ› àŠ•à§°àŠ•"</string>
+    <string name="permission_calendar_summary" msgid="8430353935747336165">"àŠ†àŠȘà§‹àŠšàŠŸà§° àŠ•à§‡àŠČà§‡àŠŁà§àŠĄàŠŸà§° àŠàŠ•à§àŠžà§‡àŠ› àŠ•à§°àŠ•"</string>
+    <string name="permission_microphone_summary" msgid="4862628553869973259">"àŠ…àŠĄàŠżàŠ…’ à§°à§‡àŠ•à§°à§àŠĄ àŠ•à§°àŠ•"</string>
+    <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"àŠšàŠżàŠ•àŠŸà§±à§°à§àŠ€à§€ àŠĄàŠżàŠ­àŠŸàŠ‡àŠšàŠžàŠźà§‚àŠč àŠŹàŠżàŠšàŠŸà§°àŠ•, àŠžà§‡àŠ‡àŠžàŠźà§‚àŠčà§° àŠžà§ˆàŠ€à§‡ àŠžàŠ‚àŠŻà§‹àŠ— àŠ•à§°àŠ• àŠ†à§°à§ àŠžà§‡àŠ‡àŠžàŠźà§‚àŠčà§° àŠȘà§à§°àŠŸàŠžàŠ‚àŠ—àŠżàŠ• àŠ…à§±àŠžà§àŠ„àŠŸàŠš àŠšàŠżà§°à§àŠ§àŠŸà§°àŠŁ àŠ•à§°àŠ•"</string>
+    <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"àŠžàŠźà§àŠȘà§°à§àŠ•àŠžà§‚àŠšà§€, àŠŹàŠŸà§°à§àŠ€àŠŸ àŠ†à§°à§ àŠ«àŠŸ’à§° àŠŠà§°à§‡ àŠ€àŠ„à§àŠŻàŠ•à§‡ àŠ§à§°àŠż àŠ†àŠŸàŠŸàŠ‡àŠŹà§‹à§° àŠœàŠŸàŠšàŠšà§€ àŠȘàŠąàŠŒàŠ•"</string>
+    <string name="permission_notifications_summary" msgid="2272810466047367030">"• àŠžàŠźà§àŠȘà§°à§àŠ•àŠžà§‚àŠšà§€, àŠŹàŠŸà§°à§àŠ€àŠŸ àŠ†à§°à§ àŠ«àŠŸ’à§° àŠŠà§°à§‡ àŠ€àŠ„à§àŠŻàŠ•à§‡ àŠ§à§°àŠż àŠ†àŠŸàŠŸàŠ‡àŠŹà§‹à§° àŠœàŠŸàŠšàŠšà§€ àŠȘàŠąàŠŒàŠ•&lt;br/&gt;• àŠœàŠŸàŠšàŠšà§€ àŠȘàŠ àŠżàŠŻàŠŒàŠŸàŠ“àŠ•&lt;br/&gt;&lt;br/&gt;àŠ†àŠȘà§àŠšàŠż àŠŻàŠżàŠ•à§‹àŠšà§‹ àŠžàŠźàŠŻàŠŒàŠ€à§‡ àŠ›à§‡àŠŸàŠżàŠ‚ &gt; àŠœàŠŸàŠšàŠšà§€àŠČৈ àŠ—à§ˆ àŠàŠ‡ àŠàŠȘ্‌àŠŸà§‹à§° àŠœàŠŸàŠšàŠšà§€ àŠȘàŠąàŠŒàŠŸ àŠ†à§°à§ àŠȘàŠ àŠżàŠ“à§±àŠŸà§° àŠžàŠ•à§àŠ·àŠźàŠ€àŠŸ àŠȘà§°àŠżàŠšàŠŸàŠČàŠšàŠŸ àŠ•à§°àŠżàŠŹ àŠȘàŠŸà§°à§‡à„€"</string>
     <string name="permission_app_streaming_summary" msgid="606923325679670624">"àŠ†àŠȘà§‹àŠšàŠŸà§° àŠ«’àŠšà§° àŠàŠȘ্ àŠ·à§àŠŸà§à§°à§€àŠź àŠ•à§°àŠ•"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
     <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"àŠ†àŠȘà§‹àŠšàŠŸà§° àŠ«’àŠšà§° àŠȘà§°àŠŸ àŠàŠȘ্‌ àŠ†à§°à§ àŠ›àŠżàŠ·à§àŠŸà§‡àŠźà§° àŠ…àŠšà§àŠŻ àŠžà§àŠŹàŠżàŠ§àŠŸàŠžàŠźà§‚àŠč àŠ·à§àŠŸà§à§°à§€àŠź àŠ•à§°àŠ•"</string>
diff --git a/packages/CompanionDeviceManager/res/values-az/strings.xml b/packages/CompanionDeviceManager/res/values-az/strings.xml
index a3ed669..65f3a62 100644
--- a/packages/CompanionDeviceManager/res/values-az/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-az/strings.xml
@@ -56,28 +56,18 @@
     <string name="permission_nearby_devices" msgid="7530973297737123481">"Yaxınlıqdakı cihazlar"</string>
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Media çıxıßını dəyißin"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Foto və media"</string>
-    <!-- no translation found for permission_notifications (4099418516590632909) -->
-    <skip />
+    <string name="permission_notifications" msgid="4099418516590632909">"BildiriƟlər"</string>
     <string name="permission_app_streaming" msgid="6009695219091526422">"Tətbiqlər"</string>
     <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Yayım"</string>
-    <!-- no translation found for permission_phone_summary (8246321093970051702) -->
-    <skip />
-    <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
-    <skip />
-    <!-- no translation found for permission_sms_summary (8499509535410068616) -->
-    <skip />
-    <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
-    <skip />
-    <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
-    <skip />
-    <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
-    <skip />
-    <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
-    <skip />
-    <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
-    <skip />
-    <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
-    <skip />
+    <string name="permission_phone_summary" msgid="8246321093970051702">"Telefon zənglərinin edilməsi və idarəsi"</string>
+    <string name="permission_call_logs_summary" msgid="7545243592757693321">"Telefon zəng siyahısının oxunması və yazılması"</string>
+    <string name="permission_sms_summary" msgid="8499509535410068616">"SMS mesajlarının göndərilməsi və onlara baxmaq"</string>
+    <string name="permission_contacts_summary" msgid="2840800622763086808">"Kontaktlara giriß"</string>
+    <string name="permission_calendar_summary" msgid="8430353935747336165">"Təqvimə giriƟ"</string>
+    <string name="permission_microphone_summary" msgid="4862628553869973259">"Audionun qeydə alınması"</string>
+    <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Yaxınlıqdakı cihazların tapılması, onlara qoßulmaq və nisbi mövqelərinin təyin edilməsi"</string>
+    <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Kontakt, mesaj və foto kimi məlumatlar daxil olmaqla bütün bildirißlərin oxunması"</string>
+    <string name="permission_notifications_summary" msgid="2272810466047367030">"• Kontakt, mesaj və foto kimi məlumatlar daxil olmaqla bütün bildirißlərin oxunması&lt;br/&gt;• Bildirißlərin göndərilməsi&lt;br/&gt;&lt;br/&gt;Bu tətbiqin bildirißləri oxumaq və göndərmək imkanını Ayarlar &gt; Bildirißlər bölməsində idarə edə bilərsiniz."</string>
     <string name="permission_app_streaming_summary" msgid="606923325679670624">"Telefonunuzun tətbiqlərini yayımlayın"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
     <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Telefondan tətbiq və digər sistem funksiyalarını yayımlayın"</string>
diff --git a/packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml b/packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml
index 071d93e..314d9ff 100644
--- a/packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml
@@ -56,28 +56,18 @@
     <string name="permission_nearby_devices" msgid="7530973297737123481">"Uređaji u blizini"</string>
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Promena medijskog izlaza"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Slike i mediji"</string>
-    <!-- no translation found for permission_notifications (4099418516590632909) -->
-    <skip />
+    <string name="permission_notifications" msgid="4099418516590632909">"Obaveštenja"</string>
     <string name="permission_app_streaming" msgid="6009695219091526422">"Aplikacije"</string>
     <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Striming"</string>
-    <!-- no translation found for permission_phone_summary (8246321093970051702) -->
-    <skip />
-    <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
-    <skip />
-    <!-- no translation found for permission_sms_summary (8499509535410068616) -->
-    <skip />
-    <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
-    <skip />
-    <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
-    <skip />
-    <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
-    <skip />
-    <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
-    <skip />
-    <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
-    <skip />
-    <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
-    <skip />
+    <string name="permission_phone_summary" msgid="8246321093970051702">"Upućivanje telefonskih poziva i upravljanje njima"</string>
+    <string name="permission_call_logs_summary" msgid="7545243592757693321">"Čitanje i pisanje evidencije poziva na telefonu"</string>
+    <string name="permission_sms_summary" msgid="8499509535410068616">"Slanje i pregled SMS poruka"</string>
+    <string name="permission_contacts_summary" msgid="2840800622763086808">"Pristup kontaktima"</string>
+    <string name="permission_calendar_summary" msgid="8430353935747336165">"Pristup kalendaru"</string>
+    <string name="permission_microphone_summary" msgid="4862628553869973259">"Snimanje zvuka"</string>
+    <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"PronalaĆŸenje uređaja u blizini, utvrđivanje njihove relativne pozicije i povezivanje sa njima"</string>
+    <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Čitanje svih obaveštenja, uključujući informacija poput kontakata, poruka i slika"</string>
+    <string name="permission_notifications_summary" msgid="2272810466047367030">"• Čitanje svih obaveštenja, uključujući informacija poput kontakata, poruka i slika&lt;br/&gt;• Slanje obaveštenja&lt;br/&gt;&lt;br/&gt;Da biste upravljali dozvolama ove aplikacije za čitanje i slanje obaveštenja, idite u Podešavanja &gt; Obaveštenja."</string>
     <string name="permission_app_streaming_summary" msgid="606923325679670624">"Strimujte aplikacije na telefonu"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
     <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Strimujte aplikacije i druge sistemske funkcije sa telefona"</string>
diff --git a/packages/CompanionDeviceManager/res/values-be/strings.xml b/packages/CompanionDeviceManager/res/values-be/strings.xml
index d99b815..fc17899 100644
--- a/packages/CompanionDeviceManager/res/values-be/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-be/strings.xml
@@ -56,28 +56,18 @@
     <string name="permission_nearby_devices" msgid="7530973297737123481">"ĐŸŃ€Ń‹Đ»Đ°ĐŽŃ‹ ĐżĐ°Đ±Đ»Ń–Đ·Ńƒ"</string>
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Đ—ĐŒŃĐœŃ–Ń†ŃŒ ĐżŃ€Ń‹Đ»Đ°ĐŽŃƒ ĐČыĐČаЎу ĐŒĐ”ĐŽŃ‹Ń"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Đ€ĐŸŃ‚Đ° і ĐŒĐ”ĐŽŃ‹ŃŃ„Đ°Đčлы"</string>
-    <!-- no translation found for permission_notifications (4099418516590632909) -->
-    <skip />
+    <string name="permission_notifications" msgid="4099418516590632909">"АпаĐČŃŃˆŃ‡ŃĐœĐœŃ–"</string>
     <string name="permission_app_streaming" msgid="6009695219091526422">"ĐŸŃ€Đ°ĐłŃ€Đ°ĐŒŃ‹"</string>
     <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"ĐŸĐ”Ń€Đ°ĐŽĐ°Ń‡Đ° ĐżĐ»Ń‹ĐœĐœŃŽ"</string>
-    <!-- no translation found for permission_phone_summary (8246321093970051702) -->
-    <skip />
-    <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
-    <skip />
-    <!-- no translation found for permission_sms_summary (8499509535410068616) -->
-    <skip />
-    <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
-    <skip />
-    <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
-    <skip />
-    <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
-    <skip />
-    <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
-    <skip />
-    <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
-    <skip />
-    <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
-    <skip />
+    <string name="permission_phone_summary" msgid="8246321093970051702">"ĐĐ¶Ń‹Ń†Ń†ŃŃžĐ»Đ”ĐœĐœĐ” Ń‚ŃĐ»Đ”Ń„ĐŸĐœĐœŃ‹Ń… ĐČыĐșліĐșаў і ĐșіраĐČĐ°ĐœĐœĐ” Ń–ĐŒŃ–"</string>
+    <string name="permission_call_logs_summary" msgid="7545243592757693321">"Đ§Ń‹Ń‚Đ°ĐœĐœĐ” і запіс Đ¶ŃƒŃ€ĐœĐ°Đ»Đ° Ń‚ŃĐ»Đ”Ń„ĐŸĐœĐœŃ‹Ń… ĐČыĐșліĐșаў"</string>
+    <string name="permission_sms_summary" msgid="8499509535410068616">"АЮпраўĐșа SMS-паĐČĐ”ĐŽĐ°ĐŒĐ»Đ”ĐœĐœŃŃž і іх ĐżŃ€Đ°ĐłĐ»ŃĐŽ"</string>
+    <string name="permission_contacts_summary" msgid="2840800622763086808">"Đ”ĐŸŃŃ‚ŃƒĐż Ўа ĐČашых ĐșĐ°ĐœŃ‚Đ°Đșтаў"</string>
+    <string name="permission_calendar_summary" msgid="8430353935747336165">"Đ”ĐŸŃŃ‚ŃƒĐż Ўа ĐČашага ĐșĐ°Đ»Đ”ĐœĐŽĐ°Ń€Đ°"</string>
+    <string name="permission_microphone_summary" msgid="4862628553869973259">"Запіс гуĐșу"</string>
+    <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"ĐŸĐŸŃˆŃƒĐș прылаЎ ĐżĐ°Đ±Đ»Ń–Đ·Ńƒ, паЎĐșĐ»ŃŽŃ‡ŃĐœĐœĐ” Ўа іх і ĐČŃ‹Đ·ĐœĐ°Ń‡ŃĐœĐœĐ” іх Đ°ĐŽĐœĐŸŃĐœĐ°ĐłĐ° ĐŒĐ”ŃŃ†Đ°Đ·ĐœĐ°Ń…ĐŸĐŽĐ¶Đ°ĐœĐœŃ"</string>
+    <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Đ§Ń‹Ń‚Đ°ĐœĐœĐ” ўсіх апаĐČŃŃˆŃ‡ŃĐœĐœŃŃž, у Ń‚Ń‹ĐŒ ліĐșу таĐșĐŸĐč Ń–ĐœŃ„Đ°Ń€ĐŒĐ°Ń†Ń‹Ń–, яĐș ĐșĐ°ĐœŃ‚Đ°Đșты, паĐČĐ”ĐŽĐ°ĐŒĐ»Đ”ĐœĐœŃ– і Ń„ĐŸŃ‚Đ°"</string>
+    <string name="permission_notifications_summary" msgid="2272810466047367030">"• Đ§Ń‹Ń‚Đ°ĐœĐœĐ” ўсіх апаĐČŃŃˆŃ‡ŃĐœĐœŃŃž, у Ń‚Ń‹ĐŒ ліĐșу таĐșĐŸĐč Ń–ĐœŃ„Đ°Ń€ĐŒĐ°Ń†Ń‹Ń–, яĐș ĐșĐ°ĐœŃ‚Đ°Đșты, паĐČĐ”ĐŽĐ°ĐŒĐ»Đ”ĐœĐœŃ– і Ń„ĐŸŃ‚Đ°.&lt;br/&gt;• АЮпраўĐșа апаĐČŃŃˆŃ‡ŃĐœĐœŃŃž.&lt;br/&gt;&lt;br/&gt;ДазĐČĐŸĐ»Ń‹ гэтаĐč ĐżŃ€Đ°ĐłŃ€Đ°ĐŒŃ‹ ĐœĐ° Ń‡Ń‹Ń‚Đ°ĐœĐœĐ” і аЮпраўĐșу апаĐČŃŃˆŃ‡ŃĐœĐœŃŃž ĐŒĐŸĐ¶ĐœĐ° Đ·ĐŒŃĐœŃ–Ń†ŃŒ у любы час, ĐČŃ‹Đ±Ń€Đ°ŃžŃˆŃ‹ ĐżŃƒĐœĐșт ĐŒĐ”ĐœŃŽ \"ĐĐ°Đ»Đ°ĐŽŃ‹ &gt; АпаĐČŃŃˆŃ‡ŃĐœĐœŃ–\"."</string>
     <string name="permission_app_streaming_summary" msgid="606923325679670624">"ĐąŃ€Đ°ĐœŃĐ»ŃŃ†Ń‹Ń Đ·ĐŒĐ”ŃŃ†Ń–ĐČа ĐżŃ€Đ°ĐłŃ€Đ°ĐŒ Đ· ĐČашага Ń‚ŃĐ»Đ”Ń„ĐŸĐœĐ°"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
     <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"ĐŸĐ”Ń€Đ°ĐŽĐ°Ń‡Đ° ĐżĐ»Ń‹ĐœĐœŃŽ Đ·ĐŒĐ”ŃŃ†Ń–ĐČа ĐżŃ€Đ°ĐłŃ€Đ°ĐŒ і Ń–ĐœŃˆŃ‹Ń… Ń„ŃƒĐœĐșцыĐč ŃŃ–ŃŃ‚ŃĐŒŃ‹ Đ· ĐČашага Ń‚ŃĐ»Đ”Ń„ĐŸĐœĐ°"</string>
diff --git a/packages/CompanionDeviceManager/res/values-bg/strings.xml b/packages/CompanionDeviceManager/res/values-bg/strings.xml
index 3840698..c9f85dd 100644
--- a/packages/CompanionDeviceManager/res/values-bg/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-bg/strings.xml
@@ -56,28 +56,18 @@
     <string name="permission_nearby_devices" msgid="7530973297737123481">"ĐŁŃŃ‚Ń€ĐŸĐčстĐČа ĐČ Đ±Đ»ĐžĐ·ĐŸŃŃ‚"</string>
     <string name="permission_media_routing_control" msgid="5498639511586715253">"ĐŸŃ€ĐŸĐŒŃĐœĐ° ĐœĐ° ĐŒŃƒĐ»Ń‚ĐžĐŒĐ”ĐŽĐžĐčĐœĐžŃ ĐžĐ·Ń…ĐŸĐŽ"</string>
     <string name="permission_storage" msgid="6831099350839392343">"ĐĄĐœĐžĐŒĐșĐž Đž ĐŒŃƒĐ»Ń‚ĐžĐŒĐ”ĐŽĐžŃ"</string>
-    <!-- no translation found for permission_notifications (4099418516590632909) -->
-    <skip />
+    <string name="permission_notifications" msgid="4099418516590632909">"ИзĐČĐ”ŃŃ‚ĐžŃ"</string>
     <string name="permission_app_streaming" msgid="6009695219091526422">"ĐŸŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžŃ"</string>
     <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"ĐŸĐŸŃ‚ĐŸŃ‡ĐœĐŸ прДЎаĐČĐ°ĐœĐ”"</string>
-    <!-- no translation found for permission_phone_summary (8246321093970051702) -->
-    <skip />
-    <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
-    <skip />
-    <!-- no translation found for permission_sms_summary (8499509535410068616) -->
-    <skip />
-    <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
-    <skip />
-    <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
-    <skip />
-    <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
-    <skip />
-    <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
-    <skip />
-    <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
-    <skip />
-    <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
-    <skip />
+    <string name="permission_phone_summary" msgid="8246321093970051702">"ИзĐČършĐČĐ°ĐœĐ” Đž упраĐČĐ»Đ”ĐœĐžĐ” ĐœĐ° Ń‚Đ”Đ»Đ”Ń„ĐŸĐœĐœĐž ĐŸĐ±Đ°Đ¶ĐŽĐ°ĐœĐžŃ"</string>
+    <string name="permission_call_logs_summary" msgid="7545243592757693321">"Đ§Đ”Ń‚Đ”ĐœĐ” Đž запОс ĐœĐ° спосъĐșа с ĐŸĐ±Đ°Đ¶ĐŽĐ°ĐœĐžŃŃ‚Đ°"</string>
+    <string name="permission_sms_summary" msgid="8499509535410068616">"Đ˜Đ·ĐżŃ€Đ°Ń‰Đ°ĐœĐ” Đž прДглДЎ ĐœĐ° SMS ŃŃŠĐŸĐ±Ń‰Đ”ĐœĐžŃ"</string>
+    <string name="permission_contacts_summary" msgid="2840800622763086808">"Đ”ĐŸŃŃ‚ŃŠĐż ĐŽĐŸ ĐșĐŸĐœŃ‚Đ°ĐșтОтД ĐČĐž"</string>
+    <string name="permission_calendar_summary" msgid="8430353935747336165">"Đ”ĐŸŃŃ‚ŃŠĐż ĐŽĐŸ ĐșĐ°Đ»Đ”ĐœĐŽĐ°Ń€Đ° ĐČĐž"</string>
+    <string name="permission_microphone_summary" msgid="4862628553869973259">"ЗапосĐČĐ°ĐœĐ” ĐœĐ° Đ·ĐČуĐș"</string>
+    <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"ĐĐ°ĐŒĐžŃ€Đ°ĐœĐ” Đž сĐČързĐČĐ°ĐœĐ” с ŃƒŃŃ‚Ń€ĐŸĐčстĐČа ĐČ Đ±Đ»ĐžĐ·ĐŸŃŃ‚, ĐșаĐșŃ‚ĐŸ Đž ĐŸĐżŃ€Đ”ĐŽĐ”Đ»ŃĐœĐ” ĐœĐ° ĐŸŃ‚ĐœĐŸŃĐžŃ‚Đ”Đ»ĐœĐ°Ń‚Đ° ĐžĐŒ ĐżĐŸĐ·ĐžŃ†ĐžŃ"</string>
+    <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Đ§Đ”Ń‚Đ”ĐœĐ” ĐœĐ° ĐČсочĐșĐž ОзĐČĐ”ŃŃ‚ĐžŃ, ĐČĐșĐ»ŃŽŃ‡ĐžŃ‚Đ”Đ»ĐœĐŸ Ń€Đ°Đ·Đ»ĐžŃ‡ĐœĐ° ĐžĐœŃ„ĐŸŃ€ĐŒĐ°Ń†ĐžŃ, ĐșĐ°Ń‚ĐŸ ĐœĐ°ĐżŃ€ĐžĐŒĐ”Ń€ ĐșĐŸĐœŃ‚Đ°Đșто, ŃŃŠĐŸĐ±Ń‰Đ”ĐœĐžŃ Đž ŃĐœĐžĐŒĐșĐž"</string>
+    <string name="permission_notifications_summary" msgid="2272810466047367030">"• Đ§Đ”Ń‚Đ”ĐœĐ” ĐœĐ° ĐČсочĐșĐž ОзĐČĐ”ŃŃ‚ĐžŃ, ĐČĐșĐ»ŃŽŃ‡ĐžŃ‚Đ”Đ»ĐœĐŸ Ń€Đ°Đ·Đ»ĐžŃ‡ĐœĐ° ĐžĐœŃ„ĐŸŃ€ĐŒĐ°Ń†ĐžŃ, ĐșĐ°Ń‚ĐŸ ĐœĐ°ĐżŃ€ĐžĐŒĐ”Ń€ ĐșĐŸĐœŃ‚Đ°Đșто, ŃŃŠĐŸĐ±Ń‰Đ”ĐœĐžŃ Đž ŃĐœĐžĐŒĐșĐž.&lt;br/&gt;• Đ˜Đ·ĐżŃ€Đ°Ń‰Đ°ĐœĐ” ĐœĐ° ОзĐČĐ”ŃŃ‚ĐžŃ.&lt;br/&gt;&lt;br/&gt;ĐœĐŸĐ¶Đ”Ń‚Đ” Ўа упраĐČĐ»ŃĐČатД ŃĐżĐŸŃĐŸĐ±ĐœĐŸŃŃ‚Ń‚Đ° ĐœĐ° Ń‚ĐŸĐČа ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžĐ” Ўа чДтД Đž Озпраща ОзĐČĐ”ŃŃ‚ĐžŃ ĐżĐŸ ĐČсяĐșĐŸ ĐČŃ€Đ”ĐŒĐ” ĐČ „ĐĐ°ŃŃ‚Ń€ĐŸĐčĐșĐž“ &gt; „ИзĐČĐ”ŃŃ‚ĐžŃ“."</string>
     <string name="permission_app_streaming_summary" msgid="606923325679670624">"ĐŸĐŸŃ‚ĐŸŃ‡ĐœĐŸ прДЎаĐČĐ°ĐœĐ” ĐœĐ° ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžŃŃ‚Đ° ĐœĐ° Ń‚Đ”Đ»Đ”Ń„ĐŸĐœĐ° ĐČĐž"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
     <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"ĐŸĐŸŃ‚ĐŸŃ‡ĐœĐŸ прДЎаĐČĐ°ĐœĐ” ĐœĐ° ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžŃ Đž Юруго ŃĐžŃŃ‚Đ”ĐŒĐœĐž Ń„ŃƒĐœĐșцоо ĐŸŃ‚ Ń‚Đ”Đ»Đ”Ń„ĐŸĐœĐ° ĐČĐž"</string>
diff --git a/packages/CompanionDeviceManager/res/values-bn/strings.xml b/packages/CompanionDeviceManager/res/values-bn/strings.xml
index 38521d6..c3ceda0 100644
--- a/packages/CompanionDeviceManager/res/values-bn/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-bn/strings.xml
@@ -54,36 +54,24 @@
     <string name="permission_microphone" msgid="2152206421428732949">"àŠźàŠŸàŠ‡àŠ•à§àŠ°à§‹àŠ«à§‹àŠš"</string>
     <string name="permission_call_logs" msgid="5546761417694586041">"àŠ•àŠČ àŠČàŠ—"</string>
     <string name="permission_nearby_devices" msgid="7530973297737123481">"àŠ†àŠ¶à§‡àŠȘàŠŸàŠ¶à§‡àŠ° àŠĄàŠżàŠ­àŠŸàŠ‡àŠž"</string>
-    <!-- no translation found for permission_media_routing_control (5498639511586715253) -->
-    <skip />
+    <string name="permission_media_routing_control" msgid="5498639511586715253">"àŠźàŠżàŠĄàŠżàŠŻàŠŒàŠŸ àŠ†àŠ‰àŠŸàŠȘà§àŠŸ àŠȘàŠ°àŠżàŠŹàŠ°à§àŠ€àŠš àŠ•àŠ°à§àŠš"</string>
     <string name="permission_storage" msgid="6831099350839392343">"àŠ«àŠŸà§‹ àŠ“ àŠźàŠżàŠĄàŠżàŠŻàŠŒàŠŸ"</string>
-    <!-- no translation found for permission_notifications (4099418516590632909) -->
-    <skip />
+    <string name="permission_notifications" msgid="4099418516590632909">"àŠŹàŠżàŠœà§àŠžàŠȘà§àŠ€àŠż"</string>
     <string name="permission_app_streaming" msgid="6009695219091526422">"àŠ…à§àŠŻàŠŸàŠȘ"</string>
     <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"àŠžà§àŠŸà§àŠ°àŠżàŠźàŠżàŠ‚"</string>
-    <!-- no translation found for permission_phone_summary (8246321093970051702) -->
-    <skip />
-    <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
-    <skip />
-    <!-- no translation found for permission_sms_summary (8499509535410068616) -->
-    <skip />
-    <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
-    <skip />
-    <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
-    <skip />
-    <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
-    <skip />
-    <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
-    <skip />
-    <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
-    <skip />
-    <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
-    <skip />
+    <string name="permission_phone_summary" msgid="8246321093970051702">"àŠ«à§‹àŠš àŠ•àŠČ àŠ•àŠ°à§àŠš àŠàŠŹàŠ‚ àŠźà§àŠŻàŠŸàŠšà§‡àŠœ àŠ•àŠ°à§àŠš"</string>
+    <string name="permission_call_logs_summary" msgid="7545243592757693321">"àŠ«à§‹àŠš àŠ•àŠČ àŠČàŠ— àŠŠà§‡àŠ–à§‡ àŠ€àŠŸàŠ€à§‡ àŠȘàŠ°àŠżàŠŹàŠ°à§àŠ€àŠš àŠ•àŠ°àŠŸàŠ° àŠ…àŠšà§àŠźàŠ€àŠż àŠŠàŠżàŠš"</string>
+    <string name="permission_sms_summary" msgid="8499509535410068616">"àŠàŠžàŠàŠźàŠàŠž àŠźà§‡àŠžà§‡àŠœ àŠŠà§‡àŠ–à§àŠš àŠ“ àŠȘàŠŸàŠ àŠŸàŠš"</string>
+    <string name="permission_contacts_summary" msgid="2840800622763086808">"àŠ†àŠȘàŠšàŠŸàŠ° àŠȘàŠ°àŠżàŠšàŠżàŠ€àŠż àŠ…à§àŠŻàŠŸàŠ•à§àŠžà§‡àŠž àŠ•àŠ°à§àŠš"</string>
+    <string name="permission_calendar_summary" msgid="8430353935747336165">"àŠ†àŠȘàŠšàŠŸàŠ° àŠ•à§àŠŻàŠŸàŠČà§‡àŠšà§àŠĄàŠŸàŠ° àŠ…à§àŠŻàŠŸàŠ•à§àŠžà§‡àŠž àŠ•àŠ°àŠŸàŠ° àŠ…àŠšà§àŠźàŠ€àŠż àŠŠàŠżàŠš"</string>
+    <string name="permission_microphone_summary" msgid="4862628553869973259">"àŠ…àŠĄàŠżàŠ“ àŠ°à§‡àŠ•àŠ°à§àŠĄ àŠ•àŠ°à§àŠš"</string>
+    <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"àŠ†àŠ¶à§‡àŠȘàŠŸàŠ¶à§‡àŠ° àŠĄàŠżàŠ­àŠŸàŠ‡àŠž àŠ–à§àŠàŠœà§àŠš, àŠ€àŠŸàŠ° àŠžàŠŸàŠ„à§‡ àŠ•àŠŸàŠšà§‡àŠ•à§àŠŸ àŠ•àŠ°à§àŠš àŠàŠŹàŠ‚ àŠ€àŠŸàŠ° àŠ†àŠȘà§‡àŠ•à§àŠ·àŠżàŠ• àŠ…àŠŹàŠžà§àŠ„àŠŸàŠš àŠšàŠżàŠ°à§àŠ§àŠŸàŠ°àŠŁ àŠ•àŠ°à§àŠš"</string>
+    <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"àŠȘàŠ°àŠżàŠšàŠżàŠ€àŠż, àŠźà§‡àŠžà§‡àŠœ àŠàŠŹàŠ‚ àŠ«àŠŸà§‹àŠ° àŠźàŠ€à§‹ àŠ€àŠ„à§àŠŻ àŠžàŠč àŠžàŠźàŠžà§àŠ€ àŠŹàŠżàŠœà§àŠžàŠȘà§àŠ€àŠż àŠȘàŠĄàŠŒà§àŠš"</string>
+    <string name="permission_notifications_summary" msgid="2272810466047367030">"• àŠȘàŠ°àŠżàŠšàŠżàŠ€àŠż, àŠźà§‡àŠžà§‡àŠœ àŠàŠŹàŠ‚ àŠ«àŠŸà§‹àŠ° àŠźàŠ€à§‹ àŠ€àŠ„à§àŠŻ àŠžàŠč àŠžàŠźàŠžà§àŠ€ àŠŹàŠżàŠœà§àŠžàŠȘà§àŠ€àŠż àŠȘàŠĄàŠŒà§àŠš&lt;br/&gt;• àŠŹàŠżàŠœà§àŠžàŠȘà§àŠ€àŠż àŠȘàŠŸàŠ àŠŸàŠš&lt;br/&gt;&lt;br/&gt;àŠ†àŠȘàŠšàŠż àŠžà§‡àŠŸàŠżàŠ‚àŠž &gt; àŠŹàŠżàŠœà§àŠžàŠȘà§àŠ€àŠż àŠ„à§‡àŠ•à§‡ àŠàŠ‡ àŠ…à§àŠŻàŠŸàŠȘàŠŸàŠżàŠ° àŠŹàŠżàŠœà§àŠžàŠȘà§àŠ€àŠż àŠȘàŠĄàŠŒàŠŸàŠ° àŠàŠŹàŠ‚ àŠȘàŠŸàŠ àŠŸàŠšà§‹àŠ° àŠ•à§àŠ·àŠźàŠ€àŠŸ àŠźà§àŠŻàŠŸàŠšà§‡àŠœ àŠ•àŠ°àŠ€à§‡ àŠȘàŠŸàŠ°àŠŹà§‡àŠšà„€"</string>
     <string name="permission_app_streaming_summary" msgid="606923325679670624">"àŠ†àŠȘàŠšàŠŸàŠ° àŠ«à§‹àŠšà§‡àŠ° àŠ…à§àŠŻàŠŸàŠȘ àŠžà§àŠŸà§àŠ°àŠżàŠź àŠ•àŠ°à§àŠš"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
     <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"àŠ†àŠȘàŠšàŠŸàŠ° àŠ«à§‹àŠš àŠ„à§‡àŠ•à§‡ àŠ…à§àŠŻàŠŸàŠȘ àŠ“ àŠ…àŠšà§àŠŻàŠŸàŠšà§àŠŻ àŠžàŠżàŠžà§àŠŸà§‡àŠź àŠ«àŠżàŠšàŠŸàŠ° àŠžà§àŠŸà§àŠ°àŠżàŠź àŠ•àŠ°à§‡"</string>
-    <!-- no translation found for permission_media_routing_control_summary (2714631092321412250) -->
-    <skip />
+    <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"àŠ‰àŠȘàŠČàŠ­à§àŠŻ àŠ„àŠŸàŠ•àŠŸ àŠĄàŠżàŠ­àŠŸàŠ‡àŠžà§‡àŠ° àŠ€àŠŸàŠČàŠżàŠ•àŠŸ àŠ…à§àŠŻàŠŸàŠ•à§àŠžà§‡àŠž àŠ•àŠ°à§àŠš àŠàŠŹàŠ‚ àŠ•à§‹àŠš àŠĄàŠżàŠ­àŠŸàŠ‡àŠž àŠ…àŠšà§àŠŻàŠŸàŠšà§àŠŻ àŠ…à§àŠŻàŠŸàŠȘ àŠ„à§‡àŠ•à§‡ àŠ…àŠĄàŠżàŠ“ àŠŹàŠŸ àŠ­àŠżàŠĄàŠżàŠ“ àŠžà§àŠŸà§àŠ°àŠżàŠź àŠ…àŠ„àŠŹàŠŸ àŠ•àŠŸàŠžà§àŠŸ àŠ•àŠ°àŠ€à§‡ àŠȘàŠŸàŠ°à§‡ àŠ€àŠŸ àŠ•àŠšà§àŠŸà§àŠ°à§‹àŠČ àŠ•àŠ°à§àŠš"</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"àŠ«à§‹àŠš"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"àŠŸà§àŠŻàŠŸàŠŹàŠČà§‡àŠŸ"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-bs/strings.xml b/packages/CompanionDeviceManager/res/values-bs/strings.xml
index 6b03d24..259a06a 100644
--- a/packages/CompanionDeviceManager/res/values-bs/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-bs/strings.xml
@@ -56,28 +56,18 @@
     <string name="permission_nearby_devices" msgid="7530973297737123481">"Uređaji u blizini"</string>
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Promijeni izlaz med. sadrĆŸaja"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Fotografije i mediji"</string>
-    <!-- no translation found for permission_notifications (4099418516590632909) -->
-    <skip />
+    <string name="permission_notifications" msgid="4099418516590632909">"Obavještenja"</string>
     <string name="permission_app_streaming" msgid="6009695219091526422">"Aplikacije"</string>
     <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Prijenos"</string>
-    <!-- no translation found for permission_phone_summary (8246321093970051702) -->
-    <skip />
-    <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
-    <skip />
-    <!-- no translation found for permission_sms_summary (8499509535410068616) -->
-    <skip />
-    <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
-    <skip />
-    <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
-    <skip />
-    <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
-    <skip />
-    <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
-    <skip />
-    <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
-    <skip />
-    <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
-    <skip />
+    <string name="permission_phone_summary" msgid="8246321093970051702">"Pozivanje i upravljanje telefonskim pozivima"</string>
+    <string name="permission_call_logs_summary" msgid="7545243592757693321">"Čitanje i pisanje zapisnika telefonskih poziva"</string>
+    <string name="permission_sms_summary" msgid="8499509535410068616">"Slanje i pregledanje SMS poruka"</string>
+    <string name="permission_contacts_summary" msgid="2840800622763086808">"Pristup vašim kontaktima"</string>
+    <string name="permission_calendar_summary" msgid="8430353935747336165">"Pristup vašem kalendaru"</string>
+    <string name="permission_microphone_summary" msgid="4862628553869973259">"Snimanje zvuka"</string>
+    <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"PronalaĆŸenje uređaja u blizini, povezivanje s njima i određivanje njihovog relativnog poloĆŸaja"</string>
+    <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Čitanje svih obavještenja, uključujući informacije poput kontakata, poruka i fotografija"</string>
+    <string name="permission_notifications_summary" msgid="2272810466047367030">"• čitanje svih obavještenja, uključujući informacije poput kontakata, poruka i fotografija&lt;br/&gt;• slanje obavještenja&lt;br/&gt;&lt;br/&gt;Da upravljate mogućnošÄ‡u ove aplikacije da čita i šalje obavještenja, idite u Postavke &gt; Obavještenja."</string>
     <string name="permission_app_streaming_summary" msgid="606923325679670624">"Prenosite aplikacije s telefona"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
     <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Prijenos aplikacija i drugih funkcija sistema s vašeg telefona"</string>
diff --git a/packages/CompanionDeviceManager/res/values-ca/strings.xml b/packages/CompanionDeviceManager/res/values-ca/strings.xml
index dcafb07c..62fe858 100644
--- a/packages/CompanionDeviceManager/res/values-ca/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ca/strings.xml
@@ -56,28 +56,18 @@
     <string name="permission_nearby_devices" msgid="7530973297737123481">"Dispositius propers"</string>
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Canvia la sortida multimèdia"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Fotos i contingut multimèdia"</string>
-    <!-- no translation found for permission_notifications (4099418516590632909) -->
-    <skip />
+    <string name="permission_notifications" msgid="4099418516590632909">"Notificacions"</string>
     <string name="permission_app_streaming" msgid="6009695219091526422">"Aplicacions"</string>
     <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Reproducció en línia"</string>
-    <!-- no translation found for permission_phone_summary (8246321093970051702) -->
-    <skip />
-    <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
-    <skip />
-    <!-- no translation found for permission_sms_summary (8499509535410068616) -->
-    <skip />
-    <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
-    <skip />
-    <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
-    <skip />
-    <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
-    <skip />
-    <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
-    <skip />
-    <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
-    <skip />
-    <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
-    <skip />
+    <string name="permission_phone_summary" msgid="8246321093970051702">"Fer i gestionar trucades telefòniques"</string>
+    <string name="permission_call_logs_summary" msgid="7545243592757693321">"Llegir i escriure el registre de trucades del telèfon"</string>
+    <string name="permission_sms_summary" msgid="8499509535410068616">"Enviar i llegir missatges SMS"</string>
+    <string name="permission_contacts_summary" msgid="2840800622763086808">"Accedir als contactes"</string>
+    <string name="permission_calendar_summary" msgid="8430353935747336165">"Accedir al calendari"</string>
+    <string name="permission_microphone_summary" msgid="4862628553869973259">"Gravar àudio"</string>
+    <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Determinar la posició relativa dels dispositius propers, trobar-los i connectar-s\'hi"</string>
+    <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Llegir totes les notificacions, inclosa informació com ara els contactes, els missatges i les fotos"</string>
+    <string name="permission_notifications_summary" msgid="2272810466047367030">"• Llegir totes les notificacions, inclosa informació com ara els contactes, els missatges i les fotos.&lt;br/&gt;• Enviar notificacions.&lt;br/&gt;&lt;br/&gt;A Configuració &gt; Notificacions, pots gestionar en qualsevol moment els permisos de l\'aplicació per llegir i enviar notificacions."</string>
     <string name="permission_app_streaming_summary" msgid="606923325679670624">"Reprodueix en continu aplicacions del telèfon"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
     <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Reprodueix en continu aplicacions i altres funcions del sistema des del telèfon"</string>
diff --git a/packages/CompanionDeviceManager/res/values-cs/strings.xml b/packages/CompanionDeviceManager/res/values-cs/strings.xml
index 98412c81..76c0a4a 100644
--- a/packages/CompanionDeviceManager/res/values-cs/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-cs/strings.xml
@@ -56,28 +56,18 @@
     <string name="permission_nearby_devices" msgid="7530973297737123481">"Zaƙízení v okolí"</string>
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Změna mediálního výstupu"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Fotky a média"</string>
-    <!-- no translation found for permission_notifications (4099418516590632909) -->
-    <skip />
+    <string name="permission_notifications" msgid="4099418516590632909">"Oznámení"</string>
     <string name="permission_app_streaming" msgid="6009695219091526422">"Aplikace"</string>
     <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Streamování"</string>
-    <!-- no translation found for permission_phone_summary (8246321093970051702) -->
-    <skip />
-    <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
-    <skip />
-    <!-- no translation found for permission_sms_summary (8499509535410068616) -->
-    <skip />
-    <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
-    <skip />
-    <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
-    <skip />
-    <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
-    <skip />
-    <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
-    <skip />
-    <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
-    <skip />
-    <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
-    <skip />
+    <string name="permission_phone_summary" msgid="8246321093970051702">"Uskutečƈování a správa telefonních hovorĆŻ"</string>
+    <string name="permission_call_logs_summary" msgid="7545243592757693321">"Čtení seznamu hovorĆŻ a zapisování do něj"</string>
+    <string name="permission_sms_summary" msgid="8499509535410068616">"Odesílání a zobrazování zpráv SMS"</string>
+    <string name="permission_contacts_summary" msgid="2840800622763086808">"Pƙístup ke kontaktĆŻm"</string>
+    <string name="permission_calendar_summary" msgid="8430353935747336165">"Pƙístup ke kalendáƙi"</string>
+    <string name="permission_microphone_summary" msgid="4862628553869973259">"Nahrávání zvuku"</string>
+    <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Vyhledávání zaƙízení v okolí, pƙipojování se k nim a zjišĆ„ování jejich relativní polohy"</string>
+    <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Čtení veškerých oznámení včetně informací, jako jsou kontakty, zprávy a fotky"</string>
+    <string name="permission_notifications_summary" msgid="2272810466047367030">"• Čtení veškerých oznámení, včetně informací, jako jsou kontakty, zprávy a fotky&lt;br/&gt;• Odesílání oznámení&lt;br/&gt;&lt;br/&gt;Oprávnění této aplikace číst a odesílat oznámení mĆŻĆŸete kdykoli změnit v Nastavení &gt; Oznámení."</string>
     <string name="permission_app_streaming_summary" msgid="606923325679670624">"Streamujte aplikace v telefonu"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
     <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Streamování aplikací a dalších systémových funkcí z telefonu"</string>
diff --git a/packages/CompanionDeviceManager/res/values-da/strings.xml b/packages/CompanionDeviceManager/res/values-da/strings.xml
index 86be312..f765c83 100644
--- a/packages/CompanionDeviceManager/res/values-da/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-da/strings.xml
@@ -56,28 +56,18 @@
     <string name="permission_nearby_devices" msgid="7530973297737123481">"Enheder i nærheden"</string>
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Skift medieoutput"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Billeder og medier"</string>
-    <!-- no translation found for permission_notifications (4099418516590632909) -->
-    <skip />
+    <string name="permission_notifications" msgid="4099418516590632909">"Notifikationer"</string>
     <string name="permission_app_streaming" msgid="6009695219091526422">"Apps"</string>
     <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Streaming"</string>
-    <!-- no translation found for permission_phone_summary (8246321093970051702) -->
-    <skip />
-    <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
-    <skip />
-    <!-- no translation found for permission_sms_summary (8499509535410068616) -->
-    <skip />
-    <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
-    <skip />
-    <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
-    <skip />
-    <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
-    <skip />
-    <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
-    <skip />
-    <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
-    <skip />
-    <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
-    <skip />
+    <string name="permission_phone_summary" msgid="8246321093970051702">"Foretage og administrere telefonopkald"</string>
+    <string name="permission_call_logs_summary" msgid="7545243592757693321">"Læse og redigere opkaldshistorik"</string>
+    <string name="permission_sms_summary" msgid="8499509535410068616">"Sende og se sms-beskeder"</string>
+    <string name="permission_contacts_summary" msgid="2840800622763086808">"Få adgang til dine kontakter"</string>
+    <string name="permission_calendar_summary" msgid="8430353935747336165">"Få adgang til din kalender"</string>
+    <string name="permission_microphone_summary" msgid="4862628553869973259">"Optage lyd"</string>
+    <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Finde, oprette forbindelse til og fastslå den omtrentlige lokation af enheder i nærheden"</string>
+    <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Læse alle notifikationer, herunder oplysninger som f.eks. kontakter, beskeder og billeder"</string>
+    <string name="permission_notifications_summary" msgid="2272810466047367030">"• Læse alle notifikationer, herunder oplysninger som f.eks. kontakter, beskeder og billeder&lt;br/&gt;• Sende notifikationer&lt;br/&gt;&lt;br/&gt;Du kan til enhver tid administrere appens mulighed for at læse og sende notifikationer under Indstillinger &gt; Notifikationer."</string>
     <string name="permission_app_streaming_summary" msgid="606923325679670624">"Stream din telefons apps"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
     <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Stream apps og andre systemfunktioner fra din telefon"</string>
diff --git a/packages/CompanionDeviceManager/res/values-de/strings.xml b/packages/CompanionDeviceManager/res/values-de/strings.xml
index 1bc2fed..e21e8cd 100644
--- a/packages/CompanionDeviceManager/res/values-de/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-de/strings.xml
@@ -56,28 +56,18 @@
     <string name="permission_nearby_devices" msgid="7530973297737123481">"Geräte in der Nähe"</string>
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Medienausgabe ändern"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Fotos und Medien"</string>
-    <!-- no translation found for permission_notifications (4099418516590632909) -->
-    <skip />
+    <string name="permission_notifications" msgid="4099418516590632909">"Benachrichtigungen"</string>
     <string name="permission_app_streaming" msgid="6009695219091526422">"Apps"</string>
     <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Streaming"</string>
-    <!-- no translation found for permission_phone_summary (8246321093970051702) -->
-    <skip />
-    <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
-    <skip />
-    <!-- no translation found for permission_sms_summary (8499509535410068616) -->
-    <skip />
-    <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
-    <skip />
-    <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
-    <skip />
-    <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
-    <skip />
-    <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
-    <skip />
-    <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
-    <skip />
-    <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
-    <skip />
+    <string name="permission_phone_summary" msgid="8246321093970051702">"Anrufe starten und verwalten"</string>
+    <string name="permission_call_logs_summary" msgid="7545243592757693321">"Auf die Anrufliste zugreifen und sie bearbeiten"</string>
+    <string name="permission_sms_summary" msgid="8499509535410068616">"SMS senden und ansehen"</string>
+    <string name="permission_contacts_summary" msgid="2840800622763086808">"Auf deine Kontakte zugreifen"</string>
+    <string name="permission_calendar_summary" msgid="8430353935747336165">"Auf deinen Kalender zugreifen"</string>
+    <string name="permission_microphone_summary" msgid="4862628553869973259">"Audio aufnehmen"</string>
+    <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Geräte in der Nähe finden, eine Verbindung zu ihnen herstellen und ihren relativen Standort ermitteln"</string>
+    <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Alle Benachrichtigungen sehen, einschließlich Informationen wie Kontakten, Nachrichten und Fotos"</string>
+    <string name="permission_notifications_summary" msgid="2272810466047367030">"• Alle Benachrichtigungen sehen, einschließlich Informationen wie Kontakte, Nachrichten und Fotos&lt;br/&gt;• Benachrichtigungen senden&lt;br/&gt;&lt;br/&gt;Du kannst die Berechtigungen dieser App zum Sehen und Senden von Benachrichtigungen jederzeit unter „Einstellungen &gt; Benachrichtigungen“ ändern."</string>
     <string name="permission_app_streaming_summary" msgid="606923325679670624">"Smartphone-Apps streamen"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
     <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Apps und andere Systemfunktionen von deinem Smartphone streamen"</string>
diff --git a/packages/CompanionDeviceManager/res/values-el/strings.xml b/packages/CompanionDeviceManager/res/values-el/strings.xml
index c8d6904..a95d063 100644
--- a/packages/CompanionDeviceManager/res/values-el/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-el/strings.xml
@@ -56,28 +56,18 @@
     <string name="permission_nearby_devices" msgid="7530973297737123481">"Συσκευές σε κοντινÎź απόσταση"</string>
     <string name="permission_media_routing_control" msgid="5498639511586715253">"ΑλλαγÎź εξόδου μέσων"</string>
     <string name="permission_storage" msgid="6831099350839392343">"ΦωτογραφÎŻες και μέσα"</string>
-    <!-- no translation found for permission_notifications (4099418516590632909) -->
-    <skip />
+    <string name="permission_notifications" msgid="4099418516590632909">"ΕιδοποιÎźσεις"</string>
     <string name="permission_app_streaming" msgid="6009695219091526422">"Εφαρμογές"</string>
     <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"ΡοÎź"</string>
-    <!-- no translation found for permission_phone_summary (8246321093970051702) -->
-    <skip />
-    <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
-    <skip />
-    <!-- no translation found for permission_sms_summary (8499509535410068616) -->
-    <skip />
-    <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
-    <skip />
-    <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
-    <skip />
-    <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
-    <skip />
-    <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
-    <skip />
-    <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
-    <skip />
-    <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
-    <skip />
+    <string name="permission_phone_summary" msgid="8246321093970051702">"ΠραγματοποÎŻηση και διαχεÎŻριση τηλεφωνικών κλÎźσεων"</string>
+    <string name="permission_call_logs_summary" msgid="7545243592757693321">"ΑνÎŹγνωση και εγγραφÎź αρχεÎŻου καταγραφÎźς τηλεφωνικών κλÎźσεων"</string>
+    <string name="permission_sms_summary" msgid="8499509535410068616">"ΑποστολÎź και προβολÎź μηνυμÎŹτων SMS"</string>
+    <string name="permission_contacts_summary" msgid="2840800622763086808">"Πρόσβαση στις επαφές σας"</string>
+    <string name="permission_calendar_summary" msgid="8430353935747336165">"Πρόσβαση στο ημερολόγιό σας"</string>
+    <string name="permission_microphone_summary" msgid="4862628553869973259">"ΗχογρÎŹφηση"</string>
+    <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Εύρεση, σύνδεση και προσδιορισμός της σχετικÎźς τοποθεσÎŻας συσκευών σε κοντινÎź απόσταση"</string>
+    <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"ΑνÎŹγνωση όλων των ειδοποιÎźσεων, συμπεριλαμβανομένων πληροφοριών όπως επαφές, μηνύματα και φωτογραφÎŻες"</string>
+    <string name="permission_notifications_summary" msgid="2272810466047367030">"• ΑνÎŹγνωση όλων των ειδοποιÎźσεων, συμπεριλαμβανομένων πληροφοριών όπως επαφές, μηνύματα και φωτογραφÎŻες&lt;br/&gt;• ΑποστολÎź ειδοποιÎźσεων&lt;br/&gt;&lt;br/&gt;ΜπορεÎŻτε να διαχειριστεÎŻτε τη δυνατότητα της εφαρμογÎźς να διαβÎŹζει και να στέλνει ειδοποιÎźσεις οποιαδÎźποτε στιγμÎź στις ΡυθμÎŻσεις &gt; ΕιδοποιÎźσεις."</string>
     <string name="permission_app_streaming_summary" msgid="606923325679670624">"Μεταδώστε σε ροÎź τις εφαρμογές του τηλεφώνου σας"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
     <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"ΡοÎź εφαρμογών και ÎŹλλων λειτουργιών του συστÎźματος από το τηλέφωνό σας"</string>
diff --git a/packages/CompanionDeviceManager/res/values-es-rUS/strings.xml b/packages/CompanionDeviceManager/res/values-es-rUS/strings.xml
index e42ee5e..75cd49c 100644
--- a/packages/CompanionDeviceManager/res/values-es-rUS/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-es-rUS/strings.xml
@@ -56,28 +56,18 @@
     <string name="permission_nearby_devices" msgid="7530973297737123481">"Dispositivos cercanos"</string>
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Cambia la salida multimedia"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Fotos y contenido multimedia"</string>
-    <!-- no translation found for permission_notifications (4099418516590632909) -->
-    <skip />
+    <string name="permission_notifications" msgid="4099418516590632909">"Notificaciones"</string>
     <string name="permission_app_streaming" msgid="6009695219091526422">"Apps"</string>
     <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Transmisión"</string>
-    <!-- no translation found for permission_phone_summary (8246321093970051702) -->
-    <skip />
-    <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
-    <skip />
-    <!-- no translation found for permission_sms_summary (8499509535410068616) -->
-    <skip />
-    <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
-    <skip />
-    <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
-    <skip />
-    <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
-    <skip />
-    <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
-    <skip />
-    <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
-    <skip />
-    <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
-    <skip />
+    <string name="permission_phone_summary" msgid="8246321093970051702">"Hacer y administrar llamadas telefónicas"</string>
+    <string name="permission_call_logs_summary" msgid="7545243592757693321">"Leer y escribir el registro de llamadas telefónicas"</string>
+    <string name="permission_sms_summary" msgid="8499509535410068616">"Enviar y ver mensajes SMS"</string>
+    <string name="permission_contacts_summary" msgid="2840800622763086808">"Acceder a tus contactos"</string>
+    <string name="permission_calendar_summary" msgid="8430353935747336165">"Acceder al calendario"</string>
+    <string name="permission_microphone_summary" msgid="4862628553869973259">"Grabar audio"</string>
+    <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Encontrar dispositivos cercanos, conectarse a ellos y determinar su posición relativa"</string>
+    <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Leer todas las notificaciones, incluso con información como contactos, mensajes y fotos"</string>
+    <string name="permission_notifications_summary" msgid="2272810466047367030">"• Leer todas las notificaciones, incluso con información como contactos, mensajes y fotos&lt;br/&gt;• Enviar notificaciones&lt;br/&gt;&lt;br/&gt;Puedes administrar la capacidad de esta app para leer y enviar notificaciones en cualquier momento en Configuración &gt; Notificaciones."</string>
     <string name="permission_app_streaming_summary" msgid="606923325679670624">"Transmitir las apps de tu teléfono"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
     <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Transmite apps y otras funciones del sistema desde tu teléfono"</string>
diff --git a/packages/CompanionDeviceManager/res/values-es/strings.xml b/packages/CompanionDeviceManager/res/values-es/strings.xml
index 40b0a64..ba6045b 100644
--- a/packages/CompanionDeviceManager/res/values-es/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-es/strings.xml
@@ -56,28 +56,18 @@
     <string name="permission_nearby_devices" msgid="7530973297737123481">"Dispositivos cercanos"</string>
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Cambiar salida multimedia"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Fotos y elementos multimedia"</string>
-    <!-- no translation found for permission_notifications (4099418516590632909) -->
-    <skip />
+    <string name="permission_notifications" msgid="4099418516590632909">"Notificaciones"</string>
     <string name="permission_app_streaming" msgid="6009695219091526422">"Aplicaciones"</string>
     <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Emitir"</string>
-    <!-- no translation found for permission_phone_summary (8246321093970051702) -->
-    <skip />
-    <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
-    <skip />
-    <!-- no translation found for permission_sms_summary (8499509535410068616) -->
-    <skip />
-    <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
-    <skip />
-    <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
-    <skip />
-    <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
-    <skip />
-    <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
-    <skip />
-    <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
-    <skip />
-    <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
-    <skip />
+    <string name="permission_phone_summary" msgid="8246321093970051702">"Hacer y gestionar llamadas telefónicas"</string>
+    <string name="permission_call_logs_summary" msgid="7545243592757693321">"Leer y escribir en el registro de llamadas del teléfono"</string>
+    <string name="permission_sms_summary" msgid="8499509535410068616">"Enviar y ver mensajes SMS"</string>
+    <string name="permission_contacts_summary" msgid="2840800622763086808">"Acceder a tus contactos"</string>
+    <string name="permission_calendar_summary" msgid="8430353935747336165">"Acceder a tu calendario"</string>
+    <string name="permission_microphone_summary" msgid="4862628553869973259">"Grabar audio"</string>
+    <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Buscar, conectarse y determinar la posición relativa de dispositivos cercanos"</string>
+    <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Leer todas las notificaciones, incluida información como contactos, mensajes y fotos"</string>
+    <string name="permission_notifications_summary" msgid="2272810466047367030">"• Leer todas las notificaciones, incluida información como contactos, mensajes y fotos&lt;br/&gt;• Enviar notificaciones&lt;br/&gt;&lt;br/&gt;Puedes gestionar los permisos de esta aplicación para leer y enviar notificaciones cuando quieras en Ajustes &gt; Notificaciones."</string>
     <string name="permission_app_streaming_summary" msgid="606923325679670624">"Muestra en streaming las aplicaciones de tu teléfono"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
     <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Emite aplicaciones y otras funciones del sistema desde tu teléfono"</string>
diff --git a/packages/CompanionDeviceManager/res/values-et/strings.xml b/packages/CompanionDeviceManager/res/values-et/strings.xml
index 8a436b3..dc8d992 100644
--- a/packages/CompanionDeviceManager/res/values-et/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-et/strings.xml
@@ -56,28 +56,18 @@
     <string name="permission_nearby_devices" msgid="7530973297737123481">"Läheduses olevad seadmed"</string>
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Muutke meediaväljundit"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Fotod ja meedia"</string>
-    <!-- no translation found for permission_notifications (4099418516590632909) -->
-    <skip />
+    <string name="permission_notifications" msgid="4099418516590632909">"Märguanded"</string>
     <string name="permission_app_streaming" msgid="6009695219091526422">"Rakendused"</string>
     <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Voogesitus"</string>
-    <!-- no translation found for permission_phone_summary (8246321093970051702) -->
-    <skip />
-    <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
-    <skip />
-    <!-- no translation found for permission_sms_summary (8499509535410068616) -->
-    <skip />
-    <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
-    <skip />
-    <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
-    <skip />
-    <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
-    <skip />
-    <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
-    <skip />
-    <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
-    <skip />
-    <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
-    <skip />
+    <string name="permission_phone_summary" msgid="8246321093970051702">"Helistamine ja telefonikõnede haldamine"</string>
+    <string name="permission_call_logs_summary" msgid="7545243592757693321">"Telefoni kõnelogi lugemine ja kirjutamine"</string>
+    <string name="permission_sms_summary" msgid="8499509535410068616">"SMS-sõnumite saatmine ja vaatamine"</string>
+    <string name="permission_contacts_summary" msgid="2840800622763086808">"Juurdepääs kontaktidele"</string>
+    <string name="permission_calendar_summary" msgid="8430353935747336165">"Juurdepääs kalendrile"</string>
+    <string name="permission_microphone_summary" msgid="4862628553869973259">"Heli salvestamine"</string>
+    <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Läheduses olevate seadmete leidmine, nendega ühenduse loomine ja nende suhtelise asendi määramine"</string>
+    <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Kõikide märguannete, sh sellise teabe nagu kontaktid, sõnumid ja fotod lugemine"</string>
+    <string name="permission_notifications_summary" msgid="2272810466047367030">"• Kõikide märguannete, sh sellise teabe nagu kontaktid, sõnumid ja fotod lugemine&lt;br/&gt;• Märguannete saatmine&lt;br/&gt;&lt;br/&gt;Saate selle rakenduse võimalusi märguannete lugemiseks ja saatmiseks igal ajal hallata jaotises Seaded &gt; Märguanded."</string>
     <string name="permission_app_streaming_summary" msgid="606923325679670624">"Telefoni rakenduste voogesitamine"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
     <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Rakenduste ja muude süsteemi funktsioonide voogesitamine teie telefonist"</string>
diff --git a/packages/CompanionDeviceManager/res/values-eu/strings.xml b/packages/CompanionDeviceManager/res/values-eu/strings.xml
index 38f0512..8cd4fde 100644
--- a/packages/CompanionDeviceManager/res/values-eu/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-eu/strings.xml
@@ -56,28 +56,18 @@
     <string name="permission_nearby_devices" msgid="7530973297737123481">"Inguruko gailuak"</string>
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Aldatu multimedia-irteera"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Argazkiak eta multimedia-edukia"</string>
-    <!-- no translation found for permission_notifications (4099418516590632909) -->
-    <skip />
+    <string name="permission_notifications" msgid="4099418516590632909">"Jakinarazpenak"</string>
     <string name="permission_app_streaming" msgid="6009695219091526422">"Aplikazioak"</string>
     <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Igortzea"</string>
-    <!-- no translation found for permission_phone_summary (8246321093970051702) -->
-    <skip />
-    <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
-    <skip />
-    <!-- no translation found for permission_sms_summary (8499509535410068616) -->
-    <skip />
-    <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
-    <skip />
-    <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
-    <skip />
-    <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
-    <skip />
-    <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
-    <skip />
-    <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
-    <skip />
-    <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
-    <skip />
+    <string name="permission_phone_summary" msgid="8246321093970051702">"Telefono-deiak egin eta kudeatu"</string>
+    <string name="permission_call_logs_summary" msgid="7545243592757693321">"Telefonoko deien erregistroa irakurri eta bertan idatzi"</string>
+    <string name="permission_sms_summary" msgid="8499509535410068616">"SMS mezuak bidali eta ikusi"</string>
+    <string name="permission_contacts_summary" msgid="2840800622763086808">"Kontaktuak atzitu"</string>
+    <string name="permission_calendar_summary" msgid="8430353935747336165">"Egutegia atzitu"</string>
+    <string name="permission_microphone_summary" msgid="4862628553869973259">"Audioa grabatu"</string>
+    <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Inguruko gailuak aurkitu, haietara konektatu eta haien posizio erlatiboa zehaztu"</string>
+    <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Jakinarazpen guztiak irakurri; besteak beste, kontaktuak, mezuak, argazkiak eta antzeko informazioa"</string>
+    <string name="permission_notifications_summary" msgid="2272810466047367030">"• Jakinarazpen guztiak irakurri; besteak beste, kontaktuak, mezuak, argazkiak eta antzeko informazioa.&lt;br/&gt;• Jakinarazpenak bidali.&lt;br/&gt;&lt;br/&gt;Aplikazioak jakinarazpenak irakurri eta bidaltzeko dituen baimenak kudeatzeko, joan Ezarpenak &gt; Jakinarazpenak atalera."</string>
     <string name="permission_app_streaming_summary" msgid="606923325679670624">"Igorri zuzenean telefonoko aplikazioak"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
     <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Igorri aplikazioak eta sistemaren beste eginbide batzuk telefonotik"</string>
diff --git a/packages/CompanionDeviceManager/res/values-fa/strings.xml b/packages/CompanionDeviceManager/res/values-fa/strings.xml
index 5d0ad19..83628cb 100644
--- a/packages/CompanionDeviceManager/res/values-fa/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-fa/strings.xml
@@ -56,28 +56,18 @@
     <string name="permission_nearby_devices" msgid="7530973297737123481">"ŰŻŰłŰȘÚŻŰ§Ù‡‌Ù‡Ű§ÛŒ Ű§Ű·Ű±Ű§Ù"</string>
     <string name="permission_media_routing_control" msgid="5498639511586715253">"ŰȘŰșÛŒÛŒŰ± ŰŻŰ§ŰŻÙ† ŰźŰ±ÙˆŰŹÛŒ Ű±ŰłŰ§Ù†Ù‡"</string>
     <string name="permission_storage" msgid="6831099350839392343">"ŰčÚ©Űł‌Ù‡Ű§ و Ű±ŰłŰ§Ù†Ù‡‌Ù‡Ű§"</string>
-    <!-- no translation found for permission_notifications (4099418516590632909) -->
-    <skip />
+    <string name="permission_notifications" msgid="4099418516590632909">"ۧŰčÙ„Ű§Ù†‌Ù‡Ű§"</string>
     <string name="permission_app_streaming" msgid="6009695219091526422">"ŰšŰ±Ù†Ű§Ù…Ù‡‌Ù‡Ű§"</string>
     <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"ŰŹŰ§Ű±ÛŒ‌۳ۧŰČی"</string>
-    <!-- no translation found for permission_phone_summary (8246321093970051702) -->
-    <skip />
-    <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
-    <skip />
-    <!-- no translation found for permission_sms_summary (8499509535410068616) -->
-    <skip />
-    <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
-    <skip />
-    <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
-    <skip />
-    <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
-    <skip />
-    <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
-    <skip />
-    <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
-    <skip />
-    <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
-    <skip />
+    <string name="permission_phone_summary" msgid="8246321093970051702">"ŰšŰ±Ù‚Ű±Ű§Ű±ÛŒ و Ù…ŰŻÛŒŰ±ÛŒŰȘ ŰȘÙ…Ű§Űł‌Ù‡Ű§ÛŒ ŰȘلفنی"</string>
+    <string name="permission_call_logs_summary" msgid="7545243592757693321">"ŰźÙˆŰ§Ù†ŰŻÙ† و Ù†ÙˆŰŽŰȘن ÚŻŰČۧ۱ێ ŰȘÙ…Ű§Űł ŰȘلفنی"</string>
+    <string name="permission_sms_summary" msgid="8499509535410068616">"Ű§Ű±ŰłŰ§Ù„ و Ù…ŰŽŰ§Ù‡ŰŻÙ‡ ÙŸÛŒŰ§Ù…Ú©‌Ù‡Ű§"</string>
+    <string name="permission_contacts_summary" msgid="2840800622763086808">"ŰŻŰłŰȘŰ±ŰłÛŒ ŰšÙ‡ Ù…ŰźŰ§Ű·ŰšÛŒÙ†"</string>
+    <string name="permission_calendar_summary" msgid="8430353935747336165">"ŰŻŰłŰȘŰ±ŰłÛŒ ŰšÙ‡ ŰȘقویم"</string>
+    <string name="permission_microphone_summary" msgid="4862628553869973259">"۶ۚ۷ Ú©Ű±ŰŻÙ† ۔ۯۧ"</string>
+    <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"ÙŸÛŒŰŻŰ§ Ú©Ű±ŰŻÙ†ŰŒ ŰȘŰčیین موقŰčیŰȘ Ù†ŰłŰšÛŒŰŒ و مŰȘŰ”Ù„ ŰŽŰŻÙ† ŰšÙ‡ ŰŻŰłŰȘÚŻŰ§Ù‡‌Ù‡Ű§ÛŒ Ű§Ű·Ű±Ű§Ù"</string>
+    <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"ŰźÙˆŰ§Ù†ŰŻÙ† همه ۧŰčÙ„Ű§Ù†‌Ù‡Ű§ŰŒ ŰŽŰ§Ù…Ù„ Ű§Ű·Ù„Ű§ŰčۧŰȘی Ù…Ű«Ù„ Ù…ŰźŰ§Ű·ŰšÛŒÙ†ŰŒ ÙŸÛŒŰ§Ù…‌Ù‡Ű§ŰŒ و ŰčÚ©Űł‌Ù‡Ű§"</string>
+    <string name="permission_notifications_summary" msgid="2272810466047367030">"‏• ŰźÙˆŰ§Ù†ŰŻÙ† همه ۧŰčÙ„Ű§Ù†‌Ù‡Ű§ŰŒ ŰŽŰ§Ù…Ù„ Ű§Ű·Ù„Ű§ŰčۧŰȘی Ù…Ű«Ù„ Ù…ŰźŰ§Ű·ŰšÛŒÙ†ŰŒ ÙŸÛŒŰ§Ù…‌Ù‡Ű§ŰŒ و ŰčÚ©Űł‌Ù‡Ű§&lt;br/&gt;• Ű§Ű±ŰłŰ§Ù„ ۧŰčÙ„Ű§Ù†&lt;br/&gt;&lt;br/&gt;Ù‡Ű±ŰČÙ…Ű§Ù† ŰšŰźÙˆŰ§Ù‡ÛŒŰŻ می‌ŰȘÙˆŰ§Ù†ÛŒŰŻ Ű§ÛŒÙ† ŰȘÙˆŰ§Ù†Ű§ÛŒÛŒ ŰšŰ±Ù†Ű§Ù…Ù‡ ŰšŰ±Ű§ÛŒ ŰźÙˆŰ§Ù†ŰŻÙ† و Ű§Ű±ŰłŰ§Ù„ ۧŰčÙ„Ű§Ù† ۱ۧ ۯ۱ «ŰȘÙ†ŰžÛŒÙ…Ű§ŰȘ > ۧŰčÙ„Ű§Ù†‌Ù‡Ű§» Ù…ŰŻÛŒŰ±ÛŒŰȘ Ú©Ù†ÛŒŰŻ."</string>
     <string name="permission_app_streaming_summary" msgid="606923325679670624">"ŰŹŰ§Ű±ÛŒ‌۳ۧŰČی ŰšŰ±Ù†Ű§Ù…Ù‡‌Ù‡Ű§ÛŒ ŰȘلفن"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
     <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"ŰšŰ±Ù†Ű§Ù…Ù‡‌Ù‡Ű§ و ŰŻÛŒÚŻŰ± ÙˆÛŒÚ˜ÚŻÛŒ‌Ù‡Ű§ÛŒ ŰłÛŒŰłŰȘم ۱ۧ ۧŰČ ŰȘلفن ŰŽÙ…Ű§ ŰŹŰ§Ű±ÛŒ‌۳ۧŰČی می‌Ú©Ù†ŰŻ"</string>
diff --git a/packages/CompanionDeviceManager/res/values-fi/strings.xml b/packages/CompanionDeviceManager/res/values-fi/strings.xml
index b40de5f..86e7904 100644
--- a/packages/CompanionDeviceManager/res/values-fi/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-fi/strings.xml
@@ -56,28 +56,18 @@
     <string name="permission_nearby_devices" msgid="7530973297737123481">"Lähellä olevat laitteet"</string>
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Median ulostulon muuttaminen"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Kuvat ja media"</string>
-    <!-- no translation found for permission_notifications (4099418516590632909) -->
-    <skip />
+    <string name="permission_notifications" msgid="4099418516590632909">"Ilmoitukset"</string>
     <string name="permission_app_streaming" msgid="6009695219091526422">"Sovellukset"</string>
     <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Striimaus"</string>
-    <!-- no translation found for permission_phone_summary (8246321093970051702) -->
-    <skip />
-    <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
-    <skip />
-    <!-- no translation found for permission_sms_summary (8499509535410068616) -->
-    <skip />
-    <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
-    <skip />
-    <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
-    <skip />
-    <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
-    <skip />
-    <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
-    <skip />
-    <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
-    <skip />
-    <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
-    <skip />
+    <string name="permission_phone_summary" msgid="8246321093970051702">"soittaa ja hallinnoida puheluita"</string>
+    <string name="permission_call_logs_summary" msgid="7545243592757693321">"lukea puhelulokia ja kirjoittaa siihen"</string>
+    <string name="permission_sms_summary" msgid="8499509535410068616">"lähettää ja lukea tekstiviestejä"</string>
+    <string name="permission_contacts_summary" msgid="2840800622763086808">"käyttää yhteystietoja"</string>
+    <string name="permission_calendar_summary" msgid="8430353935747336165">"käyttää kalenteria"</string>
+    <string name="permission_microphone_summary" msgid="4862628553869973259">"tallentaa audiota"</string>
+    <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"löytää lähellä olevia laitteita, muodostaa niihin yhteyden ja määrittää niiden suhteellisen sijainnin"</string>
+    <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"lukea kaikkia ilmoituksia, esim. yhteystietoihin, viesteihin ja kuviin liittyviä tietoja"</string>
+    <string name="permission_notifications_summary" msgid="2272810466047367030">"• lukea kaikkia ilmoituksia, esim. yhteystietoihin, viesteihin ja kuviin liittyviä tietoja&lt;br/&gt;• lähettää ilmoituksia.&lt;br/&gt;&lt;br/&gt;Voit milloin tahansa valita, saako sovellus lukea ja lähettää ilmoituksia, avaamalla Asetukset &gt; Ilmoitukset."</string>
     <string name="permission_app_streaming_summary" msgid="606923325679670624">"Striimaa puhelimen sovelluksia"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
     <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Striimaa sovelluksia ja muita järjestelmän ominaisuuksia puhelimesta"</string>
diff --git a/packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml b/packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml
index 9b5707c..b74c8ee 100644
--- a/packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml
@@ -54,36 +54,24 @@
     <string name="permission_microphone" msgid="2152206421428732949">"Microphone"</string>
     <string name="permission_call_logs" msgid="5546761417694586041">"Journaux d\'appels"</string>
     <string name="permission_nearby_devices" msgid="7530973297737123481">"Appareils à proximité"</string>
-    <!-- no translation found for permission_media_routing_control (5498639511586715253) -->
-    <skip />
+    <string name="permission_media_routing_control" msgid="5498639511586715253">"Modifier la sortie multimédia"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Photos et fichiers multimédias"</string>
-    <!-- no translation found for permission_notifications (4099418516590632909) -->
-    <skip />
+    <string name="permission_notifications" msgid="4099418516590632909">"Notifications"</string>
     <string name="permission_app_streaming" msgid="6009695219091526422">"Applications"</string>
     <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Diffusion en continu"</string>
-    <!-- no translation found for permission_phone_summary (8246321093970051702) -->
-    <skip />
-    <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
-    <skip />
-    <!-- no translation found for permission_sms_summary (8499509535410068616) -->
-    <skip />
-    <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
-    <skip />
-    <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
-    <skip />
-    <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
-    <skip />
-    <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
-    <skip />
-    <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
-    <skip />
-    <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
-    <skip />
+    <string name="permission_phone_summary" msgid="8246321093970051702">"Passez et gérez des appels téléphoniques"</string>
+    <string name="permission_call_logs_summary" msgid="7545243592757693321">"Lisez et écrivez le journal d\'appels téléphoniques"</string>
+    <string name="permission_sms_summary" msgid="8499509535410068616">"Envoyez et affichez des messages texte"</string>
+    <string name="permission_contacts_summary" msgid="2840800622763086808">"Accédez à vos contacts"</string>
+    <string name="permission_calendar_summary" msgid="8430353935747336165">"Accédez à votre agenda"</string>
+    <string name="permission_microphone_summary" msgid="4862628553869973259">"Enregistrez des fichiers audio"</string>
+    <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Trouvez et déterminez la position relative des appareils à proximité, et connectez-vous à ceux-ci"</string>
+    <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Lisez toutes les notifications, y compris les renseignements tels que les contacts, les messages et les photos"</string>
+    <string name="permission_notifications_summary" msgid="2272810466047367030">"• Lisez toutes les notifications, y compris les renseignements tels que les contacts, les messages et les photos&lt;br/&gt;• Envoyez des notifications&lt;br/&gt;&lt;br/&gt;Vous pouvez gérer la capacité de cette application à lire et à envoyer des notifications à tout moment dans Paramètres > Notifications."</string>
     <string name="permission_app_streaming_summary" msgid="606923325679670624">"Diffusez les applications de votre téléphone"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
     <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Diffusez des applications et d\'autres fonctionnalités du système à partir de votre téléphone"</string>
-    <!-- no translation found for permission_media_routing_control_summary (2714631092321412250) -->
-    <skip />
+    <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"Accédez à une liste d\'appareils accessibles et contrôlez ceux qui diffusent du contenu audio ou vidéo à partir d\'autres applications"</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"téléphone"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"tablette"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-fr/strings.xml b/packages/CompanionDeviceManager/res/values-fr/strings.xml
index 3437988..30b0c9a 100644
--- a/packages/CompanionDeviceManager/res/values-fr/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-fr/strings.xml
@@ -56,28 +56,18 @@
     <string name="permission_nearby_devices" msgid="7530973297737123481">"Appareils à proximité"</string>
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Modifier la sortie multimédia"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Photos et contenus multimédias"</string>
-    <!-- no translation found for permission_notifications (4099418516590632909) -->
-    <skip />
+    <string name="permission_notifications" msgid="4099418516590632909">"Notifications"</string>
     <string name="permission_app_streaming" msgid="6009695219091526422">"Applis"</string>
     <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Streaming"</string>
-    <!-- no translation found for permission_phone_summary (8246321093970051702) -->
-    <skip />
-    <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
-    <skip />
-    <!-- no translation found for permission_sms_summary (8499509535410068616) -->
-    <skip />
-    <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
-    <skip />
-    <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
-    <skip />
-    <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
-    <skip />
-    <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
-    <skip />
-    <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
-    <skip />
-    <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
-    <skip />
+    <string name="permission_phone_summary" msgid="8246321093970051702">"Effectuer et gérer des appels téléphoniques"</string>
+    <string name="permission_call_logs_summary" msgid="7545243592757693321">"Consulter et créer les journaux d\'appels du téléphone"</string>
+    <string name="permission_sms_summary" msgid="8499509535410068616">"Envoyer et consulter des SMS"</string>
+    <string name="permission_contacts_summary" msgid="2840800622763086808">"Accéder à vos contacts"</string>
+    <string name="permission_calendar_summary" msgid="8430353935747336165">"Accéder à votre agenda"</string>
+    <string name="permission_microphone_summary" msgid="4862628553869973259">"Enregistrer un fichier audio"</string>
+    <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Trouver les appareils à proximité, s\'y connecter et déterminer leur position relative"</string>
+    <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Lire toutes les notifications, y compris des informations comme les contacts, messages et photos"</string>
+    <string name="permission_notifications_summary" msgid="2272810466047367030">"• Lire toutes les notifications, y compris les informations comme les contacts, les messages et les photos&lt;br/&gt;• Envoyer des notifications&lt;br/&gt;&lt;br/&gt;Vous pouvez gérer les autorisations de lecture et d\'envoi des notifications à tout moment dans Paramètres &gt; Notifications."</string>
     <string name="permission_app_streaming_summary" msgid="606923325679670624">"Diffuser en streaming les applis de votre téléphone"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
     <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Diffusez des applis et d\'autres fonctionnalités système en streaming depuis votre téléphone"</string>
diff --git a/packages/CompanionDeviceManager/res/values-gl/strings.xml b/packages/CompanionDeviceManager/res/values-gl/strings.xml
index e84f2d9..1b8b8d4 100644
--- a/packages/CompanionDeviceManager/res/values-gl/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-gl/strings.xml
@@ -56,28 +56,18 @@
     <string name="permission_nearby_devices" msgid="7530973297737123481">"Dispositivos próximos"</string>
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Cambiar a saída multimedia"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Fotos e contido multimedia"</string>
-    <!-- no translation found for permission_notifications (4099418516590632909) -->
-    <skip />
+    <string name="permission_notifications" msgid="4099418516590632909">"Notificacións"</string>
     <string name="permission_app_streaming" msgid="6009695219091526422">"Aplicacións"</string>
     <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Reprodución en tempo real"</string>
-    <!-- no translation found for permission_phone_summary (8246321093970051702) -->
-    <skip />
-    <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
-    <skip />
-    <!-- no translation found for permission_sms_summary (8499509535410068616) -->
-    <skip />
-    <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
-    <skip />
-    <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
-    <skip />
-    <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
-    <skip />
-    <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
-    <skip />
-    <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
-    <skip />
-    <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
-    <skip />
+    <string name="permission_phone_summary" msgid="8246321093970051702">"Fai e xestiona chamadas telefónicas"</string>
+    <string name="permission_call_logs_summary" msgid="7545243592757693321">"Le e edita o rexistro de chamadas do teléfono"</string>
+    <string name="permission_sms_summary" msgid="8499509535410068616">"Envía e consulta mensaxes SMS"</string>
+    <string name="permission_contacts_summary" msgid="2840800622763086808">"Accede aos contactos"</string>
+    <string name="permission_calendar_summary" msgid="8430353935747336165">"Accede ao calendario"</string>
+    <string name="permission_microphone_summary" msgid="4862628553869973259">"Grava audio"</string>
+    <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Busca dispositivos próximos, establece conexión con eles e determina a súa posición relativa"</string>
+    <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Le todas as notificacións (que poden incluír información como contactos, mensaxes e fotos)"</string>
+    <string name="permission_notifications_summary" msgid="2272810466047367030">"• Le todas as notificacións (que poden incluír información como contactos, mensaxes e fotos)&lt;br/&gt;• Envía notificacións&lt;br/&gt;&lt;br/&gt;En Configuración &gt; Notificacións podes xestionar en calquera momento a capacidade desta aplicación para ler e enviar notificacións."</string>
     <string name="permission_app_streaming_summary" msgid="606923325679670624">"Emite as aplicacións do teu teléfono"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
     <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Emite o contido das aplicacións e doutras funcións do sistema desde o teléfono"</string>
diff --git a/packages/CompanionDeviceManager/res/values-gu/strings.xml b/packages/CompanionDeviceManager/res/values-gu/strings.xml
index 62e518a..474960c 100644
--- a/packages/CompanionDeviceManager/res/values-gu/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-gu/strings.xml
@@ -56,28 +56,18 @@
     <string name="permission_nearby_devices" msgid="7530973297737123481">"àȘšàȘœà«€àȘ•àȘšàȘŸ àȘĄàȘżàȘ”àȘŸàȘ‡àȘž"</string>
     <string name="permission_media_routing_control" msgid="5498639511586715253">"àȘźà«€àȘĄàȘżàȘŻàȘŸ àȘ†àȘ‰àȘŸàȘȘુàȘŸ àȘŹàȘŠàȘČો"</string>
     <string name="permission_storage" msgid="6831099350839392343">"àȘ«à«‹àȘŸàȘŸ àȘ…àȘšà«‡ àȘźà«€àȘĄàȘżàȘŻàȘŸ"</string>
-    <!-- no translation found for permission_notifications (4099418516590632909) -->
-    <skip />
+    <string name="permission_notifications" msgid="4099418516590632909">"àȘšà«‹àȘŸàȘżàȘ«àȘżàȘ•ેàȘ¶àȘš"</string>
     <string name="permission_app_streaming" msgid="6009695219091526422">"àȘàȘȘ"</string>
     <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"àȘžà«àȘŸà«àȘ°à«€àȘźàȘżàȘ‚àȘ—"</string>
-    <!-- no translation found for permission_phone_summary (8246321093970051702) -->
-    <skip />
-    <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
-    <skip />
-    <!-- no translation found for permission_sms_summary (8499509535410068616) -->
-    <skip />
-    <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
-    <skip />
-    <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
-    <skip />
-    <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
-    <skip />
-    <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
-    <skip />
-    <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
-    <skip />
-    <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
-    <skip />
+    <string name="permission_phone_summary" msgid="8246321093970051702">"àȘ«à«‹àȘš àȘ•ૉàȘČ àȘ•àȘ°àȘ”àȘŸàȘšà«€ àȘ…àȘšà«‡ àȘźà«‡àȘšà«‡àȘœ àȘ•àȘ°àȘ”àȘŸàȘšà«€"</string>
+    <string name="permission_call_logs_summary" msgid="7545243592757693321">"àȘ«à«‹àȘš àȘ•ૉàȘČ àȘČૉàȘ— àȘ”àȘŸàȘ‚àȘšàȘ”àȘŸàȘšà«€ àȘ…àȘšà«‡ àȘČàȘ–àȘ”àȘŸàȘšà«€"</string>
+    <string name="permission_sms_summary" msgid="8499509535410068616">"SMS àȘźà«‡àȘžà«‡àȘœ àȘźà«‹àȘ•àȘČàȘ”àȘŸàȘšà«€ àȘ…àȘšà«‡ àȘœà«‹àȘ”àȘŸàȘšà«€"</string>
+    <string name="permission_contacts_summary" msgid="2840800622763086808">"àȘ€àȘźàȘŸàȘ°àȘŸ àȘžàȘ‚àȘȘàȘ°à«àȘ•à«‹àȘšà«‡ àȘàȘ•્àȘžà«‡àȘž àȘ•àȘ°àȘ”àȘŸàȘšà«€"</string>
+    <string name="permission_calendar_summary" msgid="8430353935747336165">"àȘ€àȘźàȘŸàȘ°àȘŸ àȘ•ેàȘČેàȘšà«àȘĄàȘ°àȘšà«‡ àȘàȘ•્àȘžà«‡àȘž àȘ•àȘ°àȘ”àȘŸàȘšà«€"</string>
+    <string name="permission_microphone_summary" msgid="4862628553869973259">"àȘ‘àȘĄàȘżàȘŻà«‹ àȘ°à«‡àȘ•à«‹àȘ°à«àȘĄ àȘ•àȘ°àȘ”àȘŸàȘšà«€"</string>
+    <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"àȘšàȘœà«€àȘ•àȘšàȘŸ àȘĄàȘżàȘ”àȘŸàȘ‡àȘž àȘ¶à«‹àȘ§àȘ”àȘŸàȘšà«€, àȘ€à«‡àȘźàȘšà«€ àȘžàȘŸàȘ„ે àȘ•àȘšà«‡àȘ•્àȘŸ àȘ•àȘ°àȘ”àȘŸàȘšà«€ àȘ…àȘšà«‡ àȘ€à«‡àȘźàȘšà«€ àȘžàȘ‚àȘŹàȘ‚àȘ§àȘżàȘ€ àȘžà«àȘ„àȘżàȘ€àȘż àȘšàȘżàȘ°à«àȘ§àȘŸàȘ°àȘżàȘ€ àȘ•àȘ°àȘ”àȘŸàȘšà«€"</string>
+    <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"àȘžàȘ‚àȘȘàȘ°à«àȘ•à«‹, àȘźà«‡àȘžà«‡àȘœ àȘ…àȘšà«‡ àȘ«à«‹àȘŸàȘŸ àȘœà«‡àȘ”ી àȘźàȘŸàȘčàȘżàȘ€à«€ àȘžàȘčàȘżàȘ€àȘšàȘŸ àȘŹàȘ§àȘŸ àȘšà«‹àȘŸàȘżàȘ«àȘżàȘ•ેàȘ¶àȘš àȘ”àȘŸàȘ‚àȘšàȘ”àȘŸàȘšà«€"</string>
+    <string name="permission_notifications_summary" msgid="2272810466047367030">"• àȘžàȘ‚àȘȘàȘ°à«àȘ•à«‹, àȘźà«‡àȘžà«‡àȘœ àȘ…àȘšà«‡ àȘ«à«‹àȘŸàȘŸ àȘœà«‡àȘ”ી àȘźàȘŸàȘčàȘżàȘ€à«€ àȘžàȘčàȘżàȘ€àȘšàȘŸ àȘŹàȘ§àȘŸ àȘšà«‹àȘŸàȘżàȘ«àȘżàȘ•ેàȘ¶àȘš àȘ”àȘŸàȘ‚àȘšàȘ”àȘŸàȘšà«€&lt;br/&gt;• àȘšà«‹àȘŸàȘżàȘ«àȘżàȘ•ેàȘ¶àȘš àȘźà«‹àȘ•àȘČàȘ”àȘŸàȘšà«€&lt;br/&gt;&lt;br/&gt;àȘ€àȘźà«‡ àȘ•à«‹àȘˆàȘȘàȘŁ àȘžàȘźàȘŻà«‡ àȘžà«‡àȘŸàȘżàȘ‚àȘ—àȘźàȘŸàȘ‚ &gt; àȘšà«‹àȘŸàȘżàȘ«àȘżàȘ•ેàȘ¶àȘšàȘźàȘŸàȘ‚ àȘœàȘˆàȘšà«‡ àȘ† àȘàȘȘàȘšà«€ àȘšà«‹àȘŸàȘżàȘ«àȘżàȘ•ેàȘ¶àȘš àȘ”àȘŸàȘ‚àȘšàȘ”àȘŸàȘšà«€ àȘ…àȘšà«‡ àȘźà«‹àȘ•àȘČàȘ”àȘŸàȘšà«€ àȘ•્àȘ·àȘźàȘ€àȘŸàȘšà«‡ àȘźà«‡àȘšà«‡àȘœ àȘ•àȘ°à«€ àȘ¶àȘ•à«‹ àȘ›à«‹."</string>
     <string name="permission_app_streaming_summary" msgid="606923325679670624">"àȘ€àȘźàȘŸàȘ°àȘŸ àȘ«à«‹àȘšàȘšà«€ àȘàȘȘ àȘžà«àȘŸà«àȘ°à«€àȘź àȘ•àȘ°à«‹"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
     <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"àȘ€àȘźàȘŸàȘ°àȘŸ àȘ«à«‹àȘš àȘȘàȘ°àȘ„ી àȘàȘȘ àȘ…àȘšà«‡ àȘžàȘżàȘžà«àȘŸàȘźàȘšà«€ àȘ…àȘšà«àȘŻ àȘžà«àȘ”àȘżàȘ§àȘŸàȘ“ àȘžà«àȘŸà«àȘ°à«€àȘź àȘ•àȘ°à«‹"</string>
diff --git a/packages/CompanionDeviceManager/res/values-hi/strings.xml b/packages/CompanionDeviceManager/res/values-hi/strings.xml
index 3d3a2c1..931eab6 100644
--- a/packages/CompanionDeviceManager/res/values-hi/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-hi/strings.xml
@@ -56,28 +56,18 @@
     <string name="permission_nearby_devices" msgid="7530973297737123481">"à€†à€ž-à€Șà€Ÿà€ž à€źà„Œà€œà„‚à€Š à€Ąà€żà€”à€Ÿà€‡à€ž"</string>
     <string name="permission_media_routing_control" msgid="5498639511586715253">"à€źà„€à€Ąà€żà€Żà€Ÿ à€†à€‰à€Ÿà€Șà„à€Ÿ à€Źà€Šà€Čà„‡à€‚"</string>
     <string name="permission_storage" msgid="6831099350839392343">"à€«à€Œà„‹à€Ÿà„‹ à€”à€° à€źà„€à€Ąà€żà€Żà€Ÿ"</string>
-    <!-- no translation found for permission_notifications (4099418516590632909) -->
-    <skip />
+    <string name="permission_notifications" msgid="4099418516590632909">"à€žà„‚à€šà€šà€Ÿà€à€‚"</string>
     <string name="permission_app_streaming" msgid="6009695219091526422">"à€à€Șà„à€Čà€żà€•à„‡à€¶à€š"</string>
     <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"à€žà„à€Ÿà„à€°à„€à€źà€żà€‚à€—"</string>
-    <!-- no translation found for permission_phone_summary (8246321093970051702) -->
-    <skip />
-    <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
-    <skip />
-    <!-- no translation found for permission_sms_summary (8499509535410068616) -->
-    <skip />
-    <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
-    <skip />
-    <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
-    <skip />
-    <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
-    <skip />
-    <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
-    <skip />
-    <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
-    <skip />
-    <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
-    <skip />
+    <string name="permission_phone_summary" msgid="8246321093970051702">"à€«à€Œà„‹à€š à€•à„‰à€Č à€•à€°à€šà„‡ à€”à€° à€‰à€šà„à€čà„‡à€‚ à€źà„ˆà€šà„‡à€œ à€•à€°à€šà„‡ à€•à„€ à€…à€šà„à€źà€€à€ż à€Šà„‡à€‚"</string>
+    <string name="permission_call_logs_summary" msgid="7545243592757693321">"à€•à„‰à€Č à€Čà„‰à€— à€•à„€ à€œà€Ÿà€šà€•à€Ÿà€°à„€ à€Šà„‡à€–à€šà„‡ à€”à€° à€‰à€žà€źà„‡à€‚ à€Źà€Šà€Čà€Ÿà€” à€•à€°à€šà„‡ à€•à„€ à€…à€šà„à€źà€€à€ż à€Šà„‡à€‚"</string>
+    <string name="permission_sms_summary" msgid="8499509535410068616">"à€à€žà€à€źà€à€ž (à€źà„ˆà€žà„‡à€œ) à€­à„‡à€œà€šà„‡ à€”à€° à€Šà„‡à€–à€šà„‡ à€•à„€ à€…à€šà„à€źà€€à€ż à€Šà„‡à€‚"</string>
+    <string name="permission_contacts_summary" msgid="2840800622763086808">"à€…à€Șà€šà„‡ à€žà€‚à€Șà€°à„à€•à„‹à€‚ à€•à„‹ à€à€•à„à€žà„‡à€ž à€•à€°à€šà„‡ à€•à„€ à€…à€šà„à€źà€€à€ż à€Šà„‡à€‚"</string>
+    <string name="permission_calendar_summary" msgid="8430353935747336165">"à€…à€Șà€šà„‡ à€•à„ˆà€Čà„‡à€‚à€Ąà€° à€•à„‹ à€à€•à„à€žà„‡à€ž à€•à€°à€šà„‡ à€•à„€ à€…à€šà„à€źà€€à€ż à€Šà„‡à€‚"</string>
+    <string name="permission_microphone_summary" msgid="4862628553869973259">"à€‘à€Ąà€żà€Żà„‹ à€°à€żà€•à„‰à€°à„à€Ą à€•à€°à€šà„‡ à€•à„€ à€…à€šà„à€źà€€à€ż à€Šà„‡à€‚"</string>
+    <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"à€†à€ž-à€Șà€Ÿà€ž à€źà„Œà€œà„‚à€Š à€Ąà€żà€”à€Ÿà€‡à€žà„‹à€‚ à€•à„‹ à€–à„‹à€œà€šà„‡, à€‰à€šà€žà„‡ à€•à€šà„‡à€•à„à€Ÿ à€•à€°à€šà„‡, à€”à€° à€‰à€šà€•à„€ à€œà€—à€č à€•à„€ à€œà€Ÿà€šà€•à€Ÿà€°à„€ à€•à€Ÿ à€Șà€€à€Ÿ à€Čà€—à€Ÿà€šà„‡ à€•à„€ à€…à€šà„à€źà€€à€ż à€Šà„‡à€‚"</string>
+    <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"à€žà€­à„€ à€žà„‚à€šà€šà€Ÿà€à€‚ à€Šà„‡à€–à€šà„‡ à€•à„€ à€…à€šà„à€źà€€à€ż à€Šà„‡à€‚. à€œà„ˆà€žà„‡, à€žà€‚à€Șà€°à„à€•, à€źà„ˆà€žà„‡à€œ, à€”à€° à€«à€Œà„‹à€Ÿà„‹ à€•à„€ à€œà€Ÿà€šà€•à€Ÿà€°à„€"</string>
+    <string name="permission_notifications_summary" msgid="2272810466047367030">"• à€žà€­à„€ à€žà„‚à€šà€šà€Ÿà€à€‚ à€Šà„‡à€–à€šà„‡ à€•à„€ à€…à€šà„à€źà€€à€ż à€Šà„‡à€‚. à€œà„ˆà€žà„‡, à€žà€‚à€Șà€°à„à€•, à€źà„ˆà€žà„‡à€œ, à€”à€° à€«à€Œà„‹à€Ÿà„‹ à€•à„€ à€œà€Ÿà€šà€•à€Ÿà€°à„€&lt;br/&gt;• à€žà„‚à€šà€šà€Ÿà€à€‚ à€­à„‡à€œà€šà„‡ à€•à„€ à€…à€šà„à€źà€€à€ż à€Šà„‡à€‚&lt;br/&gt;&lt;br/&gt;à€žà„‡à€Ÿà€żà€‚à€— &gt; à€žà„‚à€šà€šà€Ÿà€à€‚ à€źà„‡à€‚ à€œà€Ÿà€•à€° à€‡à€ž à€à€Șà„à€Čà€żà€•à„‡à€¶à€š à€•à„‡ à€Čà€żà€, à€žà„‚à€šà€šà€Ÿà€“à€‚ à€•à„‹ à€Šà„‡à€–à€šà„‡ à€”à€° à€‰à€šà€źà„‡à€‚ à€Źà€Šà€Čà€Ÿà€” à€•à€°à€šà„‡ à€•à„€ à€…à€šà„à€źà€€à€ż à€źà„‡à€‚ à€•à€­à„€ à€­à„€ à€Źà€Šà€Čà€Ÿà€” à€•à€żà€Żà€Ÿ à€œà€Ÿ à€žà€•à€€à€Ÿ à€čà„ˆ."</string>
     <string name="permission_app_streaming_summary" msgid="606923325679670624">"à€…à€Șà€šà„‡ à€«à€Œà„‹à€š à€Șà€° à€źà„Œà€œà„‚à€Š à€à€Șà„à€Čà€żà€•à„‡à€¶à€š à€žà„à€Ÿà„à€°à„€à€ź à€•à€°à„‡à€‚"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
     <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"à€…à€Șà€šà„‡ à€«à€Œà„‹à€š à€žà„‡ à€à€Șà„à€Čà€żà€•à„‡à€¶à€š à€”à€° à€žà€żà€žà„à€Ÿà€ź à€•à„€ à€Šà„‚à€žà€°à„€ à€žà„à€”à€żà€§à€Ÿà€“à€‚ à€•à„‹ à€žà„à€Ÿà„à€°à„€à€ź à€•à€°à„‡à€‚"</string>
diff --git a/packages/CompanionDeviceManager/res/values-hr/strings.xml b/packages/CompanionDeviceManager/res/values-hr/strings.xml
index d6fbf2f..6dc59ea 100644
--- a/packages/CompanionDeviceManager/res/values-hr/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-hr/strings.xml
@@ -56,28 +56,18 @@
     <string name="permission_nearby_devices" msgid="7530973297737123481">"Uređaji u blizini"</string>
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Promjena medijskog izlaza"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Fotografije i mediji"</string>
-    <!-- no translation found for permission_notifications (4099418516590632909) -->
-    <skip />
+    <string name="permission_notifications" msgid="4099418516590632909">"Obavijesti"</string>
     <string name="permission_app_streaming" msgid="6009695219091526422">"Aplikacije"</string>
     <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Streaming"</string>
-    <!-- no translation found for permission_phone_summary (8246321093970051702) -->
-    <skip />
-    <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
-    <skip />
-    <!-- no translation found for permission_sms_summary (8499509535410068616) -->
-    <skip />
-    <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
-    <skip />
-    <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
-    <skip />
-    <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
-    <skip />
-    <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
-    <skip />
-    <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
-    <skip />
-    <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
-    <skip />
+    <string name="permission_phone_summary" msgid="8246321093970051702">"Uspostavljanje telefonskih poziva i upravljanje njima"</string>
+    <string name="permission_call_logs_summary" msgid="7545243592757693321">"Čitanje i pisanje zapisnika poziva telefona"</string>
+    <string name="permission_sms_summary" msgid="8499509535410068616">"Slanje i pregledavanje SMS poruka"</string>
+    <string name="permission_contacts_summary" msgid="2840800622763086808">"Pristup kontaktima"</string>
+    <string name="permission_calendar_summary" msgid="8430353935747336165">"Pristup kalendaru"</string>
+    <string name="permission_microphone_summary" msgid="4862628553869973259">"Snimanje zvuka"</string>
+    <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"PronalaĆŸenje i određivanje relativnog poloĆŸaja uređaja u blizini i povezivanje s njima"</string>
+    <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Čitanje svih obavijesti, uključujući podatke kao što su kontakti, poruke i fotografije"</string>
+    <string name="permission_notifications_summary" msgid="2272810466047367030">"• Čitanje svih obavijesti, uključujući podatke kao što su kontakti, poruke i fotografije&lt;br/&gt;• Slanje obavijesti&lt;br/&gt;&lt;br/&gt;Uvijek moĆŸete upravljati mogućnostima ove aplikacije da čita i šalje obavijesti u postavkama i obavijestima"</string>
     <string name="permission_app_streaming_summary" msgid="606923325679670624">"Streaming aplikacija vašeg telefona"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
     <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Emitirajte stream aplikacija i drugih značajki sustava s vašeg telefona"</string>
diff --git a/packages/CompanionDeviceManager/res/values-hu/strings.xml b/packages/CompanionDeviceManager/res/values-hu/strings.xml
index 0db1f70..4b1a6f7 100644
--- a/packages/CompanionDeviceManager/res/values-hu/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-hu/strings.xml
@@ -56,28 +56,18 @@
     <string name="permission_nearby_devices" msgid="7530973297737123481">"Közeli eszközök"</string>
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Médiakiment módosítása"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Fotók és médiatartalmak"</string>
-    <!-- no translation found for permission_notifications (4099418516590632909) -->
-    <skip />
+    <string name="permission_notifications" msgid="4099418516590632909">"Értesítések"</string>
     <string name="permission_app_streaming" msgid="6009695219091526422">"Alkalmazások"</string>
     <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Streamelés"</string>
-    <!-- no translation found for permission_phone_summary (8246321093970051702) -->
-    <skip />
-    <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
-    <skip />
-    <!-- no translation found for permission_sms_summary (8499509535410068616) -->
-    <skip />
-    <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
-    <skip />
-    <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
-    <skip />
-    <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
-    <skip />
-    <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
-    <skip />
-    <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
-    <skip />
-    <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
-    <skip />
+    <string name="permission_phone_summary" msgid="8246321093970051702">"Telefonhívások kezdeményezése és kezelése"</string>
+    <string name="permission_call_logs_summary" msgid="7545243592757693321">"Hívásnapló olvasása és írása"</string>
+    <string name="permission_sms_summary" msgid="8499509535410068616">"SMS-ek küldése és megtekintése"</string>
+    <string name="permission_contacts_summary" msgid="2840800622763086808">"Hozzáférés a névjegyekhez"</string>
+    <string name="permission_calendar_summary" msgid="8430353935747336165">"Hozzáférés a naptárhoz"</string>
+    <string name="permission_microphone_summary" msgid="4862628553869973259">"Hang rögzítése"</string>
+    <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Megkeresheti a közeli eszközöket, meghatározhatja viszonylagos helyzetüket és csatlakozhat hozzájuk."</string>
+    <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Elolvashat minden értesítést, ideértve az olyan információkat, mint a névjegyek, az üzenetek és a fotók."</string>
+    <string name="permission_notifications_summary" msgid="2272810466047367030">"• Elolvashat minden értesítést, ideértve az olyan információkat, mint a névjegyek, az üzenetek és a fotók.&lt;br/&gt;• Értesítéseket küldhet.&lt;br/&gt;&lt;br/&gt;A Beállítások &gt; Értesítések menüpontban bármikor kezelheti az alkalmazás értesítések olvasására és küldésére való képességét."</string>
     <string name="permission_app_streaming_summary" msgid="606923325679670624">"A telefon alkalmazásainak streamelése"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
     <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Alkalmazások és más rendszerfunkciók streamelése a telefonról"</string>
diff --git a/packages/CompanionDeviceManager/res/values-hy/strings.xml b/packages/CompanionDeviceManager/res/values-hy/strings.xml
index 6e08a97..405a983 100644
--- a/packages/CompanionDeviceManager/res/values-hy/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-hy/strings.xml
@@ -54,36 +54,24 @@
     <string name="permission_microphone" msgid="2152206421428732949">"ÔœŐžŐœŐĄÖƒŐžŐČ"</string>
     <string name="permission_call_logs" msgid="5546761417694586041">"ÔżŐĄŐ¶ŐčŐ„Ö€Ő« ցվւցակ"</string>
     <string name="permission_nearby_devices" msgid="7530973297737123481">"Մվտակա ŐœŐĄÖ€Ö„Ő„Ö€"</string>
-    <!-- no translation found for permission_media_routing_control (5498639511586715253) -->
-    <skip />
+    <string name="permission_media_routing_control" msgid="5498639511586715253">"Ő“ŐžŐ­Ő„ŐŹ ŐŽŐ„Ő€Ő«ŐĄ արտառվւծչ"</string>
     <string name="permission_storage" msgid="6831099350839392343">"ÔŒŐžÖ‚ŐœŐĄŐ¶ŐŻŐĄÖ€Ő¶Ő„Ö€ և ŐŽŐžÖ‚ŐŹŐżŐ«ŐŽŐ„Ő€Ő«ŐĄ"</string>
-    <!-- no translation found for permission_notifications (4099418516590632909) -->
-    <skip />
+    <string name="permission_notifications" msgid="4099418516590632909">"ÔŸŐĄŐ¶ŐžÖ‚ÖŐžÖ‚ŐŽŐ¶Ő„Ö€"</string>
     <string name="permission_app_streaming" msgid="6009695219091526422">"Ő€ŐĄŐŸŐ„ŐŹŐŸŐĄŐźŐ¶Ő„Ö€"</string>
     <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Ő€Ő„ŐŒŐĄÖ€Ő±ŐĄŐŻŐžÖ‚ŐŽ"</string>
-    <!-- no translation found for permission_phone_summary (8246321093970051702) -->
-    <skip />
-    <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
-    <skip />
-    <!-- no translation found for permission_sms_summary (8499509535410068616) -->
-    <skip />
-    <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
-    <skip />
-    <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
-    <skip />
-    <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
-    <skip />
-    <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
-    <skip />
-    <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
-    <skip />
-    <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
-    <skip />
+    <string name="permission_phone_summary" msgid="8246321093970051702">"ÔżŐĄŐżŐĄÖ€Ő„ŐŹ ŐŠŐĄŐ¶ŐŁŐ„Ö€ և ŐŻŐĄŐŒŐĄŐŸŐĄÖ€Ő„ŐŹ ŐŠŐĄŐ¶ŐŁŐ„Ö€Őš"</string>
+    <string name="permission_call_logs_summary" msgid="7545243592757693321">"ÔŽŐ«ŐżŐ„ŐŹ Ő°Ő„ŐŒŐĄŐ­ŐžŐœŐ« ŐŠŐĄŐ¶ŐŁŐ„Ö€Ő« ŐŽŐĄŐżŐ”ŐĄŐ¶Őš և ŐŻŐĄŐżŐĄÖ€Ő„ŐŹ գրենց՞ւՎ"</string>
+    <string name="permission_sms_summary" msgid="8499509535410068616">"ՈւŐČŐĄÖ€ŐŻŐ„ŐŹ և ŐŻŐĄÖ€Ő€ŐĄŐŹ SMS Ő°ŐĄŐČŐžÖ€Ő€ŐĄŐŁÖ€ŐžÖ‚Ő©Ő”ŐžÖ‚Ő¶Ő¶Ő„Ö€"</string>
+    <string name="permission_contacts_summary" msgid="2840800622763086808">"Ő•ŐŁŐżŐĄŐŁŐžÖ€ŐźŐ„ŐŹ Ő±Ő„Ö€ ŐŻŐžŐ¶ŐżŐĄŐŻŐżŐ¶Ő„Ö€Őš"</string>
+    <string name="permission_calendar_summary" msgid="8430353935747336165">"Ő•ŐŁŐżŐĄŐŁŐžÖ€ŐźŐ„ŐŹ Ő±Ő„Ö€ օրեց՞ւՔց՚"</string>
+    <string name="permission_microphone_summary" msgid="4862628553869973259">"ՁեՔնեգրՄՏ"</string>
+    <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"ÔłŐżŐ¶Ő„ŐŹ և ŐžÖ€ŐžŐ·Ő„ŐŹ ŐŽŐžŐżŐĄŐŻŐĄ ŐœŐĄÖ€Ö„Ő„Ö€Ő« ŐŽŐžŐżŐĄŐŸŐžÖ€ Ő€Ő«Ö€Ö„Őš և ŐŽŐ«ŐĄŐ¶ŐĄŐŹ Հրենց"</string>
+    <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"ÔżŐĄÖ€Ő€ŐĄŐŹ ձվլվր ՟են՞ւց՞ւՎնՄր՚, Ő¶Ő„Ö€ŐĄŐŒŐ”ŐĄŐŹ ŐżŐ„ŐČŐ„ŐŻŐžÖ‚Ő©Ő”ŐžÖ‚Ő¶Ő¶Ő„Ö€Őš, Ö…Ö€Ő«Ő¶ŐĄŐŻŐ ŐŻŐžŐ¶ŐżŐĄŐŻŐżŐ¶Ő„Ö€Őš, Ő°ŐĄŐČŐžÖ€Ő€ŐĄŐŁÖ€ŐžÖ‚Ő©Ő”ŐžÖ‚Ő¶Ő¶Ő„Ö€Őš և ŐŹŐžÖ‚ŐœŐĄŐ¶ŐŻŐĄÖ€Ő¶Ő„Ö€Őš"</string>
+    <string name="permission_notifications_summary" msgid="2272810466047367030">"• ÔżŐĄÖ€Ő€ŐĄŐŹ ձվլվր ՟են՞ւց՞ւՎնՄր՚, ŐĄŐ”Ő€ Ő©ŐŸŐžÖ‚ŐŽŐ ŐŻŐžŐ¶ŐżŐĄŐŻŐżŐ¶Ő„Ö€Őš, Ő°ŐĄŐČŐžÖ€Ő€ŐĄŐŁÖ€ŐžÖ‚Ő©Ő”ŐžÖ‚Ő¶Ő¶Ő„Ö€Őš և ŐŹŐžÖ‚ŐœŐĄŐ¶ŐŻŐĄÖ€Ő¶Ő„Ö€Őš&lt;br/&gt;• ՈւŐČŐĄÖ€ŐŻŐ„ŐŹ ՟են՞ւց՞ւՎնՄր&lt;br/&gt;&lt;br/&gt;Ԯվւք ցենկեցե՟ ŐșŐĄŐ°Ő« կարվŐČ Ő„Ö„ ŐŻŐĄŐŒŐĄŐŸŐĄÖ€Ő„ŐŹ ŐĄŐ”Őœ Ő°ŐĄŐŸŐ„ŐŹŐŸŐĄŐźŐ«Ő ՟են՞ւց՞ւՎնՄր ŐŻŐĄÖ€Ő€ŐĄŐŹŐžÖ‚ Ő°Ő¶ŐĄÖ€ŐĄŐŸŐžÖ€ŐžÖ‚Ő©Ő”ŐžÖ‚Ő¶ŐšŐ ŐĄŐ¶ÖŐ¶Ő„ŐŹŐžŐŸ ÔżŐĄÖ€ŐŁŐĄŐŸŐžÖ€ŐžÖ‚ŐŽŐ¶Ő„Ö€ &gt; ÔŸŐĄŐ¶ŐžÖ‚ÖŐžÖ‚ŐŽŐ¶Ő„Ö€Ö‰"</string>
     <string name="permission_app_streaming_summary" msgid="606923325679670624">"Ő€Ő„ŐŒŐĄÖ€Ő±ŐĄŐŻŐ„ŐŹ Ő°Ő„ŐŒŐĄŐ­ŐžŐœŐ« Ő°ŐĄŐŸŐ„ŐŹŐŸŐĄŐźŐ¶Ő„Ö€Őš"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
     <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Ő€Ő„ŐŒŐĄÖ€Ő±ŐĄŐŻŐ„ŐŹ Ő°ŐĄŐŸŐ„ŐŹŐŸŐĄŐźŐ¶Ő„Ö€ և Ő°ŐĄŐŽŐĄŐŻŐĄÖ€ŐŁŐ« ŐĄŐ”ŐŹ ŐŁŐžÖ€ŐźŐĄŐŒŐžÖ‚Ő”Ő©Ő¶Ő„Ö€ Ő°Ő„ŐŒŐĄŐ­ŐžŐœŐžÖ‚ŐŽ"</string>
-    <!-- no translation found for permission_media_routing_control_summary (2714631092321412250) -->
-    <skip />
+    <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"ÔČեցՄՏ Ő°ŐĄŐœŐĄŐ¶Ő„ŐŹŐ« ŐœŐĄÖ€Ö„Ő„Ö€Ő« ցենկ՚ և ŐŻŐĄŐŒŐĄŐŸŐĄÖ€Ő„ŐŹ, Ő©Ő„ վր ŐœŐĄÖ€Ö„Őš ŐĄŐžÖ‚Ő€Ő«Őž ŐŻŐĄŐŽ ŐŸŐ«Ő€Ő„Őž Ő°Ő„ŐŒŐĄÖ€Ő±ŐĄŐŻŐ« ŐĄŐ”ŐŹ Ő°ŐĄŐŸŐ„ŐŹŐŸŐĄŐźŐ¶Ő„Ö€Ő«Ö"</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"Ő°Ő„ŐŒŐĄŐ­ŐžŐœ"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"ŐșŐŹŐĄŐ¶Ő·Ő„Őż"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-in/strings.xml b/packages/CompanionDeviceManager/res/values-in/strings.xml
index c0b8245..8f3909c 100644
--- a/packages/CompanionDeviceManager/res/values-in/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-in/strings.xml
@@ -56,28 +56,18 @@
     <string name="permission_nearby_devices" msgid="7530973297737123481">"Perangkat di sekitar"</string>
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Mengubah output media"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Foto dan media"</string>
-    <!-- no translation found for permission_notifications (4099418516590632909) -->
-    <skip />
+    <string name="permission_notifications" msgid="4099418516590632909">"Notifikasi"</string>
     <string name="permission_app_streaming" msgid="6009695219091526422">"Aplikasi"</string>
     <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Streaming"</string>
-    <!-- no translation found for permission_phone_summary (8246321093970051702) -->
-    <skip />
-    <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
-    <skip />
-    <!-- no translation found for permission_sms_summary (8499509535410068616) -->
-    <skip />
-    <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
-    <skip />
-    <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
-    <skip />
-    <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
-    <skip />
-    <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
-    <skip />
-    <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
-    <skip />
-    <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
-    <skip />
+    <string name="permission_phone_summary" msgid="8246321093970051702">"Melakukan dan mengelola panggilan telepon"</string>
+    <string name="permission_call_logs_summary" msgid="7545243592757693321">"Membaca dan menulis log panggilan telepon"</string>
+    <string name="permission_sms_summary" msgid="8499509535410068616">"Mengirim dan melihat pesan SMS"</string>
+    <string name="permission_contacts_summary" msgid="2840800622763086808">"Mengakses kontak"</string>
+    <string name="permission_calendar_summary" msgid="8430353935747336165">"Mengakses kalender"</string>
+    <string name="permission_microphone_summary" msgid="4862628553869973259">"Merekam audio"</string>
+    <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Menemukan, menghubungkan, dan menentukan posisi relatif dari perangkat di sekitar"</string>
+    <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Membaca semua notifikasi, termasuk informasi seperti kontak, pesan, dan foto"</string>
+    <string name="permission_notifications_summary" msgid="2272810466047367030">"• Membaca semua notifikasi, termasuk informasi seperti kontak, pesan, dan foto&lt;br/&gt;• Mengirim notifikasi&lt;br/&gt;&lt;br/&gt;Anda dapat mengelola kemampuan aplikasi untuk membaca dan mengirim notifikasi kapan saja di Setelan &gt; Notifikasi."</string>
     <string name="permission_app_streaming_summary" msgid="606923325679670624">"Streaming aplikasi ponsel"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
     <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Menstreaming aplikasi dan fitur sistem lainnya dari ponsel Anda"</string>
diff --git a/packages/CompanionDeviceManager/res/values-is/strings.xml b/packages/CompanionDeviceManager/res/values-is/strings.xml
index 5951a1d..baad2cf 100644
--- a/packages/CompanionDeviceManager/res/values-is/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-is/strings.xml
@@ -56,28 +56,18 @@
     <string name="permission_nearby_devices" msgid="7530973297737123481">"Nálæg tæki"</string>
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Breyta margmiðlunarúttaki"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Myndir og efni"</string>
-    <!-- no translation found for permission_notifications (4099418516590632909) -->
-    <skip />
+    <string name="permission_notifications" msgid="4099418516590632909">"Tilkynningar"</string>
     <string name="permission_app_streaming" msgid="6009695219091526422">"Forrit"</string>
     <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Streymi"</string>
-    <!-- no translation found for permission_phone_summary (8246321093970051702) -->
-    <skip />
-    <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
-    <skip />
-    <!-- no translation found for permission_sms_summary (8499509535410068616) -->
-    <skip />
-    <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
-    <skip />
-    <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
-    <skip />
-    <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
-    <skip />
-    <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
-    <skip />
-    <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
-    <skip />
-    <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
-    <skip />
+    <string name="permission_phone_summary" msgid="8246321093970051702">"Hringja og stjórna símtölum"</string>
+    <string name="permission_call_logs_summary" msgid="7545243592757693321">"Lesa og skrifa símtalaskrá símans"</string>
+    <string name="permission_sms_summary" msgid="8499509535410068616">"Senda og skoða SMS-skilaboð"</string>
+    <string name="permission_contacts_summary" msgid="2840800622763086808">"Fá aðgang að tengiliðunum þínum"</string>
+    <string name="permission_calendar_summary" msgid="8430353935747336165">"Fá aðgang að dagatalinu þínu"</string>
+    <string name="permission_microphone_summary" msgid="4862628553869973259">"Taka upp hljóð"</string>
+    <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Finna, tengjast og áætla staðsetningu nálægra tækja"</string>
+    <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Lesa allar tilkynningar, þar á meðal upplýsingar á borð við tengiliði, skilaboð og myndir"</string>
+    <string name="permission_notifications_summary" msgid="2272810466047367030">"• Lesa allar tilkynningar, þ.m.t. upplýsingar á borð við tengiliði, skilaboð og myndir&lt;br/&gt;• Senda tilkynningar&lt;br/&gt;&lt;br/&gt;Þú getur stjórnað getu forritsins til að lesa og senda tilkynningar hvenær sem er í „Stillingar &gt; Tilkynningar“."</string>
     <string name="permission_app_streaming_summary" msgid="606923325679670624">"Streymdu forritum símans"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
     <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Streymdu forritum og öðrum kerfiseiginleikum úr símanum"</string>
diff --git a/packages/CompanionDeviceManager/res/values-it/strings.xml b/packages/CompanionDeviceManager/res/values-it/strings.xml
index 9ce1241..81f5d62 100644
--- a/packages/CompanionDeviceManager/res/values-it/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-it/strings.xml
@@ -56,28 +56,18 @@
     <string name="permission_nearby_devices" msgid="7530973297737123481">"Dispositivi nelle vicinanze"</string>
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Cambia uscita conten. multim."</string>
     <string name="permission_storage" msgid="6831099350839392343">"Foto e contenuti multimediali"</string>
-    <!-- no translation found for permission_notifications (4099418516590632909) -->
-    <skip />
+    <string name="permission_notifications" msgid="4099418516590632909">"Notifiche"</string>
     <string name="permission_app_streaming" msgid="6009695219091526422">"App"</string>
     <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Streaming"</string>
-    <!-- no translation found for permission_phone_summary (8246321093970051702) -->
-    <skip />
-    <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
-    <skip />
-    <!-- no translation found for permission_sms_summary (8499509535410068616) -->
-    <skip />
-    <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
-    <skip />
-    <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
-    <skip />
-    <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
-    <skip />
-    <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
-    <skip />
-    <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
-    <skip />
-    <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
-    <skip />
+    <string name="permission_phone_summary" msgid="8246321093970051702">"Esegue e gestisce le telefonate"</string>
+    <string name="permission_call_logs_summary" msgid="7545243592757693321">"Legge e modifica il registro chiamate dello smartphone"</string>
+    <string name="permission_sms_summary" msgid="8499509535410068616">"Invia e visualizza SMS"</string>
+    <string name="permission_contacts_summary" msgid="2840800622763086808">"Accede ai tuoi contatti"</string>
+    <string name="permission_calendar_summary" msgid="8430353935747336165">"Accede al tuo calendario"</string>
+    <string name="permission_microphone_summary" msgid="4862628553869973259">"Registra l\'audio"</string>
+    <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Trova e si connette ai dispositivi nelle vicinanze, oltre a stabilire la loro posizione relativa"</string>
+    <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Legge tutte le notifiche, incluse informazioni come contatti, messaggi e foto"</string>
+    <string name="permission_notifications_summary" msgid="2272810466047367030">"• Legge tutte le notifiche, incluse informazioni come contatti, messaggi e foto&lt;br/&gt;• Invia notifiche&lt;br/&gt;&lt;br/&gt;Puoi gestire la capacità dell\'app di leggere e inviare notifiche in qualsiasi momento in Impostazioni &gt; Notifiche."</string>
     <string name="permission_app_streaming_summary" msgid="606923325679670624">"Trasmetti in streaming le app del tuo telefono"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
     <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Trasmettere in streaming app e altre funzionalità di sistema dal telefono"</string>
diff --git a/packages/CompanionDeviceManager/res/values-iw/strings.xml b/packages/CompanionDeviceManager/res/values-iw/strings.xml
index 8e2b715..f4367c0 100644
--- a/packages/CompanionDeviceManager/res/values-iw/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-iw/strings.xml
@@ -56,28 +56,18 @@
     <string name="permission_nearby_devices" msgid="7530973297737123481">"ŚžŚ›Ś©Ś™ŚšŚ™Ś Ś‘Ś§ŚšŚ‘ŚȘ ŚžŚ§Ś•Ś"</string>
     <string name="permission_media_routing_control" msgid="5498639511586715253">"کڙڠڕڙ ڀڜژ Ś”ŚžŚ“Ś™Ś”"</string>
     <string name="permission_storage" msgid="6831099350839392343">"ŚȘŚžŚ•Ś Ś•ŚȘ Ś•ŚžŚ“Ś™Ś”"</string>
-    <!-- no translation found for permission_notifications (4099418516590632909) -->
-    <skip />
+    <string name="permission_notifications" msgid="4099418516590632909">"Ś”ŚȘŚšŚŚ•ŚȘ"</string>
     <string name="permission_app_streaming" msgid="6009695219091526422">"ŚŚ€ŚœŚ™Ś§ŚŠŚ™Ś•ŚȘ"</string>
     <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"ŚĄŚ˜ŚšŚ™ŚžŚ™Ś Ś’"</string>
-    <!-- no translation found for permission_phone_summary (8246321093970051702) -->
-    <skip />
-    <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
-    <skip />
-    <!-- no translation found for permission_sms_summary (8499509535410068616) -->
-    <skip />
-    <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
-    <skip />
-    <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
-    <skip />
-    <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
-    <skip />
-    <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
-    <skip />
-    <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
-    <skip />
-    <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
-    <skip />
+    <string name="permission_phone_summary" msgid="8246321093970051702">"Ś‘Ś™ŚŠŚ•Śą Ś•Ś Ś™Ś”Ś•Śœ کڜ کڙڗڕŚȘ Ś˜ŚœŚ€Ś•ŚŸ"</string>
+    <string name="permission_call_logs_summary" msgid="7545243592757693321">"Ś§ŚšŚ™ŚŚ” ڕڛŚȘڙڑڔ کڜ Ś ŚȘڕڠڙ Ś™Ś•ŚžŚŸ ڔکڙڗڕŚȘ Ś‘Ś˜ŚœŚ€Ś•ŚŸ"</string>
+    <string name="permission_sms_summary" msgid="8499509535410068616">"‏Ś©ŚœŚ™Ś—Ś” کڜ Ś”Ś•Ś“ŚąŚ•ŚȘ SMS Ś•ŚŠŚ€Ś™Ś™Ś” Ś‘Ś”ŚŸ"</string>
+    <string name="permission_contacts_summary" msgid="2840800622763086808">"ڒڙکڔ ڐڜ ڐڠکڙ Ś”Ś§Ś©Śš"</string>
+    <string name="permission_calendar_summary" msgid="8430353935747336165">"ڒڙکڔ ڐڜ Ś”Ś™Ś•ŚžŚŸ"</string>
+    <string name="permission_microphone_summary" msgid="4862628553869973259">"Ś”Ś§ŚœŚ˜ŚȘ ڐڕړڙڕ"</string>
+    <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"ŚŚ€Ś©ŚšŚ•ŚȘ ŚœŚžŚŠŚ•Ś ŚžŚ›Ś©Ś™ŚšŚ™Ś Ś‘Ś§ŚšŚ‘ŚȘ ŚžŚ§Ś•Ś, ŚœŚ”ŚȘŚ—Ś‘Śš ŚŚœŚ™Ś”Ś Ś•ŚœŚŚȘŚš ڐŚȘ Ś”ŚžŚ™Ś§Ś•Ś Ś”Ś™Ś—ŚĄŚ™ Ś©ŚœŚ”Ś"</string>
+    <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"ŚŚ€Ś©ŚšŚ•ŚȘ ŚœŚ§ŚšŚ•Ś ڐŚȘ Ś›Śœ ڔڔŚȘŚšŚŚ•ŚȘ, Ś›Ś•ŚœŚœ ŚžŚ™Ś“Śą Ś›ŚžŚ• ڐڠکڙ Ś§Ś©Śš, Ś”Ś•Ś“ŚąŚ•ŚȘ Ś•ŚȘŚžŚ•Ś Ś•ŚȘ"</string>
+    <string name="permission_notifications_summary" msgid="2272810466047367030">"‏• Ś§ŚšŚ™ŚŚ” کڜ Ś›Śœ ڔڔŚȘŚšŚŚ•ŚȘ, Ś›Ś•ŚœŚœ ŚžŚ™Ś“Śą Ś›ŚžŚ• ڐڠکڙ Ś§Ś©Śš, Ś”Ś•Ś“ŚąŚ•ŚȘ Ś•ŚȘŚžŚ•Ś Ś•ŚȘ&lt;br/&gt;• Ś©ŚœŚ™Ś—ŚȘ Ś”ŚȘŚšŚŚ•ŚȘ&lt;br/&gt;&lt;br/&gt;ŚŚ€Ś©Śš ŚœŚ©Ś Ś•ŚȘ ڐŚȘ Ś”Ś™Ś›Ś•ŚœŚȘ کڜ Ś”ŚŚ€ŚœŚ™Ś§ŚŠŚ™Ś” ŚœŚ§ŚšŚ•Ś Ś•ŚœŚ©ŚœŚ•Ś— Ś”ŚȘŚšŚŚ•ŚȘ Ś‘Ś›Śœ Ś©ŚœŚ‘ Ś‘\'Ś”Ś’Ś“ŚšŚ•ŚȘ\' &gt; \'Ś”ŚȘŚšŚŚ•ŚȘ\'."</string>
     <string name="permission_app_streaming_summary" msgid="606923325679670624">"Ś©Ś™Ś“Ś•Śš ŚŚ€ŚœŚ™Ś§ŚŠŚ™Ś•ŚȘ ŚžŚ”Ś˜ŚœŚ€Ś•ŚŸ"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
     <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Ś”ŚąŚ‘ŚšŚ” کڜ ŚŚ€ŚœŚ™Ś§ŚŠŚ™Ś•ŚȘ Ś•ŚȘڛڕڠڕŚȘ ŚžŚąŚšŚ›ŚȘ ŚŚ—ŚšŚ•ŚȘ Ś‘ŚĄŚ˜ŚšŚ™ŚžŚ™Ś Ś’ ŚžŚ”Ś˜ŚœŚ€Ś•ŚŸ"</string>
diff --git a/packages/CompanionDeviceManager/res/values-ja/strings.xml b/packages/CompanionDeviceManager/res/values-ja/strings.xml
index 175ea0f..2dfb4d1 100644
--- a/packages/CompanionDeviceManager/res/values-ja/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ja/strings.xml
@@ -56,28 +56,18 @@
     <string name="permission_nearby_devices" msgid="7530973297737123481">"ä»˜èż‘ăźăƒ‡ăƒă‚€ă‚č"</string>
     <string name="permission_media_routing_control" msgid="5498639511586715253">"メディケć‡șćŠ›ăźć€‰æ›Ž"</string>
     <string name="permission_storage" msgid="6831099350839392343">"憙真べメディケ"</string>
-    <!-- no translation found for permission_notifications (4099418516590632909) -->
-    <skip />
+    <string name="permission_notifications" msgid="4099418516590632909">"通矄"</string>
     <string name="permission_app_streaming" msgid="6009695219091526422">"ケプăƒȘ"</string>
     <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"ă‚čトăƒȘăƒŒăƒŸăƒłă‚°"</string>
-    <!-- no translation found for permission_phone_summary (8246321093970051702) -->
-    <skip />
-    <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
-    <skip />
-    <!-- no translation found for permission_sms_summary (8499509535410068616) -->
-    <skip />
-    <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
-    <skip />
-    <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
-    <skip />
-    <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
-    <skip />
-    <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
-    <skip />
-    <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
-    <skip />
-    <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
-    <skip />
+    <string name="permission_phone_summary" msgid="8246321093970051702">"é›»è©±ăźç™ș信べ缡理"</string>
+    <string name="permission_call_logs_summary" msgid="7545243592757693321">"é€šè©±ć±„æ­ŽăźèȘ­ăżć–ă‚Šăšæ›žăèŸŒăż"</string>
+    <string name="permission_sms_summary" msgid="8499509535410068616">"SMS ăƒĄăƒƒă‚»ăƒŒă‚žăźé€äżĄăšèĄšç€ș"</string>
+    <string name="permission_contacts_summary" msgid="2840800622763086808">"é€Łç”Ąć…ˆăžăźă‚ąă‚Żă‚»ă‚č"</string>
+    <string name="permission_calendar_summary" msgid="8430353935747336165">"ă‚«ăƒŹăƒłăƒ€ăƒŒăžăźă‚ąă‚Żă‚»ă‚č"</string>
+    <string name="permission_microphone_summary" msgid="4862628553869973259">"éŒČ音"</string>
+    <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"ä»˜èż‘ăźăƒ‡ăƒă‚€ă‚čăźæ€œć‡șă€æŽ„ç¶šă€ç›žćŻŸäœçœźăźç‰č漚"</string>
+    <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"すăčăŠăźé€šçŸ„ăźèȘ­ăżć–ă‚ŠïŒˆé€Łç”Ąć…ˆă€ăƒĄăƒƒă‚»ăƒŒă‚žă€ć†™çœŸă«é–ąă™ă‚‹æƒ…ć ±ă‚’ć«ă‚€ïŒ‰"</string>
+    <string name="permission_notifications_summary" msgid="2272810466047367030">"• すăčăŠăźé€šçŸ„ăźèȘ­ăżć–ă‚ŠïŒˆé€Łç”Ąć…ˆă€ăƒĄăƒƒă‚»ăƒŒă‚žă€ć†™çœŸă«é–ąă™ă‚‹æƒ…ć ±ă‚’ć«ă‚€ïŒ‰&lt;br/&gt;• é€šçŸ„ăźé€äżĄ&lt;br/&gt;&lt;br/&gt;こぼケプăƒȘăźé€šçŸ„èȘ­ăżć–ă‚Šæ©Ÿèƒœăšé€šçŸ„é€äżĄæ©ŸèƒœăŻă„ă€ă§ă‚‚ [èš­ćźš] &gt; [通矄] ă§çźĄç†ă§ăăŸă™ă€‚"</string>
     <string name="permission_app_streaming_summary" msgid="606923325679670624">"ă‚čăƒžăƒŒăƒˆăƒ•ă‚©ăƒłăźă‚ąăƒ—ăƒȘをă‚čトăƒȘăƒŒăƒŸăƒłă‚°ă—ăŸă™"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
     <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"ケプăƒȘă‚„ăăźä»–ăźă‚·ă‚čăƒ†ăƒ æ©Ÿèƒœă‚’ă‚čăƒžăƒŒăƒˆăƒ•ă‚©ăƒłă‹ă‚‰ă‚čトăƒȘăƒŒăƒŸăƒłă‚°ă™ă‚‹"</string>
diff --git a/packages/CompanionDeviceManager/res/values-ka/strings.xml b/packages/CompanionDeviceManager/res/values-ka/strings.xml
index c655344..f4dcaea 100644
--- a/packages/CompanionDeviceManager/res/values-ka/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ka/strings.xml
@@ -56,28 +56,18 @@
     <string name="permission_nearby_devices" msgid="7530973297737123481">"აჼლომაჼლო მოწყობილობები"</string>
     <string name="permission_media_routing_control" msgid="5498639511586715253">"გამომავალი მედიის გამოჱანის"</string>
     <string name="permission_storage" msgid="6831099350839392343">"áƒ€áƒáƒąáƒáƒ”áƒ‘áƒ˜ და მედია"</string>
-    <!-- no translation found for permission_notifications (4099418516590632909) -->
-    <skip />
+    <string name="permission_notifications" msgid="4099418516590632909">"ჹეჱყობინებები"</string>
     <string name="permission_app_streaming" msgid="6009695219091526422">"აპები"</string>
     <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"სჱრიმინგი"</string>
-    <!-- no translation found for permission_phone_summary (8246321093970051702) -->
-    <skip />
-    <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
-    <skip />
-    <!-- no translation found for permission_sms_summary (8499509535410068616) -->
-    <skip />
-    <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
-    <skip />
-    <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
-    <skip />
-    <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
-    <skip />
-    <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
-    <skip />
-    <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
-    <skip />
-    <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
-    <skip />
+    <string name="permission_phone_summary" msgid="8246321093970051702">"áƒĄáƒáƒąáƒ”áƒšáƒ”áƒ€áƒáƒœáƒ ზარების განჼორáƒȘიელება და მართვა"</string>
+    <string name="permission_call_logs_summary" msgid="7545243592757693321">"áƒĄáƒáƒąáƒ”áƒšáƒ”áƒ€áƒáƒœáƒ ზარების ჟურნალის წაკითჼვა და მასჹი áƒ©áƒáƒŹáƒ”áƒ áƒ"</string>
+    <string name="permission_sms_summary" msgid="8499509535410068616">"SMS ჹეჱყობინებების გაგზავნა და ნაჼვა"</string>
+    <string name="permission_contacts_summary" msgid="2840800622763086808">"áƒ—áƒ„áƒ•áƒ”áƒœáƒĄ áƒ™áƒáƒœáƒąáƒáƒ„áƒąáƒ”áƒ‘áƒ–áƒ” წვდომა"</string>
+    <string name="permission_calendar_summary" msgid="8430353935747336165">"áƒ—áƒ„áƒ•áƒ”áƒœáƒĄ კალენდარზე წვდომა"</string>
+    <string name="permission_microphone_summary" msgid="4862628553869973259">"აუდიოს áƒ©áƒáƒŹáƒ”áƒ áƒ"</string>
+    <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"აჼლომაჼლო მოწყობილობების პოვნა, მათთან დაკავჹირება და მათი ჹედარებითი პოზიáƒȘიიქ დადგენა"</string>
+    <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"ყველა ჹეჱყობინების, მათ ჹორის ისეთი ინჀორმაáƒȘიიქ წაკითჼვა, როგორიáƒȘაა áƒ™áƒáƒœáƒąáƒáƒ„áƒąáƒ”áƒ‘áƒ˜, ჹეჱყობინებები და áƒ€áƒáƒąáƒáƒ”áƒ‘áƒ˜"</string>
+    <string name="permission_notifications_summary" msgid="2272810466047367030">"• ყველა ჹეჱყობინების, მათ ჹორის áƒ™áƒáƒœáƒąáƒáƒ„áƒąáƒ”áƒ‘áƒ˜áƒĄ, ჹეჱყობინებებისა და áƒ€áƒáƒąáƒáƒ”áƒ‘áƒ˜áƒĄ წაკითჼვა&lt;br/&gt;• ჹეჱყობინებების გაგზავნა&lt;br/&gt;&lt;br/&gt;ლეგიძლიათ, მართოთ ამ აპის áƒšáƒ”áƒĄáƒáƒ«áƒšáƒ”áƒ‘áƒšáƒáƒ‘áƒ, წაიკითჼოს და გაგზავნოს ჹეჱყობინებები ნებისმიერ დროს პარამეჱრებჹი &gt; ჹეჱყობინებები."</string>
     <string name="permission_app_streaming_summary" msgid="606923325679670624">"თჄვენი áƒąáƒ”áƒšáƒ”áƒ€áƒáƒœáƒ˜áƒĄ აპების სჱრიმინგი"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
     <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"აწარმოეთ აპების და სისჱემის სჼვა áƒ€áƒŁáƒœáƒ„áƒȘიების სჱრიმინგი თჄვენი áƒąáƒ”áƒšáƒ”áƒ€áƒáƒœáƒ˜áƒ“áƒáƒœ"</string>
diff --git a/packages/CompanionDeviceManager/res/values-kk/strings.xml b/packages/CompanionDeviceManager/res/values-kk/strings.xml
index c4928da..122e2e6 100644
--- a/packages/CompanionDeviceManager/res/values-kk/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-kk/strings.xml
@@ -56,28 +56,18 @@
     <string name="permission_nearby_devices" msgid="7530973297737123481">"ĐœĐ°ÒŁĐ°ĐčĐŽĐ°Ò“Ń‹ Ò›Ò±Ń€Ń‹Đ»Ò“Ń‹Đ»Đ°Ń€"</string>
     <string name="permission_media_routing_control" msgid="5498639511586715253">"ĐœŃƒĐ»ŃŒŃ‚ĐžĐŒĐ”ĐŽĐžĐ° ŃˆŃ‹Ò“Ń‹ŃŃ‹Đœ Ó©Đ·ĐłĐ”Ń€Ń‚Ńƒ"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Đ€ĐŸŃ‚ĐŸŃŃƒŃ€Đ”Ń‚Ń‚Đ”Ń€ ĐŒĐ”Đœ ĐŒĐ”ĐŽĐžĐ°Ń„Đ°ĐčлЎар"</string>
-    <!-- no translation found for permission_notifications (4099418516590632909) -->
-    <skip />
+    <string name="permission_notifications" msgid="4099418516590632909">"Đ„Đ°Đ±Đ°Ń€Đ»Đ°ĐœĐŽŃ‹Ń€ŃƒĐ»Đ°Ń€"</string>
     <string name="permission_app_streaming" msgid="6009695219091526422">"ÒšĐŸĐ»ĐŽĐ°ĐœĐ±Đ°Đ»Đ°Ń€"</string>
     <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"ĐąŃ€Đ°ĐœŃĐ»ŃŃ†ĐžŃ"</string>
-    <!-- no translation found for permission_phone_summary (8246321093970051702) -->
-    <skip />
-    <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
-    <skip />
-    <!-- no translation found for permission_sms_summary (8499509535410068616) -->
-    <skip />
-    <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
-    <skip />
-    <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
-    <skip />
-    <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
-    <skip />
-    <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
-    <skip />
-    <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
-    <skip />
-    <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
-    <skip />
+    <string name="permission_phone_summary" msgid="8246321093970051702">"ÒšĐŸÒŁŃ‹Ń€Đ°Ńƒ шалу Đ¶Ó™ĐœĐ” Ń‚Đ”Đ»Đ”Ń„ĐŸĐœ Ò›ĐŸÒŁŃ‹Ń€Đ°ŃƒĐ»Đ°Ń€Ń‹Đœ Đ±Đ°ŃÒ›Đ°Ń€Ńƒ"</string>
+    <string name="permission_call_logs_summary" msgid="7545243592757693321">"ĐąĐ”Đ»Đ”Ń„ĐŸĐœĐœŃ‹ÒŁ Ò›ĐŸÒŁŃ‹Ń€Đ°ŃƒĐ»Đ°Ń€ Đ¶ŃƒŃ€ĐœĐ°Đ»Ń‹Đœ ĐŸÒ›Ńƒ Đ¶Ó™ĐœĐ” жазу"</string>
+    <string name="permission_sms_summary" msgid="8499509535410068616">"SMS Ń…Đ°Đ±Đ°Ń€Đ»Đ°Ń€Ń‹Đœ Đ¶Ń–Đ±Đ”Ń€Ńƒ Đ¶Ó™ĐœĐ” ĐșÓ©Ń€Ńƒ"</string>
+    <string name="permission_contacts_summary" msgid="2840800622763086808">"ĐšĐŸĐœŃ‚Đ°ĐșтілДргД Đșіру"</string>
+    <string name="permission_calendar_summary" msgid="8430353935747336165">"ĐšÒŻĐœŃ‚Ń–Đ·Đ±Đ”ĐłĐ” Đșіру"</string>
+    <string name="permission_microphone_summary" msgid="4862628553869973259">"ĐŃƒĐŽĐžĐŸ жазу"</string>
+    <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"ĐœĐ°ÒŁĐ°ĐčĐŽĐ°Ò“Ń‹ Ò›Ò±Ń€Ń‹Đ»Ò“Ń‹Đ»Đ°Ń€ĐŽŃ‹ тауып, ĐŸĐ»Đ°Ń€ĐŒĐ”Đœ баĐčĐ»Đ°ĐœŃ‹ŃŃ‹Đż, бір-Đ±Ń–Ń€Ń–ĐœĐ” Ò›Đ°Ń‚Ń‹ŃŃ‚Ń‹ Đ»ĐŸĐșĐ°Ń†ĐžŃŃŃ‹Đœ Đ°ĐœŃ‹Ò›Ń‚Đ°ĐčЮы."</string>
+    <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Đ‘Đ°Ń€Đ»Ń‹Ò› Ń…Đ°Đ±Đ°Ń€Đ»Đ°ĐœĐŽŃ‹Ń€ŃƒĐŽŃ‹, ŃĐŸĐœŃ‹ÒŁ Ń–ŃˆŃ–ĐœĐŽĐ” ĐșĐŸĐœŃ‚Đ°ĐșтілДр, хабарлар Đ¶Ó™ĐœĐ” Ń„ĐŸŃ‚ĐŸŃŃƒŃ€Đ”Ń‚Ń‚Đ”Ń€ ŃĐžŃÒ›Ń‚Ń‹ Đ°Ò›ĐżĐ°Ń€Đ°Ń‚Ń‚Ń‹ ĐŸÒ›ĐžĐŽŃ‹."</string>
+    <string name="permission_notifications_summary" msgid="2272810466047367030">"• Đ‘Đ°Ń€Đ»Ń‹Ò› Ń…Đ°Đ±Đ°Ń€Đ»Đ°ĐœĐŽŃ‹Ń€ŃƒĐŽŃ‹, ŃĐŸĐœŃ‹ÒŁ Ń–ŃˆŃ–ĐœĐŽĐ” ĐșĐŸĐœŃ‚Đ°ĐșтілДр, хабарлар Đ¶Ó™ĐœĐ” Ń„ĐŸŃ‚ĐŸŃŃƒŃ€Đ”Ń‚Ń‚Đ”Ń€ ŃĐžŃÒ›Ń‚Ń‹ Đ°Ò›ĐżĐ°Ń€Đ°Ń‚Ń‚Ń‹ ĐŸÒ›ĐžĐŽŃ‹&lt;br/&gt;• Đ„Đ°Đ±Đ°Ń€Đ»Đ°ĐœĐŽŃ‹Ń€ŃƒĐ»Đ°Ń€ жібДрДЎі&lt;br/&gt;&lt;br/&gt;Đ‘Ò±Đ» Ò›ĐŸĐ»ĐŽĐ°ĐœĐ±Đ°ĐœŃ‹ÒŁ Ń…Đ°Đ±Đ°Ń€Đ»Đ°ĐœĐŽŃ‹Ń€ŃƒĐ»Đ°Ń€ĐŽŃ‹ ĐŸÒ›Ńƒ Đ¶Ó™ĐœĐ” Đ¶Ń–Đ±Đ”Ń€Ńƒ ĐŒÒŻĐŒĐșŃ–ĐœĐŽŃ–ĐłŃ–Đœ \"ĐŸĐ°Ń€Đ°ĐŒĐ”Ń‚Ń€Đ»Đ”Ń€ &gt; Đ„Đ°Đ±Đ°Ń€Đ»Đ°ĐœĐŽŃ‹Ń€ŃƒĐ»Đ°Ń€\" Ń‚Đ°Ń€ĐŒĐ°Ò“Ń‹ĐœĐŽĐ° ĐșДз ĐșĐ”Đ»ĐłĐ”Đœ ŃƒĐ°Ò›Ń‹Ń‚Ń‚Đ° Đ±Đ°ŃÒ›Đ°Ń€Đ° аласыз."</string>
     <string name="permission_app_streaming_summary" msgid="606923325679670624">"ĐąĐ”Đ»Đ”Ń„ĐŸĐœ Ò›ĐŸĐ»ĐŽĐ°ĐœĐ±Đ°Đ»Đ°Ń€Ń‹Đœ Ń‚Ń€Đ°ĐœŃĐ»ŃŃ†ĐžŃĐ»Đ°ĐčЮы."</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
     <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"ÒšĐŸĐ»ĐŽĐ°ĐœĐ±Đ°Đ»Đ°Ń€ ĐŒĐ”Đœ Đ±Đ°ŃÒ›Đ° Ўа Đ¶ÒŻĐčĐ” Ń„ŃƒĐœĐșŃ†ĐžŃĐ»Đ°Ń€Ń‹Đœ Ń‚Đ”Đ»Đ”Ń„ĐŸĐœĐœĐ°Đœ Ń‚Ń€Đ°ĐœŃĐ»ŃŃ†ĐžŃĐ»Đ°Ńƒ"</string>
diff --git a/packages/CompanionDeviceManager/res/values-km/strings.xml b/packages/CompanionDeviceManager/res/values-km/strings.xml
index 28b6340..d6f3037 100644
--- a/packages/CompanionDeviceManager/res/values-km/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-km/strings.xml
@@ -56,28 +56,18 @@
     <string name="permission_nearby_devices" msgid="7530973297737123481">"ឧបករណ៍នៅជិត"</string>
     <string name="permission_media_routing_control" msgid="5498639511586715253">"ប្ដឌរឧបករណ៍មេឌៀ"</string>
     <string name="permission_storage" msgid="6831099350839392343">"រឌបថត និងមេឌៀ"</string>
-    <!-- no translation found for permission_notifications (4099418516590632909) -->
-    <skip />
+    <string name="permission_notifications" msgid="4099418516590632909">"ការ​ជឌនដំណážčង"</string>
     <string name="permission_app_streaming" msgid="6009695219091526422">"កម្មវិធឞ"</string>
     <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"ការផ្សាយ"</string>
-    <!-- no translation found for permission_phone_summary (8246321093970051702) -->
-    <skip />
-    <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
-    <skip />
-    <!-- no translation found for permission_sms_summary (8499509535410068616) -->
-    <skip />
-    <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
-    <skip />
-    <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
-    <skip />
-    <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
-    <skip />
-    <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
-    <skip />
-    <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
-    <skip />
-    <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
-    <skip />
+    <string name="permission_phone_summary" msgid="8246321093970051702">"ហៅទឌរសព្ទ និងគ្រប់គ្រងការហៅទឌរសព្ទ"</string>
+    <string name="permission_call_logs_summary" msgid="7545243592757693321">"ážąáž¶áž“ និងសរសេរ​កំណត់ហេតុហៅទឌរសព្ទ"</string>
+    <string name="permission_sms_summary" msgid="8499509535410068616">"ផ្ញស និងមសលសារ SMS"</string>
+    <string name="permission_contacts_summary" msgid="2840800622763086808">"áž…ážŒáž›áž”áŸ’ážšážŸáž‘áŸ†áž“áž¶áž€áŸ‹áž‘áŸ†áž“áž„ážšáž”ážŸáŸ‹ážąáŸ’áž“áž€"</string>
+    <string name="permission_calendar_summary" msgid="8430353935747336165">"áž…ážŒáž›áž”áŸ’ážšážŸáž”áŸ’ážšážáž·áž‘áž·áž“ážšáž”ážŸáŸ‹ážąáŸ’áž“áž€"</string>
+    <string name="permission_microphone_summary" msgid="4862628553869973259">"ថត​សំឡេង"</string>
+    <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"ស្វែងរក ភ្ជាប់ និងកំណត់​ទឞតាំងដែលពាក់ព័ន្ធ​របស់ឧបករណ៍​នៅជិត"</string>
+    <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"ážąáž¶áž“â€‹áž€áž¶ážšáž‡ážŒáž“ážŠáŸ†ážŽážčáž„â€‹áž‘áž¶áŸ†áž„ážąážŸáŸ‹ រវមទាំង​ព័ត៌មាន​ដឌចជា ទំនាក់ទំនង សារ និងរឌបថតជាដសម"</string>
+    <string name="permission_notifications_summary" msgid="2272810466047367030">"• ážąáž¶áž“áž€áž¶ážšáž‡ážŒáž“ážŠáŸ†ážŽážčáž„áž‘áž¶áŸ†áž„ážąážŸáŸ‹ រវមទាំងព័ត៌មានដឌចជា ទំនាក់ទំនង សារ និងរឌបថតជាដសម&lt;br/&gt;• ផ្ញសការជឌនដំណážčង&lt;br/&gt;&lt;br/&gt;ážąáŸ’áž“áž€ážąáž¶áž…áž‚áŸ’ážšáž”áŸ‹áž‚áŸ’ážšáž„áž›áž‘áŸ’áž’áž—áž¶áž–ážšáž”ážŸáŸ‹áž€áž˜áŸ’áž˜ážœáž·áž’ážžáž“áŸáŸ‡áž€áŸ’áž“áž»áž„áž€áž¶ážšážąáž¶áž“ និងផ្ញសការជឌនដំណážčងបានគ្រប់ពេលនៅក្នុងការកំណត់ &gt; ការជឌនដំណážčង។"</string>
     <string name="permission_app_streaming_summary" msgid="606923325679670624">"áž•áŸ’ážŸáž¶áž™áž€áž˜áŸ’áž˜ážœáž·áž’ážžážšáž”ážŸáŸ‹áž‘ážŒážšážŸáž–áŸ’áž‘ážąáŸ’áž“áž€"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
     <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"ចាក់ផ្សាយ​កម្មវិធឞ áž“áž·áž„áž˜áž»ážáž„áž¶ážšáž”áŸ’ážšáž–áŸáž“áŸ’áž’â€‹áž•áŸ’ážŸáŸáž„áž‘áŸ€ážâ€‹áž–ážžáž‘ážŒážšážŸáž–áŸ’áž‘â€‹ážšáž”ážŸáŸ‹ážąáŸ’áž“áž€"</string>
diff --git a/packages/CompanionDeviceManager/res/values-kn/strings.xml b/packages/CompanionDeviceManager/res/values-kn/strings.xml
index eb94178..661d344 100644
--- a/packages/CompanionDeviceManager/res/values-kn/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-kn/strings.xml
@@ -56,28 +56,18 @@
     <string name="permission_nearby_devices" msgid="7530973297737123481">"àČžàČźàł€àČȘàČŠàČČàłàČČàČżàČ°àłàČ” àČžàČŸàȧàČšàȗàČłàł"</string>
     <string name="permission_media_routing_control" msgid="5498639511586715253">"àČźàł€àČĄàČżàČŻàČŸ àȔàČŸàł‌‌àČȘàłàČŸàł àČŹàČŠàČČàČŸàČŻàČżàČžàČż"</string>
     <string name="permission_storage" msgid="6831099350839392343">"àČ«àł‹àČŸàł‹àȗàČłàł àČźàČ€àłàČ€àł àČźàČŸàČ§àłàČŻàČź"</string>
-    <!-- no translation found for permission_notifications (4099418516590632909) -->
-    <skip />
+    <string name="permission_notifications" msgid="4099418516590632909">"àČšàł‹àȟàČżàČ«àČżàČ•àł‡àȶàČšàł‌àȗàČłàł"</string>
     <string name="permission_app_streaming" msgid="6009695219091526422">"àČ†àłàČŻàČȘàł‌àȗàČłàł"</string>
     <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"àČžàłàČŸàłàČ°àł€àČźàČżàȂàČ—àł"</string>
-    <!-- no translation found for permission_phone_summary (8246321093970051702) -->
-    <skip />
-    <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
-    <skip />
-    <!-- no translation found for permission_sms_summary (8499509535410068616) -->
-    <skip />
-    <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
-    <skip />
-    <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
-    <skip />
-    <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
-    <skip />
-    <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
-    <skip />
-    <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
-    <skip />
-    <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
-    <skip />
+    <string name="permission_phone_summary" msgid="8246321093970051702">"àČ«àł‹àČšàł àȕàČ°àł†àȗàČłàČšàłàČšàł àČźàČŸàČĄàČż àČčàČŸàČ—àł‚ àČšàČżàČ°àłàČ”àČčàČżàČžàČż"</string>
+    <string name="permission_call_logs_summary" msgid="7545243592757693321">"àČȘàł‹àČšàł‌ àȕàČ°àł†àČŻ àČČàČŸàČ—àł‌ àȅàČšàłàČšàł àȓàČŠàČż àČźàČ€àłàČ€àł àČŹàČ°àł†àČŻàČżàȰàČż"</string>
+    <string name="permission_sms_summary" msgid="8499509535410068616">"SMS àČžàȂàČŠàł‡àȶàȗàČłàČšàłàČšàł àȕàČłàłàČčàČżàČžàČż àČźàČ€àłàČ€àł àČ”àł€àČ•àłàČ·àČżàČžàČż"</string>
+    <string name="permission_contacts_summary" msgid="2840800622763086808">"àČšàČżàČźàłàČź àČžàȂàČȘàČ°àłàȕàȗàČłàČšàłàČšàł àČ†àłàČŻàČ•àłàČžàł†àČžàł àČźàČŸàČĄàČż"</string>
+    <string name="permission_calendar_summary" msgid="8430353935747336165">"àČšàČżàČźàłàČź àČ•àłàČŻàČŸàČČàł†àȂàČĄàČ°àł àȅàČšàłàČšàł àČ†àłàČŻàČ•àłàČžàł†àČžàł àČźàČŸàČĄàČż"</string>
+    <string name="permission_microphone_summary" msgid="4862628553869973259">"àȆàČĄàČżàČŻàł‹ àČ°àł†àȕàČŸàČ°àłàČĄàł àČźàČŸàČĄàČż"</string>
+    <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"àČžàČźàł€àČȘàČŠàČČàłàČČàČżàČ°àłàČ” àČžàČŸàȧàČšàȗàČłàČšàłàČšàł àČčàłàČĄàłàȕàČŹàČčàłàČŠàł, àȅàČ”àłàȗàČłàČżàČ—àł† àȕàČšàł†àČ•àłàČŸàł àȆàȗàČŹàČčàłàČŠàł àČźàČ€àłàČ€àł àȅàČ”àłàȗàČł àČžàȂàČŹàȂàȧàČżàČ€ àČžàłàČ„àČŸàČšàČ”àČšàłàČšàł àČšàČżàČ°àłàȧàȰàČżàČžàČŹàČčàłàČŠàł"</string>
+    <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"àČžàȂàČȘàČ°àłàȕàȗàČłàł, àČžàȂàČŠàł‡àȶàȗàČłàł àČźàČ€àłàČ€àł àČ«àł‹àČŸàł‹àȗàČłàȂàČ€àČč àČźàČŸàČčàČżàČ€àČżàČŻàČšàłàČšàł àȒàČłàČ—àłŠàȂàČĄàȂàČ€àł† àȎàČČàłàČČàČŸ àČšàł‹àȟàČżàČ«àČżàČ•àł‡àȶàČšàł‌àȗàČłàČšàłàČšàł àȓàČŠàČŹàČčàłàČŠàł"</string>
+    <string name="permission_notifications_summary" msgid="2272810466047367030">"• àČžàȂàČȘàČ°àłàȕàȗàČłàł, àČžàȂàČŠàł‡àȶàȗàČłàł àČźàČ€àłàČ€àł àČ«àł‹àČŸàł‹àȗàČłàȂàČ€àČč àČźàČŸàČčàČżàČ€àČżàČŻàČšàłàČšàł àȒàČłàČ—àłŠàȂàČĄàȂàČ€àł† àȎàČČàłàČČàČŸ àČšàł‹àȟàČżàČ«àČżàČ•àł‡àȶàČšàł‌àȗàČłàČšàłàČšàł àȓàČŠàČż&lt;br/&gt;• àČšàł‹àȟàČżàČ«àČżàČ•àł‡àȶàČšàł‌àȗàČłàČšàłàČšàł àȕàČłàłàČčàČżàČžàČż&lt;br/&gt;&lt;br/&gt;àȈ àČ†àłàČŻàČȘàł‌àČš àČšàł‹àȟàČżàČ«àČżàČ•àł‡àȶàČšàł‌àȗàČłàČšàłàČšàł àȓàČŠàłàČ” àČźàČ€àłàČ€àł àȕàČłàłàČčàČżàČžàłàČ” àČžàČŸàČźàČ°àłàČ„àłàČŻàČ”àČšàłàČšàł àČŻàČŸàČ”àłàČŠàł‡ àČžàČźàČŻàČŠàČČàłàČČàČż àČžàł†àČŸàłàȟàČżàȂàČ—àł‌àȗàČłàł &gt; àČšàł‹àȟàČżàČ«àČżàČ•àł‡àȶàČšàł‌àȗàČłàČČàłàČČàČż àČšàČżàČ°àłàČ”àČčàČżàČžàČŹàČčàłàČŠàł."</string>
     <string name="permission_app_streaming_summary" msgid="606923325679670624">"àČšàČżàČźàłàČź àČ«àł‹àČšàł‍àČš àČ†àłàČŻàČȘàł‌àȗàČłàČšàłàČšàł àČžàłàČŸàłàČ°àł€àČźàł àČźàČŸàČĄàČż"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
     <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"àČšàČżàČźàłàČź àČ«àł‹àČšàł‌àČšàČżàȂàČŠ àČ†àłàČŻàČȘàł‌àȗàČłàł àČźàČ€àłàČ€àł àȇàČ€àȰ àČžàČżàČžàłàȟàȂ àČ«àł€àȚàČ°àł‌àȗàČłàČšàłàČšàł àČžàłàČŸàłàČ°àł€àČźàł àČźàČŸàČĄàČż"</string>
diff --git a/packages/CompanionDeviceManager/res/values-ko/strings.xml b/packages/CompanionDeviceManager/res/values-ko/strings.xml
index 4685397..aefda7c 100644
--- a/packages/CompanionDeviceManager/res/values-ko/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ko/strings.xml
@@ -56,28 +56,18 @@
     <string name="permission_nearby_devices" msgid="7530973297737123481">"ê·ŒìȘ êž°êž°"</string>
     <string name="permission_media_routing_control" msgid="5498639511586715253">"ëŻžë””ì–Ž 출렄 변êČœ"</string>
     <string name="permission_storage" msgid="6831099350839392343">"ì‚Źì§„ 및 ëŻžë””ì–Ž"</string>
-    <!-- no translation found for permission_notifications (4099418516590632909) -->
-    <skip />
+    <string name="permission_notifications" msgid="4099418516590632909">"알늌"</string>
     <string name="permission_app_streaming" msgid="6009695219091526422">"앱"</string>
     <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"ìŠ€íŠžëŠŹë°"</string>
-    <!-- no translation found for permission_phone_summary (8246321093970051702) -->
-    <skip />
-    <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
-    <skip />
-    <!-- no translation found for permission_sms_summary (8499509535410068616) -->
-    <skip />
-    <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
-    <skip />
-    <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
-    <skip />
-    <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
-    <skip />
-    <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
-    <skip />
-    <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
-    <skip />
-    <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
-    <skip />
+    <string name="permission_phone_summary" msgid="8246321093970051702">"전화 걞Ʞ 및 êŽ€ëŠŹ"</string>
+    <string name="permission_call_logs_summary" msgid="7545243592757693321">"톔화 êž°ëĄ ìœêł  ì“°êž°"</string>
+    <string name="permission_sms_summary" msgid="8499509535410068616">"SMS 메시지 ëłŽë‚Žêž° 및 ëłŽêž°"</string>
+    <string name="permission_contacts_summary" msgid="2840800622763086808">"연띜ìČ˜ì— ì•Ąì„žìŠ€"</string>
+    <string name="permission_calendar_summary" msgid="8430353935747336165">"ìș˜ëŠ°ë”ì— ì•Ąì„žìŠ€"</string>
+    <string name="permission_microphone_summary" msgid="4862628553869973259">"였디였 ë…č음"</string>
+    <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"ê·ŒìȘ ꞰꞰ넌 ì°Ÿì•„ 연êČ°í•˜êł  êž°êž° 간 상대 위ìč˜ë„Œ 파악할 수 있슔니닀."</string>
+    <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"연띜ìȘ, 메시지, ì‚Źì§„ 등의 ì •ëłŽë„Œ 포핹한 ëȘšë“  알늌을 읜을 수 있슔니닀."</string>
+    <string name="permission_notifications_summary" msgid="2272810466047367030">"• 연띜ìȘ, 메시지, ì‚Źì§„êłŒ 같은 ì •ëłŽë„Œ 포핹핮 ëȘšë“  알늌을 읜슔니닀.&lt;br/&gt;• 알늌을 ì „ì†Ąí•©ë‹ˆë‹€.&lt;br/&gt;&lt;br/&gt;얞제든지 섀정 &gt; 알늌에서 알늌을 ìœêł  ì „ì†Ąí•  수 있는 읎 앱의 늄렄을 êŽ€ëŠŹí•  수 있슔니닀."</string>
     <string name="permission_app_streaming_summary" msgid="606923325679670624">"휎대전화의 앱을 ìŠ€íŠžëŠŹë°í•©ë‹ˆë‹€."</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
     <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"낮 휎대전화의 앱 및 Ʞ타 시슀템 Ʞ늄 ìŠ€íŠžëŠŹë°"</string>
diff --git a/packages/CompanionDeviceManager/res/values-ky/strings.xml b/packages/CompanionDeviceManager/res/values-ky/strings.xml
index 76aefe1..099b5c5 100644
--- a/packages/CompanionDeviceManager/res/values-ky/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ky/strings.xml
@@ -56,28 +56,18 @@
     <string name="permission_nearby_devices" msgid="7530973297737123481">"ЖаĐșŃ‹Đœ жДрЎДгО Ń‚ÒŻĐ·ĐŒÓ©Đșтөр"</string>
     <string name="permission_media_routing_control" msgid="5498639511586715253">"ĐœĐ”ĐŽĐžĐ° Ń‡Ń‹ĐłĐ°Ń€Ń‹Đ»Ń‹ŃˆŃ‹Đœ Ó©Đ·ĐłÓ©Ń€Ń‚ÒŻÒŻ"</string>
     <string name="permission_storage" msgid="6831099350839392343">"ĐĄÒŻŃ€Ó©Ń‚Ń‚Ó©Ń€ Đ¶Đ°ĐœĐ° ĐŒĐ”ĐŽĐžĐ°Ń„Đ°ĐčлЎар"</string>
-    <!-- no translation found for permission_notifications (4099418516590632909) -->
-    <skip />
+    <string name="permission_notifications" msgid="4099418516590632909">"Đ‘ĐžĐ»ĐŽĐžŃ€ĐŒĐ”Đ»Đ”Ń€"</string>
     <string name="permission_app_streaming" msgid="6009695219091526422">"ĐšĐŸĐ»ĐŽĐŸĐœĐŒĐŸĐ»ĐŸŃ€"</string>
     <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Алып ĐŸĐčĐœĐŸŃ‚ŃƒŃƒ"</string>
-    <!-- no translation found for permission_phone_summary (8246321093970051702) -->
-    <skip />
-    <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
-    <skip />
-    <!-- no translation found for permission_sms_summary (8499509535410068616) -->
-    <skip />
-    <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
-    <skip />
-    <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
-    <skip />
-    <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
-    <skip />
-    <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
-    <skip />
-    <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
-    <skip />
-    <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
-    <skip />
+    <string name="permission_phone_summary" msgid="8246321093970051702">"ĐąĐ”Đ»Đ”Ń„ĐŸĐœ Ń‡Đ°Đ»ŃƒŃƒ Đ¶Đ°ĐœĐ° аларЎы башĐșаруу"</string>
+    <string name="permission_call_logs_summary" msgid="7545243592757693321">"ĐąĐ”Đ»Đ”Ń„ĐŸĐœĐŽĐŸĐłŃƒ Ń‡Đ°Đ»ŃƒŃƒĐ»Đ°Ń€ Ń‚ĐžĐ·ĐŒĐ”ŃĐžĐœ ĐŸĐșуу Đ¶Đ°ĐœĐ° жазуу"</string>
+    <string name="permission_sms_summary" msgid="8499509535410068616">"SMS Đ±ĐžĐ»ĐŽĐžŃ€ÒŻÒŻĐ»Ó©Ń€ĐŽÒŻ Đ¶Ó©ĐœÓ©Ń‚ÒŻÒŻ Đ¶Đ°ĐœĐ° ĐșÓ©Ń€ŃÓ©Ń‚ÒŻÒŻ"</string>
+    <string name="permission_contacts_summary" msgid="2840800622763086808">"БаĐčĐ»Đ°ĐœŃ‹ŃˆŃ‚Đ°Ń€Ń‹ÒŁŃ‹Đ·ĐŽŃ‹ ĐșÓ©Ń€ÒŻÒŻ"</string>
+    <string name="permission_calendar_summary" msgid="8430353935747336165">"Đ–Ń‹Đ»ĐœĐ°Đ°ĐŒĐ°ÒŁŃ‹Đ·ĐŽŃ‹ паĐčĐŽĐ°Đ»Đ°ĐœŃƒŃƒ"</string>
+    <string name="permission_microphone_summary" msgid="4862628553869973259">"ĐŃƒĐŽĐžĐŸ Đ¶Đ°Đ·ĐŽŃ‹Ń€ŃƒŃƒ"</string>
+    <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"ЖаĐșŃ‹Đœ жДрЎДгО Ń‚ÒŻĐ·ĐŒÓ©ĐșŃ‚Ó©Ń€ĐŽÒŻ таап, аларга туташып, Đ°Đ±Đ°Đ»Ń‹Đœ Đ°ĐœŃ‹ĐșŃ‚ĐŸĐŸ"</string>
+    <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"БарЮыĐș Đ±ĐžĐ»ĐŽĐžŃ€ĐŒĐ”Đ»Đ”Ń€ĐŽĐž, Đ°ĐœŃ‹Đœ ĐžŃ‡ĐžĐœĐŽĐ” баĐčĐ»Đ°ĐœŃ‹ŃˆŃ‚Đ°Ń€, Đ±ĐžĐ»ĐŽĐžŃ€ÒŻÒŻĐ»Ó©Ń€ Đ¶Đ°ĐœĐ° ŃÒŻŃ€Ó©Ń‚Ń‚Ó©Ń€ сыяĐșтуу ĐŒĐ°Đ°Đ»Ń‹ĐŒĐ°Ń‚Ń‚Ń‹ ĐŸĐșуу"</string>
+    <string name="permission_notifications_summary" msgid="2272810466047367030">"• БарЮыĐș Đ±ĐžĐ»ĐŽĐžŃ€ĐŒĐ”Đ»Đ”Ń€ĐŽĐž, Đ°ĐœŃ‹Đœ ĐžŃ‡ĐžĐœĐŽĐ” баĐčĐ»Đ°ĐœŃ‹ŃˆŃ‚Đ°Ń€, Đ±ĐžĐ»ĐŽĐžŃ€ÒŻÒŻĐ»Ó©Ń€ Đ¶Đ°ĐœĐ° ŃÒŻŃ€Ó©Ń‚Ń‚Ó©Ń€ сыяĐșтуу ĐŒĐ°Đ°Đ»Ń‹ĐŒĐ°Ń‚Ń‚Ń‹ ĐŸĐșуу&lt;br/&gt;• Đ‘ĐžĐ»ĐŽĐžŃ€ĐŒĐ”Đ»Đ”Ń€ĐŽĐž Đ¶Ó©ĐœÓ©Ń‚ÒŻÒŻ&lt;br/&gt;&lt;br/&gt;Đ‘ŃƒĐ» ĐșĐŸĐ»ĐŽĐŸĐœĐŒĐŸĐœŃƒĐœ Đ±ĐžĐ»ĐŽĐžŃ€ĐŒĐ”Đ»Đ”Ń€ĐŽĐž ĐŸĐșуу Đ¶Đ°ĐœĐ° Đ¶Ó©ĐœÓ©Ń‚ÒŻÒŻ ĐŒÒŻĐŒĐșÒŻĐœŃ‡ÒŻĐ»ÒŻĐłÒŻĐœ ĐșĐ°Đ°Đ»Đ°ĐłĐ°Đœ убаĐșта ĐŸĐ°Ń€Đ°ĐŒĐ”Ń‚Ń€Đ»Đ”Ń€ &gt; Đ‘ĐžĐ»ĐŽĐžŃ€ĐŒĐ”Đ»Đ”Ń€ Đ±Ó©Đ»ÒŻĐŒÒŻĐœÓ©Đœ тДсĐșĐ”Đč аласыз."</string>
     <string name="permission_app_streaming_summary" msgid="606923325679670624">"ĐąĐ”Đ»Đ”Ń„ĐŸĐœĐŽĐŸĐłŃƒ ĐșĐŸĐ»ĐŽĐŸĐœĐŒĐŸĐ»ĐŸŃ€ĐŽŃƒ алып ĐŸĐčĐœĐŸŃ‚ŃƒŃƒ"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
     <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"ĐąĐ”Đ»Đ”Ń„ĐŸĐœŃƒÒŁŃƒĐ·ĐŽĐ°ĐłŃ‹ ĐșĐŸĐ»ĐŽĐŸĐœĐŒĐŸĐ»ĐŸŃ€ĐŽŃƒ Đ¶Đ°ĐœĐ° ŃĐžŃŃ‚Đ”ĐŒĐ°ĐœŃ‹Đœ башĐșа Ń„ŃƒĐœĐșŃ†ĐžŃĐ»Đ°Ń€Ń‹Đœ алып ĐŸĐčĐœĐŸŃ‚ŃƒŃƒ"</string>
diff --git a/packages/CompanionDeviceManager/res/values-lo/strings.xml b/packages/CompanionDeviceManager/res/values-lo/strings.xml
index bbf0be3..fa378ca 100644
--- a/packages/CompanionDeviceManager/res/values-lo/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-lo/strings.xml
@@ -56,28 +56,18 @@
     <string name="permission_nearby_devices" msgid="7530973297737123481">"àș­àșžàș›àș°àșàș­àș™àș—àș”່àșąàșč່ໃàșà»‰àș„àșœàș‡"</string>
     <string name="permission_media_routing_control" msgid="5498639511586715253">"àș›à»ˆàșœàș™à»€àș­àș»à»‰àșČàșžàșžàș”àșȘàș·à»ˆ"</string>
     <string name="permission_storage" msgid="6831099350839392343">"àșźàșčàșšàșžàșČàșš ແàș„àș° àșĄàș”ເàș”àș"</string>
-    <!-- no translation found for permission_notifications (4099418516590632909) -->
-    <skip />
+    <string name="permission_notifications" msgid="4099418516590632909">"àșàșČàș™à»àșˆà»‰àș‡à»€àș•àș·àș­àș™"</string>
     <string name="permission_app_streaming" msgid="6009695219091526422">"ແàș­àș±àșš"</string>
     <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"àșàșłàș„àș±àș‡àșȘàș°àș•àșŁàș”àșĄ"</string>
-    <!-- no translation found for permission_phone_summary (8246321093970051702) -->
-    <skip />
-    <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
-    <skip />
-    <!-- no translation found for permission_sms_summary (8499509535410068616) -->
-    <skip />
-    <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
-    <skip />
-    <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
-    <skip />
-    <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
-    <skip />
-    <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
-    <skip />
-    <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
-    <skip />
-    <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
-    <skip />
+    <string name="permission_phone_summary" msgid="8246321093970051702">"ໂàș—àș­àș­àș ແàș„àș° àșˆàș±àș”àșàșČàș™àșàșČàș™à»‚àș—"</string>
+    <string name="permission_call_logs_summary" msgid="7545243592757693321">"àș­à»ˆàșČàș™ ແàș„àș° àș‚àșœàș™àșšàș±àș™àș—àș¶àșàșàșČàș™à»‚àș—àș‚àș­àș‡à»‚àș—àș„àș°àșȘàș±àșš"</string>
+    <string name="permission_sms_summary" msgid="8499509535410068616">"àșȘàș»à»ˆàș‡ ແàș„àș° ເàșšàșŽà»ˆàș‡àș‚ໍ້àș„àș§àșČàșĄ SMS"</string>
+    <string name="permission_contacts_summary" msgid="2840800622763086808">"ເàș‚àș»à»‰àșČເàș–àșŽàș‡àș„àșČàșàșŠàș·à»ˆàșœàșč້àș•àșŽàș”àș•ໍ່àș‚àș­àș‡àș—່àșČàș™"</string>
+    <string name="permission_calendar_summary" msgid="8430353935747336165">"ເàș‚àș»à»‰àșČເàș–àșŽàș‡àș›àș°àș•àșŽàș—àșŽàș™àș‚àș­àș‡àș—່àșČàș™"</string>
+    <string name="permission_microphone_summary" msgid="4862628553869973259">"àșšàș±àș™àș—àș¶àșàșȘàșœàș‡"</string>
+    <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"àșŠàș­àșàș«àșČ, ເàșŠàș·à»ˆàș­àșĄàș•ໍ່ ແàș„àș° àș„àș°àșšàșžàșȘàș°àș–àșČàș™àș—àș”່àș—àș”່àșà»ˆàșœàș§àș‚້àș­àș‡àșàș±àș™àș‚àș­àș‡àș­àșžàș›àș°àșàș­àș™àș—àș”່àșąàșč່ໃàșà»‰àș„àșœàș‡"</string>
+    <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"àș­à»ˆàșČàș™àșàșČàș™à»àșˆà»‰àș‡à»€àș•àș·àș­àș™àș—àș±àș‡à»àș»àș”, àșźàș§àșĄàș—àș±àș‡àș‚ໍ້àșĄàșčàș™ ເàșŠàș±à»ˆàș™: àș„àșČàșàșŠàș·à»ˆàșœàșč້àș•àșŽàș”àș•ໍ່, àș‚ໍ້àș„àș§àșČàșĄ ແàș„àș° àșźàșčàșšàșžàșČàșš"</string>
+    <string name="permission_notifications_summary" msgid="2272810466047367030">"• àș­à»ˆàșČàș™àșàșČàș™à»àșˆà»‰àș‡à»€àș•àș·àș­àș™àș—àș±àș‡à»àș»àș”, àșźàș§àșĄàș—àș±àș‡àș‚ໍ້àșĄàșčàș™ ເàșŠàș±à»ˆàș™: àș„àșČàșàșŠàș·à»ˆàșœàșč້àș•àșŽàș”àș•ໍ່, àș‚ໍ້àș„àș§àșČàșĄ ແàș„àș° àșźàșčàșšàșžàșČàșš&lt;br/&gt;• àșȘàș»à»ˆàș‡àșàșČàș™à»àșˆà»‰àș‡à»€àș•àș·àș­àș™&lt;br/&gt;&lt;br/&gt;àș—່àșČàș™àșȘàșČàșĄàșČàș”àșˆàș±àș”àșàșČàș™àș„àș§àșČàșĄàșȘàșČàșĄàșČàș”àș‚àș­àș‡à»àș­àș±àșšàș™àș”້ໃàș™àșàșČàș™àș­à»ˆàșČàș™ ແàș„àș° àșȘàș»à»ˆàș‡àșàșČàș™à»àșˆà»‰àș‡à»€àș•àș·àș­àș™à»„àș”້àș—àșžàșà»€àș§àș„àșČໃàș™àșàșČàș™àș•àș±à»‰àș‡àș„່àșČ&gt; àșàșČàș™à»àșˆà»‰àș‡à»€àș•àș·àș­àș™."</string>
     <string name="permission_app_streaming_summary" msgid="606923325679670624">"àșȘàș°àș•àșŁàș”àșĄà»àș­àș±àșšàș‚àș­àș‡à»‚àș—àș„àș°àșȘàș±àșšàș—່àșČàș™"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
     <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"àșȘàș°àș•àșŁàș”àșĄà»àș­àș±àșš ແàș„àș° àș„àșžàș™àșȘàș»àșĄàșšàș±àș”àș„àș°àșšàș»àșšàș­àș·à»ˆàș™à»†àșˆàșČàșà»‚àș—àș„àș°àșȘàș±àșšàș‚àș­àș‡àș—່àșČàș™"</string>
diff --git a/packages/CompanionDeviceManager/res/values-lt/strings.xml b/packages/CompanionDeviceManager/res/values-lt/strings.xml
index 1c28f14..54f8408 100644
--- a/packages/CompanionDeviceManager/res/values-lt/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-lt/strings.xml
@@ -56,28 +56,18 @@
     <string name="permission_nearby_devices" msgid="7530973297737123481">"Äźrenginiai netoliese"</string>
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Keisti medijos išvestÄŻ"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Nuotraukos ir medija"</string>
-    <!-- no translation found for permission_notifications (4099418516590632909) -->
-    <skip />
+    <string name="permission_notifications" msgid="4099418516590632909">"Pranešimai"</string>
     <string name="permission_app_streaming" msgid="6009695219091526422">"Programos"</string>
     <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Srautinis perdavimas"</string>
-    <!-- no translation found for permission_phone_summary (8246321093970051702) -->
-    <skip />
-    <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
-    <skip />
-    <!-- no translation found for permission_sms_summary (8499509535410068616) -->
-    <skip />
-    <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
-    <skip />
-    <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
-    <skip />
-    <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
-    <skip />
-    <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
-    <skip />
-    <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
-    <skip />
-    <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
-    <skip />
+    <string name="permission_phone_summary" msgid="8246321093970051702">"Skambinti ir tvarkyti telefonƳ skambučius"</string>
+    <string name="permission_call_logs_summary" msgid="7545243592757693321">"Skaityti ir rašyti telefono skambučiĆł ĆŸurnalą"</string>
+    <string name="permission_sms_summary" msgid="8499509535410068616">"SiĆłsti ir perĆŸiĆ«rėti SMS pranešimus"</string>
+    <string name="permission_contacts_summary" msgid="2840800622763086808">"Pasiekti kontaktus"</string>
+    <string name="permission_calendar_summary" msgid="8430353935747336165">"Pasiekti kalendoriĆł"</string>
+    <string name="permission_microphone_summary" msgid="4862628553869973259">"Äźrašyti garsą"</string>
+    <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Rasti apytikslę netoliese esančiƳ įrenginiƳ poziciją, aptikti juos ir prisijungti prie jƳ"</string>
+    <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Skaityti visus pranešimus, ÄŻskaitant tokią informaciją kaip kontaktai, pranešimai ir nuotraukos"</string>
+    <string name="permission_notifications_summary" msgid="2272810466047367030">"• Skaityti visus pranešimus, ÄŻskaitant tokią informaciją kaip kontaktai, pranešimai ir nuotraukos&lt;br/&gt;• SiĆłsti pranešimus&lt;br/&gt;&lt;br/&gt;Galite bet kada tvarkyti šios programos leidimą skaityti ir siĆłsti pranešimus skiltyje „Nustatymai“ &gt; „Pranešimai“."</string>
     <string name="permission_app_streaming_summary" msgid="606923325679670624">"Telefono programĆł perdavimas srautu"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
     <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Srautu perduokite programas ir kitas sistemos funkcijas iš telefono"</string>
diff --git a/packages/CompanionDeviceManager/res/values-lv/strings.xml b/packages/CompanionDeviceManager/res/values-lv/strings.xml
index 5126812..390d544 100644
--- a/packages/CompanionDeviceManager/res/values-lv/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-lv/strings.xml
@@ -56,28 +56,18 @@
     <string name="permission_nearby_devices" msgid="7530973297737123481">"Tuvumā esošas ierÄ«ces"</string>
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Mainīt multivides izvadi"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Fotoattēli un multivides faili"</string>
-    <!-- no translation found for permission_notifications (4099418516590632909) -->
-    <skip />
+    <string name="permission_notifications" msgid="4099418516590632909">"PaziƆojumi"</string>
     <string name="permission_app_streaming" msgid="6009695219091526422">"Lietotnes"</string>
     <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Straumēšana"</string>
-    <!-- no translation found for permission_phone_summary (8246321093970051702) -->
-    <skip />
-    <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
-    <skip />
-    <!-- no translation found for permission_sms_summary (8499509535410068616) -->
-    <skip />
-    <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
-    <skip />
-    <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
-    <skip />
-    <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
-    <skip />
-    <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
-    <skip />
-    <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
-    <skip />
-    <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
-    <skip />
+    <string name="permission_phone_summary" msgid="8246321093970051702">"Veikt un pārvaldÄ«t tālruƆa zvanus"</string>
+    <string name="permission_call_logs_summary" msgid="7545243592757693321">"LasÄ«t un rakstÄ«t tālruƆa zvanu ĆŸurnālu"</string>
+    <string name="permission_sms_summary" msgid="8499509535410068616">"SĆ«tÄ«t un skatÄ«t Ä«sziƆas"</string>
+    <string name="permission_contacts_summary" msgid="2840800622763086808">"PiekÄŒĆ«t jĆ«su kontaktpersonu datiem"</string>
+    <string name="permission_calendar_summary" msgid="8430353935747336165">"PiekÄŒĆ«t jĆ«su kalendāram"</string>
+    <string name="permission_microphone_summary" msgid="4862628553869973259">"Ierakstīt audio"</string>
+    <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Atrast tuvumā esošas ierÄ«ces, izveidot ar tām savienojumu un noteikt to relatÄ«vo atrašanās vietu"</string>
+    <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"LasÄ«t visus paziƆojumus, tostarp tādu informāciju kā kontaktpersonas, ziƆojumi un fotoattēli"</string>
+    <string name="permission_notifications_summary" msgid="2272810466047367030">"• LasÄ«t visus paziƆojumus, tostarp tādu informāciju kā kontaktpersonas, ziƆojumi un fotoattēli&lt;br/&gt;• SĆ«tÄ«t paziƆojumus&lt;br/&gt;&lt;br/&gt;Jebkurā brÄ«dÄ« sadaČā “IestatÄ«jumi un paziƆojumi” varat pārvaldÄ«t šÄ«s lietotnes atÄŒauju lasÄ«t un sĆ«tÄ«t paziƆojumus."</string>
     <string name="permission_app_streaming_summary" msgid="606923325679670624">"Straumēt jĆ«su tālruƆa lietotnes"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
     <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"No sava tālruƆa straumējiet lietotnes un citas sistēmas funkcijas"</string>
diff --git a/packages/CompanionDeviceManager/res/values-mk/strings.xml b/packages/CompanionDeviceManager/res/values-mk/strings.xml
index 24fbaa7..dc15899 100644
--- a/packages/CompanionDeviceManager/res/values-mk/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-mk/strings.xml
@@ -56,28 +56,18 @@
     <string name="permission_nearby_devices" msgid="7530973297737123481">"УрДЎО ĐČĐŸ Đ±Đ»ĐžĐ·ĐžĐœĐ°"</string>
     <string name="permission_media_routing_control" msgid="5498639511586715253">"ĐŸŃ€ĐŸĐŒĐ”ĐœĐ° ĐœĐ° ĐžĐ·Đ»Đ”Đ·ĐŸŃ‚ за Đ°ŃƒĐŽĐžĐŸ"</string>
     <string name="permission_storage" msgid="6831099350839392343">"ĐŃƒĐŽĐžĐŸĐČĐžĐ·ŃƒĐ”Đ»ĐœĐž ŃĐŸĐŽŃ€Đ¶ĐžĐœĐž"</string>
-    <!-- no translation found for permission_notifications (4099418516590632909) -->
-    <skip />
+    <string name="permission_notifications" msgid="4099418516590632909">"ИзĐČĐ”ŃŃ‚ŃƒĐČања"</string>
     <string name="permission_app_streaming" msgid="6009695219091526422">"АплОĐșацоо"</string>
     <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"ĐĄŃ‚Ń€ĐžĐŒĐžĐœĐł"</string>
-    <!-- no translation found for permission_phone_summary (8246321093970051702) -->
-    <skip />
-    <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
-    <skip />
-    <!-- no translation found for permission_sms_summary (8499509535410068616) -->
-    <skip />
-    <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
-    <skip />
-    <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
-    <skip />
-    <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
-    <skip />
-    <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
-    <skip />
-    <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
-    <skip />
-    <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
-    <skip />
+    <string name="permission_phone_summary" msgid="8246321093970051702">"УпатуĐČа Đž упраĐČуĐČа ŃĐŸ Ń‚Đ”Đ»Đ”Ń„ĐŸĐœŃĐșĐž ĐżĐŸĐČоцо"</string>
+    <string name="permission_call_logs_summary" msgid="7545243592757693321">"Чота Đž пОшуĐČа Đ”ĐČĐžĐŽĐ”ĐœŃ†ĐžŃ˜Đ° ĐœĐ° ĐżĐŸĐČоцо ĐČĐŸ Ń‚Đ”Đ»Đ”Ń„ĐŸĐœĐŸŃ‚"</string>
+    <string name="permission_sms_summary" msgid="8499509535410068616">"Испраќа Đž проĐșажуĐČа SMS-ĐżĐŸŃ€Đ°ĐșĐž"</string>
+    <string name="permission_contacts_summary" msgid="2840800622763086808">"ПростапуĐČа ĐŽĐŸ ĐșĐŸĐœŃ‚Đ°ĐșтОтД"</string>
+    <string name="permission_calendar_summary" msgid="8430353935747336165">"ПростапуĐČа ĐŽĐŸ ĐșĐ°Đ»Đ”ĐœĐŽĐ°Ń€ĐŸŃ‚"</string>
+    <string name="permission_microphone_summary" msgid="4862628553869973259">"ĐĄĐœĐžĐŒĐ° Đ°ŃƒĐŽĐžĐŸ"</string>
+    <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"ĐĐ°ĐŸŃ“Đ° Đž сД ĐżĐŸĐČŃ€Đ·ŃƒĐČа ŃĐŸ ŃƒŃ€Đ”ĐŽĐž ĐČĐŸ Đ±Đ»ĐžĐ·ĐžĐœĐ° Đž Ўа ја утĐČрЮуĐČа ĐœĐžĐČĐœĐ°Ń‚Đ° рДлатОĐČĐœĐ° ĐżĐŸĐ»ĐŸĐ¶Đ±Đ°"</string>
+    <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Го чота сОтД ОзĐČĐ”ŃŃ‚ŃƒĐČања, ĐŒĐ”Ń“Ńƒ ĐșĐŸĐž Đž ĐżĐŸĐŽĐ°Ń‚ĐŸŃ†Đž ĐșаĐșĐŸ ĐșĐŸĐœŃ‚Đ°Đșто, ĐżĐŸŃ€Đ°ĐșĐž Đž Ń„ĐŸŃ‚ĐŸĐłŃ€Đ°Ń„ĐžĐž"</string>
+    <string name="permission_notifications_summary" msgid="2272810466047367030">"• Го чота сОтД ОзĐČĐ”ŃŃ‚ŃƒĐČања, ĐŒĐ”Ń“Ńƒ ĐșĐŸĐž Đž ĐżĐŸĐŽĐ°Ń‚ĐŸŃ†Đž ĐșаĐșĐŸ ĐșĐŸĐœŃ‚Đ°Đșто, ĐżĐŸŃ€Đ°ĐșĐž Đž Ń„ĐŸŃ‚ĐŸĐłŃ€Đ°Ń„ĐžĐž&lt;br/&gt;• Испраќа ОзĐČĐ”ŃŃ‚ŃƒĐČања&lt;br/&gt;&lt;br/&gt;ĐœĐŸĐ¶Đ” Ўа упраĐČуĐČатД ŃĐŸ ŃĐżĐŸŃĐŸĐ±ĐœĐŸŃŃ‚Đ° ĐœĐ° аплОĐșацојаĐČа Ўа чота Đž оспраќа ОзĐČĐ”ŃŃ‚ŃƒĐČања ĐșĐŸĐłĐ° Đ±ĐžĐ»ĐŸ ĐČĐŸ „ĐŸĐŸŃŃ‚Đ°ĐČĐșĐž &gt; ИзĐČĐ”ŃŃ‚ŃƒĐČања“."</string>
     <string name="permission_app_streaming_summary" msgid="606923325679670624">"ĐĄŃ‚Ń€ĐžĐŒŃƒĐČĐ°Ń˜Ń‚Đ” гО аплОĐșацООтД ĐœĐ° Ń‚Đ”Đ»Đ”Ń„ĐŸĐœĐŸŃ‚"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
     <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"АплОĐșацоо за ŃŃ‚Ń€ĐžĐŒĐžĐœĐł Đž Юруго ŃĐžŃŃ‚Đ”ĐŒŃĐșĐž Ń„ŃƒĐœĐșцоо ĐŸĐŽ ĐČĐ°ŃˆĐžĐŸŃ‚ Ń‚Đ”Đ»Đ”Ń„ĐŸĐœ"</string>
diff --git a/packages/CompanionDeviceManager/res/values-ml/strings.xml b/packages/CompanionDeviceManager/res/values-ml/strings.xml
index a3e07ca..6c09e63 100644
--- a/packages/CompanionDeviceManager/res/values-ml/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ml/strings.xml
@@ -56,28 +56,18 @@
     <string name="permission_nearby_devices" msgid="7530973297737123481">"àŽžàŽźà”€àŽȘàŽźà”àŽłà”àŽł àŽ‰àŽȘàŽ•àŽ°àŽŁàŽ™à”àŽ™à”Ÿ"</string>
     <string name="permission_media_routing_control" msgid="5498639511586715253">"àŽźà”€àŽĄàŽżàŽŻ àŽ”àŽŸà”àŽŸà”àŽȘà”àŽŸà”àŽŸà” àŽźàŽŸàŽ±à”àŽ±à”àŽ•"</string>
     <string name="permission_storage" msgid="6831099350839392343">"àŽ«à”‹àŽŸà”àŽŸà”‹àŽ•àŽłà”àŽ‚ àŽźà”€àŽĄàŽżàŽŻàŽŻà”àŽ‚"</string>
-    <!-- no translation found for permission_notifications (4099418516590632909) -->
-    <skip />
+    <string name="permission_notifications" msgid="4099418516590632909">"àŽ…àŽ±àŽżàŽŻàŽżàŽȘà”àŽȘà”àŽ•à”Ÿ"</string>
     <string name="permission_app_streaming" msgid="6009695219091526422">"àŽ†àŽȘà”àŽȘà”àŽ•à”Ÿ"</string>
     <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"àŽžà”àŽŸà”àŽ°à”€àŽźàŽżàŽ‚àŽ—à”"</string>
-    <!-- no translation found for permission_phone_summary (8246321093970051702) -->
-    <skip />
-    <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
-    <skip />
-    <!-- no translation found for permission_sms_summary (8499509535410068616) -->
-    <skip />
-    <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
-    <skip />
-    <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
-    <skip />
-    <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
-    <skip />
-    <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
-    <skip />
-    <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
-    <skip />
-    <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
-    <skip />
+    <string name="permission_phone_summary" msgid="8246321093970051702">"àŽ«à”‹à”ș àŽ•à”‹àŽłà”àŽ•à”Ÿ àŽ”àŽżàŽłàŽżàŽ•à”àŽ•à”àŽ•, àŽźàŽŸàŽšà”‡àŽœà” àŽšà”†àŽŻà”àŽŻà”àŽ•"</string>
+    <string name="permission_call_logs_summary" msgid="7545243592757693321">"àŽ«à”‹à”ș àŽ•à”‹à”Ÿ àŽšàŽ°àŽżàŽ€à”àŽ°àŽ‚ àŽŽàŽŽà”àŽ€à”àŽ•àŽŻà”àŽ‚ àŽ”àŽŸàŽŻàŽżàŽ•à”àŽ•à”àŽ•àŽŻà”àŽ‚ àŽšà”†àŽŻà”àŽŻà”àŽ•"</string>
+    <string name="permission_sms_summary" msgid="8499509535410068616">"SMS àŽžàŽšà”àŽŠà”‡àŽ¶àŽ™à”àŽ™à”Ÿ àŽ…àŽŻàŽŻà”àŽ•à”àŽ•à”àŽ•, àŽ•àŽŸàŽŁà”àŽ•"</string>
+    <string name="permission_contacts_summary" msgid="2840800622763086808">"àŽšàŽżàŽ™à”àŽ™àŽłà”àŽŸà”† àŽ•à”‹à”șàŽŸàŽŸàŽ•à”àŽ±à”àŽ±à”àŽ•à”Ÿ àŽ†àŽ•à”‌àŽžàŽžà” àŽšà”†àŽŻà”àŽŻà”àŽ•"</string>
+    <string name="permission_calendar_summary" msgid="8430353935747336165">"àŽšàŽżàŽ™à”àŽ™àŽłà”àŽŸà”† àŽ•àŽČàŽŁà”àŽŸà”Œ àŽ†àŽ•à”‌àŽžàŽžà” àŽšà”†àŽŻà”àŽŻà”àŽ•"</string>
+    <string name="permission_microphone_summary" msgid="4862628553869973259">"àŽ“àŽĄàŽżàŽŻà”‹ àŽ±à”†àŽ•à”àŽ•à”‹à”ŒàŽĄà” àŽšà”†àŽŻà”àŽŻà”àŽ•"</string>
+    <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"àŽžàŽźà”€àŽȘàŽźà”àŽłà”àŽł àŽ‰àŽȘàŽ•àŽ°àŽŁàŽ™à”àŽ™à”Ÿ àŽ•àŽŁà”àŽŸà”†àŽ€à”àŽ€àŽŸàŽšà”àŽ‚ àŽ…àŽ”àŽŻàŽżàŽČà”‡àŽ•à”àŽ•à” àŽ•àŽŁàŽ•à”àŽ±à”àŽ±à” àŽšà”†àŽŻà”àŽŻàŽŸàŽšà”àŽ‚ àŽ…àŽ”àŽŻà”àŽŸà”† àŽ†àŽȘà”‡àŽ•à”àŽ·àŽżàŽ• àŽžà”àŽ„àŽŸàŽšàŽ‚ àŽšàŽżà”ŒàŽŁà”àŽŁàŽŻàŽżàŽ•à”àŽ•àŽŸàŽšà”àŽ‚ àŽ•àŽŽàŽżàŽŻà”àŽ‚"</string>
+    <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"àŽ•à”‹à”șàŽŸàŽŸàŽ•à”‌àŽ±à”àŽ±à”àŽ•à”Ÿ, àŽžàŽšà”àŽŠà”‡àŽ¶àŽ™à”àŽ™à”Ÿ, àŽ«à”‹àŽŸà”àŽŸà”‹àŽ•à”Ÿ àŽŽàŽšà”àŽšàŽżàŽ” àŽȘà”‹àŽČà”àŽłà”àŽł àŽ”àŽżàŽ”àŽ°àŽ™à”àŽ™à”Ÿ àŽ‰à”ŸàŽȘà”àŽȘà”†àŽŸà”† àŽŽàŽČà”àŽČàŽŸ àŽ…àŽ±àŽżàŽŻàŽżàŽȘà”àŽȘà”àŽ•àŽłà”àŽ‚ àŽ”àŽŸàŽŻàŽżàŽ•à”àŽ•à”àŽ•"</string>
+    <string name="permission_notifications_summary" msgid="2272810466047367030">"• àŽ•à”‹à”șàŽŸàŽŸàŽ•à”‌àŽ±à”àŽ±à”àŽ•à”Ÿ, àŽžàŽšà”àŽŠà”‡àŽ¶àŽ™à”àŽ™à”Ÿ, àŽ«à”‹àŽŸà”àŽŸà”‹àŽ•à”Ÿ àŽŽàŽšà”àŽšàŽżàŽ” àŽȘà”‹àŽČà”àŽłà”àŽł àŽ”àŽżàŽ”àŽ°àŽ™à”àŽ™à”Ÿ àŽ‰à”ŸàŽȘà”àŽȘà”†àŽŸà”† àŽŽàŽČà”àŽČàŽŸ àŽ…àŽ±àŽżàŽŻàŽżàŽȘà”àŽȘà”àŽ•àŽłà”àŽ‚ àŽ”àŽŸàŽŻàŽżàŽ•à”àŽ•à”àŽ•&lt;br/&gt;• àŽ…àŽ±àŽżàŽŻàŽżàŽȘà”àŽȘà”àŽ•à”Ÿ àŽ…àŽŻàŽŻà”àŽ•à”àŽ•à”àŽ•&lt;br/&gt;&lt;br/&gt;àŽ…àŽ±àŽżàŽŻàŽżàŽȘà”àŽȘà”àŽ•à”Ÿ àŽ”àŽŸàŽŻàŽżàŽ•à”àŽ•àŽŸàŽšà”àŽ‚ àŽ…àŽŻàŽŻà”àŽ•à”àŽ•àŽŸàŽšà”àŽźà”àŽłà”àŽł àŽˆ àŽ†àŽȘà”àŽȘàŽżàŽšà”àŽ±à”† àŽ¶à”‡àŽ·àŽż àŽ•à”àŽ°àŽźà”€àŽ•àŽ°àŽŁàŽ”à”àŽ‚ àŽ…àŽ±àŽżàŽŻàŽżàŽȘà”àŽȘà”àŽ•àŽłà”àŽ‚ àŽŽàŽšà”àŽšàŽ€àŽżà”œ àŽàŽ€à”àŽžàŽźàŽŻàŽ€à”àŽ€à”àŽ‚ àŽźàŽŸàŽšà”‡àŽœà” àŽšà”†àŽŻà”àŽŻàŽŸàŽ‚."</string>
     <string name="permission_app_streaming_summary" msgid="606923325679670624">"àŽšàŽżàŽ™à”àŽ™àŽłà”àŽŸà”† àŽ«à”‹àŽŁàŽżàŽČà”† àŽ†àŽȘà”àŽȘà”àŽ•à”Ÿ àŽžà”‌àŽŸà”àŽ°à”€àŽ‚ àŽšà”†àŽŻà”àŽŻà”àŽ•"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
     <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"àŽšàŽżàŽ™à”àŽ™àŽłà”àŽŸà”† àŽ«à”‹àŽŁàŽżà”œ àŽšàŽżàŽšà”àŽšà” àŽ†àŽȘà”àŽȘà”àŽ•àŽłà”àŽ‚ àŽźàŽ±à”àŽ±à” àŽžàŽżàŽžà”àŽ±à”àŽ±àŽ‚ àŽ«à”€àŽšà”àŽšàŽ±à”àŽ•àŽłà”àŽ‚ àŽžà”àŽŸà”àŽ°à”€àŽ‚ àŽšà”†àŽŻà”àŽŻàŽŸàŽ‚"</string>
diff --git a/packages/CompanionDeviceManager/res/values-mn/strings.xml b/packages/CompanionDeviceManager/res/values-mn/strings.xml
index 584185e..4cbccb4 100644
--- a/packages/CompanionDeviceManager/res/values-mn/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-mn/strings.xml
@@ -56,28 +56,18 @@
     <string name="permission_nearby_devices" msgid="7530973297737123481">"ОĐčŃ€ĐŸĐ»Ń†ĐŸĐŸŃ… Ń‚Ó©Ń…Ó©Ó©Ń€Ó©ĐŒĐ¶ÒŻÒŻĐŽ"</string>
     <string name="permission_media_routing_control" msgid="5498639511586715253">"ĐœĐ”ĐŽĐžĐ° гаралтыг өөрчлөх"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Зураг Đ±ĐŸĐ»ĐŸĐœ ĐŒĐ”ĐŽĐžĐ°"</string>
-    <!-- no translation found for permission_notifications (4099418516590632909) -->
-    <skip />
+    <string name="permission_notifications" msgid="4099418516590632909">"ĐœŃĐŽŃĐłĐŽŃĐ»"</string>
     <string name="permission_app_streaming" msgid="6009695219091526422">"АппууЎ"</string>
     <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Đ”Đ°ĐŒĐ¶ŃƒŃƒĐ»Đ°Ń…"</string>
-    <!-- no translation found for permission_phone_summary (8246321093970051702) -->
-    <skip />
-    <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
-    <skip />
-    <!-- no translation found for permission_sms_summary (8499509535410068616) -->
-    <skip />
-    <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
-    <skip />
-    <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
-    <skip />
-    <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
-    <skip />
-    <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
-    <skip />
-    <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
-    <skip />
-    <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
-    <skip />
+    <string name="permission_phone_summary" msgid="8246321093970051702">"ĐŁŃ‚Đ°ŃĐœŃ‹ ЎууЎлага хоĐčх Đ±ĐŸĐ»ĐŸĐœ уЮорЮах"</string>
+    <string name="permission_call_logs_summary" msgid="7545243592757693321">"ĐŁŃ‚Đ°ŃĐœŃ‹ ĐŽŃƒŃƒĐŽĐ»Đ°ĐłŃ‹Đœ жагсаалтыг ŃƒĐœŃˆĐžŃ… Đ±ĐŸĐ»ĐŸĐœ бОчОх"</string>
+    <string name="permission_sms_summary" msgid="8499509535410068616">"SMS ĐŒĐ”ŃŃĐ”Đ¶ ĐžĐ»ĐłŃŃŃ… Đ±ĐŸĐ»ĐŸĐœ харах"</string>
+    <string name="permission_contacts_summary" msgid="2840800622763086808">"ĐąĐ°ĐœŃ‹ харОлцагчЎаЎ Ń…Đ°ĐœĐŽĐ°Ń…"</string>
+    <string name="permission_calendar_summary" msgid="8430353935747336165">"ĐąĐ°ĐœŃ‹ ĐșĐ°Đ»Đ”ĐœĐŽĐ°Ń€ŃŒŃ‚ Ń…Đ°ĐœĐŽĐ°Ń…"</string>
+    <string name="permission_microphone_summary" msgid="4862628553869973259">"ĐŃƒĐŽĐžĐŸ бОчОх"</string>
+    <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"ОĐčŃ€ĐŸĐ»Ń†ĐŸĐŸŃ… Ń‚Ó©Ń…Ó©Ó©Ń€Ó©ĐŒĐ¶ÒŻÒŻĐŽĐžĐčĐœ Ń…Đ°Ń€ŃŒŃ†Đ°ĐœĐłŃƒĐč баĐčрлалыг ĐŸĐ»ĐŸŃ…, Ń‚ĐŸĐŽĐŸŃ€Ń…ĐŸĐčĐ»ĐŸŃ… Đ±ĐŸĐ»ĐŸĐœ тэЮгээрт Ń…ĐŸĐ»Đ±ĐŸĐłĐŽĐŸŃ…"</string>
+    <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"ЄарОлцагчОЎ, ĐŒĐ”ŃŃĐ”Đ¶ Đ±ĐŸĐ»ĐŸĐœ Đ·ŃƒŃ€ĐłŃƒŃƒĐŽ Đ·ŃŃ€ŃĐł ĐŒŃĐŽŃŃĐ»Đ»ĐžĐčĐł ĐŸŃ€ŃƒŃƒĐ»Đ°Đ°ĐŽ Đ±ÒŻŃ… ĐŒŃĐŽŃĐłĐŽĐ»ĐžĐčĐł ŃƒĐœŃˆĐžŃ…"</string>
+    <string name="permission_notifications_summary" msgid="2272810466047367030">"• ЄарОлцагчОЎ, ĐŒĐ”ŃŃĐ”Đ¶ Đ±ĐŸĐ»ĐŸĐœ Đ·ŃƒŃ€ĐłŃƒŃƒĐŽ Đ·ŃŃ€ŃĐł ĐŒŃĐŽŃŃĐ»Đ»ĐžĐčĐł ĐŸŃ€ŃƒŃƒĐ»Đ°Đ°ĐŽ Đ±ÒŻŃ… ĐŒŃĐŽŃĐłĐŽĐ»ĐžĐčĐł ŃƒĐœŃˆĐžŃ…&lt;br/&gt;• ĐœŃĐŽŃĐłĐŽĐ»ÒŻÒŻĐŽĐžĐčĐł ĐžĐ»ĐłŃŃŃ…&lt;br/&gt;&lt;br/&gt;йа ŃĐœŃ Đ°ĐżĐżŃ‹Đœ ĐŒŃĐŽŃĐłĐŽĐ»ÒŻÒŻĐŽĐžĐčĐł ŃƒĐœŃˆĐžŃ… Đ±ĐŸĐ»ĐŸĐœ ĐžĐ»ĐłŃŃŃ… Ń‡Đ°ĐŽĐ°ĐŒĐ¶ĐžĐčĐł ĐąĐŸŃ…ĐžŃ€ĐłĐŸĐŸ &gt; ĐŒŃĐŽŃĐłĐŽĐ»ÒŻÒŻĐŽŃĐŽ Ń…ÒŻŃŃŃĐœ ÒŻĐ”ĐŽŃŃ уЮорЮах Đ±ĐŸĐ»ĐŸĐŒĐ¶Ń‚ĐŸĐč."</string>
     <string name="permission_app_streaming_summary" msgid="606923325679670624">"ĐŁŃ‚Đ°ŃĐœŃ‹Ń…Đ°Đ° аппууЮыг ĐŽĐ°ĐŒĐ¶ŃƒŃƒĐ»Đ°Đ°Ń€Đ°Đč"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
     <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"ĐŁŃ‚Đ°ŃĐœĐ°Đ°ŃĐ°Đ° аппууЎ Đ±ĐŸĐ»ĐŸĐœ ŃĐžŃŃ‚Đ”ĐŒĐžĐčĐœ бусаЎ ĐŸĐœŃ†Đ»ĐŸĐłĐžĐčĐł ĐŽĐ°ĐŒĐ¶ŃƒŃƒĐ»Đ°Đ°Ń€Đ°Đč"</string>
diff --git a/packages/CompanionDeviceManager/res/values-mr/strings.xml b/packages/CompanionDeviceManager/res/values-mr/strings.xml
index de4f7fd..a01759b 100644
--- a/packages/CompanionDeviceManager/res/values-mr/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-mr/strings.xml
@@ -56,28 +56,18 @@
     <string name="permission_nearby_devices" msgid="7530973297737123481">"à€œà€”à€łà€Șà€Ÿà€žà€šà„€ à€Ąà€żà€”à„à€čà€Ÿà€‡à€ž"</string>
     <string name="permission_media_routing_control" msgid="5498639511586715253">"à€źà„€à€Ąà€żà€Żà€Ÿ à€†à€‰à€Ÿà€Șà„à€Ÿ à€Źà€Šà€Čà€Ÿ"</string>
     <string name="permission_storage" msgid="6831099350839392343">"à€«à„‹à€Ÿà„‹ à€†à€Łà€ż à€źà„€à€Ąà€żà€Żà€Ÿ"</string>
-    <!-- no translation found for permission_notifications (4099418516590632909) -->
-    <skip />
+    <string name="permission_notifications" msgid="4099418516590632909">"à€žà„‚à€šà€šà€Ÿ"</string>
     <string name="permission_app_streaming" msgid="6009695219091526422">"à„Čà€Șà„à€ž"</string>
     <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"à€žà„à€Ÿà„à€°à„€à€źà€żà€‚à€—"</string>
-    <!-- no translation found for permission_phone_summary (8246321093970051702) -->
-    <skip />
-    <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
-    <skip />
-    <!-- no translation found for permission_sms_summary (8499509535410068616) -->
-    <skip />
-    <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
-    <skip />
-    <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
-    <skip />
-    <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
-    <skip />
-    <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
-    <skip />
-    <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
-    <skip />
-    <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
-    <skip />
+    <string name="permission_phone_summary" msgid="8246321093970051702">"à€«à„‹à€š à€•à„‰à€Č à€•à€°à€Łà„‡ à€†à€Łà€ż à€€à„‡ à€”à„à€Żà€”à€žà„à€„à€Ÿà€Șà€żà€€ à€•à€°à€Łà„‡"</string>
+    <string name="permission_call_logs_summary" msgid="7545243592757693321">"à€«à„‹à€š à€•à„‰à€Č à€Čà„‰à€— à€°à„€à€Ą à€…à€à€Ą à€°à€Ÿà€‡à€Ÿ à€•à€°à€Łà„‡"</string>
+    <string name="permission_sms_summary" msgid="8499509535410068616">"à€à€žà€à€źà€à€ž à€Șà€Ÿà€ à€”à€Łà„‡ à€†à€Łà€ż à€Șà€Ÿà€čà€Łà„‡"</string>
+    <string name="permission_contacts_summary" msgid="2840800622763086808">"à€€à„à€źà€šà„‡ à€žà€‚à€Șà€°à„à€• à„Čà€•à„à€žà„‡à€ž à€•à€°à€Łà„‡"</string>
+    <string name="permission_calendar_summary" msgid="8430353935747336165">"à€€à„à€źà€šà„‡ à€•à„…à€Čà„‡à€‚à€Ąà€° à€…‍à„…à€•à„à€žà„‡à€ž à€•à€°à€Łà„‡"</string>
+    <string name="permission_microphone_summary" msgid="4862628553869973259">"à€‘à€Ąà€żà€“ à€°à„‡à€•à„‰à€°à„à€Ą à€•à€°à€Łà„‡"</string>
+    <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"à€œà€”à€łà€Șà€Ÿà€žà€šà„€ à€Ąà€żà€”à„à€čà€Ÿà€‡à€ž à€¶à„‹à€§à€Łà„‡, à€€à„à€Żà€Ÿà€‚à€šà„à€Żà€Ÿà€¶à„€ à€•à€šà„‡à€•à„à€Ÿ à€•à€°à€Łà„‡ à€†à€Łà€ż à€€à„à€Żà€Ÿà€‚à€šà„‡ à€žà€‚à€Źà€‚à€§à€żà€€ à€žà„à€„à€Ÿà€š à€šà€żà€°à„à€§à€Ÿà€°à€żà€€ à€•à€°à€Łà„‡"</string>
+    <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"à€žà€‚à€Șà€°à„à€•, à€źà„‡à€žà„‡à€œ à€†à€Łà€ż à€«à„‹à€Ÿà„‹ à€Żà€Ÿà€‚à€žà€Ÿà€°à€–à„à€Żà€Ÿ à€źà€Ÿà€čà€żà€€à„€à€žà€č à€žà€°à„à€” à€žà„‚à€šà€šà€Ÿ à€”à€Ÿà€šà€Łà„‡"</string>
+    <string name="permission_notifications_summary" msgid="2272810466047367030">"• à€žà€‚à€Șà€°à„à€•, à€źà„‡à€žà„‡à€œ à€†à€Łà€ż à€«à„‹à€Ÿà„‹à€žà€Ÿà€°à€–à„à€Żà€Ÿ à€źà€Ÿà€čà€żà€€à„€à€žà€č à€žà€°à„à€” à€žà„‚à€šà€šà€Ÿ à€”à€Ÿà€šà€Łà„‡&lt;br/&gt;• à€žà„‚à€šà€šà€Ÿ à€Șà€Ÿà€ à€”à€Łà„‡&lt;br/&gt;&lt;br/&gt;à€€à„à€źà„à€čà„€ à€Żà€Ÿ à€…‍à„…à€Șà€šà„€ à€žà„‚à€šà€šà€Ÿ à€”à€Ÿà€šà€Łà„à€Żà€Ÿà€šà„€ à€†à€Łà€ż à€Șà€Ÿà€ à€”à€Łà„à€Żà€Ÿà€šà„€ à€•à„à€·à€źà€€à€Ÿ à€žà„‡à€Ÿà€żà€‚à€—à„à€œ &gt; à€žà„‚à€šà€šà€Ÿ à€źà€§à„à€Żà„‡ à€•à€§à„€à€čà„€ à€”à„à€Żà€”à€žà„à€„à€Ÿà€Șà€żà€€ à€•à€°à„‚ à€¶à€•à€€à€Ÿ."</string>
     <string name="permission_app_streaming_summary" msgid="606923325679670624">"à€€à„à€źà€šà„à€Żà€Ÿ à€«à„‹à€šà€”à€°à„€à€Č à„Čà€Șà„à€ž à€žà„à€Ÿà„à€°à„€à€ź à€•à€°à€Ÿ"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
     <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"à€€à„à€źà€šà„à€Żà€Ÿ à€«à„‹à€šà€”à€°à„‚à€š à€…‍à„…à€Șà„à€ž à€†à€Łà€ż à€‡à€€à€° à€žà€żà€žà„à€Ÿà„€à€ź à€”à„ˆà€¶à€żà€·à„‍à€Ÿà„à€Żà„‡ à€žà„à€Ÿà„à€°à„€à€ź à€•à€°à€Ÿ"</string>
diff --git a/packages/CompanionDeviceManager/res/values-ms/strings.xml b/packages/CompanionDeviceManager/res/values-ms/strings.xml
index 62b68f3..008535b 100644
--- a/packages/CompanionDeviceManager/res/values-ms/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ms/strings.xml
@@ -56,28 +56,18 @@
     <string name="permission_nearby_devices" msgid="7530973297737123481">"Peranti berdekatan"</string>
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Tukar output media"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Foto dan media"</string>
-    <!-- no translation found for permission_notifications (4099418516590632909) -->
-    <skip />
+    <string name="permission_notifications" msgid="4099418516590632909">"Pemberitahuan"</string>
     <string name="permission_app_streaming" msgid="6009695219091526422">"Apl"</string>
     <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Penstriman"</string>
-    <!-- no translation found for permission_phone_summary (8246321093970051702) -->
-    <skip />
-    <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
-    <skip />
-    <!-- no translation found for permission_sms_summary (8499509535410068616) -->
-    <skip />
-    <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
-    <skip />
-    <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
-    <skip />
-    <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
-    <skip />
-    <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
-    <skip />
-    <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
-    <skip />
-    <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
-    <skip />
+    <string name="permission_phone_summary" msgid="8246321093970051702">"Membuat dan mengurus panggilan telefon"</string>
+    <string name="permission_call_logs_summary" msgid="7545243592757693321">"Baca dan tulis log panggilan telefon"</string>
+    <string name="permission_sms_summary" msgid="8499509535410068616">"Menghantar dan melihat mesej SMS"</string>
+    <string name="permission_contacts_summary" msgid="2840800622763086808">"Akses kenalan anda"</string>
+    <string name="permission_calendar_summary" msgid="8430353935747336165">"Akses kalendar anda"</string>
+    <string name="permission_microphone_summary" msgid="4862628553869973259">"Rakam audio"</string>
+    <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Mencari, menyambung dan menentukan kedudukan relatif peranti berdekatan"</string>
+    <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Membaca semua pemberitahuan, termasuk maklumat seperti kenalan, mesej dan foto"</string>
+    <string name="permission_notifications_summary" msgid="2272810466047367030">"• Membaca semua pemberitahuan, termasuk maklumat seperti kenalan, mesej dan foto&lt;br/&gt;• Menghantar pemberitahuan&lt;br/&gt;&lt;br/&gt;Anda boleh mengurus keupayaan apl ini untuk membaca dan menghantar pemberitahuan pada bila-bila masa dalam Tetapan &gt; Pemberitahuan."</string>
     <string name="permission_app_streaming_summary" msgid="606923325679670624">"Strim apl telefon anda"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
     <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Strim apl dan ciri sistem yang lain daripada telefon anda"</string>
diff --git a/packages/CompanionDeviceManager/res/values-my/strings.xml b/packages/CompanionDeviceManager/res/values-my/strings.xml
index f99009c..05a3e88 100644
--- a/packages/CompanionDeviceManager/res/values-my/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-my/strings.xml
@@ -56,28 +56,18 @@
     <string name="permission_nearby_devices" msgid="7530973297737123481">"အနြှတစá€șဝိုကá€șရဟိ စကá€șမျဏသ"</string>
     <string name="permission_media_routing_control" msgid="5498639511586715253">"á€™á€źá€’á€źá€šá€Źá€Ąá€‘á€œá€€á€ș á€•á€Œá€±á€Źá€„á€șသခဌငá€șှ"</string>
     <string name="permission_storage" msgid="6831099350839392343">"ဓာတá€șá€•á€Żá€¶á€”á€Ÿá€„á€·á€ș မဟဒဟယဏမျဏသ"</string>
-    <!-- no translation found for permission_notifications (4099418516590632909) -->
-    <skip />
+    <string name="permission_notifications" msgid="4099418516590632909">"á€Ąá€€á€Œá€±á€Źá€„á€șá€žá€€á€Œá€Źá€žá€á€»á€€á€șမျဏသ"</string>
     <string name="permission_app_streaming" msgid="6009695219091526422">"အကá€șပá€șမျဏသ"</string>
     <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"တိုကá€șရိုကá€șဖလင့á€șခဌငá€șှ"</string>
-    <!-- no translation found for permission_phone_summary (8246321093970051702) -->
-    <skip />
-    <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
-    <skip />
-    <!-- no translation found for permission_sms_summary (8499509535410068616) -->
-    <skip />
-    <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
-    <skip />
-    <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
-    <skip />
-    <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
-    <skip />
-    <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
-    <skip />
-    <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
-    <skip />
-    <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
-    <skip />
+    <string name="permission_phone_summary" msgid="8246321093970051702">"ဖုနá€șသခေါá€șá€†á€­á€Żá€™á€Ÿá€Żá€™á€»á€Źá€ž á€•á€Œá€Żá€œá€Żá€•á€șနိုငá€ș၊ စဟမံနိုငá€șသညá€ș"</string>
+    <string name="permission_call_logs_summary" msgid="7545243592757693321">"ဖုနá€șသခေါá€șá€†á€­á€Żá€™á€Ÿá€á€șတမá€șှကို ဖတá€șနိုငá€ș၊ ရေသနိုငá€șသညá€ș"</string>
+    <string name="permission_sms_summary" msgid="8499509535410068616">"SMS မကá€șဆေ့ဂျá€șမျဏသ ပို့နိုငá€ș၊ ကဌည့á€șနိုငá€șသညá€ș"</string>
+    <string name="permission_contacts_summary" msgid="2840800622763086808">"သင့á€șအဆကá€șá€Ąá€žá€œá€šá€șမျဏသကို ဝငá€șကဌည့á€șနိုငá€șသညá€ș"</string>
+    <string name="permission_calendar_summary" msgid="8430353935747336165">"သင့á€șပဌကá€čခဒိနá€șကို ဝငá€șကဌည့á€șနိုငá€șသညá€ș"</string>
+    <string name="permission_microphone_summary" msgid="4862628553869973259">"á€Ąá€žá€¶á€žá€œá€„á€șှနိုငá€șသညá€ș"</string>
+    <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"အနြှတစá€șဝိုကá€șရဟိ စကá€șá€™á€»á€Źá€žá ဆကá€șစပá€șနေရဏကို á€›á€Ÿá€Źá€á€Œá€„á€șှ၊ ချိတá€șဆကá€șခဌငá€șသနဟင့á€ș သတá€șမဟတá€șခဌငá€șသတို့ လုပá€șနိုငá€șသညá€ș"</string>
+    <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"အဆကá€șá€Ąá€žá€œá€šá€ș၊ မကá€șဆေ့ဂျá€șနဟင့á€ș ဓာတá€șပုံမျဏသကá€Č့သို့ ဥချကá€șအလကá€șá€™á€»á€Źá€žá€Ąá€•á€«á€Ąá€á€„á€ș á€Ąá€€á€Œá€±á€Źá€„á€șá€žá€€á€Œá€Źá€žá€á€»á€€á€șá€Ąá€Źá€žá€œá€Żá€¶á€žá€€á€­á€Ż ဖတá€șနိုငá€șသညá€ș"</string>
+    <string name="permission_notifications_summary" msgid="2272810466047367030">"• အဆကá€șá€Ąá€žá€œá€šá€ș၊ မကá€șဆေ့ဂျá€șနဟင့á€ș ဓာတá€șပုံမျဏသကá€Č့သို့ ဥချကá€șအလကá€șá€™á€»á€Źá€žá€Ąá€•á€«á€Ąá€á€„á€ș á€Ąá€€á€Œá€±á€Źá€„á€șá€žá€€á€Œá€Źá€žá€á€»á€€á€șá€Ąá€Źá€žá€œá€Żá€¶á€žá€€á€­á€Ż ဖတá€șခဌငá€șှ&lt;br/&gt;• á€Ąá€€á€Œá€±á€Źá€„á€șá€žá€€á€Œá€Źá€žá€á€»á€€á€șမျဏသ á€•á€­á€Żá€·á€á€Œá€„á€șှ&lt;br/&gt;&lt;br/&gt;ဆကá€șတငá€șမျဏသ &gt; á€Ąá€€á€Œá€±á€Źá€„á€șá€žá€€á€Œá€Źá€žá€á€»á€€á€șမျဏသ တလငá€ș á€Ąá€€á€Œá€±á€Źá€„á€șá€žá€€á€Œá€Źá€žá€á€»á€€á€șမျဏသဥဏသ ကဥကá€șပá€ș၏ ဖတá€șခလင့á€șနဟင့á€ș á€•á€­á€Żá€·á€á€œá€„á€·á€șကို ဥချိနá€șမရလေသ စဟမံနိုငá€șသညá€ș။"</string>
     <string name="permission_app_streaming_summary" msgid="606923325679670624">"သင့á€șဖုနá€șá€žá€›á€Ÿá€­á€Ąá€€á€șပá€șမျဏသကို တိုကá€șရိုကá€șဖလင့á€șနိုငá€șသညá€ș"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
     <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"သင့á€șဖုနá€șသမဟ အကá€șပá€șá€™á€»á€Źá€žá€”á€Ÿá€„á€·á€ș á€Ąá€á€Œá€Źá€žá€…á€”á€…á€șအငá€șá€čဂါရပá€șမျဏသကို တိုကá€șရိုကá€șဖလင့á€șသညá€ș"</string>
diff --git a/packages/CompanionDeviceManager/res/values-nb/strings.xml b/packages/CompanionDeviceManager/res/values-nb/strings.xml
index 7f09149..d910f2f 100644
--- a/packages/CompanionDeviceManager/res/values-nb/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-nb/strings.xml
@@ -56,28 +56,18 @@
     <string name="permission_nearby_devices" msgid="7530973297737123481">"Enheter i nærheten"</string>
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Bytt medieutgang"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Bilder og medier"</string>
-    <!-- no translation found for permission_notifications (4099418516590632909) -->
-    <skip />
+    <string name="permission_notifications" msgid="4099418516590632909">"Varsler"</string>
     <string name="permission_app_streaming" msgid="6009695219091526422">"Apper"</string>
     <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Strømming"</string>
-    <!-- no translation found for permission_phone_summary (8246321093970051702) -->
-    <skip />
-    <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
-    <skip />
-    <!-- no translation found for permission_sms_summary (8499509535410068616) -->
-    <skip />
-    <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
-    <skip />
-    <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
-    <skip />
-    <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
-    <skip />
-    <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
-    <skip />
-    <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
-    <skip />
-    <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
-    <skip />
+    <string name="permission_phone_summary" msgid="8246321093970051702">"Ring og administrer anrop"</string>
+    <string name="permission_call_logs_summary" msgid="7545243592757693321">"Les og skriv samtalelogg"</string>
+    <string name="permission_sms_summary" msgid="8499509535410068616">"Send og les SMS-meldinger"</string>
+    <string name="permission_contacts_summary" msgid="2840800622763086808">"Se kontaktene dine"</string>
+    <string name="permission_calendar_summary" msgid="8430353935747336165">"Åpne kalenderen din"</string>
+    <string name="permission_microphone_summary" msgid="4862628553869973259">"Ta opp lyd"</string>
+    <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Finn, koble til og fastslå den relative posisjonen til enheter i nærheten"</string>
+    <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Les alle varsler, inkludert informasjon som kontakter, meldinger og bilder"</string>
+    <string name="permission_notifications_summary" msgid="2272810466047367030">"• Les alle varsler, inkludert informasjon som kontakter, meldinger og bilder&lt;br/&gt;• Send varsler&lt;br/&gt;&lt;br/&gt;Du kan når som helst administrere om denne appen kan lese og sende varsler, i Innstillinger &gt; Varsler."</string>
     <string name="permission_app_streaming_summary" msgid="606923325679670624">"Strøm appene på telefonen"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
     <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Strøm apper og andre systemfunksjoner fra telefonen"</string>
diff --git a/packages/CompanionDeviceManager/res/values-ne/strings.xml b/packages/CompanionDeviceManager/res/values-ne/strings.xml
index 38facb9..2001af2 100644
--- a/packages/CompanionDeviceManager/res/values-ne/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ne/strings.xml
@@ -56,28 +56,18 @@
     <string name="permission_nearby_devices" msgid="7530973297737123481">"à€šà€œà€żà€•à„ˆà€•à€Ÿ à€Ąà€żà€­à€Ÿà€‡à€žà€čà€°à„‚"</string>
     <string name="permission_media_routing_control" msgid="5498639511586715253">"à€źà€żà€Ąà€żà€Żà€Ÿ à€†à€‰à€Ÿà€Șà„à€Ÿ à€Źà€Šà€Čà„à€šà„à€čà„‹à€žà„"</string>
     <string name="permission_storage" msgid="6831099350839392343">"à€«à„‹à€Ÿà„‹ à€° à€źà€żà€Ąà€żà€Żà€Ÿ"</string>
-    <!-- no translation found for permission_notifications (4099418516590632909) -->
-    <skip />
+    <string name="permission_notifications" msgid="4099418516590632909">"à€žà„‚à€šà€šà€Ÿà€čà€°à„‚"</string>
     <string name="permission_app_streaming" msgid="6009695219091526422">"à€à€Șà€čà€°à„‚"</string>
     <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"à€žà„à€Ÿà„à€°à€żà€źà€żà€™"</string>
-    <!-- no translation found for permission_phone_summary (8246321093970051702) -->
-    <skip />
-    <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
-    <skip />
-    <!-- no translation found for permission_sms_summary (8499509535410068616) -->
-    <skip />
-    <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
-    <skip />
-    <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
-    <skip />
-    <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
-    <skip />
-    <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
-    <skip />
-    <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
-    <skip />
-    <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
-    <skip />
+    <string name="permission_phone_summary" msgid="8246321093970051702">"à€«à„‹à€š à€•à€Č à€—à€°à„à€šà„‡ à€° à€”à„à€Żà€”à€žà„à€„à€Ÿà€Șà€š à€—à€°à„à€šà„‡"</string>
+    <string name="permission_call_logs_summary" msgid="7545243592757693321">"à€«à„‹à€š à€•à€Čà€•à„‹ à€Čà€— à€°à€żà€Ą à€° à€°à€Ÿà€‡à€Ÿ à€—à€°à„à€šà„‡"</string>
+    <string name="permission_sms_summary" msgid="8499509535410068616">"SMS à€źà„à€Żà€Ÿà€žà„‡à€œ à€Șà€ à€Ÿà€‰à€šà„‡ à€° à€čà„‡à€°à„à€šà„‡"</string>
+    <string name="permission_contacts_summary" msgid="2840800622763086808">"à€€à€Șà€Ÿà€ˆà€‚à€•à€Ÿ à€•à€šà„à€Ÿà„à€Żà€Ÿà€•à„à€Ÿà€čà€°à„‚ à€čà„‡à€°à„à€šà„‡"</string>
+    <string name="permission_calendar_summary" msgid="8430353935747336165">"à€€à€Șà€Ÿà€ˆà€‚à€•à„‹ à€Șà€Ÿà€€à„à€°à„‹ à€Șà„à€°à€Żà„‹à€— à€—à€°à„à€šà„‡"</string>
+    <string name="permission_microphone_summary" msgid="4862628553869973259">"à€…à€Ąà€żà€Żà„‹ à€°à„‡à€•à€°à„à€Ą à€—à€°à„à€šà„‡"</string>
+    <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"à€šà€œà€żà€•à„ˆà€•à€Ÿ à€Ąà€żà€­à€Ÿà€‡à€žà€čà€°à„‚ à€­à„‡à€Ÿà„à€Ÿà€Ÿà€‰à€šà„‡, à€€à„€ à€Ąà€żà€­à€Ÿà€‡à€žà€žà€à€— à€•à€šà„‡à€•à„à€Ÿ à€—à€°à„à€šà„‡ à€° à€€à€żà€šà€•à„‹ à€žà€Ÿà€Șà„‡à€•à„à€· à€žà„à€„à€żà€€à€ż à€šà€żà€°à„à€§à€Ÿà€°à€Ł à€—à€°à„à€šà„‡"</string>
+    <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"à€•à€šà„à€Ÿà„à€Żà€Ÿà€•à„à€Ÿ, à€źà„à€Żà€Ÿà€žà„‡à€œ à€° à€«à„‹à€Ÿà„‹ à€œà€žà„à€€à€Ÿ à€œà€Ÿà€šà€•à€Ÿà€°à„€à€Čà€—à€Ÿà€Żà€€à€•à€Ÿ à€žà€Źà„ˆ à€žà„‚à€šà€šà€Ÿà€čà€°à„‚ à€°à€żà€Ą à€—à€°à„à€šà„‡"</string>
+    <string name="permission_notifications_summary" msgid="2272810466047367030">"• à€•à€šà„à€Ÿà„à€Żà€Ÿà€•à„à€Ÿ, à€źà„à€Żà€Ÿà€žà„‡à€œ à€° à€«à„‹à€Ÿà„‹ à€œà€žà„à€€à€Ÿ à€œà€Ÿà€šà€•à€Ÿà€°à„€à€Čà€—à€Ÿà€Żà€€à€•à€Ÿ à€žà€Źà„ˆ à€žà„‚à€šà€šà€Ÿà€čà€°à„‚ à€°à€żà€Ą à€—à€°à„à€šà„‡&lt;br/&gt;• à€žà„‚à€šà€šà€Ÿà€čà€°à„‚ à€Șà€ à€Ÿà€‰à€šà„‡&lt;br/&gt;&lt;br/&gt;à€€à€Șà€Ÿà€ˆà€‚ à€œà„à€šà€žà„à€•à„ˆ à€Źà„‡à€Čà€Ÿ à€žà„‡à€Ÿà€żà€™ &gt; à€žà„‚à€šà€šà€Ÿà€čà€°à„‚ à€–à€Łà„à€Ąà€źà€Ÿ à€—à€ˆ à€Żà„‹ à€à€Șà€Čà€Ÿà€ˆ à€žà„‚à€šà€šà€Ÿà€čà€°à„‚ à€°à€żà€Ą à€—à€°à„à€š à€° à€Șà€ à€Ÿà€‰à€š à€Šà€żà€šà„‡ à€•à€ż à€šà€Šà€żà€šà„‡ à€­à€šà„à€šà„‡ à€•à„à€°à€Ÿ à€”à„à€Żà€”à€žà„à€„à€Ÿà€Șà€š à€—à€°à„à€š à€žà€•à„à€šà„à€čà„à€šà„à€›à„€"</string>
     <string name="permission_app_streaming_summary" msgid="606923325679670624">"à€†à€«à„à€šà„‹ à€«à„‹à€šà€•à€Ÿ à€à€Șà€čà€°à„‚ à€Șà„à€°à€Żà„‹à€— à€—à€°à„à€šà„à€čà„‹à€žà„"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
     <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"à€†à€«à„à€šà„‹ à€«à„‹à€šà€Źà€Ÿà€Ÿ à€à€Ș à€° à€žà€żà€žà„à€Ÿà€źà€•à€Ÿ à€…à€šà„à€Ż à€žà„à€”à€żà€§à€Ÿà€čà€°à„‚ à€žà„à€Ÿà„à€°à€żà€ź à€—à€°à„à€šà„à€čà„‹à€žà„"</string>
diff --git a/packages/CompanionDeviceManager/res/values-nl/strings.xml b/packages/CompanionDeviceManager/res/values-nl/strings.xml
index 38c92e5..e8ccb58 100644
--- a/packages/CompanionDeviceManager/res/values-nl/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-nl/strings.xml
@@ -56,28 +56,18 @@
     <string name="permission_nearby_devices" msgid="7530973297737123481">"Apparaten in de buurt"</string>
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Media-uitvoer wijzigen"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Foto\'s en media"</string>
-    <!-- no translation found for permission_notifications (4099418516590632909) -->
-    <skip />
+    <string name="permission_notifications" msgid="4099418516590632909">"Meldingen"</string>
     <string name="permission_app_streaming" msgid="6009695219091526422">"Apps"</string>
     <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Streaming"</string>
-    <!-- no translation found for permission_phone_summary (8246321093970051702) -->
-    <skip />
-    <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
-    <skip />
-    <!-- no translation found for permission_sms_summary (8499509535410068616) -->
-    <skip />
-    <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
-    <skip />
-    <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
-    <skip />
-    <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
-    <skip />
-    <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
-    <skip />
-    <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
-    <skip />
-    <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
-    <skip />
+    <string name="permission_phone_summary" msgid="8246321093970051702">"Bellen en gesprekken beheren"</string>
+    <string name="permission_call_logs_summary" msgid="7545243592757693321">"Telefoongesprekslijst lezen en schrijven"</string>
+    <string name="permission_sms_summary" msgid="8499509535410068616">"Sms-berichten sturen en bekijken"</string>
+    <string name="permission_contacts_summary" msgid="2840800622763086808">"Toegang tot je contacten"</string>
+    <string name="permission_calendar_summary" msgid="8430353935747336165">"Toegang tot je agenda"</string>
+    <string name="permission_microphone_summary" msgid="4862628553869973259">"Audio opnemen"</string>
+    <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Apparaten in de buurt vinden, er verbinding mee maken en de relatieve positie ervan bepalen"</string>
+    <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Alle meldingen lezen, waaronder informatie zoals contacten, berichten en foto\'s"</string>
+    <string name="permission_notifications_summary" msgid="2272810466047367030">"• Alle meldingen lezen, waaronder informatie zoals contacten, berichten en foto\'s&lt;br/&gt;• Meldingen sturen&lt;br/&gt;&lt;br/&gt;Je kunt de mogelijkheden van deze app om meldingen te lezen en te sturen, beheren wanneer je wilt via Instellingen &gt; Meldingen."</string>
     <string name="permission_app_streaming_summary" msgid="606923325679670624">"Stream de apps van je telefoon"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
     <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Apps en andere systeemfuncties streamen vanaf je telefoon"</string>
diff --git a/packages/CompanionDeviceManager/res/values-or/strings.xml b/packages/CompanionDeviceManager/res/values-or/strings.xml
index 16985fa..0bb47b8 100644
--- a/packages/CompanionDeviceManager/res/values-or/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-or/strings.xml
@@ -56,28 +56,18 @@
     <string name="permission_nearby_devices" msgid="7530973297737123481">"àŹ†àŹ–àŹȘàŹŸàŹ–àŹ° àŹĄàŹżàŹ­àŹŸàŹ‡àŹžàŹ—à­àŹĄàŹŒàŹżàŹ•"</string>
     <string name="permission_media_routing_control" msgid="5498639511586715253">"àŹźàŹżàŹĄàŹżàŹ† àŹ†àŹ‰àŹŸàŹȘà­àŹŸ àŹȘàŹ°àŹżàŹŹàŹ°à­àŹ€à­àŹ€àŹš àŹ•àŹ°àŹšà­àŹ€à­"</string>
     <string name="permission_storage" msgid="6831099350839392343">"àŹ«àŹŸà­‹ àŹàŹŹàŹ‚ àŹźàŹżàŹĄàŹżàŹ†"</string>
-    <!-- no translation found for permission_notifications (4099418516590632909) -->
-    <skip />
+    <string name="permission_notifications" msgid="4099418516590632909">"àŹŹàŹżàŹœà­àŹžàŹȘà­àŹ€àŹżàŹ—à­àŹĄàŹŒàŹżàŹ•"</string>
     <string name="permission_app_streaming" msgid="6009695219091526422">"àŹ†àŹȘà­àŹž"</string>
     <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"àŹ·à­àŹŸà­àŹ°àŹżàŹźàŹżàŹ‚"</string>
-    <!-- no translation found for permission_phone_summary (8246321093970051702) -->
-    <skip />
-    <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
-    <skip />
-    <!-- no translation found for permission_sms_summary (8499509535410068616) -->
-    <skip />
-    <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
-    <skip />
-    <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
-    <skip />
-    <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
-    <skip />
-    <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
-    <skip />
-    <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
-    <skip />
-    <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
-    <skip />
+    <string name="permission_phone_summary" msgid="8246321093970051702">"àŹ«à­‹àŹš àŹ•àŹČàŹ—à­àŹĄàŹŒàŹżàŹ• àŹ•àŹ°àŹżàŹŹàŹŸ àŹàŹŹàŹ‚ àŹžà­‡àŹ—à­àŹĄàŹŒàŹżàŹ•à­ àŹȘàŹ°àŹżàŹšàŹŸàŹłàŹšàŹŸ àŹ•àŹ°àŹżàŹŹàŹŸ"</string>
+    <string name="permission_call_logs_summary" msgid="7545243592757693321">"àŹ«à­‹àŹš àŹ•àŹČ àŹČàŹ—àŹ•à­ àŹȘàŹąàŹŒàŹżàŹŹàŹŸ àŹàŹŹàŹ‚ àŹČà­‡àŹ–àŹżàŹŹàŹŸ"</string>
+    <string name="permission_sms_summary" msgid="8499509535410068616">"SMS àŹźà­‡àŹžà­‡àŹœ àŹȘàŹ àŹŸàŹ‡àŹŹàŹŸ àŹàŹŹàŹ‚ àŹ­à­à­Ÿà­ àŹ•àŹ°àŹżàŹŹàŹŸ"</string>
+    <string name="permission_contacts_summary" msgid="2840800622763086808">"àŹ†àŹȘàŹŁàŹ™à­àŹ• àŹ•àŹŁà­àŹŸàŹŸàŹ•à­àŹŸàŹ—à­àŹĄàŹŒàŹżàŹ•à­ àŹ†àŹ•à­àŹžà­‡àŹž àŹ•àŹ°àŹżàŹŹàŹŸ"</string>
+    <string name="permission_calendar_summary" msgid="8430353935747336165">"àŹ†àŹȘàŹŁàŹ™à­àŹ• àŹ•à­‡àŹČà­‡àŹŁà­àŹĄàŹ°àŹ•à­ àŹ†àŹ•à­àŹžà­‡àŹž àŹ•àŹ°àŹżàŹŹàŹŸ"</string>
+    <string name="permission_microphone_summary" msgid="4862628553869973259">"àŹ…àŹĄàŹżàŹ“ àŹ°à­‡àŹ•àŹ°à­àŹĄ àŹ•àŹ°àŹżàŹŹàŹŸ"</string>
+    <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"àŹ†àŹ–àŹȘàŹŸàŹ–àŹ° àŹĄàŹżàŹ­àŹŸàŹ‡àŹžàŹ—à­àŹĄàŹŒàŹżàŹ•à­ àŹ–à­‹àŹœàŹżàŹŹàŹŸ, àŹ•àŹšà­‡àŹ•à­àŹŸ àŹ•àŹ°àŹżàŹŹàŹŸ àŹàŹŹàŹ‚ àŹžà­‡àŹ—à­àŹĄàŹŒàŹżàŹ•àŹ° àŹ†àŹȘà­‡àŹ•à­àŹ·àŹżàŹ• àŹ…àŹŹàŹžà­àŹ„àŹżàŹ€àŹżàŹ•à­ àŹšàŹżàŹ°à­àŹŠà­àŹ§àŹŸàŹ°àŹŁ àŹ•àŹ°àŹżàŹŹàŹŸ"</string>
+    <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"àŹ•àŹŁà­àŹŸàŹŸàŹ•à­àŹŸ, àŹźà­‡àŹžà­‡àŹœ àŹàŹŹàŹ‚ àŹ«àŹŸà­‹àŹ—à­àŹĄàŹŒàŹżàŹ• àŹȘàŹ°àŹż àŹžà­‚àŹšàŹšàŹŸ àŹžàŹźà­‡àŹ€ àŹžàŹźàŹžà­àŹ€ àŹŹàŹżàŹœà­àŹžàŹȘà­àŹ€àŹżàŹ•à­ àŹȘàŹąàŹŒàŹšà­àŹ€à­"</string>
+    <string name="permission_notifications_summary" msgid="2272810466047367030">"• àŹ•àŹŁà­àŹŸàŹŸàŹ•à­àŹŸ, àŹźà­‡àŹžà­‡àŹœ àŹàŹŹàŹ‚ àŹ«àŹŸà­‹ àŹȘàŹ°àŹż àŹžà­‚àŹšàŹšàŹŸ àŹžàŹźà­‡àŹ€ àŹžàŹźàŹžà­àŹ€ àŹŹàŹżàŹœà­àŹžàŹȘà­àŹ€àŹżàŹ•à­ àŹȘàŹąàŹŒàŹšà­àŹ€à­&lt;br/&gt;• àŹŹàŹżàŹœà­àŹžàŹȘà­àŹ€àŹż àŹȘàŹ àŹŸàŹšà­àŹ€à­&lt;br/&gt;&lt;br/&gt;àŹ†àŹȘàŹŁ àŹžà­‡àŹŸàŹżàŹ‚àŹž &gt; àŹŹàŹżàŹœà­àŹžàŹȘà­àŹ€àŹżàŹ—à­àŹĄàŹŒàŹżàŹ•àŹ°à­‡ àŹŻà­‡ àŹ•à­ŒàŹŁàŹžàŹż àŹžàŹźà­ŸàŹ°à­‡ àŹŹàŹżàŹœà­àŹžàŹȘà­àŹ€àŹżàŹ—à­àŹĄàŹŒàŹżàŹ• àŹȘàŹąàŹżàŹŹàŹŸ àŹàŹŹàŹ‚ àŹȘàŹ àŹŸàŹ‡àŹŹàŹŸ àŹȘàŹŸàŹ‡àŹ àŹàŹčàŹż àŹ†àŹȘàŹ° àŹ•à­àŹ·àŹźàŹ€àŹŸàŹ•à­ àŹȘàŹ°àŹżàŹšàŹŸàŹłàŹšàŹŸ àŹ•àŹ°àŹżàŹȘàŹŸàŹ°àŹżàŹŹà­‡à„€"</string>
     <string name="permission_app_streaming_summary" msgid="606923325679670624">"àŹ†àŹȘàŹŁàŹ™à­àŹ• àŹ«à­‹àŹšàŹ° àŹ†àŹȘà­àŹžàŹ•à­ àŹ·à­àŹŸà­àŹ°àŹżàŹź àŹ•àŹ°àŹšà­àŹ€à­"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
     <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"àŹ†àŹȘàŹŁàŹ™à­àŹ• àŹ«à­‹àŹšàŹ°à­ àŹ†àŹȘà­àŹž àŹàŹŹàŹ‚ àŹ…àŹšà­à­Ÿ àŹžàŹżàŹ·à­àŹŸàŹź àŹ«àŹżàŹšàŹ°àŹ—à­àŹĄàŹŒàŹżàŹ•à­ àŹ·à­àŹŸà­àŹ°àŹżàŹź àŹ•àŹ°àŹšà­àŹ€à­"</string>
diff --git a/packages/CompanionDeviceManager/res/values-pa/strings.xml b/packages/CompanionDeviceManager/res/values-pa/strings.xml
index 31f9ec9..445afa3 100644
--- a/packages/CompanionDeviceManager/res/values-pa/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-pa/strings.xml
@@ -56,28 +56,18 @@
     <string name="permission_nearby_devices" msgid="7530973297737123481">"àššàšœàšŒàšŠà©€àš•à©€ àšĄà©€àš”àšŸàšˆàšž"</string>
     <string name="permission_media_routing_control" msgid="5498639511586715253">"àšźà©€àšĄà©€àš† àš†àšŠàšŸàšȘà©à©±àšŸ àšŹàšŠàšČੋ"</string>
     <string name="permission_storage" msgid="6831099350839392343">"àš«àšŒà©‹àšŸà©‹àš†àš‚ àš…àš€à©‡ àšźà©€àšĄà©€àš†"</string>
-    <!-- no translation found for permission_notifications (4099418516590632909) -->
-    <skip />
+    <string name="permission_notifications" msgid="4099418516590632909">"àšžà©‚àššàššàšŸàš”àšŸàš‚"</string>
     <string name="permission_app_streaming" msgid="6009695219091526422">"àšàšȘàšŸàš‚"</string>
     <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"àšžàšŸà©àš°à©€àšźàšżà©°àš—"</string>
-    <!-- no translation found for permission_phone_summary (8246321093970051702) -->
-    <skip />
-    <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
-    <skip />
-    <!-- no translation found for permission_sms_summary (8499509535410068616) -->
-    <skip />
-    <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
-    <skip />
-    <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
-    <skip />
-    <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
-    <skip />
-    <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
-    <skip />
-    <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
-    <skip />
-    <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
-    <skip />
+    <string name="permission_phone_summary" msgid="8246321093970051702">"àš«àšŒà©‹àšš àš•àšŸàšČàšŸàš‚ àš•àš°àšš àš…àš€à©‡ àš‰àššà©àščàšŸàš‚ àšŠàšŸ àšȘà©àš°àšŹà©°àš§àšš àš•àš°àšš àšŠà©€ àš‡àšœàšŸàšœàšŒàš€ àšŠàšżàš“"</string>
+    <string name="permission_call_logs_summary" msgid="7545243592757693321">"àš«àšŒà©‹àšš àšŠà©‡ àš•àšŸàšČ àšČà©Œàš— àššà©‚à©° àšȘੜ੍àščàšŁ àš…àš€à©‡ àšČàšżàš–àšŁ àšŠà©€ àš‡àšœàšŸàšœàšŒàš€ àšŠàšżàš“"</string>
+    <string name="permission_sms_summary" msgid="8499509535410068616">"SMS àšžà©àššà©‡àščੇ àš­à©‡àšœàšŁ àš…àš€à©‡ àšŠà©‡àš–àšŁ àšŠà©€ àš‡àšœàšŸàšœàšŒàš€ àšŠàšżàš“"</string>
+    <string name="permission_contacts_summary" msgid="2840800622763086808">"àš†àšȘàšŁà©‡ àšžà©°àšȘàš°àš•àšŸàš‚ àš€à©±àš• àšȘàščà©à©°àšš àš•àš°àšš àšŠà©€ àš‡àšœàšŸàšœàšŒàš€ àšŠàšżàš“"</string>
+    <string name="permission_calendar_summary" msgid="8430353935747336165">"àš†àšȘàšŁà©‡ àš•à©ˆàšČà©°àšĄàš° àš€à©±àš• àšȘàščà©à©°àšš àš•àš°àšš àšŠà©€ àš‡àšœàšŸàšœàšŒàš€ àšŠàšżàš“"</string>
+    <string name="permission_microphone_summary" msgid="4862628553869973259">"àš†àšĄà©€àš“ àš°àšżàš•àšŸàš°àšĄ àš•àš°àšš àšŠà©€ àš‡àšœàšŸàšœàšŒàš€ àšŠàšżàš“"</string>
+    <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"àššàšœàšŒàšŠà©€àš•à©€ àšĄà©€àš”àšŸàšˆàšžàšŸàš‚ àššà©‚à©° àšČà©±àš­àšŁ, àš‰àššà©àščàšŸàš‚ àššàšŸàšČ àš•àššà©ˆàš•àšŸ àš•àš°àšš àš…àš€à©‡ àšžà©°àšŹà©°àš§àšżàš€ àšžàš„àšżàš€à©€ àššàšżàš°àš§àšŸàš°àšżàš€ àš•àš°àšš àšŠà©€ àš‡àšœàšŸàšœàšŒàš€ àšŠàšżàš“"</string>
+    <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"àšžàšŸàš°à©€àš†àš‚ àšžà©‚àššàššàšŸàš”àšŸàš‚ àšȘੜ੍àščàšš àšŠà©€ àš‡àšœàšŸàšœàšŒàš€ àšŠàšżàš“, àšœàšżàš”à©‡àš‚ àš•àšż àšžà©°àšȘàš°àš•àšŸàš‚, àšžà©àššà©‡àščàšżàš†àš‚ àš…àš€à©‡ àš«àšŒà©‹àšŸà©‹àš†àš‚ àšŠà©€ àšœàšŸàšŁàš•àšŸàš°à©€"</string>
+    <string name="permission_notifications_summary" msgid="2272810466047367030">"• àšžàšŸàš°à©€àš†àš‚ àšžà©‚àššàššàšŸàš”àšŸàš‚ àšȘੜ੍àščàšš àšŠà©€ àš‡àšœàšŸàšœàšŒàš€ àšŠàšżàš“, àšœàšżàš”à©‡àš‚ àš•àšż àšžà©°àšȘàš°àš•àšŸàš‚, àšžà©àššà©‡àščàšżàš†àš‚ àš…àš€à©‡ àš«àšŒà©‹àšŸà©‹àš†àš‚ àšŠà©€ àšœàšŸàšŁàš•àšŸàš°à©€&lt;br/&gt;• àšžà©‚àššàššàšŸàš”àšŸàš‚ àš­à©‡àšœàšŁ àšŠà©€ àš‡àšœàšŸàšœàšŒàš€ àšŠàšżàš“&lt;br/&gt;&lt;br/&gt;àš€à©àšžà©€àš‚ àšžà©ˆàšŸàšżà©°àš—àšŸàš‚ &gt; àšžà©‚àššàššàšŸàš”àšŸàš‚ àš”àšżà©±àšš àšœàšŸ àš•à©‡ àš•àšżàšžà©‡ àš”à©€ àš”à©‡àšČੇ àšžà©‚àššàššàšŸàš”àšŸàš‚ àššà©‚à©° àšȘੜ੍àščàšš àš…àš€à©‡ àš­à©‡àšœàšŁ àšŠà©€ àš‡àšž àšàšȘ àšŠà©€ àšŻà©‹àš—àš€àšŸ àšŠàšŸ àšȘà©àš°àšŹà©°àš§àšš àš•àš° àšžàš•àšŠà©‡ àščà©‹à„€"</string>
     <string name="permission_app_streaming_summary" msgid="606923325679670624">"àš†àšȘàšŁà©‡ àš«àšŒà©‹àšš àšŠà©€àš†àš‚ àšàšȘàšŸàš‚ àššà©‚à©° àšžàšŸà©àš°à©€àšź àš•àš°à©‹"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
     <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"àš†àšȘàšŁà©‡ àš«àšŒà©‹àšš àš€à©‹àš‚ àšàšȘàšŸàš‚ àš…àš€à©‡ àščà©‹àš° àšžàšżàšžàšŸàšź àšžà©°àšŹà©°àš§à©€ àš”àšżàšžàšŒà©‡àšžàšŒàš€àšŸàš”àšŸàš‚ àššà©‚à©° àšžàšŸà©àš°à©€àšź àš•àš°à©‹"</string>
diff --git a/packages/CompanionDeviceManager/res/values-pl/strings.xml b/packages/CompanionDeviceManager/res/values-pl/strings.xml
index abffff1..aec7b68 100644
--- a/packages/CompanionDeviceManager/res/values-pl/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-pl/strings.xml
@@ -56,28 +56,18 @@
     <string name="permission_nearby_devices" msgid="7530973297737123481">"Urządzenia w pobliĆŒu"</string>
     <string name="permission_media_routing_control" msgid="5498639511586715253">"ZmieƄ wyjƛcie multimediów"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Zdjęcia i multimedia"</string>
-    <!-- no translation found for permission_notifications (4099418516590632909) -->
-    <skip />
+    <string name="permission_notifications" msgid="4099418516590632909">"Powiadomienia"</string>
     <string name="permission_app_streaming" msgid="6009695219091526422">"Aplikacje"</string>
     <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Strumieniowanie"</string>
-    <!-- no translation found for permission_phone_summary (8246321093970051702) -->
-    <skip />
-    <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
-    <skip />
-    <!-- no translation found for permission_sms_summary (8499509535410068616) -->
-    <skip />
-    <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
-    <skip />
-    <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
-    <skip />
-    <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
-    <skip />
-    <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
-    <skip />
-    <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
-    <skip />
-    <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
-    <skip />
+    <string name="permission_phone_summary" msgid="8246321093970051702">"Nawiązywanie poƂączeƄ telefonicznych i zarządzanie nimi"</string>
+    <string name="permission_call_logs_summary" msgid="7545243592757693321">"Odczytywanie i zapisywanie rejestru poƂączeƄ telefonicznych"</string>
+    <string name="permission_sms_summary" msgid="8499509535410068616">"WysyƂanie i wyƛwietlanie SMS‑ów"</string>
+    <string name="permission_contacts_summary" msgid="2840800622763086808">"Dostęp do kontaktów"</string>
+    <string name="permission_calendar_summary" msgid="8430353935747336165">"Dostęp do kalendarza"</string>
+    <string name="permission_microphone_summary" msgid="4862628553869973259">"Nagrywanie dĆșwięku"</string>
+    <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Znajdowanie urządzeƄ w pobliĆŒu, okreƛlanie ich względnego poƂoĆŒenia oraz Ƃączenie się z nimi"</string>
+    <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Odczytywanie wszystkich powiadomieƄ, w tym informacji takich jak kontakty, wiadomoƛci i zdjęcia"</string>
+    <string name="permission_notifications_summary" msgid="2272810466047367030">"• Odczytywanie wszystkich powiadomieƄ, w tym informacji takich jak kontakty, wiadomoƛci i zdjęcia&lt;br/&gt;• WysyƂanie powiadomieƄ&lt;br/&gt;&lt;br/&gt;W kaĆŒdej chwili moĆŒesz zmienić uprawnienia tej aplikacji do odczytywania i wysyƂania powiadomieƄ, klikając Ustawienia &gt; Powiadomienia."</string>
     <string name="permission_app_streaming_summary" msgid="606923325679670624">"Odtwarzaj strumieniowo aplikacje z telefonu"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
     <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Aplikacje do odtwarzania strumieniowego i inne funkcje systemowe na Twoim telefonie"</string>
diff --git a/packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml b/packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml
index a95dcb6..ef1d6cd 100644
--- a/packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml
@@ -56,28 +56,18 @@
     <string name="permission_nearby_devices" msgid="7530973297737123481">"Dispositivos por perto"</string>
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Mudar saída de mídia"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Fotos e mídia"</string>
-    <!-- no translation found for permission_notifications (4099418516590632909) -->
-    <skip />
+    <string name="permission_notifications" msgid="4099418516590632909">"Notificações"</string>
     <string name="permission_app_streaming" msgid="6009695219091526422">"Apps"</string>
     <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Streaming"</string>
-    <!-- no translation found for permission_phone_summary (8246321093970051702) -->
-    <skip />
-    <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
-    <skip />
-    <!-- no translation found for permission_sms_summary (8499509535410068616) -->
-    <skip />
-    <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
-    <skip />
-    <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
-    <skip />
-    <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
-    <skip />
-    <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
-    <skip />
-    <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
-    <skip />
-    <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
-    <skip />
+    <string name="permission_phone_summary" msgid="8246321093970051702">"Fazer e gerenciar ligações telefônicas"</string>
+    <string name="permission_call_logs_summary" msgid="7545243592757693321">"Ler e gravar o registro de chamadas telefônicas"</string>
+    <string name="permission_sms_summary" msgid="8499509535410068616">"Enviar e visualizar mensagens SMS"</string>
+    <string name="permission_contacts_summary" msgid="2840800622763086808">"Acessar seus contatos"</string>
+    <string name="permission_calendar_summary" msgid="8430353935747336165">"Acessar sua agenda"</string>
+    <string name="permission_microphone_summary" msgid="4862628553869973259">"Gravar áudio"</string>
+    <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Encontrar, determinar o posicionamento relativo e se conectar a dispositivos por perto"</string>
+    <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Ler todas as notificações, incluindo informações como contatos, mensagens e fotos"</string>
+    <string name="permission_notifications_summary" msgid="2272810466047367030">"• Ler todas as notificações, incluindo informações como, por exemplo, contatos, mensagens e fotos&lt;br/&gt;• Enviar notificações&lt;br/&gt;&lt;br/&gt;Você pode gerenciar a capacidade deste app de ler e enviar notificações a qualquer momento em \"Configurações\" e \"Notificações\"."</string>
     <string name="permission_app_streaming_summary" msgid="606923325679670624">"Fazer transmissão dos apps no seu smartphone"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
     <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Fazer streaming de apps e outros recursos do sistema pelo smartphone"</string>
diff --git a/packages/CompanionDeviceManager/res/values-pt/strings.xml b/packages/CompanionDeviceManager/res/values-pt/strings.xml
index a95dcb6..ef1d6cd 100644
--- a/packages/CompanionDeviceManager/res/values-pt/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-pt/strings.xml
@@ -56,28 +56,18 @@
     <string name="permission_nearby_devices" msgid="7530973297737123481">"Dispositivos por perto"</string>
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Mudar saída de mídia"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Fotos e mídia"</string>
-    <!-- no translation found for permission_notifications (4099418516590632909) -->
-    <skip />
+    <string name="permission_notifications" msgid="4099418516590632909">"Notificações"</string>
     <string name="permission_app_streaming" msgid="6009695219091526422">"Apps"</string>
     <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Streaming"</string>
-    <!-- no translation found for permission_phone_summary (8246321093970051702) -->
-    <skip />
-    <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
-    <skip />
-    <!-- no translation found for permission_sms_summary (8499509535410068616) -->
-    <skip />
-    <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
-    <skip />
-    <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
-    <skip />
-    <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
-    <skip />
-    <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
-    <skip />
-    <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
-    <skip />
-    <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
-    <skip />
+    <string name="permission_phone_summary" msgid="8246321093970051702">"Fazer e gerenciar ligações telefônicas"</string>
+    <string name="permission_call_logs_summary" msgid="7545243592757693321">"Ler e gravar o registro de chamadas telefônicas"</string>
+    <string name="permission_sms_summary" msgid="8499509535410068616">"Enviar e visualizar mensagens SMS"</string>
+    <string name="permission_contacts_summary" msgid="2840800622763086808">"Acessar seus contatos"</string>
+    <string name="permission_calendar_summary" msgid="8430353935747336165">"Acessar sua agenda"</string>
+    <string name="permission_microphone_summary" msgid="4862628553869973259">"Gravar áudio"</string>
+    <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Encontrar, determinar o posicionamento relativo e se conectar a dispositivos por perto"</string>
+    <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Ler todas as notificações, incluindo informações como contatos, mensagens e fotos"</string>
+    <string name="permission_notifications_summary" msgid="2272810466047367030">"• Ler todas as notificações, incluindo informações como, por exemplo, contatos, mensagens e fotos&lt;br/&gt;• Enviar notificações&lt;br/&gt;&lt;br/&gt;Você pode gerenciar a capacidade deste app de ler e enviar notificações a qualquer momento em \"Configurações\" e \"Notificações\"."</string>
     <string name="permission_app_streaming_summary" msgid="606923325679670624">"Fazer transmissão dos apps no seu smartphone"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
     <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Fazer streaming de apps e outros recursos do sistema pelo smartphone"</string>
diff --git a/packages/CompanionDeviceManager/res/values-ro/strings.xml b/packages/CompanionDeviceManager/res/values-ro/strings.xml
index a89da9dc..d2c3099 100644
--- a/packages/CompanionDeviceManager/res/values-ro/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ro/strings.xml
@@ -56,28 +56,18 @@
     <string name="permission_nearby_devices" msgid="7530973297737123481">"Dispozitive din apropiere"</string>
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Schimbă ieșirea media"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Fotografii și media"</string>
-    <!-- no translation found for permission_notifications (4099418516590632909) -->
-    <skip />
+    <string name="permission_notifications" msgid="4099418516590632909">"Notificări"</string>
     <string name="permission_app_streaming" msgid="6009695219091526422">"Aplicații"</string>
     <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Streaming"</string>
-    <!-- no translation found for permission_phone_summary (8246321093970051702) -->
-    <skip />
-    <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
-    <skip />
-    <!-- no translation found for permission_sms_summary (8499509535410068616) -->
-    <skip />
-    <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
-    <skip />
-    <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
-    <skip />
-    <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
-    <skip />
-    <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
-    <skip />
-    <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
-    <skip />
-    <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
-    <skip />
+    <string name="permission_phone_summary" msgid="8246321093970051702">"Să inițieze și să gestioneze apeluri telefonice"</string>
+    <string name="permission_call_logs_summary" msgid="7545243592757693321">"Să citească și să scrie jurnalul de apeluri telefonice"</string>
+    <string name="permission_sms_summary" msgid="8499509535410068616">"Să trimită și să vadă SMS-urile"</string>
+    <string name="permission_contacts_summary" msgid="2840800622763086808">"Să acceseze agenda"</string>
+    <string name="permission_calendar_summary" msgid="8430353935747336165">"Să acceseze calendarul"</string>
+    <string name="permission_microphone_summary" msgid="4862628553869973259">"Să înregistreze conținut audio"</string>
+    <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Să găsească, să se conecteze la dispozitivele apropiate și să determine poziția relativă a acestora"</string>
+    <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Să citească toate notificările, inclusiv informații cum ar fi agenda, mesajele și fotografiile"</string>
+    <string name="permission_notifications_summary" msgid="2272810466047367030">"• Să citească toate notificările, inclusiv informații cum ar fi agenda, mesajele și fotografiile&lt;br/&gt;• Să trimită notificări&lt;br/&gt;&lt;br/&gt;Poți să gestionezi oricând permisiunea acestei aplicații de a citi și trimite notificări în Setări &gt; Notificări."</string>
     <string name="permission_app_streaming_summary" msgid="606923325679670624">"Să redea în stream aplicațiile telefonului"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
     <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Redă în stream conținut din aplicații și alte funcții de sistem de pe telefon"</string>
diff --git a/packages/CompanionDeviceManager/res/values-ru/strings.xml b/packages/CompanionDeviceManager/res/values-ru/strings.xml
index 9cd823f..77a2c465 100644
--- a/packages/CompanionDeviceManager/res/values-ru/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ru/strings.xml
@@ -56,28 +56,18 @@
     <string name="permission_nearby_devices" msgid="7530973297737123481">"ĐŁŃŃ‚Ń€ĐŸĐčстĐČа ĐżĐŸĐ±Đ»ĐžĐ·ĐŸŃŃ‚Đž"</string>
     <string name="permission_media_routing_control" msgid="5498639511586715253">"ĐĄĐŒĐ”ĐœĐ° ĐžŃŃ‚ĐŸŃ‡ĐœĐžĐșа ĐČыĐČĐŸĐŽĐ° ĐŒĐ”ĐŽĐžĐ°"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Đ€ĐŸŃ‚ĐŸĐłŃ€Đ°Ń„ĐžĐž Đž ĐŒĐ”ĐŽĐžĐ°Ń„Đ°Đčлы"</string>
-    <!-- no translation found for permission_notifications (4099418516590632909) -->
-    <skip />
+    <string name="permission_notifications" msgid="4099418516590632909">"ĐŁĐČĐ”ĐŽĐŸĐŒĐ»Đ”ĐœĐžŃ"</string>
     <string name="permission_app_streaming" msgid="6009695219091526422">"ĐŸŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžŃ"</string>
     <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"ĐŸĐŸŃ‚ĐŸĐșĐŸĐČая пДрДЎача"</string>
-    <!-- no translation found for permission_phone_summary (8246321093970051702) -->
-    <skip />
-    <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
-    <skip />
-    <!-- no translation found for permission_sms_summary (8499509535410068616) -->
-    <skip />
-    <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
-    <skip />
-    <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
-    <skip />
-    <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
-    <skip />
-    <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
-    <skip />
-    <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
-    <skip />
-    <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
-    <skip />
+    <string name="permission_phone_summary" msgid="8246321093970051702">"ĐĄĐŸĐČĐ”Ń€ŃˆĐ”ĐœĐžĐ” Ń‚Đ”Đ»Đ”Ń„ĐŸĐœĐœŃ‹Ń… Đ·ĐČĐŸĐœĐșĐŸĐČ Đž упраĐČĐ»Đ”ĐœĐžĐ” ĐžĐŒĐž"</string>
+    <string name="permission_call_logs_summary" msgid="7545243592757693321">"ĐŸŃ€ĐŸŃĐŒĐŸŃ‚Ń€ спОсĐșа Ń‚Đ”Đ»Đ”Ń„ĐŸĐœĐœŃ‹Ń… ĐČŃ‹Đ·ĐŸĐČĐŸĐČ Đž ŃĐŸĐ·ĐŽĐ°ĐœĐžĐ” запОсДĐč ĐČ ĐœĐ”ĐŒ"</string>
+    <string name="permission_sms_summary" msgid="8499509535410068616">"ОтпраĐČĐșа Đž ĐżŃ€ĐŸŃĐŒĐŸŃ‚Ń€ SMS-ŃĐŸĐŸĐ±Ń‰Đ”ĐœĐžĐč"</string>
+    <string name="permission_contacts_summary" msgid="2840800622763086808">"Đ”ĐŸŃŃ‚ŃƒĐż Đș ĐșĐŸĐœŃ‚Đ°ĐșŃ‚Đ°ĐŒ"</string>
+    <string name="permission_calendar_summary" msgid="8430353935747336165">"Đ”ĐŸŃŃ‚ŃƒĐż Đș ĐșĐ°Đ»Đ”ĐœĐŽĐ°Ń€ŃŽ"</string>
+    <string name="permission_microphone_summary" msgid="4862628553869973259">"Запось Đ°ŃƒĐŽĐžĐŸ"</string>
+    <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"ĐŸĐŸĐžŃĐș ŃƒŃŃ‚Ń€ĐŸĐčстĐČ ĐżĐŸĐ±Đ»ĐžĐ·ĐŸŃŃ‚Đž, ĐżĐŸĐŽĐșĐ»ŃŽŃ‡Đ”ĐœĐžĐ” Đș ĐœĐžĐŒ Đž ĐŸĐżŃ€Đ”ĐŽĐ”Đ»Đ”ĐœĐžĐ” ох ĐŸŃ‚ĐœĐŸŃĐžŃ‚Đ”Đ»ŃŒĐœĐŸĐłĐŸ ĐŒĐ”ŃŃ‚ĐŸĐżĐŸĐ»ĐŸĐ¶Đ”ĐœĐžŃ"</string>
+    <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Đ§Ń‚Đ”ĐœĐžĐ” ĐČсДх уĐČĐ”ĐŽĐŸĐŒĐ»Đ”ĐœĐžĐč, ĐČ Ń‚ĐŸĐŒ чОслД сĐČĐ”ĐŽĐ”ĐœĐžĐč ĐŸ ĐșĐŸĐœŃ‚Đ°Đșтах, ŃĐŸĐŸĐ±Ń‰Đ”ĐœĐžŃŃ… Đž Ń„ĐŸŃ‚ĐŸĐłŃ€Đ°Ń„ĐžŃŃ…"</string>
+    <string name="permission_notifications_summary" msgid="2272810466047367030">"• Đ§Ń‚Đ”ĐœĐžĐ” ĐČсДх уĐČĐ”ĐŽĐŸĐŒĐ»Đ”ĐœĐžĐč, ĐČ Ń‚ĐŸĐŒ чОслД сĐČĐ”ĐŽĐ”ĐœĐžĐč ĐŸ ĐșĐŸĐœŃ‚Đ°Đșтах, ŃĐŸĐŸĐ±Ń‰Đ”ĐœĐžŃŃ… Đž Ń„ĐŸŃ‚ĐŸĐłŃ€Đ°Ń„ĐžŃŃ…&lt;br/&gt;• ОтпраĐČĐșа уĐČĐ”ĐŽĐŸĐŒĐ»Đ”ĐœĐžĐč&lt;br/&gt;&lt;br/&gt;В разЎДлД \"ĐĐ°ŃŃ‚Ń€ĐŸĐčĐșĐž &gt; ĐŁĐČĐ”ĐŽĐŸĐŒĐ»Đ”ĐœĐžŃ\" ĐČы ĐŒĐŸĐ¶Đ”Ń‚Đ” ĐČ Đ»ŃŽĐ±ĐŸĐ” ĐČŃ€Đ”ĐŒŃ Ń€Đ°Đ·Ń€Đ”ŃˆĐžŃ‚ŃŒ ОлО Đ·Đ°ĐżŃ€Đ”Ń‚ĐžŃ‚ŃŒ ŃŃ‚ĐŸĐŒŃƒ ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžŃŽ чотать Đž ĐŸŃ‚ĐżŃ€Đ°ĐČĐ»ŃŃ‚ŃŒ уĐČĐ”ĐŽĐŸĐŒĐ»Đ”ĐœĐžŃ."</string>
     <string name="permission_app_streaming_summary" msgid="606923325679670624">"ĐąŃ€Đ°ĐœŃĐ»ŃŃ†ĐžŃ ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžĐč с Ń‚Đ”Đ»Đ”Ń„ĐŸĐœĐ°."</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
     <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"ĐąŃ€Đ°ĐœŃĐ»ŃŃ†ĐžŃ ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžĐč Đž ŃĐžŃŃ‚Đ”ĐŒĐœŃ‹Ń… Ń„ŃƒĐœĐșцоĐč с Ń‚Đ”Đ»Đ”Ń„ĐŸĐœĐ°"</string>
diff --git a/packages/CompanionDeviceManager/res/values-si/strings.xml b/packages/CompanionDeviceManager/res/values-si/strings.xml
index d83c1f1..1ba83fd 100644
--- a/packages/CompanionDeviceManager/res/values-si/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-si/strings.xml
@@ -56,28 +56,18 @@
     <string name="permission_nearby_devices" msgid="7530973297737123481">"අවට උඎාංග"</string>
     <string name="permission_media_routing_control" msgid="5498639511586715253">"ඞාධ්‍à¶ș à¶Žà·Š‍à¶»à¶­à·’à¶Żà·à¶±à¶ș වෙනස් කරන්න"</string>
     <string name="permission_storage" msgid="6831099350839392343">"à¶Ąà·à¶șාරූඎ සහ ඞාධ්‍à¶ș"</string>
-    <!-- no translation found for permission_notifications (4099418516590632909) -->
-    <skip />
+    <string name="permission_notifications" msgid="4099418516590632909">"à¶Żà·à¶±à·”à¶žà·Šà¶Żà·“à¶žà·Š"</string>
     <string name="permission_app_streaming" msgid="6009695219091526422">"à¶șà·™à¶Żà·”à¶žà·Š"</string>
     <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"à¶Žà·Š‍රවාහ කිරීඞ"</string>
-    <!-- no translation found for permission_phone_summary (8246321093970051702) -->
-    <skip />
-    <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
-    <skip />
-    <!-- no translation found for permission_sms_summary (8499509535410068616) -->
-    <skip />
-    <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
-    <skip />
-    <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
-    <skip />
-    <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
-    <skip />
-    <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
-    <skip />
-    <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
-    <skip />
-    <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
-    <skip />
+    <string name="permission_phone_summary" msgid="8246321093970051702">"à¶Żà·”à¶»à¶šà¶źà¶± ඇඞතුඞ් ගැනීඞ සහ කළඞනාකරණà¶ș කිරීඞ"</string>
+    <string name="permission_call_logs_summary" msgid="7545243592757693321">"à¶Żà·”à¶»à¶šà¶źà¶± ඇඞතුඞ් ගොගà¶ș කිà¶șවන්න සහ ගිà¶șන්න"</string>
+    <string name="permission_sms_summary" msgid="8499509535410068616">"SMS ඎණිවිඩ à¶șැවීඞ සහ බැගීඞ"</string>
+    <string name="permission_contacts_summary" msgid="2840800622763086808">"ඔබේ සඞ්බන්ධතා වෙත à¶Žà·Š‍රවේශ වන්න"</string>
+    <string name="permission_calendar_summary" msgid="8430353935747336165">"ඔබේ à¶Żà·’à¶± à¶Żà¶»à·Šà·à¶±à¶șà¶§ à¶Žà·Š‍රවේශ වන්න"</string>
+    <string name="permission_microphone_summary" msgid="4862628553869973259">"ශ්‍රව්‍à¶ș ඎටිගත කරන්න"</string>
+    <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"අවට උඎාංගවග සාඎේක්ෂ à¶Žà·’à·„à·’à¶§à·“à¶ž සොà¶șා ගන්න, සඞ්බන්ධ වන්න, සහ තීරණà¶ș කරන්න"</string>
+    <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"සඞ්බන්ධතා, ඎණිවිඩ, සහ à¶Ąà·à¶șාරූඎ වැනි තොරතුරු ඇතුළුව, සිà¶șගු à¶Żà·à¶±à·”à¶žà·Šà¶Żà·“à¶žà·Š කිà¶șවන්න"</string>
+    <string name="permission_notifications_summary" msgid="2272810466047367030">"• සඞ්බන්ධතා, ඎණිවිඩ, සහ à¶Ąà·à¶șාරූඎ වැනි තතු ඇතුළුව, සිà¶șගු à¶Żà·à¶±à·”à¶žà·Šà¶Żà·“à¶žà·Š කිà¶șවන්න&lt;br/&gt;• à¶Żà·à¶±à·”à¶žà·Šà¶Żà·“à¶žà·Š à¶șවන්න&lt;br/&gt;&lt;br/&gt;ඔබට සැකසීඞ් &gt; à¶Żà·à¶±à·”à¶žà·Šà¶Żà·“à¶žà·Š තුළ ඕනෑඞ වේගාවක ඞෙඞ à¶șà·™à¶Żà·”à¶žà¶§ à¶Żà·à¶±à·”à¶žà·Šà¶Żà·“à¶žà·Š කිà¶șවීඞට සහ à¶șැවීඞට ඇති හැකිà¶șාව කළඞනාකරණà¶ș කළ හැක."</string>
     <string name="permission_app_streaming_summary" msgid="606923325679670624">"ඔබේ à¶Żà·”à¶»à¶šà¶źà¶±à¶șේ à¶șà·™à¶Żà·”à¶žà·Š à¶Žà·Š‍රවාහ කරන්න"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
     <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"ඔබේ à¶Żà·”à¶»à¶šà¶źà¶±à¶șෙන් à¶șà·™à¶Żà·”à¶žà·Š සහ අනෙකුත් à¶Žà¶Żà·Šà¶°à¶­à·’ විශේෂාංග à¶Žà·Š‍රවාහ කරන්න"</string>
diff --git a/packages/CompanionDeviceManager/res/values-sk/strings.xml b/packages/CompanionDeviceManager/res/values-sk/strings.xml
index 55e6daf8..637a240 100644
--- a/packages/CompanionDeviceManager/res/values-sk/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sk/strings.xml
@@ -56,28 +56,18 @@
     <string name="permission_nearby_devices" msgid="7530973297737123481">"Zariadenia v okolí"</string>
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Zmena výstupu médií"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Fotky a médiá"</string>
-    <!-- no translation found for permission_notifications (4099418516590632909) -->
-    <skip />
+    <string name="permission_notifications" msgid="4099418516590632909">"Upozornenia"</string>
     <string name="permission_app_streaming" msgid="6009695219091526422">"Aplikácie"</string>
     <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Streaming"</string>
-    <!-- no translation found for permission_phone_summary (8246321093970051702) -->
-    <skip />
-    <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
-    <skip />
-    <!-- no translation found for permission_sms_summary (8499509535410068616) -->
-    <skip />
-    <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
-    <skip />
-    <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
-    <skip />
-    <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
-    <skip />
-    <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
-    <skip />
-    <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
-    <skip />
-    <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
-    <skip />
+    <string name="permission_phone_summary" msgid="8246321093970051702">"Volanie a správa telefonických hovorov"</string>
+    <string name="permission_call_logs_summary" msgid="7545243592757693321">"Čítanie a zapisovanie do zoznamu telefonických hovorov"</string>
+    <string name="permission_sms_summary" msgid="8499509535410068616">"Posielanie a zobrazovanie správ SMS"</string>
+    <string name="permission_contacts_summary" msgid="2840800622763086808">"Prístup ku kontaktom"</string>
+    <string name="permission_calendar_summary" msgid="8430353935747336165">"Prístup ku kalendáru"</string>
+    <string name="permission_microphone_summary" msgid="4862628553869973259">"Nahrávanie zvuku"</string>
+    <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"VyhÄŸadajte zariadenia v okolí, pripojte sa k nim a určite ich relatívnu polohu"</string>
+    <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Čítajte všetky upozornenia vrátane informácií, ako sú kontakty, správy a fotky"</string>
+    <string name="permission_notifications_summary" msgid="2272810466047367030">"• Čítajte všetky upozornenia vrátane informácií, ako sú kontakty, správy a fotky&lt;br/&gt;• Odosielajte upozornenia&lt;br/&gt;&lt;br/&gt;SchopnosĆ„ tejto aplikácie čítaĆ„ a odosielaĆ„ upozornenia môĆŸete kedykoÄŸvek spravovaĆ„ v sekcii Nastavenia &gt; Upozornenia."</string>
     <string name="permission_app_streaming_summary" msgid="606923325679670624">"StreamovaĆ„ aplikácie telefónu"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
     <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Streaming aplikácii a Äalších systémových funkcií zo zariadenia"</string>
diff --git a/packages/CompanionDeviceManager/res/values-sl/strings.xml b/packages/CompanionDeviceManager/res/values-sl/strings.xml
index 097b7b6..b03c27a 100644
--- a/packages/CompanionDeviceManager/res/values-sl/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sl/strings.xml
@@ -56,28 +56,18 @@
     <string name="permission_nearby_devices" msgid="7530973297737123481">"Naprave v bliĆŸini"</string>
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Spreminjanje izhoda za predstavnost"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Fotografije in predstavnost"</string>
-    <!-- no translation found for permission_notifications (4099418516590632909) -->
-    <skip />
+    <string name="permission_notifications" msgid="4099418516590632909">"Obvestila"</string>
     <string name="permission_app_streaming" msgid="6009695219091526422">"Aplikacije"</string>
     <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Pretočno predvajanje"</string>
-    <!-- no translation found for permission_phone_summary (8246321093970051702) -->
-    <skip />
-    <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
-    <skip />
-    <!-- no translation found for permission_sms_summary (8499509535410068616) -->
-    <skip />
-    <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
-    <skip />
-    <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
-    <skip />
-    <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
-    <skip />
-    <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
-    <skip />
-    <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
-    <skip />
-    <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
-    <skip />
+    <string name="permission_phone_summary" msgid="8246321093970051702">"Opravljanje in upravljanje telefonskih klicev"</string>
+    <string name="permission_call_logs_summary" msgid="7545243592757693321">"Branje in zapisovanje dnevnika klicev v telefonu"</string>
+    <string name="permission_sms_summary" msgid="8499509535410068616">"Pošiljanje in ogled sporočil SMS"</string>
+    <string name="permission_contacts_summary" msgid="2840800622763086808">"Dostop do stikov"</string>
+    <string name="permission_calendar_summary" msgid="8430353935747336165">"Dostop do koledarja"</string>
+    <string name="permission_microphone_summary" msgid="4862628553869973259">"Snemanje zvoka"</string>
+    <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Iskanje naprav v bliĆŸini, povezovanje z njimi in določanje njihovega relativnega poloĆŸaja"</string>
+    <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Branje vseh obvestil, vključno s podatki, kot so stiki, sporočila in fotografije"</string>
+    <string name="permission_notifications_summary" msgid="2272810466047367030">"• Branje vseh obvestil, vključno s podatki, kot so stiki, sporočila in fotografije&lt;br/&gt;• Pošiljanje obvestil&lt;br/&gt;&lt;br/&gt;ZmoĆŸnost branja in pošiljanja obvestil za to aplikacijo lahko kadar koli upravljate v meniju »Nastavitve« &gt; »Obvestila«."</string>
     <string name="permission_app_streaming_summary" msgid="606923325679670624">"Pretočno predvajanje aplikacij telefona"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
     <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Pretočno predvajanje aplikacij in drugih sistemskih funkcij iz telefona"</string>
diff --git a/packages/CompanionDeviceManager/res/values-sq/strings.xml b/packages/CompanionDeviceManager/res/values-sq/strings.xml
index 416de58..5eeeeb8 100644
--- a/packages/CompanionDeviceManager/res/values-sq/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sq/strings.xml
@@ -54,36 +54,24 @@
     <string name="permission_microphone" msgid="2152206421428732949">"Mikrofoni"</string>
     <string name="permission_call_logs" msgid="5546761417694586041">"Evidencat e telefonatave"</string>
     <string name="permission_nearby_devices" msgid="7530973297737123481">"Pajisjet në afërsi"</string>
-    <!-- no translation found for permission_media_routing_control (5498639511586715253) -->
-    <skip />
+    <string name="permission_media_routing_control" msgid="5498639511586715253">"Të ndryshojë daljen e medias"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Fotografitë dhe media"</string>
-    <!-- no translation found for permission_notifications (4099418516590632909) -->
-    <skip />
+    <string name="permission_notifications" msgid="4099418516590632909">"Njoftimet"</string>
     <string name="permission_app_streaming" msgid="6009695219091526422">"Aplikacionet"</string>
     <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Transmetimi"</string>
-    <!-- no translation found for permission_phone_summary (8246321093970051702) -->
-    <skip />
-    <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
-    <skip />
-    <!-- no translation found for permission_sms_summary (8499509535410068616) -->
-    <skip />
-    <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
-    <skip />
-    <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
-    <skip />
-    <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
-    <skip />
-    <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
-    <skip />
-    <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
-    <skip />
-    <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
-    <skip />
+    <string name="permission_phone_summary" msgid="8246321093970051702">"Të kryejë dhe të menaxhojë telefonatat"</string>
+    <string name="permission_call_logs_summary" msgid="7545243592757693321">"Të lexojë dhe shkruajë në evidencën e telefonatave"</string>
+    <string name="permission_sms_summary" msgid="8499509535410068616">"Të dërgojë dhe të shikojë mesazhet SMS"</string>
+    <string name="permission_contacts_summary" msgid="2840800622763086808">"Të qaset te kontaktet e tua"</string>
+    <string name="permission_calendar_summary" msgid="8430353935747336165">"Të qaset te kalendari yt"</string>
+    <string name="permission_microphone_summary" msgid="4862628553869973259">"Të regjistrojë audion"</string>
+    <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Të gjejë, të lidhet dhe të përcaktojë pozicionin e përafërt të pajisjeve në afërsi"</string>
+    <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Të lexojë të gjitha njoftimet, duke përfshirë informacione si kontaktet, mesazhet dhe fotografitë"</string>
+    <string name="permission_notifications_summary" msgid="2272810466047367030">"• Të lexojë të gjitha njoftimet, duke përfshirë informacione si kontaktet, mesazhet dhe fotografitë&lt;br/&gt;• Të dërgojë njoftime&lt;br/&gt;&lt;br/&gt;Mund ta menaxhosh aftësinë e këtij aplikacioni që të lexojë dhe të dërgojë njoftime në çdo kohë te Cilësimet &gt; Njoftimet."</string>
     <string name="permission_app_streaming_summary" msgid="606923325679670624">"Transmeto aplikacionet e telefonit tënd"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
     <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Transmeto aplikacionet dhe veçoritë e tjera të sistemit nga telefoni yt"</string>
-    <!-- no translation found for permission_media_routing_control_summary (2714631092321412250) -->
-    <skip />
+    <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"Të qaset te një listë e pajisjeve të disponueshme dhe të kontrollojë se cila transmeton audion ose videon nga aplikacionet e tjera"</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"telefon"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"tablet"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-sr/strings.xml b/packages/CompanionDeviceManager/res/values-sr/strings.xml
index 9b95769..2271092 100644
--- a/packages/CompanionDeviceManager/res/values-sr/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sr/strings.xml
@@ -56,28 +56,18 @@
     <string name="permission_nearby_devices" msgid="7530973297737123481">"ĐŁŃ€Đ”Ń’Đ°Ń˜Đž у Đ±Đ»ĐžĐ·ĐžĐœĐž"</string>
     <string name="permission_media_routing_control" msgid="5498639511586715253">"ĐŸŃ€ĐŸĐŒĐ”ĐœĐ° ĐŒĐ”ĐŽĐžŃ˜ŃĐșĐŸĐł Озлаза"</string>
     <string name="permission_storage" msgid="6831099350839392343">"ХлОĐșĐ” Đž ĐŒĐ”ĐŽĐžŃ˜Đž"</string>
-    <!-- no translation found for permission_notifications (4099418516590632909) -->
-    <skip />
+    <string name="permission_notifications" msgid="4099418516590632909">"ОбаĐČĐ”ŃˆŃ‚Đ”ŃšĐ°"</string>
     <string name="permission_app_streaming" msgid="6009695219091526422">"АплОĐșĐ°Ń†ĐžŃ˜Đ”"</string>
     <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"ĐĄŃ‚Ń€ĐžĐŒĐžĐœĐł"</string>
-    <!-- no translation found for permission_phone_summary (8246321093970051702) -->
-    <skip />
-    <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
-    <skip />
-    <!-- no translation found for permission_sms_summary (8499509535410068616) -->
-    <skip />
-    <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
-    <skip />
-    <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
-    <skip />
-    <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
-    <skip />
-    <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
-    <skip />
-    <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
-    <skip />
-    <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
-    <skip />
+    <string name="permission_phone_summary" msgid="8246321093970051702">"УпућоĐČањД Ń‚Đ”Đ»Đ”Ń„ĐŸĐœŃĐșох ĐżĐŸĐ·ĐžĐČа Đž упраĐČљањД ŃšĐžĐŒĐ°"</string>
+    <string name="permission_call_logs_summary" msgid="7545243592757693321">"ЧОтањД Đž пОсањД Đ”ĐČĐžĐŽĐ”ĐœŃ†ĐžŃ˜Đ” ĐżĐŸĐ·ĐžĐČа ĐœĐ° Ń‚Đ”Đ»Đ”Ń„ĐŸĐœŃƒ"</string>
+    <string name="permission_sms_summary" msgid="8499509535410068616">"ХлањД Đž прДглДЎ SMS ĐżĐŸŃ€ŃƒĐșа"</string>
+    <string name="permission_contacts_summary" msgid="2840800622763086808">"Проступ ĐșĐŸĐœŃ‚Đ°ĐșŃ‚ĐžĐŒĐ°"</string>
+    <string name="permission_calendar_summary" msgid="8430353935747336165">"Проступ ĐșĐ°Đ»Đ”ĐœĐŽĐ°Ń€Ńƒ"</string>
+    <string name="permission_microphone_summary" msgid="4862628553869973259">"ĐĄĐœĐžĐŒĐ°ŃšĐ” Đ·ĐČуĐșа"</string>
+    <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"ĐŸŃ€ĐŸĐœĐ°Đ»Đ°Đ¶Đ”ŃšĐ” ŃƒŃ€Đ”Ń’Đ°Ń˜Đ° у Đ±Đ»ĐžĐ·ĐžĐœĐž, утĐČрђоĐČањД ŃšĐžŃ…ĐŸĐČĐ” рДлатОĐČĐœĐ” ĐżĐŸĐ·ĐžŃ†ĐžŃ˜Đ” Đž ĐżĐŸĐČДзОĐČањД са ŃšĐžĐŒĐ°"</string>
+    <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"ЧОтањД сĐČох ĐŸĐ±Đ°ĐČĐ”ŃˆŃ‚Đ”ŃšĐ°, уĐșључујућо ĐžĐœŃ„ĐŸŃ€ĐŒĐ°Ń†ĐžŃ˜Đ° ĐżĐŸĐżŃƒŃ‚ ĐșĐŸĐœŃ‚Đ°Đșата, ĐżĐŸŃ€ŃƒĐșа Đž слОĐșа"</string>
+    <string name="permission_notifications_summary" msgid="2272810466047367030">"• ЧОтањД сĐČох ĐŸĐ±Đ°ĐČĐ”ŃˆŃ‚Đ”ŃšĐ°, уĐșључујућо ĐžĐœŃ„ĐŸŃ€ĐŒĐ°Ń†ĐžŃ˜Đ° ĐżĐŸĐżŃƒŃ‚ ĐșĐŸĐœŃ‚Đ°Đșата, ĐżĐŸŃ€ŃƒĐșа Đž слОĐșа&lt;br/&gt;• ХлањД ĐŸĐ±Đ°ĐČĐ”ŃˆŃ‚Đ”ŃšĐ°&lt;br/&gt;&lt;br/&gt;Да бОстД упраĐČљалО ĐŽĐŸĐ·ĐČĐŸĐ»Đ°ĐŒĐ° ĐŸĐČĐ” аплОĐșĐ°Ń†ĐžŃ˜Đ” за чОтањД Đž слањД ĐŸĐ±Đ°ĐČĐ”ŃˆŃ‚Đ”ŃšĐ°, ОЎОтД у ĐŸĐŸĐŽĐ”ŃˆĐ°ĐČања &gt; ОбаĐČĐ”ŃˆŃ‚Đ”ŃšĐ°."</string>
     <string name="permission_app_streaming_summary" msgid="606923325679670624">"ĐĄŃ‚Ń€ĐžĐŒŃƒŃ˜Ń‚Đ” аплОĐșĐ°Ń†ĐžŃ˜Đ” ĐœĐ° Ń‚Đ”Đ»Đ”Ń„ĐŸĐœŃƒ"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
     <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"ĐĄŃ‚Ń€ĐžĐŒŃƒŃ˜Ń‚Đ” аплОĐșĐ°Ń†ĐžŃ˜Đ” Đž ĐŽŃ€ŃƒĐłĐ” ŃĐžŃŃ‚Đ”ĐŒŃĐșĐ” Ń„ŃƒĐœĐșŃ†ĐžŃ˜Đ” са Ń‚Đ”Đ»Đ”Ń„ĐŸĐœĐ°"</string>
diff --git a/packages/CompanionDeviceManager/res/values-sv/strings.xml b/packages/CompanionDeviceManager/res/values-sv/strings.xml
index e17fb04..a893476 100644
--- a/packages/CompanionDeviceManager/res/values-sv/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sv/strings.xml
@@ -56,28 +56,18 @@
     <string name="permission_nearby_devices" msgid="7530973297737123481">"Enheter i närheten"</string>
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Ändra uppspelning av media"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Foton och media"</string>
-    <!-- no translation found for permission_notifications (4099418516590632909) -->
-    <skip />
+    <string name="permission_notifications" msgid="4099418516590632909">"Aviseringar"</string>
     <string name="permission_app_streaming" msgid="6009695219091526422">"Appar"</string>
     <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Streaming"</string>
-    <!-- no translation found for permission_phone_summary (8246321093970051702) -->
-    <skip />
-    <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
-    <skip />
-    <!-- no translation found for permission_sms_summary (8499509535410068616) -->
-    <skip />
-    <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
-    <skip />
-    <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
-    <skip />
-    <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
-    <skip />
-    <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
-    <skip />
-    <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
-    <skip />
-    <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
-    <skip />
+    <string name="permission_phone_summary" msgid="8246321093970051702">"Ringa och hantera telefonsamtal"</string>
+    <string name="permission_call_logs_summary" msgid="7545243592757693321">"Läsa och skriva samtalshistorik"</string>
+    <string name="permission_sms_summary" msgid="8499509535410068616">"Skicka och se sms"</string>
+    <string name="permission_contacts_summary" msgid="2840800622763086808">"Få tillgång till dina kontakter"</string>
+    <string name="permission_calendar_summary" msgid="8430353935747336165">"Få tillgång till din kalender"</string>
+    <string name="permission_microphone_summary" msgid="4862628553869973259">"Spela in ljud"</string>
+    <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Hitta, ansluta till och avgöra den relativa positionen för enheter i närheten"</string>
+    <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Läsa alla aviseringar, inklusive information som kontakter, meddelanden och foton"</string>
+    <string name="permission_notifications_summary" msgid="2272810466047367030">"• Läsa alla aviseringar, inklusive sådant som kontakter, meddelanden och foton&lt;br/&gt;• Skicka aviseringar&lt;br/&gt;&lt;br/&gt;Du kan hantera appens möjlighet att läsa och skicka aviseringar när du vill i Inställningar &gt; Aviseringar."</string>
     <string name="permission_app_streaming_summary" msgid="606923325679670624">"Streama telefonens appar"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
     <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Streama appar och andra systemfunktioner från din telefon"</string>
diff --git a/packages/CompanionDeviceManager/res/values-sw/strings.xml b/packages/CompanionDeviceManager/res/values-sw/strings.xml
index 59a6a0e..c35c407 100644
--- a/packages/CompanionDeviceManager/res/values-sw/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sw/strings.xml
@@ -54,36 +54,24 @@
     <string name="permission_microphone" msgid="2152206421428732949">"Maikrofoni"</string>
     <string name="permission_call_logs" msgid="5546761417694586041">"Rekodi za namba za simu"</string>
     <string name="permission_nearby_devices" msgid="7530973297737123481">"Vifaa vilivyo karibu"</string>
-    <!-- no translation found for permission_media_routing_control (5498639511586715253) -->
-    <skip />
+    <string name="permission_media_routing_control" msgid="5498639511586715253">"Badilisha tokeo la maudhui"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Picha na maudhui"</string>
-    <!-- no translation found for permission_notifications (4099418516590632909) -->
-    <skip />
+    <string name="permission_notifications" msgid="4099418516590632909">"Arifa"</string>
     <string name="permission_app_streaming" msgid="6009695219091526422">"Programu"</string>
     <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Kutiririsha maudhui"</string>
-    <!-- no translation found for permission_phone_summary (8246321093970051702) -->
-    <skip />
-    <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
-    <skip />
-    <!-- no translation found for permission_sms_summary (8499509535410068616) -->
-    <skip />
-    <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
-    <skip />
-    <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
-    <skip />
-    <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
-    <skip />
-    <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
-    <skip />
-    <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
-    <skip />
-    <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
-    <skip />
+    <string name="permission_phone_summary" msgid="8246321093970051702">"Kupiga na kudhibiti simu"</string>
+    <string name="permission_call_logs_summary" msgid="7545243592757693321">"Kusoma na kuandika rekodi ya namba za simu"</string>
+    <string name="permission_sms_summary" msgid="8499509535410068616">"Kutuma na kuona ujumbe wa SMS"</string>
+    <string name="permission_contacts_summary" msgid="2840800622763086808">"Kufikia anwani zako"</string>
+    <string name="permission_calendar_summary" msgid="8430353935747336165">"Kufikia kalenda yako"</string>
+    <string name="permission_microphone_summary" msgid="4862628553869973259">"Kurekodi sauti"</string>
+    <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Kutafuta, kuunganisha na kubaini nafasi ya makadirio ya vifaa vilivyo karibu"</string>
+    <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Kusoma arifa zote, ikiwa ni pamoja na maelezo kama vile anwani, ujumbe na picha"</string>
+    <string name="permission_notifications_summary" msgid="2272810466047367030">"• Soma arifa zote, ikiwa ni pamoja na maelezo kama vile anwani, ujumbe na picha&lt;br/&gt;• Tuma arifa&lt;br/&gt;&lt;br/&gt;Unaweza kudhibiti uwezo wa programu hii wa kusoma na kutuma arifa wakati wowote katika Mipangilio &gt; Arifa."</string>
     <string name="permission_app_streaming_summary" msgid="606923325679670624">"Tiririsha programu za simu yako"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
     <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Kutiririsha programu na vipengele vya mfumo kwenye simu yako"</string>
-    <!-- no translation found for permission_media_routing_control_summary (2714631092321412250) -->
-    <skip />
+    <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"Fikia orodha ya vifaa vinavyopatikana na udhibiti unavyotumia kutiririsha au kutuma maudhui ya sauti au video kutoka kwenye programu nyingine`"</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"simu"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"kompyuta kibao"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-ta/strings.xml b/packages/CompanionDeviceManager/res/values-ta/strings.xml
index 97d59fd..1efdca4 100644
--- a/packages/CompanionDeviceManager/res/values-ta/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ta/strings.xml
@@ -54,36 +54,24 @@
     <string name="permission_microphone" msgid="2152206421428732949">"àźźàŻˆàź•àŻàź°àŻ‹àźƒàźȘàŻ‹àź©àŻ"</string>
     <string name="permission_call_logs" msgid="5546761417694586041">"àź…àźŽàŻˆàźȘàŻàźȘàŻàźȘàŻ àźȘàź€àźżàź”àŻàź•àźłàŻ"</string>
     <string name="permission_nearby_devices" msgid="7530973297737123481">"àź…àź°àŻàź•àźżàźČàŻàźłàŻàźł àźšàźŸàź€àź©àź™àŻàź•àźłàŻ"</string>
-    <!-- no translation found for permission_media_routing_control (5498639511586715253) -->
-    <skip />
+    <string name="permission_media_routing_control" msgid="5498639511586715253">"àźźàŻ€àźŸàźżàźŻàźŸ àź…àź”àŻàźŸàŻàźȘàŻàźŸàŻàźŸàŻˆ àźźàźŸàź±àŻàź±àŻàź€àźČàŻ"</string>
     <string name="permission_storage" msgid="6831099350839392343">"àźȘàźŸàź™àŻàź•àźłàŻ àźźàź±àŻàź±àŻàźźàŻ àźźàŻ€àźŸàźżàźŻàźŸ"</string>
-    <!-- no translation found for permission_notifications (4099418516590632909) -->
-    <skip />
+    <string name="permission_notifications" msgid="4099418516590632909">"àź…àź±àźżàź”àźżàźȘàŻàźȘàŻàź•àźłàŻ"</string>
     <string name="permission_app_streaming" msgid="6009695219091526422">"àź†àźȘàŻàźžàŻ"</string>
     <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"àźžàŻàźŸàŻàź°àŻ€àźźàźżàź™àŻ"</string>
-    <!-- no translation found for permission_phone_summary (8246321093970051702) -->
-    <skip />
-    <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
-    <skip />
-    <!-- no translation found for permission_sms_summary (8499509535410068616) -->
-    <skip />
-    <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
-    <skip />
-    <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
-    <skip />
-    <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
-    <skip />
-    <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
-    <skip />
-    <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
-    <skip />
-    <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
-    <skip />
+    <string name="permission_phone_summary" msgid="8246321093970051702">"àźźàŻŠàźȘàŻˆàźČàŻ àź…àźŽàŻˆàźȘàŻàźȘàŻàź•àźłàŻˆàźšàŻ àźšàŻ†àźŻàŻàźŻàźČàźŸàźźàŻ àźšàźżàź°àŻàź”àź•àźżàź•àŻàź•àźČàźŸàźźàŻ"</string>
+    <string name="permission_call_logs_summary" msgid="7545243592757693321">"àźźàŻŠàźȘàŻˆàźČàŻ àź…àźŽàŻˆàźȘàŻàźȘàŻàźȘàŻ àźȘàź€àźżàź”àŻˆàźȘàŻ àźȘàźŸàźżàź•àŻàź•àźČàźŸàźźàŻ àźŽàźŽàŻàź€àźČàźŸàźźàŻ"</string>
+    <string name="permission_sms_summary" msgid="8499509535410068616">"SMS àźšàŻ†àźŻàŻàź€àźżàź•àźłàŻˆ àź…àź©àŻàźȘàŻàźȘàźČàźŸàźźàŻ àźȘàźŸàź°àŻàź•àŻàź•àźČàźŸàźźàŻ"</string>
+    <string name="permission_contacts_summary" msgid="2840800622763086808">"àź€àŻŠàźŸàź°àŻàźȘàŻàź•àźłàŻˆ àź…àźŁàŻàź•àźČàźŸàźźàŻ"</string>
+    <string name="permission_calendar_summary" msgid="8430353935747336165">"àź•àŻ‡àźČàŻ†àźŁàŻàźŸàź°àŻˆ àź…àźŁàŻàź•àźČàźŸàźźàŻ"</string>
+    <string name="permission_microphone_summary" msgid="4862628553869973259">"àź†àźŸàźżàźŻàŻ‹àź”àŻˆ àź°àŻ†àź•àŻàź•àźŸàź°àŻàźŸàŻ àźšàŻ†àźŻàŻàźŻàźČàźŸàźźàŻ"</string>
+    <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"àź…àź°àŻàź•àźżàźČàŻàźłàŻàźł àźšàźŸàź€àź©àź™àŻàź•àźłàŻˆàź•àŻ àź•àźŁàŻàźŸàź±àźżàźŻàźČàźŸàźźàŻ, àź…àź”àź±àŻàź±àŻàźŸàź©àŻ àź‡àźŁàŻˆàźŻàźČàźŸàźźàŻ, àź…àź”àź±àŻàź±àźżàź©àŻ àź€àŻ‚àź°àź€àŻàź€àŻˆàź€àŻ àź€àŻ€àź°àŻàźźàźŸàź©àźżàź•àŻàź•àźČàźŸàźźàŻ"</string>
+    <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"àź€àŻŠàźŸàź°àŻàźȘàŻàź•àźłàŻ, àźźàŻ†àźšàŻ‡àźœàŻàź•àźłàŻ, àźȘàźŸàź™àŻàź•àźłàŻ àźȘàŻ‹àź©àŻàź± àź€àź•àź”àźČàŻàź•àźłàŻ àź‰àźŸàŻàźȘàźŸ àź…àź©àŻˆàź€àŻàź€àŻ àź…àź±àźżàź”àźżàźȘàŻàźȘàŻàź•àźłàŻˆàźŻàŻàźźàŻ àźȘàźŸàźżàź•àŻàź•àźČàźŸàźźàŻ"</string>
+    <string name="permission_notifications_summary" msgid="2272810466047367030">"• àź€àŻŠàźŸàź°àŻàźȘàŻàź•àźłàŻ, àźźàŻ†àźšàŻ‡àźœàŻàź•àźłàŻ, àźȘàźŸàź™àŻàź•àźłàŻ àźȘàŻ‹àź©àŻàź± àź€àź•àź”àźČàŻàź•àźłàŻ àź‰àźŸàŻàźȘàźŸ àź…àź©àŻˆàź€àŻàź€àŻ àź…àź±àźżàź”àźżàźȘàŻàźȘàŻàź•àźłàŻˆàźŻàŻàźźàŻ àźȘàźŸàźżàź•àŻàź•àźČàźŸàźźàŻ&lt;br/&gt;• àź…àź±àźżàź”àźżàźȘàŻàźȘàŻàź•àźłàŻˆ àź…àź©àŻàźȘàŻàźȘàźČàźŸàźźàŻ&lt;br/&gt;&lt;br/&gt;àź‡àźšàŻàź€ àź†àźȘàŻàźžàźżàź©àŻ, àź…àź±àźżàź”àźżàźȘàŻàźȘàŻàź•àźłàŻˆàźȘàŻ àźȘàźŸàźżàź•àŻàź•àŻàźźàŻ àźźàź±àŻàź±àŻàźźàŻ àź…àź©àŻàźȘàŻàźȘàŻàźźàŻ àź€àźżàź±àź©àŻˆ àźŽàźȘàŻàźȘàŻ‹àź€àŻ àź”àŻ‡àźŁàŻàźŸàŻàźźàźŸàź©àźŸàźČàŻàźźàŻ àź…àźźàŻˆàźȘàŻàźȘàŻàź•àźłàŻ &gt; àź…àź±àźżàź”àźżàźȘàŻàźȘàŻàź•àźłàŻ àźŽàź©àŻàźȘàź€àźżàźČàŻ àźšàźżàź°àŻàź”àź•àźżàź•àŻàź•àźČàźŸàźźàŻ."</string>
     <string name="permission_app_streaming_summary" msgid="606923325679670624">"àź‰àź™àŻàź•àźłàŻ àźźàŻŠàźȘàŻˆàźČàŻ àź†àźȘàŻàźžàŻˆ àźžàŻàźŸàŻàź°àŻ€àźźàŻ àźšàŻ†àźŻàŻàźŻàźČàźŸàźźàŻ"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
     <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"àź‰àź™àŻàź•àźłàŻ àźźàŻŠàźȘàŻˆàźČàźżàźČàŻ àź‡àź°àŻàźšàŻàź€àŻ àź†àźȘàŻàźžàŻˆàźŻàŻàźźàŻ àźȘàźżàź± àźšàźżàźžàŻàźŸàźźàŻ àź…àźźàŻàźšàź™àŻàź•àźłàŻˆàźŻàŻàźźàŻ àźžàŻàźŸàŻàź°àŻ€àźźàŻ àźšàŻ†àźŻàŻàźŻàźČàźŸàźźàŻ"</string>
-    <!-- no translation found for permission_media_routing_control_summary (2714631092321412250) -->
-    <skip />
+    <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"àź•àźżàźŸàŻˆàź•àŻàź•àź•àŻàź•àŻ‚àźŸàźżàźŻ àźšàźŸàź€àź©àź™àŻàź•àźłàźżàź©àŻ àźȘàźŸàŻàźŸàźżàźŻàźČàŻˆ àź…àźŁàŻàź•àźż, àź…àź”àź±àŻàź±àźżàźČàŻ àźŽàź€àŻ àźȘàźżàź± àź†àźȘàŻàźžàźżàź©àŻ àź†àźŸàźżàźŻàŻ‹/àź”àŻ€àźŸàźżàźŻàŻ‹àź”àŻˆàźȘàŻ àźȘàźżàźłàŻ‡ àźšàŻ†àźŻàŻàźŻàźČàźŸàźźàŻ àź…àźČàŻàźČàź€àŻ àź…àźČàŻˆàźȘàź°àźȘàŻàźȘàźČàźŸàźźàŻ àźŽàź©àŻàźȘàź€àŻˆàź•àŻ àź•àźŸàŻàźŸàŻàźȘàŻàźȘàźŸàŻàź€àŻàź€àŻàźźàŻ"</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"àźźàŻŠàźȘàŻˆàźČàŻ"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"àźŸàŻ‡àźȘàŻàźČàŻ†àźŸàŻ"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-te/strings.xml b/packages/CompanionDeviceManager/res/values-te/strings.xml
index c6af6ed..3dd183e 100644
--- a/packages/CompanionDeviceManager/res/values-te/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-te/strings.xml
@@ -56,28 +56,18 @@
     <string name="permission_nearby_devices" msgid="7530973297737123481">"à°žà°źà±€à°Șంà°Čà±‹à°šà°ż à°Șà°°à°żà°•à°°à°Ÿà°Čు"</string>
     <string name="permission_media_routing_control" msgid="5498639511586715253">"à°źà±€à°Ąà°żà°Żà°Ÿ అఔుట్‌à°Șుట్‌చు à°źà°Ÿà°°à±à°šà°‚à°Ąà°ż"</string>
     <string name="permission_storage" msgid="6831099350839392343">"ఫోటోà°Čు, à°źà±€à°Ąà°żà°Żà°Ÿ"</string>
-    <!-- no translation found for permission_notifications (4099418516590632909) -->
-    <skip />
+    <string name="permission_notifications" msgid="4099418516590632909">"à°šà±‹à°Ÿà°żà°«à°żà°•à±‡à°·à°šà±‌à°Čు"</string>
     <string name="permission_app_streaming" msgid="6009695219091526422">"à°Żà°Ÿà°Ș్‌à°Čు"</string>
     <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"à°žà±à°Ÿà±à°°à±€à°źà°żà°‚à°—à±"</string>
-    <!-- no translation found for permission_phone_summary (8246321093970051702) -->
-    <skip />
-    <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
-    <skip />
-    <!-- no translation found for permission_sms_summary (8499509535410068616) -->
-    <skip />
-    <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
-    <skip />
-    <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
-    <skip />
-    <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
-    <skip />
-    <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
-    <skip />
-    <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
-    <skip />
-    <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
-    <skip />
+    <string name="permission_phone_summary" msgid="8246321093970051702">"ఫోచ్ à°•à°Ÿà°Č్ఞ్‌చు à°šà±‡à°Żà°—à°Čఊు, à°źà±‡à°šà±‡à°œà± à°šà±‡à°Żà°—à°Čఊు"</string>
+    <string name="permission_call_logs_summary" msgid="7545243592757693321">"ఫోచ్ à°•à°Ÿà°Č్ à°Čటగ్‌చు చఊఔగà°Čఊు, à°°à°Ÿà°Żà°—à°Čఊు"</string>
+    <string name="permission_sms_summary" msgid="8499509535410068616">"SMS à°źà±†à°žà±‡à°œà±‌à°Čచు à°Șంà°Șగà°Čఊు, à°šà±‚à°Ąà°—à°Čఊు"</string>
+    <string name="permission_contacts_summary" msgid="2840800622763086808">"à°źà±€ కటంటటక్ట్‌à°Čచు à°Żà°Ÿà°•à±à°žà±†à°žà± à°šà±‡à°Żà°—à°Čఊు"</string>
+    <string name="permission_calendar_summary" msgid="8430353935747336165">"à°źà±€ à°•à±à°Żà°Ÿà°Čà±†à°‚à°Ąà°°à±‌చు à°Żà°Ÿà°•à±à°žà±†à°žà± à°šà±‡à°Żà°—à°Čఊు"</string>
+    <string name="permission_microphone_summary" msgid="4862628553869973259">"à°†à°Ąà°żà°Żà±‹à°šà± à°°à°żà°•à°Ÿà°°à±à°Ąà± à°šà±‡à°Żà°—à°Čఊు"</string>
+    <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"à°žà°źà±€à°Șంà°Čà±‹à°šà°ż à°Șà°°à°żà°•à°°à°Ÿà°Čచు కచుగొచగà°Čఊు, à°”à°Ÿà°Ÿà°żà°•à°ż కచెక్ట్ అఔ్ఔగà°Čఊు, à°…à°”à°ż à°Žà°‚à°€ ఊూరంà°Čో à°‰à°šà±à°šà°Ÿà°Żà±‹ ఀెà°Čుఞుకోగà°Čఊు"</string>
+    <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"కటంటటక్ట్‌à°Čు, à°źà±†à°žà±‡à°œà±‌à°Čు, ఫోటోà°Č à°”à°‚à°Ÿà°ż à°žà°źà°Ÿà°šà°Ÿà°°à°‚à°€à±‹ à°žà°čà°Ÿ à°…à°šà±à°šà°ż à°šà±‹à°Ÿà°żà°«à°żà°•à±‡à°·à°šà±‌à°Čచు చఊఔగà°Čఊు"</string>
+    <string name="permission_notifications_summary" msgid="2272810466047367030">"• కటంటటక్ట్‌à°Čు, à°źà±†à°žà±‡à°œà±‌à°Čు, ఫోటోà°Č à°”à°‚à°Ÿà°ż à°žà°źà°Ÿà°šà°Ÿà°°à°‚à°€à±‹ à°žà°čà°Ÿ à°šà±‹à°Ÿà°żà°«à°żà°•à±‡à°·à°šà±‌à°Čà°šà±à°šà°żà°‚à°Ÿà°żà°šà°ż చఊఔగà°Čఊు&lt;br/&gt;• à°šà±‹à°Ÿà°żà°«à°żà°•à±‡à°·à°šà±‌à°Čచు à°Șంà°Șగà°Čఊు&lt;br/&gt;&lt;br/&gt;à°šà±‹à°Ÿà°żà°«à°żà°•à±‡à°·à°šà±‌à°Čచు చఊఔగà°Č, à°Șంà°Șగà°Č ఈ à°Żà°Ÿà°Ș్ à°žà°Ÿà°źà°°à±à°„à±à°Żà°Ÿà°šà±à°šà°ż à°źà±€à°°à± à°žà±†à°Ÿà±à°Ÿà°żà°‚à°—à±‌à°Čు &gt; à°šà±‹à°Ÿà°żà°«à°żà°•à±‡à°·à°šà±‌à°Čà°Čో ఎà°Ș్à°Șà±à°Ąà±ˆà°šà°Ÿ à°źà±‡à°šà±‡à°œà± à°šà±‡à°Żà°”à°šà±à°šà±."</string>
     <string name="permission_app_streaming_summary" msgid="606923325679670624">"à°źà±€ ఫోచ్‌à°Čà±‹à°šà°ż à°Żà°Ÿà°Ș్‌à°Čచు à°žà±à°Ÿà±à°°à±€à°źà± à°šà±‡à°Żà°‚à°Ąà°ż"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
     <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"à°źà±€ ఫోచ్ à°šà±à°‚à°Ąà°ż à°Żà°Ÿà°Ș్‌à°Čచు, ఇఀర à°žà°żà°žà±à°Ÿà°źà± ఫీచర్‌à°Čచు à°žà±à°Ÿà±à°°à±€à°źà± à°šà±‡à°žà±à°€à±à°‚à°Šà°ż"</string>
diff --git a/packages/CompanionDeviceManager/res/values-th/strings.xml b/packages/CompanionDeviceManager/res/values-th/strings.xml
index 3925747..1ed5abf 100644
--- a/packages/CompanionDeviceManager/res/values-th/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-th/strings.xml
@@ -56,28 +56,18 @@
     <string name="permission_nearby_devices" msgid="7530973297737123481">"àž­àžžàž›àžàžŁàž“àčŒàž—àž”àčˆàž­àžąàžčàčˆàčƒàžàž„àč‰àč€àž„àž”àžąàž‡"</string>
     <string name="permission_media_routing_control" msgid="5498639511586715253">"àč€àž›àž„àž”àčˆàžąàž™àč€àž­àžČàž•àčŒàžžàžžàž•àžȘàž·àčˆàž­"</string>
     <string name="permission_storage" msgid="6831099350839392343">"àžŁàžčàž›àž àžČàžžàčàž„àž°àžȘàž·àčˆàž­"</string>
-    <!-- no translation found for permission_notifications (4099418516590632909) -->
-    <skip />
+    <string name="permission_notifications" msgid="4099418516590632909">"àžàžČàžŁàčàžˆàč‰àž‡àč€àž•àž·àž­àž™"</string>
     <string name="permission_app_streaming" msgid="6009695219091526422">"àčàž­àž›"</string>
     <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"àžȘàž•àžŁàž”àžĄàžĄàžŽàž‡"</string>
-    <!-- no translation found for permission_phone_summary (8246321093970051702) -->
-    <skip />
-    <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
-    <skip />
-    <!-- no translation found for permission_sms_summary (8499509535410068616) -->
-    <skip />
-    <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
-    <skip />
-    <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
-    <skip />
-    <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
-    <skip />
-    <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
-    <skip />
-    <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
-    <skip />
-    <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
-    <skip />
+    <string name="permission_phone_summary" msgid="8246321093970051702">"àč‚àž—àžŁàčàž„àž°àžˆàž±àž”àžàžČàžŁàžàžČàžŁàč‚àž—àžŁ"</string>
+    <string name="permission_call_logs_summary" msgid="7545243592757693321">"àž­àčˆàžČàž™àčàž„àž°àč€àž‚àž”àžąàž™àžšàž±àž™àž—àž¶àžàžàžČàžŁàč‚àž—àžŁàž‚àž­àž‡àč‚àž—àžŁàžšàž±àžžàž—àčŒ"</string>
+    <string name="permission_sms_summary" msgid="8499509535410068616">"àžȘàčˆàž‡àčàž„àž°àž”àžčàž‚àč‰àž­àž„àž§àžČàžĄ SMS"</string>
+    <string name="permission_contacts_summary" msgid="2840800622763086808">"àč€àž‚àč‰àžČàž–àž¶àž‡àžŁàžČàžąàžŠàž·àčˆàž­àž•àžŽàž”àž•àčˆàž­"</string>
+    <string name="permission_calendar_summary" msgid="8430353935747336165">"àč€àž‚àč‰àžČàž–àž¶àž‡àž›àžàžŽàž—àžŽàž™"</string>
+    <string name="permission_microphone_summary" msgid="4862628553869973259">"àžšàž±àž™àž—àž¶àžàč€àžȘàž”àžąàž‡"</string>
+    <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"àž„àč‰àž™àž«àžČ àč€àžŠàž·àčˆàž­àžĄàž•àčˆàž­ àčàž„àž°àžŁàž°àžšàžžàž•àžłàčàž«àž™àčˆàž‡àž‹àž¶àčˆàž‡àžȘàž±àžĄàžžàž±àž™àž˜àčŒàžàž±àžšàž­àžžàž›àžàžŁàž“àčŒàž—àž”àčˆàž­àžąàžčàčˆàčƒàžàž„àč‰àč€àž„àž”àžąàž‡"</string>
+    <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"àž­àčˆàžČàž™àžàžČàžŁàčàžˆàč‰àž‡àč€àž•àž·àž­àž™àž—àž±àč‰àž‡àž«àžĄàž” àžŁàž§àžĄàž–àž¶àž‡àž‚àč‰àž­àžĄàžčàž„àž­àžąàčˆàžČàž‡àžŁàžČàžąàžŠàž·àčˆàž­àž•àžŽàž”àž•àčˆàž­ àž‚àč‰àž­àž„àž§àžČàžĄ àčàž„àž°àžŁàžčàž›àž àžČàžž"</string>
+    <string name="permission_notifications_summary" msgid="2272810466047367030">"• àž­àčˆàžČàž™àžàžČàžŁàčàžˆàč‰àž‡àč€àž•àž·àž­àž™àž—àž±àč‰àž‡àž«àžĄàž” àžŁàž§àžĄàž–àž¶àž‡àž‚àč‰àž­àžĄàžčàž„àž­àžąàčˆàžČàž‡àžŁàžČàžąàžŠàž·àčˆàž­àž•àžŽàž”àž•àčˆàž­ àž‚àč‰àž­àž„àž§àžČàžĄ àčàž„àž°àžŁàžčàž›àž àžČàžž&lt;br/&gt;• àžȘàčˆàž‡àžàžČàžŁàčàžˆàč‰àž‡àč€àž•àž·àž­àž™&lt;br/&gt;&lt;br/&gt;àž„àžžàž“àžˆàž±àž”àžàžČàžŁàž„àž§àžČàžĄàžȘàžČàžĄàžČàžŁàž–àčƒàž™àžàžČàžŁàž­àčˆàžČàž™àčàž„àž°àžȘàčˆàž‡àžàžČàžŁàčàžˆàč‰àž‡àč€àž•àž·àž­àž™àž‚àž­àž‡àčàž­àž›àž™àž”àč‰àč„àž”àč‰àž—àžžàžàč€àžĄàž·àčˆàž­àčƒàž™àžàžČàžŁàž•àž±àč‰àž‡àž„àčˆàžČ &gt; àžàžČàžŁàčàžˆàč‰àž‡àč€àž•àž·àž­àž™"</string>
     <string name="permission_app_streaming_summary" msgid="606923325679670624">"àžȘàž•àžŁàž”àžĄàčàž­àž›àž‚àž­àž‡àč‚àž—àžŁàžšàž±àžžàž—àčŒàž„àžžàž“"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
     <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"àžȘàž•àžŁàž”àžĄàčàž­àž›àčàž„àž°àžŸàž”àč€àžˆàž­àžŁàčŒàž­àž·àčˆàž™àč† àž‚àž­àž‡àžŁàž°àžšàžšàžˆàžČàžàč‚àž—àžŁàžšàž±àžžàž—àčŒ"</string>
diff --git a/packages/CompanionDeviceManager/res/values-tl/strings.xml b/packages/CompanionDeviceManager/res/values-tl/strings.xml
index f467770..1b82d91 100644
--- a/packages/CompanionDeviceManager/res/values-tl/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-tl/strings.xml
@@ -56,28 +56,18 @@
     <string name="permission_nearby_devices" msgid="7530973297737123481">"Mga kalapit na device"</string>
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Palitan ang media output"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Mga larawan at media"</string>
-    <!-- no translation found for permission_notifications (4099418516590632909) -->
-    <skip />
+    <string name="permission_notifications" msgid="4099418516590632909">"Mga Notification"</string>
     <string name="permission_app_streaming" msgid="6009695219091526422">"Mga App"</string>
     <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Streaming"</string>
-    <!-- no translation found for permission_phone_summary (8246321093970051702) -->
-    <skip />
-    <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
-    <skip />
-    <!-- no translation found for permission_sms_summary (8499509535410068616) -->
-    <skip />
-    <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
-    <skip />
-    <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
-    <skip />
-    <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
-    <skip />
-    <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
-    <skip />
-    <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
-    <skip />
-    <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
-    <skip />
+    <string name="permission_phone_summary" msgid="8246321093970051702">"Tumawag at mamahala ng mga tawag sa telepono"</string>
+    <string name="permission_call_logs_summary" msgid="7545243592757693321">"I-read at i-write ang log ng tawag sa telepono"</string>
+    <string name="permission_sms_summary" msgid="8499509535410068616">"Magpadala at tumingin ng mga mensaheng SMS"</string>
+    <string name="permission_contacts_summary" msgid="2840800622763086808">"I-access ang iyong mga contact"</string>
+    <string name="permission_calendar_summary" msgid="8430353935747336165">"I-access ang iyong kalendaryo"</string>
+    <string name="permission_microphone_summary" msgid="4862628553869973259">"Mag-record ng audio"</string>
+    <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Maghanap ng, kumonekta sa, at tukuyin ang relatibong posisyon ng mga kalapit na device"</string>
+    <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Basahin ang lahat ng notification, kabilang ang impormasyon tulad ng mga contact, mensahe, at larawan"</string>
+    <string name="permission_notifications_summary" msgid="2272810466047367030">"• Basahin ang lahat ng notification, kabilang ang impormasyon tulad ng mga contact, mensahe, at larawan&lt;br/&gt;• Magpadala ng mga notification&lt;br/&gt;&lt;br/&gt;Puwede mong pamahalaan ang kakayahan ng app na ito na magbasa at magpadala ng mga notification kahit kailan sa Mga Setting &gt; Mga Notification."</string>
     <string name="permission_app_streaming_summary" msgid="606923325679670624">"I-stream ang mga app ng iyong telepono"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
     <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Mag-stream ng mga app at iba pang feature ng system mula sa iyong telepono"</string>
diff --git a/packages/CompanionDeviceManager/res/values-tr/strings.xml b/packages/CompanionDeviceManager/res/values-tr/strings.xml
index cc8e898..392e158 100644
--- a/packages/CompanionDeviceManager/res/values-tr/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-tr/strings.xml
@@ -56,28 +56,18 @@
     <string name="permission_nearby_devices" msgid="7530973297737123481">"Yakındaki cihazlar"</string>
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Medya çıkıßını değißtirin"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Fotoğraflar ve medya"</string>
-    <!-- no translation found for permission_notifications (4099418516590632909) -->
-    <skip />
+    <string name="permission_notifications" msgid="4099418516590632909">"Bildirimler"</string>
     <string name="permission_app_streaming" msgid="6009695219091526422">"Uygulamalar"</string>
     <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Yayınlama"</string>
-    <!-- no translation found for permission_phone_summary (8246321093970051702) -->
-    <skip />
-    <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
-    <skip />
-    <!-- no translation found for permission_sms_summary (8499509535410068616) -->
-    <skip />
-    <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
-    <skip />
-    <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
-    <skip />
-    <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
-    <skip />
-    <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
-    <skip />
-    <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
-    <skip />
-    <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
-    <skip />
+    <string name="permission_phone_summary" msgid="8246321093970051702">"Telefon aramaları yapma ve yönetme"</string>
+    <string name="permission_call_logs_summary" msgid="7545243592757693321">"Telefon arama kaydını okuma ve yazma"</string>
+    <string name="permission_sms_summary" msgid="8499509535410068616">"SMS mesajları gönderme ve görüntüleme"</string>
+    <string name="permission_contacts_summary" msgid="2840800622763086808">"Kißilerinize erißme"</string>
+    <string name="permission_calendar_summary" msgid="8430353935747336165">"Takviminize erißme"</string>
+    <string name="permission_microphone_summary" msgid="4862628553869973259">"Ses kaydetme"</string>
+    <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Yakındaki cihazları keßfedip bağlanma ve bu cihazların göreli konumunu belirleme"</string>
+    <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Kißiler, mesajlar ve fotoğraflar da dahil olmak üzere tüm bildirimleri okuma"</string>
+    <string name="permission_notifications_summary" msgid="2272810466047367030">"• Kißiler, mesajlar ve fotoğraflar gibi bilgiler dahil tüm bildirimleri okuma&lt;br/&gt;• Bildirim gönderme&lt;br/&gt;&lt;br/&gt;Dilediğiniz zaman bu uygulamanın bildirim okuma ve gönderme iznini Ayarlar &gt; Bildirimler bölümünden yönetebilirsiniz."</string>
     <string name="permission_app_streaming_summary" msgid="606923325679670624">"Telefonunuzun uygulamalarını yayınlayabilir"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
     <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Telefonunuzdan uygulamaları ve diğer sistem özelliklerini yayınlayabilir"</string>
diff --git a/packages/CompanionDeviceManager/res/values-uk/strings.xml b/packages/CompanionDeviceManager/res/values-uk/strings.xml
index f0433ae..9f88d9f 100644
--- a/packages/CompanionDeviceManager/res/values-uk/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-uk/strings.xml
@@ -54,36 +54,24 @@
     <string name="permission_microphone" msgid="2152206421428732949">"МіĐșŃ€ĐŸŃ„ĐŸĐœ"</string>
     <string name="permission_call_logs" msgid="5546761417694586041">"Đ–ŃƒŃ€ĐœĐ°Đ»Đž ĐČĐžĐșлОĐșіĐČ"</string>
     <string name="permission_nearby_devices" msgid="7530973297737123481">"ĐŸŃ€ĐžŃŃ‚Ń€ĐŸŃ— ĐżĐŸĐ±Đ»ĐžĐ·Ńƒ"</string>
-    <!-- no translation found for permission_media_routing_control (5498639511586715253) -->
-    <skip />
+    <string name="permission_media_routing_control" msgid="5498639511586715253">"Đ—ĐŒŃ–Đœ. простр. ĐŽĐ»Ń ĐČіЮтĐČ. ĐŒĐ”ĐŽŃ–Đ°"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Đ€ĐŸŃ‚ĐŸĐłŃ€Đ°Ń„Ń–Ń— та ĐŒĐ”ĐŽŃ–Đ°Ń„Đ°ĐčлО"</string>
-    <!-- no translation found for permission_notifications (4099418516590632909) -->
-    <skip />
+    <string name="permission_notifications" msgid="4099418516590632909">"ĐĄĐżĐŸĐČŃ–Ń‰Đ”ĐœĐœŃ"</string>
     <string name="permission_app_streaming" msgid="6009695219091526422">"Đ”ĐŸĐŽĐ°Ń‚ĐșĐž"</string>
     <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"ĐŸĐŸŃ‚ĐŸĐșĐŸĐČĐ” пДрДЎаĐČĐ°ĐœĐœŃ"</string>
-    <!-- no translation found for permission_phone_summary (8246321093970051702) -->
-    <skip />
-    <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
-    <skip />
-    <!-- no translation found for permission_sms_summary (8499509535410068616) -->
-    <skip />
-    <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
-    <skip />
-    <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
-    <skip />
-    <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
-    <skip />
-    <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
-    <skip />
-    <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
-    <skip />
-    <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
-    <skip />
+    <string name="permission_phone_summary" msgid="8246321093970051702">"ĐąĐ”Đ»Đ”Ń„ĐŸĐœŃƒĐČато Đč ĐșĐ”Ń€ŃƒĐČато ĐŽĐ·ĐČŃ–ĐœĐșĐ°ĐŒĐž"</string>
+    <string name="permission_call_logs_summary" msgid="7545243592757693321">"ĐŸĐ”Ń€Đ”ĐłĐ»ŃĐŽĐ°Ń‚Đž Đč запОсуĐČато ĐŽĐ°ĐœŃ– ĐČ Đ¶ŃƒŃ€ĐœĐ°Đ»Ń– ĐČĐžĐșлОĐșіĐČ Ń‚Đ”Đ»Đ”Ń„ĐŸĐœĐ°"</string>
+    <string name="permission_sms_summary" msgid="8499509535410068616">"ĐĐ°ĐŽŃĐžĐ»Đ°Ń‚Đž Đč ĐżĐ”Ń€Đ”ĐłĐ»ŃĐŽĐ°Ń‚Đž SMS-ĐżĐŸĐČŃ–ĐŽĐŸĐŒĐ»Đ”ĐœĐœŃ"</string>
+    <string name="permission_contacts_summary" msgid="2840800622763086808">"ĐžŃ‚Ń€ĐžĐŒŃƒĐČато ĐŽĐŸŃŃ‚ŃƒĐż ĐŽĐŸ ĐșĐŸĐœŃ‚Đ°ĐșтіĐČ"</string>
+    <string name="permission_calendar_summary" msgid="8430353935747336165">"ĐžŃ‚Ń€ĐžĐŒŃƒĐČато ĐŽĐŸŃŃ‚ŃƒĐż ĐŽĐŸ ĐșĐ°Đ»Đ”ĐœĐŽĐ°Ń€Ń"</string>
+    <string name="permission_microphone_summary" msgid="4862628553869973259">"ЗапосуĐČато Đ°ŃƒĐŽŃ–ĐŸ"</string>
+    <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Đ—ĐœĐ°Ń…ĐŸĐŽĐžŃ‚Đž ĐżŃ€ĐžŃŃ‚Ń€ĐŸŃ— ĐżĐŸĐ±Đ»ĐžĐ·Ńƒ, піЮĐșĐ»ŃŽŃ‡Đ°Ń‚ĐžŃŃ ĐŽĐŸ ĐœĐžŃ… і ĐČĐžĐ·ĐœĐ°Ń‡Đ°Ń‚Đž їх ĐČŃ–ĐŽĐœĐŸŃĐœĐ” Ń€ĐŸĐ·Ń‚Đ°ŃˆŃƒĐČĐ°ĐœĐœŃ"</string>
+    <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Чотато ĐČсі ŃĐżĐŸĐČŃ–Ń‰Đ”ĐœĐœŃ, Đ·ĐŸĐșŃ€Đ”ĐŒĐ° таĐșу Ń–ĐœŃ„ĐŸŃ€ĐŒĐ°Ń†Ń–ŃŽ, яĐș ĐșĐŸĐœŃ‚Đ°Đșто, ĐżĐŸĐČŃ–ĐŽĐŸĐŒĐ»Đ”ĐœĐœŃ Đč Ń„ĐŸŃ‚ĐŸĐłŃ€Đ°Ń„Ń–Ń—"</string>
+    <string name="permission_notifications_summary" msgid="2272810466047367030">"• Чотато ĐČсі ŃĐżĐŸĐČŃ–Ń‰Đ”ĐœĐœŃ, Đ·ĐŸĐșŃ€Đ”ĐŒĐ° таĐșу Ń–ĐœŃ„ĐŸŃ€ĐŒĐ°Ń†Ń–ŃŽ, яĐș ĐșĐŸĐœŃ‚Đ°Đșто, ĐżĐŸĐČŃ–ĐŽĐŸĐŒĐ»Đ”ĐœĐœŃ Đč Ń„ĐŸŃ‚ĐŸĐłŃ€Đ°Ń„Ń–Ń—&lt;br/&gt;• ĐĐ°ĐŽŃĐžĐ»Đ°Ń‚Đž ŃĐżĐŸĐČŃ–Ń‰Đ”ĐœĐœŃ&lt;br/&gt;&lt;br/&gt;Во ĐŒĐŸĐ¶Đ”Ń‚Đ” Đ±ŃƒĐŽŃŒ-ĐșĐŸĐ»Đž Đ·ĐŒŃ–ĐœĐžŃ‚Đž ĐŽĐŸĐ·ĐČŃ–Đ» Ń†ŃŒĐŸĐłĐŸ ĐŽĐŸĐŽĐ°Ń‚Đșа ĐœĐ° ĐżĐ”Ń€Đ”ĐłĐ»ŃĐŽ і ĐœĐ°ĐŽŃĐžĐ»Đ°ĐœĐœŃ ŃĐżĐŸĐČŃ–Ń‰Đ”ĐœŃŒ, ĐČОбраĐČшО \"ĐĐ°Đ»Đ°ŃˆŃ‚ŃƒĐČĐ°ĐœĐœŃ\" &gt; \"ĐĄĐżĐŸĐČŃ–Ń‰Đ”ĐœĐœŃ\"."</string>
     <string name="permission_app_streaming_summary" msgid="606923325679670624">"ĐąŃ€Đ°ĐœŃĐ»ŃŽĐČато ĐŽĐŸĐŽĐ°Ń‚ĐșĐž Ń‚Đ”Đ»Đ”Ń„ĐŸĐœĐ°"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
     <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"ĐąŃ€Đ°ĐœŃĐ»ŃŽĐčтД ĐŽĐŸĐŽĐ°Ń‚ĐșĐž Đč Ń–ĐœŃˆŃ– ŃĐžŃŃ‚Đ”ĐŒĐœŃ– Ń„ŃƒĐœĐșції Đ·Ń– сĐČĐŸĐłĐŸ Ń‚Đ”Đ»Đ”Ń„ĐŸĐœĐ°"</string>
-    <!-- no translation found for permission_media_routing_control_summary (2714631092321412250) -->
-    <skip />
+    <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"ĐžŃ‚Ń€ĐžĐŒŃƒĐČато ĐŽĐŸŃŃ‚ŃƒĐż ĐŽĐŸ спОсĐșу ĐŽĐŸŃŃ‚ŃƒĐżĐœĐžŃ… ĐżŃ€ĐžŃŃ‚Ń€ĐŸŃ—ĐČ Ń– ĐČОбОратО, Đ· яĐșĐŸĐłĐŸ Đ· ĐœĐžŃ… Ń‚Ń€Đ°ĐœŃĐ»ŃŽĐČĐ°Ń‚ĐžĐŒĐ”Ń‚ŃŒŃŃ Đ°ŃƒĐŽŃ–ĐŸ Đ°Đ±ĐŸ ĐČŃ–ĐŽĐ”ĐŸ Đ· Ń–ĐœŃˆĐžŃ… ĐŽĐŸĐŽĐ°Ń‚ĐșіĐČ"</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"Ń‚Đ”Đ»Đ”Ń„ĐŸĐœŃ–"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"ĐżĐ»Đ°ĐœŃˆĐ”Ń‚Ń–"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-ur/strings.xml b/packages/CompanionDeviceManager/res/values-ur/strings.xml
index 58c1ab6..36f8b4f 100644
--- a/packages/CompanionDeviceManager/res/values-ur/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ur/strings.xml
@@ -56,28 +56,18 @@
     <string name="permission_nearby_devices" msgid="7530973297737123481">"Ù‚Ű±ÛŒŰšÛŒ ŰąÙ„Ű§ŰȘ"</string>
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Ù…ÛŒÚˆÛŒŰ§ ۹ۀÙč ÙŸÙč کو ŰȘŰšŰŻÛŒÙ„ Ú©Ű±ÛŒÚș"</string>
     <string name="permission_storage" msgid="6831099350839392343">"ŰȘŰ”Ű§ÙˆÛŒŰ± Ű§ÙˆŰ± Ù…ÛŒÚˆÛŒŰ§"</string>
-    <!-- no translation found for permission_notifications (4099418516590632909) -->
-    <skip />
+    <string name="permission_notifications" msgid="4099418516590632909">"Ű§Ű·Ù„Ű§ŰčۧŰȘ"</string>
     <string name="permission_app_streaming" msgid="6009695219091526422">"Ű§ÛŒÙŸŰł"</string>
     <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"ŰłÙ„ŰłÙ„Û ŰšÙ†ŰŻÛŒ"</string>
-    <!-- no translation found for permission_phone_summary (8246321093970051702) -->
-    <skip />
-    <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
-    <skip />
-    <!-- no translation found for permission_sms_summary (8499509535410068616) -->
-    <skip />
-    <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
-    <skip />
-    <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
-    <skip />
-    <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
-    <skip />
-    <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
-    <skip />
-    <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
-    <skip />
-    <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
-    <skip />
+    <string name="permission_phone_summary" msgid="8246321093970051702">"فون Ú©Ű§Ù„ŰČ Ú©Ű±ÛŒÚș Ű§ÙˆŰ± Ű§Ù† کۧ Ù†ŰžÙ… Ú©Ű±ÛŒÚș"</string>
+    <string name="permission_call_logs_summary" msgid="7545243592757693321">"فون Ú©Ű§Ù„ Ù„Ű§ÚŻŰČ ÙŸÚ‘ÚŸÛŒÚș Ű§ÙˆŰ± Ù„Ú©ÚŸÛŒÚș"</string>
+    <string name="permission_sms_summary" msgid="8499509535410068616">"‏SMS ÙŸÛŒŰșŰ§Ù…Ű§ŰȘ ŰšÚŸÛŒŰŹÛŒÚș Ű§ÙˆŰ± ŰŻÛŒÚ©ÚŸÛŒÚș"</string>
+    <string name="permission_contacts_summary" msgid="2840800622763086808">"Ű§ÙŸÙ†Û’ Ű±Ű§ŰšŰ·ÙˆÚș ŰȘÚ© Ű±ŰłŰ§ŰŠÛŒ Ű­Ű§Ű”Ù„ Ú©Ű±ÛŒÚș"</string>
+    <string name="permission_calendar_summary" msgid="8430353935747336165">"Ű§ÙŸÙ†Û’ Ú©ÛŒÙ„Ù†ÚˆŰ± ŰȘÚ© Ű±ŰłŰ§ŰŠÛŒ Ű­Ű§Ű”Ù„ Ú©Ű±ÛŒÚș"</string>
+    <string name="permission_microphone_summary" msgid="4862628553869973259">"ŰąÚˆÛŒÙˆ Ű±ÛŒÚ©Ű§Ű±Úˆ Ú©Ű±ÛŒÚș"</string>
+    <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Ù‚Ű±ÛŒŰšÛŒ ŰąÙ„Ű§ŰȘ کی مŰȘŰčلقہ ٟوŰČÛŒŰŽÙ† ŰȘÙ„Ű§ŰŽ Ú©Ű±ÛŒÚșی Ű§Ù† ŰłÛ’ Ù…Ù†ŰłÙ„Ú© Ú©Ű±ÛŒÚș Ű§ÙˆŰ± ۧ۳ کۧ ŰȘŰčین Ú©Ű±ÛŒÚș"</string>
+    <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Ű±Ű§ŰšŰ·ÙˆÚșی ÙŸÛŒŰșŰ§Ù…Ű§ŰȘ Ű§ÙˆŰ± ŰȘŰ”Ű§ÙˆÛŒŰ± ŰŹÛŒŰłÛŒ مŰčÙ„ÙˆÙ…Ű§ŰȘ ŰłÙ…ÛŒŰȘ ŰȘÙ…Ű§Ù… Ű§Ű·Ù„Ű§ŰčۧŰȘ ÙŸÚ‘ÚŸÛŒÚș"</string>
+    <string name="permission_notifications_summary" msgid="2272810466047367030">"‏• ŰȘÙ…Ű§Ù… Ű§Ű·Ù„Ű§ŰčۧŰȘ کو ÙŸÚ‘ÚŸÛŒÚșی ŰšŰŽÙ…ÙˆÙ„ Ű±Ű§ŰšŰ·Û’ŰŒ ÙŸÛŒŰșŰ§Ù…Ű§ŰȘ Ű§ÙˆŰ± ŰȘŰ”Ű§ÙˆÛŒŰ±&lt;br/&gt;• Ű§Ű·Ù„Ű§ŰčۧŰȘ ŰšÚŸÛŒŰŹÛŒÚș&lt;br/&gt;&lt;br/&gt;ŰąÙŸ ŰȘ۱ŰȘÛŒŰšŰ§ŰȘ &gt; Ű§Ű·Ù„Ű§ŰčۧŰȘ میÚș Ú©ŰłÛŒ ŰšÚŸÛŒ وقŰȘ ۧ۳ Ű§ÛŒÙŸ کی Ű§Ű·Ù„Ű§ŰčۧŰȘ کو ÙŸÚ‘ÚŸÙ†Û’ Ű§ÙˆŰ± ŰšÚŸÛŒŰŹÙ†Û’ کی Ű§ÛÙ„ÛŒŰȘ کۧ Ù†ŰžÙ… ک۱ ŰłÚ©ŰȘے ہیÚș۔"</string>
     <string name="permission_app_streaming_summary" msgid="606923325679670624">"Ű§ÙŸÙ†Û’ فون کی Ű§ÛŒÙŸŰł کی ŰłÙ„ŰłÙ„Û ŰšÙ†ŰŻÛŒ Ú©Ű±ÛŒÚș"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
     <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Ű§ÙŸÙ†Û’ فون ŰłÛ’ Ű§ÛŒÙŸŰł Ű§ÙˆŰ± ۳۳Ùčم کی ŰŻÛŒÚŻŰ± ŰźŰ”ÙˆŰ”ÛŒŰ§ŰȘ کی ŰłÙ„ŰłÙ„Û ŰšÙ†ŰŻÛŒ Ú©Ű±ÛŒÚș"</string>
diff --git a/packages/CompanionDeviceManager/res/values-uz/strings.xml b/packages/CompanionDeviceManager/res/values-uz/strings.xml
index 16a7d1d..8defcbc 100644
--- a/packages/CompanionDeviceManager/res/values-uz/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-uz/strings.xml
@@ -56,28 +56,18 @@
     <string name="permission_nearby_devices" msgid="7530973297737123481">"Atrofdagi qurilmalar"</string>
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Media chiqishini tanlash"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Suratlar va media"</string>
-    <!-- no translation found for permission_notifications (4099418516590632909) -->
-    <skip />
+    <string name="permission_notifications" msgid="4099418516590632909">"Bildirishnomalar"</string>
     <string name="permission_app_streaming" msgid="6009695219091526422">"Ilovalar"</string>
     <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Striming"</string>
-    <!-- no translation found for permission_phone_summary (8246321093970051702) -->
-    <skip />
-    <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
-    <skip />
-    <!-- no translation found for permission_sms_summary (8499509535410068616) -->
-    <skip />
-    <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
-    <skip />
-    <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
-    <skip />
-    <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
-    <skip />
-    <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
-    <skip />
-    <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
-    <skip />
-    <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
-    <skip />
+    <string name="permission_phone_summary" msgid="8246321093970051702">"Telefon qilish va chaqiruvlarni boshqarish"</string>
+    <string name="permission_call_logs_summary" msgid="7545243592757693321">"Telefon chaqiruvlari jurnalini koʻrish va oʻzgartirish"</string>
+    <string name="permission_sms_summary" msgid="8499509535410068616">"SMS yuborish va ularni oʻqish"</string>
+    <string name="permission_contacts_summary" msgid="2840800622763086808">"Kontaktlar bilan ishlash"</string>
+    <string name="permission_calendar_summary" msgid="8430353935747336165">"Taqvim bilan ishlash"</string>
+    <string name="permission_microphone_summary" msgid="4862628553869973259">"Audio yozib olish"</string>
+    <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Atrofdagi qurilmalarni qidirish, joylashuvini aniqlash va ularga ulanish"</string>
+    <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Barcha bildirishnomalarni, jumladan, kontaktlar, xabarlar va suratlarni oʻqish"</string>
+    <string name="permission_notifications_summary" msgid="2272810466047367030">"• Barcha bildirishnomalarni, jumladan, kontaktlar, xabarlar va suratlarni oÊ»qish&lt;br/&gt;• Bildirishnoma yuborish&lt;br/&gt;&lt;br/&gt;Ilovaning bildirishnomalarga ruxsatini istalgan vaqt Sozlamalar > Bildirishnomalar orqali boshqarish mumkin."</string>
     <string name="permission_app_streaming_summary" msgid="606923325679670624">"Telefondagi ilovalarni translatsiya qilish"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
     <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Telefoningizdan ilovalar va tizim funksiyalarini translatsiya qilish"</string>
diff --git a/packages/CompanionDeviceManager/res/values-vi/strings.xml b/packages/CompanionDeviceManager/res/values-vi/strings.xml
index a080a5c..7797008 100644
--- a/packages/CompanionDeviceManager/res/values-vi/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-vi/strings.xml
@@ -54,36 +54,24 @@
     <string name="permission_microphone" msgid="2152206421428732949">"Micrô"</string>
     <string name="permission_call_logs" msgid="5546761417694586041">"Nháș­t ký cuộc gọi"</string>
     <string name="permission_nearby_devices" msgid="7530973297737123481">"Thiáșżt bị ở gáș§n"</string>
-    <!-- no translation found for permission_media_routing_control (5498639511586715253) -->
-    <skip />
+    <string name="permission_media_routing_control" msgid="5498639511586715253">"Thay đổi đáș§u ra đa phÆ°ÆĄng tiện"</string>
     <string name="permission_storage" msgid="6831099350839392343">"áșąnh và nội dung nghe nhìn"</string>
-    <!-- no translation found for permission_notifications (4099418516590632909) -->
-    <skip />
+    <string name="permission_notifications" msgid="4099418516590632909">"Thông báo"</string>
     <string name="permission_app_streaming" msgid="6009695219091526422">"Ớng dỄng"</string>
     <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Truyền trá»±c tuyáșżn"</string>
-    <!-- no translation found for permission_phone_summary (8246321093970051702) -->
-    <skip />
-    <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
-    <skip />
-    <!-- no translation found for permission_sms_summary (8499509535410068616) -->
-    <skip />
-    <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
-    <skip />
-    <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
-    <skip />
-    <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
-    <skip />
-    <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
-    <skip />
-    <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
-    <skip />
-    <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
-    <skip />
+    <string name="permission_phone_summary" msgid="8246321093970051702">"Gọi và quáșŁn lý cuộc gọi điện thoáșĄi"</string>
+    <string name="permission_call_logs_summary" msgid="7545243592757693321">"Đọc và ghi nháș­t ký cuộc gọi điện thoáșĄi"</string>
+    <string name="permission_sms_summary" msgid="8499509535410068616">"Gá»­i và xem tin nháșŻn SMS"</string>
+    <string name="permission_contacts_summary" msgid="2840800622763086808">"Truy cáș­p vào danh báșĄ cá»§a báșĄn"</string>
+    <string name="permission_calendar_summary" msgid="8430353935747336165">"Truy cáș­p lịch cá»§a báșĄn"</string>
+    <string name="permission_microphone_summary" msgid="4862628553869973259">"Ghi âm"</string>
+    <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Tìm, káșżt nối và xác định vị trí tÆ°ÆĄng đối cá»§a các thiáșżt bị ở gáș§n"</string>
+    <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Đọc táș„t cáșŁ thông báo, kể cáșŁ những thông tin như danh báșĄ, tin nháșŻn và áșŁnh"</string>
+    <string name="permission_notifications_summary" msgid="2272810466047367030">"• Đọc táș„t cáșŁ thông báo, kể cáșŁ những thông tin như danh báșĄ, tin nháșŻn và áșŁnh&lt;br/&gt;• Gá»­i thông báo&lt;br/&gt;&lt;br/&gt;BáșĄn có thể quáșŁn lý tính năng đọc và gá»­i thông báo cá»§a ứng dỄng này báș„t cứ lúc nào trong pháș§n Cài đáș·t &gt; Thông báo."</string>
     <string name="permission_app_streaming_summary" msgid="606923325679670624">"Truyền các ứng dỄng trên điện thoáșĄi cá»§a báșĄn"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
     <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Truyền trá»±c tuyáșżn ứng dỄng và các tính năng khác cá»§a hệ thống từ điện thoáșĄi cá»§a báșĄn"</string>
-    <!-- no translation found for permission_media_routing_control_summary (2714631092321412250) -->
-    <skip />
+    <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"Truy cáș­p danh sách thiáșżt bị hiện có và kiểm soát việc thiáșżt bị nào sáșœ phát trá»±c tuyáșżn hoáș·c truyền âm thanh hoáș·c video từ các ứng dỄng khác"</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"điện thoáșĄi"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"máy tính báșŁng"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml b/packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml
index aaa5bef..11e3332 100644
--- a/packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml
@@ -56,28 +56,18 @@
     <string name="permission_nearby_devices" msgid="7530973297737123481">"é™„èż‘çš„èźŸć€‡"</string>
     <string name="permission_media_routing_control" msgid="5498639511586715253">"曎æ”čćȘ’䜓蟓ć‡ș"</string>
     <string name="permission_storage" msgid="6831099350839392343">"照片撌ćȘ’äœ“ć†…ćźč"</string>
-    <!-- no translation found for permission_notifications (4099418516590632909) -->
-    <skip />
+    <string name="permission_notifications" msgid="4099418516590632909">"通矄"</string>
     <string name="permission_app_streaming" msgid="6009695219091526422">"ćș”甚"</string>
     <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"æ”ćŒäŒ èŸ“"</string>
-    <!-- no translation found for permission_phone_summary (8246321093970051702) -->
-    <skip />
-    <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
-    <skip />
-    <!-- no translation found for permission_sms_summary (8499509535410068616) -->
-    <skip />
-    <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
-    <skip />
-    <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
-    <skip />
-    <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
-    <skip />
-    <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
-    <skip />
-    <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
-    <skip />
-    <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
-    <skip />
+    <string name="permission_phone_summary" msgid="8246321093970051702">"æ‹šæ‰“ç””èŻć’ŒçźĄç†é€šèŻ"</string>
+    <string name="permission_call_logs_summary" msgid="7545243592757693321">"èŻ»ć–ć’Œć†™ć…„æ‰‹æœșé€šèŻèź°ćœ•"</string>
+    <string name="permission_sms_summary" msgid="8499509535410068616">"ć‘é€ć’ŒæŸ„çœ‹çŸ­äżĄ"</string>
+    <string name="permission_contacts_summary" msgid="2840800622763086808">"èźżé—źæ‚šçš„é€šèźŻćœ•"</string>
+    <string name="permission_calendar_summary" msgid="8430353935747336165">"èźżé—źæ‚šçš„æ—„ćŽ†"</string>
+    <string name="permission_microphone_summary" msgid="4862628553869973259">"ćœ•éŸł"</string>
+    <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"æŸ„æ‰Ÿă€èżžæŽ„é™„èż‘çš„èźŸć€‡ä»„ćŠçĄźćźšé™„èż‘èźŸć€‡çš„ç›žćŻčäœçœź"</string>
+    <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"èŻ»ć–æ‰€æœ‰é€šçŸ„ïŒŒćŒ…æ‹Źé€šèźŻćœ•ă€æ¶ˆæŻć’Œç…§ç‰‡ç­‰äżĄæŻ"</string>
+    <string name="permission_notifications_summary" msgid="2272810466047367030">"• èŻ»ć–æ‰€æœ‰é€šçŸ„ïŒŒćŒ…æ‹Źé€šèźŻćœ•ă€æ¶ˆæŻć’Œç…§ç‰‡ç­‰äżĄæŻ&lt;br/&gt;• ć‘é€é€šçŸ„&lt;br/&gt;&lt;br/&gt;æ‚šéšæ—¶ćŻä»„äŸæŹĄć‰ćŸ€“èźŸçœź”&gt;“通矄”ćč¶ćœšć…¶äž­çźĄç†æ­€ćș”ç”šèŻ»ć–ć’Œć‘é€é€šçŸ„çš„æƒé™ă€‚"</string>
     <string name="permission_app_streaming_summary" msgid="606923325679670624">"æ”ćŒäŒ èŸ“æ‰‹æœș侊的ćș”甚"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
     <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"从悚的手æœșæ”ćŒäŒ èŸ“ćș”ç”šć’Œć…¶ä»–çł»ç»ŸćŠŸèƒœ"</string>
diff --git a/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml b/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml
index 7c28623..e340d5a 100644
--- a/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml
@@ -56,28 +56,18 @@
     <string name="permission_nearby_devices" msgid="7530973297737123481">"é™„èż‘çš„èŁçœź"</string>
     <string name="permission_media_routing_control" msgid="5498639511586715253">"èźŠæ›ŽćȘ’體茞ć‡ș"</string>
     <string name="permission_storage" msgid="6831099350839392343">"盾片撌ćȘ’é«”"</string>
-    <!-- no translation found for permission_notifications (4099418516590632909) -->
-    <skip />
+    <string name="permission_notifications" msgid="4099418516590632909">"通矄"</string>
     <string name="permission_app_streaming" msgid="6009695219091526422">"æ‡‰ç”šçš‹ćŒ"</string>
     <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"äžČ攁"</string>
-    <!-- no translation found for permission_phone_summary (8246321093970051702) -->
-    <skip />
-    <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
-    <skip />
-    <!-- no translation found for permission_sms_summary (8499509535410068616) -->
-    <skip />
-    <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
-    <skip />
-    <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
-    <skip />
-    <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
-    <skip />
-    <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
-    <skip />
-    <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
-    <skip />
-    <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
-    <skip />
+    <string name="permission_phone_summary" msgid="8246321093970051702">"æ’„æ‰“ćŠçźĄç†é€šè©±"</string>
+    <string name="permission_call_logs_summary" msgid="7545243592757693321">"èź€ć–ćŠćŻ«ć…„æ‰‹æ©Ÿé€šè©±èš˜éŒ„"</string>
+    <string name="permission_sms_summary" msgid="8499509535410068616">"ć‚łé€ć’ŒæŸ„çœ‹çŸ­èšŠ"</string>
+    <string name="permission_contacts_summary" msgid="2840800622763086808">"ć­˜ć–äœ çš„é€šèšŠéŒ„"</string>
+    <string name="permission_calendar_summary" msgid="8430353935747336165">"ć­˜ć–äœ çš„æ—„æ›†"</string>
+    <string name="permission_microphone_summary" msgid="4862628553869973259">"錄音"</string>
+    <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"ć°‹æ‰Ÿă€é€ŁæŽ„ćŠćˆ€æ–·é™„èż‘èŁçœźçš„ç›žć°äœçœź"</string>
+    <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"èź€ć–æ‰€æœ‰é€šçŸ„ïŒŒćŒ…æ‹ŹèŻç”Ąäșșă€èšŠæŻć’Œç›žç‰‡ç­‰èł‡èšŠ"</string>
+    <string name="permission_notifications_summary" msgid="2272810466047367030">"• èź€ć–æ‰€æœ‰é€šçŸ„ïŒŒćŒ…æ‹ŹèŻç”Ąäșșă€èšŠæŻć’Œç›žç‰‡ç­‰èł‡èšŠ&lt;br/&gt;• ć‚łé€é€šçŸ„&lt;br/&gt;&lt;br/&gt;äœ éššæ™‚ćŻć‰ćŸ€ [èš­ćźš] &gt; [通矄]ïŒŒçźĄç†æ­€æ‡‰ç”šçš‹ćŒèź€ć–ć’Œć‚łé€é€šçŸ„çš„ćŠŸèƒœă€‚"</string>
     <string name="permission_app_streaming_summary" msgid="606923325679670624">"äžČæ”æ’­æ”Ÿæ‰‹æ©Ÿæ‡‰ç”šçš‹ćŒć…§ćźč"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
     <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"äžČæ”æ’­æ”Ÿæ‰‹æ©Ÿäž­çš„æ‡‰ç”šçš‹ćŒć’Œć…¶ä»–çł»ç”±ćŠŸèƒœ"</string>
diff --git a/packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml b/packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml
index fa2d60c..82ad021 100644
--- a/packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml
@@ -56,28 +56,18 @@
     <string name="permission_nearby_devices" msgid="7530973297737123481">"é„°èż‘èŁçœź"</string>
     <string name="permission_media_routing_control" msgid="5498639511586715253">"èźŠæ›ŽćȘ’體茞ć‡ș"</string>
     <string name="permission_storage" msgid="6831099350839392343">"盾片撌ćȘ’é«”"</string>
-    <!-- no translation found for permission_notifications (4099418516590632909) -->
-    <skip />
+    <string name="permission_notifications" msgid="4099418516590632909">"通矄"</string>
     <string name="permission_app_streaming" msgid="6009695219091526422">"æ‡‰ç”šçš‹ćŒ"</string>
     <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"äžČ攁"</string>
-    <!-- no translation found for permission_phone_summary (8246321093970051702) -->
-    <skip />
-    <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
-    <skip />
-    <!-- no translation found for permission_sms_summary (8499509535410068616) -->
-    <skip />
-    <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
-    <skip />
-    <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
-    <skip />
-    <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
-    <skip />
-    <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
-    <skip />
-    <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
-    <skip />
-    <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
-    <skip />
+    <string name="permission_phone_summary" msgid="8246321093970051702">"æ’„æ‰“é›»è©±ćŠçźĄç†é€šè©±"</string>
+    <string name="permission_call_logs_summary" msgid="7545243592757693321">"èź€ć–ćŠćŻ«ć…„é€šè©±èš˜éŒ„"</string>
+    <string name="permission_sms_summary" msgid="8499509535410068616">"ć‚łé€ćŠæŸ„çœ‹ç°ĄèšŠ"</string>
+    <string name="permission_contacts_summary" msgid="2840800622763086808">"ć­˜ć–äœ çš„èŻç”Ąäșș"</string>
+    <string name="permission_calendar_summary" msgid="8430353935747336165">"ć­˜ć–äœ çš„æ—„æ›†"</string>
+    <string name="permission_microphone_summary" msgid="4862628553869973259">"錄音"</string>
+    <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"ć°‹æ‰Ÿă€é€Łç·šćŠćˆ€æ–·é„°èż‘èŁçœźçš„ç›žć°äœçœź"</string>
+    <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"èź€ć–æ‰€æœ‰é€šçŸ„ïŒŒćŒ…æ‹ŹèŻç”Ąäșșă€èšŠæŻć’Œç›žç‰‡ç­‰èł‡èšŠ"</string>
+    <string name="permission_notifications_summary" msgid="2272810466047367030">"• èź€ć–æ‰€æœ‰é€šçŸ„ïŒŒćŒ…æ‹ŹèŻç”Ąäșșă€èšŠæŻć’Œç›žç‰‡ç­‰èł‡èšŠ&lt;br/&gt;• ć‚łé€é€šçŸ„&lt;br/&gt;&lt;br/&gt;äœ éššæ™‚ćŻä»„ć‰ćŸ€ă€Œèš­ćźšă€&gt;ă€Œé€šçŸ„ă€ïŒŒçźĄç†é€™ć€‹æ‡‰ç”šçš‹ćŒèź€ć–ć’Œć‚łé€é€šçŸ„çš„ćŠŸèƒœă€‚"</string>
     <string name="permission_app_streaming_summary" msgid="606923325679670624">"äžČæ”ć‚łèŒžæ‰‹æ©Ÿæ‡‰ç”šçš‹ćŒć…§ćźč"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
     <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"äžČæ”æ’­æ”Ÿæ‰‹æ©Ÿäž­çš„æ‡‰ç”šçš‹ćŒć’Œć…¶ä»–çł»ç”±ćŠŸèƒœ"</string>
diff --git a/packages/CompanionDeviceManager/res/values-zu/strings.xml b/packages/CompanionDeviceManager/res/values-zu/strings.xml
index 2d90872..c35ff6a 100644
--- a/packages/CompanionDeviceManager/res/values-zu/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-zu/strings.xml
@@ -56,28 +56,18 @@
     <string name="permission_nearby_devices" msgid="7530973297737123481">"Amadivayisi aseduze"</string>
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Shintsha umphumela wemidiya"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Izithombe nemidiya"</string>
-    <!-- no translation found for permission_notifications (4099418516590632909) -->
-    <skip />
+    <string name="permission_notifications" msgid="4099418516590632909">"Izaziso"</string>
     <string name="permission_app_streaming" msgid="6009695219091526422">"Ama-app"</string>
     <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Iyasakaza"</string>
-    <!-- no translation found for permission_phone_summary (8246321093970051702) -->
-    <skip />
-    <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
-    <skip />
-    <!-- no translation found for permission_sms_summary (8499509535410068616) -->
-    <skip />
-    <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
-    <skip />
-    <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
-    <skip />
-    <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
-    <skip />
-    <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
-    <skip />
-    <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
-    <skip />
-    <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
-    <skip />
+    <string name="permission_phone_summary" msgid="8246321093970051702">"Yenza futhi ulawule amakholi efoni"</string>
+    <string name="permission_call_logs_summary" msgid="7545243592757693321">"Funda futhi ubhale irekhodi lamakholi efoni"</string>
+    <string name="permission_sms_summary" msgid="8499509535410068616">"Thumela futhi ubuke imiyalezo ye-SMS"</string>
+    <string name="permission_contacts_summary" msgid="2840800622763086808">"Finyelela koxhumana nabo"</string>
+    <string name="permission_calendar_summary" msgid="8430353935747336165">"Finyelela ikhalenda lakho"</string>
+    <string name="permission_microphone_summary" msgid="4862628553869973259">"Rekhoda umsindo"</string>
+    <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Thola, uxhume, futhi unqume indawo yamadivayisi aseduze"</string>
+    <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Funda zonke izaziso, okuhlanganisa ulwazi loxhumana nabo, imiyalezo, nezithombe"</string>
+    <string name="permission_notifications_summary" msgid="2272810466047367030">"• Funda zonke izaziso, okuhlanganisa ulwazi loxhumana nabo, imiyalezo, nezithombe&lt;br/&gt;•Thumela izaziso&lt;br/&gt;&lt;br/&gt;Ungakwazi ukulawula ikhono lale app lokufunda nokuthumela izaziso noma nini kokuthi Amasethingi Nezaziso."</string>
     <string name="permission_app_streaming_summary" msgid="606923325679670624">"Sakaza ama-app wefoni yakho"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
     <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Sakaza ama-app nezinye izakhi zesistimu kusuka kufoni yakho"</string>
diff --git a/packages/CredentialManager/res/drawable/fill_dialog_dynamic_list_item_one.xml b/packages/CredentialManager/res/drawable/fill_dialog_dynamic_list_item_one.xml
new file mode 100644
index 0000000..5becc86
--- /dev/null
+++ b/packages/CredentialManager/res/drawable/fill_dialog_dynamic_list_item_one.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2024 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:tools="http://schemas.android.com/tools" tools:ignore="NewApi"
+        android:color="@android:color/transparent">
+    <item
+        android:bottom="1dp"
+        android:shape="rectangle"
+        android:top="1dp">
+        <shape>
+            <corners android:radius="16dp" />
+            <solid android:color="@color/dropdown_container" />
+        </shape>
+    </item>
+</ripple>
\ No newline at end of file
diff --git a/packages/CredentialManager/res/drawable/fill_dialog_dynamic_list_item_one_dark.xml b/packages/CredentialManager/res/drawable/fill_dialog_dynamic_list_item_one_dark.xml
new file mode 100644
index 0000000..39f49ca
--- /dev/null
+++ b/packages/CredentialManager/res/drawable/fill_dialog_dynamic_list_item_one_dark.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2023 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:tools="http://schemas.android.com/tools" tools:ignore="NewApi"
+        android:color="@android:color/transparent">
+    <item
+        android:bottom="1dp"
+        android:shape="rectangle"
+        android:top="1dp">
+        <shape>
+            <corners android:radius="28dp" />
+            <solid android:color="@android:color/system_surface_container_high_dark" />
+        </shape>
+    </item>
+</ripple>
\ No newline at end of file
diff --git a/packages/CredentialManager/res/drawable/more_horiz_24px.xml b/packages/CredentialManager/res/drawable/more_horiz_24px.xml
new file mode 100644
index 0000000..7b235f8
--- /dev/null
+++ b/packages/CredentialManager/res/drawable/more_horiz_24px.xml
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="960"
+    android:viewportHeight="960"
+    android:tint="?attr/colorControlNormal">
+  <path
+      android:fillColor="@android:color/white"
+      android:pathData="M240,560Q207,560 183.5,536.5Q160,513 160,480Q160,447 183.5,423.5Q207,400 240,400Q273,400 296.5,423.5Q320,447 320,480Q320,513 296.5,536.5Q273,560 240,560ZM480,560Q447,560 423.5,536.5Q400,513 400,480Q400,447 423.5,423.5Q447,400 480,400Q513,400 536.5,423.5Q560,447 560,480Q560,513 536.5,536.5Q513,560 480,560ZM720,560Q687,560 663.5,536.5Q640,513 640,480Q640,447 663.5,423.5Q687,400 720,400Q753,400 776.5,423.5Q800,447 800,480Q800,513 776.5,536.5Q753,560 720,560Z"/>
+</vector>
diff --git a/packages/CredentialManager/res/layout/credman_dropdown_presentation_layout.xml b/packages/CredentialManager/res/layout/credman_dropdown_presentation_layout.xml
new file mode 100644
index 0000000..cb6c6b4
--- /dev/null
+++ b/packages/CredentialManager/res/layout/credman_dropdown_presentation_layout.xml
@@ -0,0 +1,45 @@
+<!--
+  ~ Copyright (C) 2023 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+                android:id="@android:id/content"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:maxWidth="@dimen/autofill_dropdown_layout_width"
+                android:elevation="3dp">
+
+        <ImageView
+            android:id="@android:id/icon1"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_centerVertical="true"
+            android:layout_alignParentStart="true"
+            android:background="@null"/>
+        <TextView
+            android:id="@android:id/text1"
+            android:layout_width="@dimen/autofill_dropdown_text_width"
+            android:layout_height="wrap_content"
+            android:layout_alignParentTop="true"
+            android:layout_toEndOf="@android:id/icon1"
+            style="@style/autofill.TextTitle"/>
+        <TextView
+            android:id="@android:id/text2"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_below="@android:id/text1"
+            android:layout_toEndOf="@android:id/icon1"
+            style="@style/autofill.TextSubtitle"/>
+
+</RelativeLayout>
diff --git a/packages/CredentialManager/res/values/colors.xml b/packages/CredentialManager/res/values/colors.xml
new file mode 100644
index 0000000..dcb7ef9
--- /dev/null
+++ b/packages/CredentialManager/res/values/colors.xml
@@ -0,0 +1,23 @@
+<!--
+  ~ Copyright (C) 2023 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<!-- Color palette -->
+<resources>
+    <!-- These colors are used for Remote Views. -->
+    <color name="text_primary">#1A1B20</color>
+    <color name="text_secondary">#44474F</color>
+    <color name="dropdown_container">#F3F3FA</color>
+</resources>
\ No newline at end of file
diff --git a/packages/CredentialManager/res/values/dimens.xml b/packages/CredentialManager/res/values/dimens.xml
new file mode 100644
index 0000000..2a4719d
--- /dev/null
+++ b/packages/CredentialManager/res/values/dimens.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+  ~ Copyright (C) 2023 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<resources>
+    <dimen name="autofill_view_top_padding">12dp</dimen>
+    <dimen name="autofill_view_right_padding">24dp</dimen>
+    <dimen name="autofill_view_bottom_padding">12dp</dimen>
+    <dimen name="autofill_view_left_padding">16dp</dimen>
+    <dimen name="autofill_view_icon_to_text_padding">10dp</dimen>
+    <dimen name="autofill_icon_size">24dp</dimen>
+    <dimen name="autofill_dropdown_layout_width">296dp</dimen>
+    <dimen name="autofill_dropdown_text_width">240dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/packages/CredentialManager/res/values/styles.xml b/packages/CredentialManager/res/values/styles.xml
new file mode 100644
index 0000000..7de941e
--- /dev/null
+++ b/packages/CredentialManager/res/values/styles.xml
@@ -0,0 +1,27 @@
+<!--
+  ~ Copyright (C) 2023 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="autofill.TextTitle" parent="">
+        <item name="android:fontFamily">google-sans-medium</item>
+        <item name="android:textSize">14sp</item>
+    </style>
+
+    <style name="autofill.TextSubtitle" parent="">
+        <item name="android:fontFamily">google-sans-text</item>
+        <item name="android:textSize">12sp</item>
+    </style>
+</resources>
\ No newline at end of file
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
index a78509d..c0d7149 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
@@ -53,6 +53,7 @@
     isNewActivity: Boolean,
 ) {
     val requestInfo: RequestInfo?
+    var isReqForAllOptions: Boolean = false
     private val providerEnabledList: List<ProviderData>
     private val providerDisabledList: List<DisabledProviderData>?
     val resultReceiver: ResultReceiver?
@@ -102,6 +103,11 @@
             ResultReceiver::class.java
         )
 
+        isReqForAllOptions = intent.getBooleanExtra(
+                Constants.EXTRA_REQ_FOR_ALL_OPTIONS,
+                /*defaultValue=*/ false
+        )
+
         val cancellationRequest = getCancelUiRequest(intent)
         val cancelUiRequestState = cancellationRequest?.let {
             CancelUiRequestState(getAppLabel(context.getPackageManager(), it.appPackageName))
@@ -141,7 +147,8 @@
                 )
             }
             RequestInfo.TYPE_GET -> {
-                val getCredentialInitialUiState = getCredentialInitialUiState(originName)!!
+                val getCredentialInitialUiState = getCredentialInitialUiState(originName,
+                        isReqForAllOptions)!!
                 val autoSelectEntry =
                     findAutoSelectEntry(getCredentialInitialUiState.providerDisplayInfo)
                 UiState(
@@ -216,14 +223,18 @@
     }
 
     // IMPORTANT: new invocation should be mindful that this method can throw.
-    private fun getCredentialInitialUiState(originName: String?): GetCredentialUiState? {
+    private fun getCredentialInitialUiState(
+            originName: String?,
+            isReqForAllOptions: Boolean
+    ): GetCredentialUiState? {
         val providerEnabledList = GetFlowUtils.toProviderList(
             providerEnabledList as List<GetCredentialProviderData>, context
         )
         val requestDisplayInfo = GetFlowUtils.toRequestDisplayInfo(requestInfo, context, originName)
         return GetCredentialUiState(
-            providerEnabledList,
-            requestDisplayInfo ?: return null,
+                isReqForAllOptions,
+                providerEnabledList,
+                requestDisplayInfo ?: return null
         )
     }
 
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
index 20d2f09..58467af 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
@@ -16,18 +16,21 @@
 
 package com.android.credentialmanager.autofill
 
+import android.R
 import android.app.assist.AssistStructure
 import android.content.Context
-import android.credentials.CredentialManager
-import android.credentials.CredentialOption
-import android.credentials.GetCandidateCredentialsException
-import android.credentials.GetCandidateCredentialsResponse
+import android.app.PendingIntent
+import android.credentials.GetCredentialResponse
 import android.credentials.GetCredentialRequest
-import android.credentials.ui.GetCredentialProviderData
+import android.credentials.GetCandidateCredentialsResponse
+import android.credentials.GetCandidateCredentialsException
+import android.credentials.CredentialOption
 import android.graphics.drawable.Icon
+import android.credentials.ui.GetCredentialProviderData
 import android.os.Bundle
 import android.os.CancellationSignal
 import android.os.OutcomeReceiver
+import android.credentials.Credential
 import android.service.autofill.AutofillService
 import android.service.autofill.Dataset
 import android.service.autofill.Field
@@ -40,27 +43,35 @@
 import android.service.autofill.SaveRequest
 import android.service.credentials.CredentialProviderService
 import android.util.Log
+import android.view.autofill.AutofillValue
+import android.view.autofill.IAutoFillManagerClient
 import android.view.autofill.AutofillId
-import org.json.JSONException
 import android.widget.inline.InlinePresentationSpec
+import android.credentials.CredentialManager
 import androidx.autofill.inline.v1.InlineSuggestionUi
 import androidx.credentials.provider.CustomCredentialEntry
 import androidx.credentials.provider.PasswordCredentialEntry
 import androidx.credentials.provider.PublicKeyCredentialEntry
 import com.android.credentialmanager.GetFlowUtils
-import com.android.credentialmanager.model.get.CredentialEntryInfo
+import com.android.credentialmanager.common.ui.RemoteViewsFactory
 import com.android.credentialmanager.getflow.ProviderDisplayInfo
-import com.android.credentialmanager.model.get.ProviderInfo
 import com.android.credentialmanager.getflow.toProviderDisplayInfo
 import com.android.credentialmanager.ktx.credentialEntry
+import com.android.credentialmanager.model.CredentialType
+import com.android.credentialmanager.model.get.CredentialEntryInfo
+import com.android.credentialmanager.model.get.ProviderInfo
+import org.json.JSONException
 import org.json.JSONObject
 import java.util.concurrent.Executors
 
+
 class CredentialAutofillService : AutofillService() {
 
     companion object {
         private const val TAG = "CredAutofill"
 
+        private const val SESSION_ID_KEY = "autofill_session_id"
+        private const val REQUEST_ID_KEY = "autofill_request_id"
         private const val CRED_HINT_PREFIX = "credential="
         private const val REQUEST_DATA_KEY = "requestData"
         private const val CANDIDATE_DATA_KEY = "candidateQueryData"
@@ -75,12 +86,36 @@
             cancellationSignal: CancellationSignal,
             callback: FillCallback
     ) {
+    }
+
+    override fun onFillCredentialRequest(
+            request: FillRequest,
+            cancellationSignal: CancellationSignal,
+            callback: FillCallback,
+            autofillCallback: IAutoFillManagerClient
+    ) {
         val context = request.fillContexts
         val structure = context[context.size - 1].structure
         val callingPackage = structure.activityComponent.packageName
-        Log.i(TAG, "onFillRequest called for $callingPackage")
+        Log.i(TAG, "onFillCredentialRequest called for $callingPackage")
 
-        val getCredRequest: GetCredentialRequest? = getCredManRequest(structure)
+        val clientState = request.clientState
+        if (clientState == null) {
+            Log.i(TAG, "Client state not found")
+            callback.onFailure("Client state not found")
+            return
+        }
+        val sessionId = clientState.getInt(SESSION_ID_KEY)
+        val requestId = clientState.getInt(REQUEST_ID_KEY)
+        Log.i(TAG, "Autofill sessionId: $sessionId, autofill requestId: $requestId")
+        if (sessionId == 0 || requestId == 0) {
+            Log.i(TAG, "Session Id or request Id not found")
+            callback.onFailure("Session Id or request Id not found")
+            return
+        }
+
+        val getCredRequest: GetCredentialRequest? = getCredManRequest(structure, sessionId,
+            requestId)
         if (getCredRequest == null) {
             Log.i(TAG, "No credential manager request found")
             callback.onFailure("No credential manager request found")
@@ -93,7 +128,31 @@
                 GetCandidateCredentialsException> {
             override fun onResult(result: GetCandidateCredentialsResponse) {
                 Log.i(TAG, "getCandidateCredentials onResponse")
-                val fillResponse = convertToFillResponse(result, request)
+
+                if (result.getCredentialResponse != null) {
+                    val autofillId: AutofillId? = result.getCredentialResponse
+                            .credential.data.getParcelable(
+                                    CredentialProviderService.EXTRA_AUTOFILL_ID,
+                                    AutofillId::class.java)
+                    Log.i(TAG, "getCandidateCredentials final response, autofillId: " +
+                            autofillId)
+
+                    if (autofillId != null) {
+                        autofillCallback.autofill(
+                                sessionId,
+                                mutableListOf(autofillId),
+                                mutableListOf(
+                                        AutofillValue.forText(
+                                                convertResponseToJson(result.getCredentialResponse)
+                                        )
+                                ),
+                                false)
+                    }
+                    return
+                }
+
+                val fillResponse = convertToFillResponse(result, request,
+                        this@CredentialAutofillService)
                 if (fillResponse != null) {
                     callback.onSuccess(fillResponse)
                 } else {
@@ -113,10 +172,62 @@
                 callingPackage,
                 CancellationSignal(),
                 Executors.newSingleThreadExecutor(),
-                outcome
+                outcome,
+                autofillCallback
         )
     }
 
+    // TODO(b/318118018): Use from Jetpack
+    private fun convertResponseToJson(response: GetCredentialResponse): String? {
+        try {
+            val jsonObject = JSONObject()
+            jsonObject.put("type", "get")
+            val jsonCred = JSONObject()
+            jsonCred.put("type", response.credential.type)
+            jsonCred.put("data", credentialToJSON(
+                    response.credential))
+            jsonObject.put("credential", jsonCred)
+            return jsonObject.toString()
+        } catch (e: JSONException) {
+            Log.i(
+                    TAG, "Exception while constructing response JSON: " +
+                    e.message
+            )
+        }
+        return null
+    }
+
+    // TODO(b/318118018): Replace with calls to Jetpack
+    private fun credentialToJSON(credential: Credential): JSONObject? {
+        Log.i(TAG, "credentialToJSON")
+        try {
+            if (credential.type == "android.credentials.TYPE_PASSWORD_CREDENTIAL") {
+                Log.i(TAG, "toJSON PasswordCredential")
+
+                val json = JSONObject()
+                val id = credential.data.getString("androidx.credentials.BUNDLE_KEY_ID")
+                val pass = credential.data.getString("androidx.credentials.BUNDLE_KEY_PASSWORD")
+                json.put("androidx.credentials.BUNDLE_KEY_ID", id)
+                json.put("androidx.credentials.BUNDLE_KEY_PASSWORD", pass)
+                return json
+            } else if (credential.type == "androidx.credentials.TYPE_PUBLIC_KEY_CREDENTIAL") {
+                Log.i(TAG, "toJSON PublicKeyCredential")
+
+                val json = JSONObject()
+                val responseJson = credential
+                        .data
+                        .getString("androidx.credentials.BUNDLE_KEY_AUTHENTICATION_RESPONSE_JSON")
+                json.put("androidx.credentials.BUNDLE_KEY_AUTHENTICATION_RESPONSE_JSON",
+                        responseJson)
+                return json
+            }
+        } catch (e: JSONException) {
+            Log.i(TAG, "issue while converting credential response to JSON")
+        }
+        Log.i(TAG, "Unsupported credential type")
+        return null
+    }
+
     private fun getEntryToIconMap(
             candidateProviderDataList: MutableList<GetCredentialProviderData>
     ): Map<String, Icon> {
@@ -127,9 +238,11 @@
                     is PasswordCredentialEntry -> {
                         entryIconMap[entry.key + entry.subkey] = credentialEntry.icon
                     }
+
                     is PublicKeyCredentialEntry -> {
                         entryIconMap[entry.key + entry.subkey] = credentialEntry.icon
                     }
+
                     is CustomCredentialEntry -> {
                         entryIconMap[entry.key + entry.subkey] = credentialEntry.icon
                     }
@@ -146,14 +259,16 @@
 
     private fun convertToFillResponse(
             getCredResponse: GetCandidateCredentialsResponse,
-            filLRequest: FillRequest
+            filLRequest: FillRequest,
+            context: Context
     ): FillResponse? {
         val providerList = GetFlowUtils.toProviderList(
                 getCredResponse.candidateProviderDataList,
-                this@CredentialAutofillService)
+                context)
         if (providerList.isEmpty()) {
             return null
         }
+
         val entryIconMap: Map<String, Icon> =
                 getEntryToIconMap(getCredResponse.candidateProviderDataList)
         val autofillIdToProvidersMap: Map<AutofillId, List<ProviderInfo>> =
@@ -162,7 +277,8 @@
         var validFillResponse = false
         autofillIdToProvidersMap.forEach { (autofillId, providers) ->
             validFillResponse = processProvidersForAutofillId(
-                    filLRequest, autofillId, providers, entryIconMap, fillResponseBuilder)
+                    filLRequest, autofillId, providers, entryIconMap, fillResponseBuilder,
+                    getCredResponse.pendingIntent)
                     .or(validFillResponse)
         }
         if (!validFillResponse) {
@@ -172,11 +288,12 @@
     }
 
     private fun processProvidersForAutofillId(
-        filLRequest: FillRequest,
-        autofillId: AutofillId,
-        providerList: List<ProviderInfo>,
-        entryIconMap: Map<String, Icon>,
-        fillResponseBuilder: FillResponse.Builder
+            filLRequest: FillRequest,
+            autofillId: AutofillId,
+            providerList: List<ProviderInfo>,
+            entryIconMap: Map<String, Icon>,
+            fillResponseBuilder: FillResponse.Builder,
+            bottomSheetPendingIntent: PendingIntent?
     ): Boolean {
         if (providerList.isEmpty()) {
             return false
@@ -197,7 +314,17 @@
         var i = 0
         var datasetAdded = false
 
-        providerDisplayInfo.sortedUserNameToCredentialEntryList.forEach usernameLoop@ {
+        val duplicateDisplayNamesForPasskeys: MutableMap<String, Boolean> = mutableMapOf()
+        providerDisplayInfo.sortedUserNameToCredentialEntryList.forEach {
+            val credentialEntry = it.sortedCredentialEntryList.first()
+            if (credentialEntry.credentialType == CredentialType.PASSKEY) {
+                credentialEntry.displayName?.let { displayName ->
+                    val duplicateEntry = duplicateDisplayNamesForPasskeys.contains(displayName)
+                    duplicateDisplayNamesForPasskeys[displayName] = duplicateEntry
+                }
+            }
+        }
+        providerDisplayInfo.sortedUserNameToCredentialEntryList.forEach usernameLoop@{
             val primaryEntry = it.sortedCredentialEntryList.first()
             val pendingIntent = primaryEntry.pendingIntent
             val fillInIntent = primaryEntry.fillInIntent
@@ -206,37 +333,59 @@
                 Log.e(TAG, "PendingIntent was missing from the entry.")
                 return@usernameLoop
             }
-            if (inlinePresentationSpecs == null || i >= maxItemCount) {
+            if (inlinePresentationSpecs == null) {
+                Log.i(TAG, "Inline presentation spec is null, " +
+                        "building dropdown presentation only")
+            }
+            if (i >= maxItemCount) {
                 Log.e(TAG, "Skipping because reached the max item count.")
                 return@usernameLoop
             }
-            // Create inline presentation
-            val spec: InlinePresentationSpec
-            if (i < inlinePresentationSpecsCount) {
-                spec = inlinePresentationSpecs[i]
-            } else {
-                spec = inlinePresentationSpecs[inlinePresentationSpecsCount - 1]
-            }
-            val sliceBuilder = InlineSuggestionUi
-                    .newContentBuilder(pendingIntent)
-                    .setTitle(primaryEntry.userName)
-            val icon: Icon
-            if (primaryEntry.icon == null) {
+            val icon: Icon = if (primaryEntry.icon == null) {
                 // The empty entry icon has non-null icon reference but null drawable reference.
                 // If the drawable reference is null, then use the default icon.
-                icon = getDefaultIcon()
+                getDefaultIcon()
             } else {
-                icon = entryIconMap[primaryEntry.entryKey + primaryEntry.entrySubkey]
+                entryIconMap[primaryEntry.entryKey + primaryEntry.entrySubkey]
                         ?: getDefaultIcon()
             }
-            sliceBuilder.setStartIcon(icon)
-            val inlinePresentation = InlinePresentation(
-                    sliceBuilder.build().slice, spec, /* pinned= */ false)
+            // Create inline presentation
+            var inlinePresentation: InlinePresentation? = null
+            var spec: InlinePresentationSpec?
+            if (inlinePresentationSpecs != null) {
+                if (i < inlinePresentationSpecsCount) {
+                    spec = inlinePresentationSpecs[i]
+                } else {
+                    spec = inlinePresentationSpecs[inlinePresentationSpecsCount - 1]
+                }
+                val displayName: String = if (primaryEntry.credentialType == CredentialType.PASSKEY
+                        && primaryEntry.displayName != null) {
+                    primaryEntry.displayName!!
+                } else {
+                    primaryEntry.userName
+                }
+                val sliceBuilder = InlineSuggestionUi
+                        .newContentBuilder(pendingIntent)
+                        .setTitle(displayName)
+                sliceBuilder.setStartIcon(icon)
+                if (primaryEntry.credentialType ==
+                        CredentialType.PASSKEY && duplicateDisplayNamesForPasskeys[displayName]
+                        == true) {
+                    sliceBuilder.setSubtitle(primaryEntry.userName)
+                }
+                inlinePresentation = InlinePresentation(
+                        sliceBuilder.build().slice, spec, /* pinned= */ false)
+            }
+            val dropdownPresentation = RemoteViewsFactory.createDropdownPresentation(
+                    this, icon, primaryEntry)
             i++
 
             val dataSetBuilder = Dataset.Builder()
             val presentationBuilder = Presentations.Builder()
-                    .setInlinePresentation(inlinePresentation)
+                    .setMenuPresentation(dropdownPresentation)
+            if (inlinePresentation != null) {
+                presentationBuilder.setInlinePresentation(inlinePresentation)
+            }
 
             fillResponseBuilder.addDataset(
                     dataSetBuilder
@@ -250,9 +399,52 @@
                             .build())
             datasetAdded = true
         }
+        val pinnedSpec = getLastInlinePresentationSpec(inlinePresentationSpecs,
+                inlinePresentationSpecsCount)
+        if (datasetAdded && bottomSheetPendingIntent != null && pinnedSpec != null) {
+            addPinnedInlineSuggestion(bottomSheetPendingIntent, pinnedSpec, autofillId,
+                    fillResponseBuilder)
+        }
         return datasetAdded
     }
 
+    private fun getLastInlinePresentationSpec(
+            inlinePresentationSpecs: List<InlinePresentationSpec>?,
+            inlinePresentationSpecsCount: Int
+    ): InlinePresentationSpec? {
+        if (inlinePresentationSpecs != null) {
+            return inlinePresentationSpecs[inlinePresentationSpecsCount - 1]
+        }
+        return null
+    }
+
+    private fun addPinnedInlineSuggestion(
+            bottomSheetPendingIntent: PendingIntent,
+            spec: InlinePresentationSpec,
+            autofillId: AutofillId,
+            fillResponseBuilder: FillResponse.Builder
+    ) {
+        val dataSetBuilder = Dataset.Builder()
+        val sliceBuilder = InlineSuggestionUi
+                .newContentBuilder(bottomSheetPendingIntent)
+                .setStartIcon(Icon.createWithResource(this,
+                        com.android.credentialmanager.R.drawable.more_horiz_24px))
+        val presentationBuilder = Presentations.Builder()
+                .setInlinePresentation(InlinePresentation(
+                        sliceBuilder.build().slice, spec, /* pinned= */ true))
+
+        fillResponseBuilder.addDataset(
+                dataSetBuilder
+                        .setField(
+                                autofillId,
+                                Field.Builder().setPresentations(
+                                        presentationBuilder.build())
+                                        .build())
+                        .setAuthentication(bottomSheetPendingIntent.intentSender)
+                        .build()
+        )
+    }
+
     /**
      *  Maps Autofill Id to provider list. For example, passing in a provider info
      *
@@ -305,7 +497,7 @@
     ): MutableMap<AutofillId, MutableList<CredentialEntryInfo>> {
         val autofillIdToCredentialEntries:
                 MutableMap<AutofillId, MutableList<CredentialEntryInfo>> = mutableMapOf()
-        credentialEntryList.forEach entryLoop@ { credentialEntry ->
+        credentialEntryList.forEach entryLoop@{ credentialEntry ->
             val autofillId: AutofillId? = credentialEntry
                     .fillInIntent
                     ?.getParcelableExtra(
@@ -323,8 +515,8 @@
     }
 
     private fun copyProviderInfo(
-        providerInfo: ProviderInfo,
-        credentialList: List<CredentialEntryInfo>
+            providerInfo: ProviderInfo,
+            credentialList: List<CredentialEntryInfo>
     ): ProviderInfo {
         return ProviderInfo(
                 providerInfo.id,
@@ -341,12 +533,19 @@
         TODO("Not yet implemented")
     }
 
-    private fun getCredManRequest(structure: AssistStructure): GetCredentialRequest? {
+    private fun getCredManRequest(
+        structure: AssistStructure,
+        sessionId: Int,
+        requestId: Int
+    ): GetCredentialRequest? {
         val credentialOptions: MutableList<CredentialOption> = mutableListOf()
         traverseStructure(structure, credentialOptions)
 
         if (credentialOptions.isNotEmpty()) {
-            return GetCredentialRequest.Builder(Bundle.EMPTY)
+            val dataBundle = Bundle()
+            dataBundle.putInt(SESSION_ID_KEY, sessionId)
+            dataBundle.putInt(REQUEST_ID_KEY, requestId)
+            return GetCredentialRequest.Builder(dataBundle)
                     .setCredentialOptions(credentialOptions)
                     .build()
         }
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/RemoteViewsFactory.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/RemoteViewsFactory.kt
new file mode 100644
index 0000000..e039dea
--- /dev/null
+++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/RemoteViewsFactory.kt
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.common.ui
+
+import android.content.Context
+import android.content.res.Configuration
+import android.widget.RemoteViews
+import androidx.core.content.ContextCompat
+import com.android.credentialmanager.model.get.CredentialEntryInfo
+import com.android.credentialmanager.model.CredentialType
+import android.graphics.drawable.Icon
+
+class RemoteViewsFactory {
+
+    companion object {
+        private const val setAdjustViewBoundsMethodName = "setAdjustViewBounds"
+        private const val setMaxHeightMethodName = "setMaxHeight"
+        private const val setBackgroundResourceMethodName = "setBackgroundResource"
+        private const val bulletPoint = "\u2022"
+        private const val passwordCharacterLength = 15
+
+        fun createDropdownPresentation(
+                context: Context,
+                icon: Icon,
+                credentialEntryInfo: CredentialEntryInfo
+        ): RemoteViews {
+            var layoutId: Int = com.android.credentialmanager.R.layout
+                    .credman_dropdown_presentation_layout
+            val remoteViews = RemoteViews(context.packageName, layoutId)
+            if (credentialEntryInfo.credentialType == CredentialType.UNKNOWN) {
+                return remoteViews
+            }
+            setRemoteViewsPaddings(remoteViews, context)
+            if (credentialEntryInfo.credentialType == CredentialType.PASSKEY) {
+                val displayName = credentialEntryInfo.displayName ?: credentialEntryInfo.userName
+                remoteViews.setTextViewText(android.R.id.text1, displayName)
+                val secondaryText = if (credentialEntryInfo.displayName != null)
+                    (credentialEntryInfo.userName + " " + bulletPoint + " "
+                            + credentialEntryInfo.credentialTypeDisplayName
+                            + " " + bulletPoint + " " + credentialEntryInfo.providerDisplayName)
+                else (credentialEntryInfo.credentialTypeDisplayName + " " + bulletPoint + " "
+                        + credentialEntryInfo.providerDisplayName)
+                remoteViews.setTextViewText(android.R.id.text2, secondaryText)
+            } else {
+                remoteViews.setTextViewText(android.R.id.text1, credentialEntryInfo.userName)
+                remoteViews.setTextViewText(android.R.id.text2,
+                        bulletPoint.repeat(passwordCharacterLength))
+            }
+            val textColorPrimary = ContextCompat.getColor(context,
+                    com.android.credentialmanager.R.color.text_primary)
+            remoteViews.setTextColor(android.R.id.text1, textColorPrimary)
+            val textColorSecondary = ContextCompat.getColor(context, com.android
+                    .credentialmanager.R.color.text_secondary)
+            remoteViews.setTextColor(android.R.id.text2, textColorSecondary)
+            remoteViews.setImageViewIcon(android.R.id.icon1, icon);
+            remoteViews.setBoolean(
+                    android.R.id.icon1, setAdjustViewBoundsMethodName, true);
+            remoteViews.setInt(
+                    android.R.id.icon1,
+                    setMaxHeightMethodName,
+                    context.resources.getDimensionPixelSize(
+                            com.android.credentialmanager.R.dimen.autofill_icon_size));
+            val drawableId =
+                    com.android.credentialmanager.R.drawable.fill_dialog_dynamic_list_item_one
+            remoteViews.setInt(
+                    android.R.id.content, setBackgroundResourceMethodName, drawableId);
+            return remoteViews
+        }
+
+        private fun setRemoteViewsPaddings(
+                remoteViews: RemoteViews, context: Context) {
+            val leftPadding = context.resources.getDimensionPixelSize(
+                    com.android.credentialmanager.R.dimen.autofill_view_left_padding)
+            val iconToTextPadding = context.resources.getDimensionPixelSize(
+                    com.android.credentialmanager.R.dimen.autofill_view_icon_to_text_padding)
+            val rightPadding = context.resources.getDimensionPixelSize(
+                    com.android.credentialmanager.R.dimen.autofill_view_right_padding)
+            val topPadding = context.resources.getDimensionPixelSize(
+                    com.android.credentialmanager.R.dimen.autofill_view_top_padding)
+            val bottomPadding = context.resources.getDimensionPixelSize(
+                    com.android.credentialmanager.R.dimen.autofill_view_bottom_padding)
+            remoteViews.setViewPadding(
+                    android.R.id.icon1,
+                    leftPadding,
+                    /* top=*/0,
+                    /* right=*/0,
+                    /* bottom=*/0)
+            remoteViews.setViewPadding(
+                    android.R.id.text1,
+                    iconToTextPadding,
+                    /* top=*/topPadding,
+                    /* right=*/rightPadding,
+                    /* bottom=*/0)
+            remoteViews.setViewPadding(
+                    android.R.id.text2,
+                    iconToTextPadding,
+                    /* top=*/0,
+                    /* right=*/rightPadding,
+                    /* bottom=*/bottomPadding)
+        }
+
+        private fun isDarkMode(context: Context): Boolean {
+            val currentNightMode = context.resources.configuration.uiMode and
+                    Configuration.UI_MODE_NIGHT_MASK
+            return currentNightMode == Configuration.UI_MODE_NIGHT_YES
+        }
+    }
+}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt
index 46bebc4..a291f59 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt
@@ -26,10 +26,12 @@
 import com.android.internal.util.Preconditions
 
 data class GetCredentialUiState(
+        val isRequestForAllOptions: Boolean,
     val providerInfoList: List<ProviderInfo>,
     val requestDisplayInfo: RequestDisplayInfo,
     val providerDisplayInfo: ProviderDisplayInfo = toProviderDisplayInfo(providerInfoList),
-    val currentScreenState: GetScreenState = toGetScreenState(providerDisplayInfo),
+    val currentScreenState: GetScreenState = toGetScreenState(
+            providerDisplayInfo, isRequestForAllOptions),
     val activeEntry: EntryInfo? = toActiveEntry(providerDisplayInfo),
     val isNoAccount: Boolean = false,
 )
@@ -184,7 +186,8 @@
 }
 
 private fun toGetScreenState(
-    providerDisplayInfo: ProviderDisplayInfo
+    providerDisplayInfo: ProviderDisplayInfo,
+    isRequestForAllOptions: Boolean
 ): GetScreenState {
     return if (providerDisplayInfo.sortedUserNameToCredentialEntryList.isEmpty() &&
         providerDisplayInfo.remoteEntry == null &&
@@ -194,6 +197,8 @@
         providerDisplayInfo.authenticationEntryList.isEmpty() &&
         providerDisplayInfo.remoteEntry != null)
         GetScreenState.REMOTE_ONLY
+    else if (isRequestForAllOptions)
+        GetScreenState.ALL_SIGN_IN_OPTIONS
     else GetScreenState.PRIMARY_SELECTION
 }
 
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorViewModel.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
index 2a7e9e1..59e6142 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
@@ -21,11 +21,14 @@
 import androidx.lifecycle.viewModelScope
 import com.android.credentialmanager.model.Request
 import com.android.credentialmanager.client.CredentialManagerClient
+import com.android.credentialmanager.model.get.ActionEntryInfo
+import com.android.credentialmanager.model.get.CredentialEntryInfo
 import com.android.credentialmanager.ui.mappers.toGet
 import dagger.hilt.android.lifecycle.HiltViewModel
+import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.stateIn
 import javax.inject.Inject
 
@@ -33,15 +36,15 @@
 class CredentialSelectorViewModel @Inject constructor(
     private val credentialManagerClient: CredentialManagerClient,
 ) : ViewModel() {
-
+    private val isPrimaryScreen = MutableStateFlow(false)
     val uiState: StateFlow<CredentialSelectorUiState> = credentialManagerClient.requests
-        .map { request ->
+        .combine(isPrimaryScreen) { request, isPrimary ->
             when (request) {
                 null -> CredentialSelectorUiState.Idle
                 is Request.Cancel -> CredentialSelectorUiState.Cancel(request.appName)
                 is Request.Close -> CredentialSelectorUiState.Close
                 is Request.Create -> CredentialSelectorUiState.Create
-                is Request.Get -> request.toGet()
+                is Request.Get -> request.toGet(isPrimary)
             }
         }
         .stateIn(
@@ -57,9 +60,18 @@
 
 sealed class CredentialSelectorUiState {
     data object Idle : CredentialSelectorUiState()
-    sealed class Get : CredentialSelectorUiState() {
-        data object SingleProviderSinglePasskey : Get()
-        data object SingleProviderSinglePassword : Get()
+    sealed class Get() : CredentialSelectorUiState() {
+        data class SingleEntry(val entry: CredentialEntryInfo) : Get()
+        data class SingleEntryPerAccount(val sortedEntries: List<CredentialEntryInfo>) : Get()
+        data class MultipleEntry(
+            val accounts: List<PerUserNameEntries>,
+            val actionEntryList: List<ActionEntryInfo>,
+        ) : Get() {
+            data class PerUserNameEntries(
+                val userName: String,
+                val sortedCredentialEntryList: List<CredentialEntryInfo>,
+            )
+        }
 
         // TODO: b/301206470 add the remaining states
     }
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/WearApp.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/WearApp.kt
index 7e0ea30..790f5b1 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/WearApp.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/WearApp.kt
@@ -26,6 +26,7 @@
 import androidx.wear.compose.navigation.rememberSwipeDismissableNavController
 import androidx.wear.compose.navigation.rememberSwipeDismissableNavHostState
 import com.android.credentialmanager.CredentialSelectorUiState
+import com.android.credentialmanager.CredentialSelectorUiState.Get.SingleEntry
 import com.android.credentialmanager.CredentialSelectorViewModel
 import com.android.credentialmanager.ui.screens.LoadingScreen
 import com.android.credentialmanager.ui.screens.single.password.SinglePasswordScreen
@@ -57,6 +58,7 @@
 
         scrollable(Screen.SinglePasswordScreen.route) {
             SinglePasswordScreen(
+                state = viewModel.uiState.value as SingleEntry,
                 columnState = it.columnState,
                 onCloseApp = onCloseApp,
             )
@@ -100,7 +102,7 @@
     onCloseApp: () -> Unit,
 ) {
     when (state) {
-        is CredentialSelectorUiState.Get.SingleProviderSinglePassword -> {
+        is SingleEntry -> {
             navController.navigateToSinglePasswordScreen()
         }
 
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/mappers/CredentialSelectorUiStateGetMapper.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/mappers/CredentialSelectorUiStateGetMapper.kt
index 14b992a..44a838d 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/mappers/CredentialSelectorUiStateGetMapper.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/mappers/CredentialSelectorUiStateGetMapper.kt
@@ -18,18 +18,45 @@
 
 import com.android.credentialmanager.model.Request
 import com.android.credentialmanager.CredentialSelectorUiState
+import com.android.credentialmanager.CredentialSelectorUiState.Get.MultipleEntry.PerUserNameEntries
+import com.android.credentialmanager.model.CredentialType
+import com.android.credentialmanager.model.get.CredentialEntryInfo
 
-fun Request.Get.toGet(): CredentialSelectorUiState.Get {
+fun Request.Get.toGet(isPrimary: Boolean): CredentialSelectorUiState.Get {
     // TODO: b/301206470 returning a hard coded state for MVP
-    if (true) return CredentialSelectorUiState.Get.SingleProviderSinglePassword
-
-    return if (providerInfos.size == 1) {
-        if (providerInfos.first().credentialEntryList.size == 1) {
-            CredentialSelectorUiState.Get.SingleProviderSinglePassword
+    if (true) return CredentialSelectorUiState.Get.SingleEntry(
+        providerInfos
+            .flatMap { it.credentialEntryList }
+            .first { it.credentialType == CredentialType.PASSWORD }
+    )
+    val accounts = providerInfos
+        .flatMap { it.credentialEntryList }
+        .groupBy { it.userName}
+        .entries
+        .toList()
+    return if (isPrimary) {
+        if (accounts.size == 1) {
+            CredentialSelectorUiState.Get.SingleEntry(
+                accounts[0].value.minWith(comparator)
+            )
         } else {
-            TODO() // b/301206470 - Implement other get flows
+            CredentialSelectorUiState.Get.SingleEntryPerAccount(
+                accounts.map { it.value.minWith(comparator) }.sortedWith(comparator)
+            )
         }
     } else {
-        TODO() // b/301206470 - Implement other get flows
+        CredentialSelectorUiState.Get.MultipleEntry(
+            accounts = accounts.map { PerUserNameEntries(
+                it.key,
+                it.value.sortedWith(comparator)
+            )
+            },
+            actionEntryList = providerInfos.flatMap { it.actionEntryList },
+        )
     }
 }
+val comparator = compareBy<CredentialEntryInfo> { entryInfo ->
+    // Passkey type always go first
+    entryInfo.credentialType.let{ if (it == CredentialType.PASSKEY) 0 else 1 }
+}
+    .thenByDescending{ it.lastUsedTimeMillis }
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreen.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreen.kt
index b64f581..9f971ae 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreen.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreen.kt
@@ -29,6 +29,7 @@
 import androidx.compose.ui.unit.dp
 import androidx.hilt.navigation.compose.hiltViewModel
 import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import com.android.credentialmanager.CredentialSelectorUiState.Get.SingleEntry
 import com.android.credentialmanager.R
 import com.android.credentialmanager.TAG
 import com.android.credentialmanager.activity.StartBalIntentSenderForResultContract
@@ -44,12 +45,13 @@
 
 @Composable
 fun SinglePasswordScreen(
+    state: SingleEntry,
     columnState: ScalingLazyColumnState,
     onCloseApp: () -> Unit,
     modifier: Modifier = Modifier,
     viewModel: SinglePasswordScreenViewModel = hiltViewModel(),
 ) {
-    viewModel.initialize()
+    viewModel.initialize(state.entry)
 
     val uiState by viewModel.uiState.collectAsStateWithLifecycle()
 
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreenViewModel.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreenViewModel.kt
index 26bee1f..4f9fc46 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreenViewModel.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreenViewModel.kt
@@ -19,12 +19,9 @@
 import android.content.Intent
 import android.credentials.ui.ProviderPendingIntentResponse
 import android.credentials.ui.UserSelectionDialogResult
-import android.util.Log
 import androidx.activity.result.IntentSenderRequest
 import androidx.annotation.MainThread
 import androidx.lifecycle.ViewModel
-import androidx.lifecycle.viewModelScope
-import com.android.credentialmanager.TAG
 import com.android.credentialmanager.ktx.getIntentSenderRequest
 import com.android.credentialmanager.model.Request
 import com.android.credentialmanager.client.CredentialManagerClient
@@ -33,7 +30,6 @@
 import dagger.hilt.android.lifecycle.HiltViewModel
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.launch
 import javax.inject.Inject
 
 @HiltViewModel
@@ -51,32 +47,14 @@
     val uiState: StateFlow<SinglePasswordScreenUiState> = _uiState
 
     @MainThread
-    fun initialize() {
+    fun initialize(entryInfo: CredentialEntryInfo) {
         if (initializeCalled) return
         initializeCalled = true
-
-        viewModelScope.launch {
-            val request = credentialManagerClient.requests.value
-            Log.d(TAG, "request: $request, client instance: $credentialManagerClient")
-
-            if (request !is Request.Get) {
-                _uiState.value = SinglePasswordScreenUiState.Error
-            } else {
-                requestGet = request
-
-                if (requestGet.providerInfos.all { it.credentialEntryList.isEmpty() }) {
-                    Log.d(TAG, "Empty passwordEntries")
-                    _uiState.value = SinglePasswordScreenUiState.Error
-                } else {
-                    entryInfo = requestGet.providerInfos.first().credentialEntryList.first()
-                    _uiState.value = SinglePasswordScreenUiState.Loaded(
-                        PasswordUiModel(
-                            email = entryInfo.userName,
-                        )
-                    )
-                }
-            }
-        }
+        _uiState.value = SinglePasswordScreenUiState.Loaded(
+            PasswordUiModel(
+                email = entryInfo.userName,
+            )
+        )
     }
 
     fun onCancelClick() {
diff --git a/packages/InputDevices/res/values-uk/strings.xml b/packages/InputDevices/res/values-uk/strings.xml
index 5368f2c..71b3496 100644
--- a/packages/InputDevices/res/values-uk/strings.xml
+++ b/packages/InputDevices/res/values-uk/strings.xml
@@ -3,49 +3,49 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="8016145283189546017">"ĐŸŃ€ĐžŃŃ‚Ń€ĐŸŃ— ĐČĐČĐŸĐŽŃƒ"</string>
     <string name="keyboard_layouts_label" msgid="6688773268302087545">"КлаĐČіатура Android"</string>
-    <string name="keyboard_layout_english_uk_label" msgid="6664258463319999632">"Đ°ĐœĐłĐ»Ń–ĐčсьĐșа (ВДлОĐșа Đ‘Ń€ĐžŃ‚Đ°ĐœŃ–Ń)"</string>
-    <string name="keyboard_layout_english_us_label" msgid="8994890249649106291">"Đ°ĐœĐłĐ»Ń–ĐčсьĐșа (ХКА)"</string>
-    <string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"Đ°ĐœĐłĐ»Ń–ĐčсьĐșа (ХКА), ĐŒŃ–Đ¶ĐœĐ°Ń€ĐŸĐŽĐœĐ°"</string>
-    <string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"Đ°ĐœĐłĐ»Ń–ĐčсьĐșа (ХКА), Ń€ĐŸĐ·ĐșлаЎĐșа Colemak"</string>
-    <string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"Đ°ĐœĐłĐ»Ń–ĐčсьĐșа (ХКА), Ń€ĐŸĐ·ĐșлаЎĐșа ДĐČĐŸŃ€Đ°Đșа"</string>
-    <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"Đ°ĐœĐłĐ»Ń–ĐčсьĐșа (ХКА), Ń€ĐŸĐ·ĐșлаЎĐșа Workman"</string>
-    <string name="keyboard_layout_german_label" msgid="8451565865467909999">"ĐœŃ–ĐŒĐ”Ń†ŃŒĐșа"</string>
-    <string name="keyboard_layout_french_label" msgid="813450119589383723">"Ń„Ń€Đ°ĐœŃ†ŃƒĐ·ŃŒĐșа"</string>
-    <string name="keyboard_layout_french_ca_label" msgid="365352601060604832">"Ń„Ń€Đ°ĐœŃ†ŃƒĐ·ŃŒĐșа (ĐšĐ°ĐœĐ°ĐŽĐ°)"</string>
-    <string name="keyboard_layout_russian_label" msgid="8724879775815042968">"Ń€ĐŸŃŃ–ĐčсьĐșа"</string>
-    <string name="keyboard_layout_russian_mac_label" msgid="3795866869038264796">"Ń€ĐŸŃŃ–ĐčсьĐșа, Ń€ĐŸĐ·ĐșлаЎĐșа Mac"</string>
-    <string name="keyboard_layout_spanish_label" msgid="7091555148131908240">"Ń–ŃĐżĐ°ĐœŃŃŒĐșа"</string>
-    <string name="keyboard_layout_swiss_french_label" msgid="4659191025396371684">"Ń„Ń€Đ°ĐœŃ†ŃƒĐ·ŃŒĐșа (ĐšĐČĐ”Đčцарія)"</string>
-    <string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"ĐœŃ–ĐŒĐ”Ń†ŃŒĐșа (ĐšĐČĐ”Đčцарія)"</string>
-    <string name="keyboard_layout_belgian" msgid="2011984572838651558">"Đ±Đ”Đ»ŃŒĐłŃ–ĐčсьĐșа"</string>
-    <string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Đ±ĐŸĐ»ĐłĐ°Ń€ŃŃŒĐșа"</string>
+    <string name="keyboard_layout_english_uk_label" msgid="6664258463319999632">"ĐĐœĐłĐ»Ń–ĐčсьĐșа (ВДлОĐșа Đ‘Ń€ĐžŃ‚Đ°ĐœŃ–Ń)"</string>
+    <string name="keyboard_layout_english_us_label" msgid="8994890249649106291">"ĐĐœĐłĐ»Ń–ĐčсьĐșа (ХКА)"</string>
+    <string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"ĐĐœĐłĐ»Ń–ĐčсьĐșа (ХКА), ĐŒŃ–Đ¶ĐœĐ°Ń€ĐŸĐŽĐœĐ°"</string>
+    <string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"ĐĐœĐłĐ»Ń–ĐčсьĐșа (ХКА), Ń€ĐŸĐ·ĐșлаЎĐșа Colemak"</string>
+    <string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"ĐĐœĐłĐ»Ń–ĐčсьĐșа (ХКА), Ń€ĐŸĐ·ĐșлаЎĐșа ДĐČĐŸŃ€Đ°Đșа"</string>
+    <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"ĐĐœĐłĐ»Ń–ĐčсьĐșа (ХКА), Ń€ĐŸĐ·ĐșлаЎĐșа Workman"</string>
+    <string name="keyboard_layout_german_label" msgid="8451565865467909999">"ĐŃ–ĐŒĐ”Ń†ŃŒĐșа"</string>
+    <string name="keyboard_layout_french_label" msgid="813450119589383723">"Đ€Ń€Đ°ĐœŃ†ŃƒĐ·ŃŒĐșа"</string>
+    <string name="keyboard_layout_french_ca_label" msgid="365352601060604832">"Đ€Ń€Đ°ĐœŃ†ŃƒĐ·ŃŒĐșа (ĐšĐ°ĐœĐ°ĐŽĐ°)"</string>
+    <string name="keyboard_layout_russian_label" msgid="8724879775815042968">"Đ ĐŸŃŃ–ĐčсьĐșа"</string>
+    <string name="keyboard_layout_russian_mac_label" msgid="3795866869038264796">"Đ ĐŸŃŃ–ĐčсьĐșа, Ń€ĐŸĐ·ĐșлаЎĐșа Mac"</string>
+    <string name="keyboard_layout_spanish_label" msgid="7091555148131908240">"Đ†ŃĐżĐ°ĐœŃŃŒĐșа"</string>
+    <string name="keyboard_layout_swiss_french_label" msgid="4659191025396371684">"Đ€Ń€Đ°ĐœŃ†ŃƒĐ·ŃŒĐșа (ĐšĐČĐ”Đčцарія)"</string>
+    <string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"ĐŃ–ĐŒĐ”Ń†ŃŒĐșа (ĐšĐČĐ”Đčцарія)"</string>
+    <string name="keyboard_layout_belgian" msgid="2011984572838651558">"Đ‘Đ”Đ»ŃŒĐłŃ–ĐčсьĐșа"</string>
+    <string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Đ‘ĐŸĐ»ĐłĐ°Ń€ŃŃŒĐșа"</string>
     <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Đ‘ĐŸĐ»ĐłĐ°Ń€ŃŃŒĐșа (Ń„ĐŸĐœĐ”Ń‚ĐžŃ‡ĐœĐ°)"</string>
-    <string name="keyboard_layout_italian" msgid="6497079660449781213">"італіĐčсьĐșа"</string>
-    <string name="keyboard_layout_danish" msgid="8036432066627127851">"ĐŽĐ°ĐœŃŃŒĐșа"</string>
-    <string name="keyboard_layout_norwegian" msgid="9090097917011040937">"ĐœĐŸŃ€ĐČĐ”Đ·ŃŒĐșа"</string>
-    <string name="keyboard_layout_swedish" msgid="732959109088479351">"шĐČĐ”ĐŽŃŃŒĐșа"</string>
-    <string name="keyboard_layout_finnish" msgid="5585659438924315466">"Ń„Ń–ĐœŃŃŒĐșа"</string>
-    <string name="keyboard_layout_croatian" msgid="4172229471079281138">"Ń…ĐŸŃ€ĐČатсьĐșа"</string>
-    <string name="keyboard_layout_czech" msgid="1349256901452975343">"Ń‡Đ”ŃŃŒĐșа"</string>
+    <string name="keyboard_layout_italian" msgid="6497079660449781213">"ІталіĐčсьĐșа"</string>
+    <string name="keyboard_layout_danish" msgid="8036432066627127851">"Đ”Đ°ĐœŃŃŒĐșа"</string>
+    <string name="keyboard_layout_norwegian" msgid="9090097917011040937">"ĐĐŸŃ€ĐČĐ”Đ·ŃŒĐșа"</string>
+    <string name="keyboard_layout_swedish" msgid="732959109088479351">"ĐšĐČĐ”ĐŽŃŃŒĐșа"</string>
+    <string name="keyboard_layout_finnish" msgid="5585659438924315466">"Đ€Ń–ĐœŃŃŒĐșа"</string>
+    <string name="keyboard_layout_croatian" msgid="4172229471079281138">"Đ„ĐŸŃ€ĐČатсьĐșа"</string>
+    <string name="keyboard_layout_czech" msgid="1349256901452975343">"Đ§Đ”ŃŃŒĐșа"</string>
     <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Đ§Đ”ŃŃŒĐșа (QWERTY)"</string>
-    <string name="keyboard_layout_estonian" msgid="8775830985185665274">"Đ”ŃŃ‚ĐŸĐœŃŃŒĐșа"</string>
-    <string name="keyboard_layout_hungarian" msgid="4154963661406035109">"ŃƒĐłĐŸŃ€ŃŃŒĐșа"</string>
-    <string name="keyboard_layout_icelandic" msgid="5836645650912489642">"Ń–ŃĐ»Đ°ĐœĐŽŃŃŒĐșа"</string>
-    <string name="keyboard_layout_brazilian" msgid="5117896443147781939">"Đ±Ń€Đ°Đ·ĐžĐ»ŃŒŃŃŒĐșа"</string>
-    <string name="keyboard_layout_portuguese" msgid="2888198587329660305">"ĐżĐŸŃ€Ń‚ŃƒĐłĐ°Đ»ŃŒŃŃŒĐșа"</string>
-    <string name="keyboard_layout_slovak" msgid="2469379934672837296">"ŃĐ»ĐŸĐČацьĐșа"</string>
-    <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"ŃĐ»ĐŸĐČĐ”ĐœŃŃŒĐșа"</string>
-    <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Ń‚ŃƒŃ€Đ”Ń†ŃŒĐșа"</string>
+    <string name="keyboard_layout_estonian" msgid="8775830985185665274">"Đ•ŃŃ‚ĐŸĐœŃŃŒĐșа"</string>
+    <string name="keyboard_layout_hungarian" msgid="4154963661406035109">"ĐŁĐłĐŸŃ€ŃŃŒĐșа"</string>
+    <string name="keyboard_layout_icelandic" msgid="5836645650912489642">"Đ†ŃĐ»Đ°ĐœĐŽŃŃŒĐșа"</string>
+    <string name="keyboard_layout_brazilian" msgid="5117896443147781939">"Đ‘Ń€Đ°Đ·ĐžĐ»ŃŒŃŃŒĐșа"</string>
+    <string name="keyboard_layout_portuguese" msgid="2888198587329660305">"ĐŸĐŸŃ€Ń‚ŃƒĐłĐ°Đ»ŃŒŃŃŒĐșа"</string>
+    <string name="keyboard_layout_slovak" msgid="2469379934672837296">"ĐĄĐ»ĐŸĐČацьĐșа"</string>
+    <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"ĐĄĐ»ĐŸĐČĐ”ĐœŃŃŒĐșа"</string>
+    <string name="keyboard_layout_turkish" msgid="7736163250907964898">"ĐąŃƒŃ€Đ”Ń†ŃŒĐșа"</string>
     <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"ĐąŃƒŃ€Đ”Ń†ŃŒĐșа-F"</string>
-    <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"уĐșŃ€Đ°Ń—ĐœŃŃŒĐșа"</string>
-    <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Đ°Ń€Đ°Đ±ŃŃŒĐșа"</string>
+    <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"ĐŁĐșŃ€Đ°Ń—ĐœŃŃŒĐșа"</string>
+    <string name="keyboard_layout_arabic" msgid="5671970465174968712">"ĐŃ€Đ°Đ±ŃŃŒĐșа"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"Đ“Ń€Đ”Ń†ŃŒĐșа"</string>
     <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"ІĐČрот"</string>
     <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Đ›ĐžŃ‚ĐŸĐČсьĐșа"</string>
     <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Đ†ŃĐżĐ°ĐœŃŃŒĐșа (Đ»Đ°Ń‚ĐžĐœĐžŃ†Ń)"</string>
     <string name="keyboard_layout_latvian" msgid="4405417142306250595">"ЛатĐČіĐčсьĐșа"</string>
     <string name="keyboard_layout_persian" msgid="3920643161015888527">"ĐŸĐ”Ń€ŃŃŒĐșа"</string>
-    <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"азДрбаĐčĐŽĐ¶Đ°ĐœŃŃŒĐșа"</string>
+    <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"АзДрбаĐčĐŽĐ¶Đ°ĐœŃŃŒĐșа"</string>
     <string name="keyboard_layout_polish" msgid="1121588624094925325">"ĐŸĐŸĐ»ŃŒŃŃŒĐșа"</string>
     <string name="keyboard_layout_belarusian" msgid="7619281752698687588">"Đ‘Ń–Đ»ĐŸŃ€ŃƒŃŃŒĐșа"</string>
     <string name="keyboard_layout_mongolian" msgid="7678483495823936626">"ĐœĐŸĐœĐłĐŸĐ»ŃŒŃŃŒĐșа"</string>
diff --git a/packages/LocalTransport/src/com/android/localtransport/LocalTransport.java b/packages/LocalTransport/src/com/android/localtransport/LocalTransport.java
index 933be11..6a4bb21 100644
--- a/packages/LocalTransport/src/com/android/localtransport/LocalTransport.java
+++ b/packages/LocalTransport/src/com/android/localtransport/LocalTransport.java
@@ -134,8 +134,7 @@
     @UsesReflection({
             // As the runtime class name is used to generate the returned name, and the returned
             // name may be used used with reflection, generate the necessary keep rules.
-            @KeepTarget(classConstant = LocalTransport.class),
-            @KeepTarget(extendsClassConstant = LocalTransport.class)
+            @KeepTarget(instanceOfClassConstant = LocalTransport.class)
     })
     @Override
     public String name() {
diff --git a/packages/PackageInstaller/Android.bp b/packages/PackageInstaller/Android.bp
index 25ad9b8..98a5a67 100644
--- a/packages/PackageInstaller/Android.bp
+++ b/packages/PackageInstaller/Android.bp
@@ -35,7 +35,10 @@
     name: "PackageInstaller",
     defaults: ["platform_app_defaults"],
 
-    srcs: ["src/**/*.java"],
+    srcs: [
+        "src/**/*.java",
+        "src/**/*.kt",
+    ],
 
     certificate: "platform",
     privileged: true,
@@ -62,7 +65,10 @@
     name: "PackageInstaller_tablet",
     defaults: ["platform_app_defaults"],
 
-    srcs: ["src/**/*.java"],
+    srcs: [
+        "src/**/*.java",
+        "src/**/*.kt",
+    ],
 
     certificate: "platform",
     privileged: true,
@@ -91,7 +97,10 @@
     name: "PackageInstaller_tv",
     defaults: ["platform_app_defaults"],
 
-    srcs: ["src/**/*.java"],
+    srcs: [
+        "src/**/*.java",
+        "src/**/*.kt",
+    ],
 
     certificate: "platform",
     privileged: true,
diff --git a/packages/PackageInstaller/res/values-af/strings.xml b/packages/PackageInstaller/res/values-af/strings.xml
index 25afe99..acd16b9 100644
--- a/packages/PackageInstaller/res/values-af/strings.xml
+++ b/packages/PackageInstaller/res/values-af/strings.xml
@@ -44,8 +44,7 @@
     <string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Hierdie gebruiker kan nie onbekende programme installeer nie"</string>
     <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Hierdie gebruiker word nie toegelaat om programme te installeer nie"</string>
     <string name="ok" msgid="7871959885003339302">"OK"</string>
-    <!-- no translation found for archive (4447791830199354721) -->
-    <skip />
+    <string name="archive" msgid="4447791830199354721">"Argiveer"</string>
     <string name="update_anyway" msgid="8792432341346261969">"Dateer in elk geval op"</string>
     <string name="manage_applications" msgid="5400164782453975580">"Bestuur programme"</string>
     <string name="out_of_space_dlg_title" msgid="4156690013884649502">"Geen spasie oor nie"</string>
@@ -60,16 +59,11 @@
     <string name="uninstall_update_title" msgid="824411791011583031">"Deïnstalleer opdatering"</string>
     <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> is deel van die volgende program:"</string>
     <string name="uninstall_application_text" msgid="3816830743706143980">"Wil jy hierdie program deïnstalleer?"</string>
-    <!-- no translation found for archive_application_text (8482325710714386348) -->
-    <skip />
-    <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
-    <skip />
-    <!-- no translation found for archive_application_text_user (2586558895535581451) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
-    <skip />
+    <string name="archive_application_text" msgid="8482325710714386348">"Jou persoonlike data sal gestoor word"</string>
+    <string name="archive_application_text_all_users" msgid="3151229641681672580">"Argiveer hierdie app vir alle gebruikers? Jou persoonlike data sal gestoor word"</string>
+    <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Argiveer hierdie app op jou werkprofiel? Jou persoonlike data sal gestoor word"</string>
+    <string name="archive_application_text_user" msgid="2586558895535581451">"Argiveer hierdie app vir <xliff:g id="USERNAME">%1$s</xliff:g>? Jou persoonlike data sal gestoor word"</string>
+    <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Wil jy hierdie app wat in jou privaat ruimte is, argiveer? Jou persoonlike data sal gestoor word"</string>
     <string name="uninstall_application_text_all_users" msgid="575491774380227119">"Wil jy hierdie program vir "<b>"alle"</b>" gebruikers deïnstalleer? Die program en sy data sal van "<b>"alle"</b>" gebruikers op hierdie toestel verwyder word."</string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Wil jy hierdie program vir die gebruiker <xliff:g id="USERNAME">%1$s</xliff:g> deïnstalleer?"</string>
     <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Wil jy hierdie program op jou werkprofiel deïnstalleer?"</string>
@@ -108,8 +102,7 @@
     <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Jou tablet en persoonlike data is meer kwesbaar vir aanvalle deur onbekende programme. Deur hierdie program te installeer, stem jy in dat jy verantwoordelik is vir enige skade aan jou tablet of verlies van data wat uit sy gebruik kan spruit."</string>
     <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Jou TV en persoonlike data is meer kwesbaar vir aanvalle deur onbekende programme. Deur hierdie program te installeer, stem jy in dat jy verantwoordelik is vir enige skade aan jou TV of verlies van data wat uit sy gebruik kan spruit."</string>
     <string name="cloned_app_label" msgid="7503612829833756160">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>-kloon"</string>
-    <!-- no translation found for archiving_app_label (1127085259724124725) -->
-    <skip />
+    <string name="archiving_app_label" msgid="1127085259724124725">"Argiveer <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>?"</string>
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Gaan voort"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Instellings"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Installeer/deïnstalleer Wear-programme"</string>
diff --git a/packages/PackageInstaller/res/values-am/strings.xml b/packages/PackageInstaller/res/values-am/strings.xml
index f47ec6a..d2e8400 100644
--- a/packages/PackageInstaller/res/values-am/strings.xml
+++ b/packages/PackageInstaller/res/values-am/strings.xml
@@ -44,8 +44,7 @@
     <string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"á‹«áˆá‰łá‹ˆá‰ መተግበáˆȘያዎቜ በዚህ ተጠቃሚ ሊጫኑ አይቜሉም"</string>
     <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"ይህ ተጠቃሚ መተግበáˆȘያዎቜን ኄንá‹Čጭን አልተፈቀደለቔም"</string>
     <string name="ok" msgid="7871959885003339302">"ኄáˆș"</string>
-    <!-- no translation found for archive (4447791830199354721) -->
-    <skip />
+    <string name="archive" msgid="4447791830199354721">"በማህደር አሔቀምጄ"</string>
     <string name="update_anyway" msgid="8792432341346261969">"ዹሆነው ሆኖ አዘምን"</string>
     <string name="manage_applications" msgid="5400164782453975580">"መተግበáˆȘያዎቜን ያቀናቄሩ"</string>
     <string name="out_of_space_dlg_title" msgid="4156690013884649502">"ቩታ ሞልቷል"</string>
@@ -60,16 +59,11 @@
     <string name="uninstall_update_title" msgid="824411791011583031">"ዝማኔን አራግፍ"</string>
     <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> ዹሚኹተለው መተግበáˆȘያ አካል ነው፩"</string>
     <string name="uninstall_application_text" msgid="3816830743706143980">"ይህን መተግበáˆȘያ ማራገፍ ይፈልጋሉ?"</string>
-    <!-- no translation found for archive_application_text (8482325710714386348) -->
-    <skip />
-    <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
-    <skip />
-    <!-- no translation found for archive_application_text_user (2586558895535581451) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
-    <skip />
+    <string name="archive_application_text" msgid="8482325710714386348">"ዹግል ውሂቄዎ ይቀመጣል"</string>
+    <string name="archive_application_text_all_users" msgid="3151229641681672580">"ይህ መተግበáˆȘያ ለሁሉም ተጠቃሚዎቜ በማህደር ይቀመጄ? ዹግል ውሂቄዎ ይቀመጣል"</string>
+    <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"ይህ መተግበáˆȘያ በሔራ መገለጫዎ ላይ በማህደር ይቀመጄ? ዹግል ውሂቄዎ ይቀመጣል"</string>
+    <string name="archive_application_text_user" msgid="2586558895535581451">"ይህ መተግበáˆȘያ ለ<xliff:g id="USERNAME">%1$s</xliff:g> በማህደር ይቀመጄ? ዹግል ውሂቄዎ ይቀመጣል"</string>
+    <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"ይህን መተግበáˆȘያ ኹግል ቩታዎ በማህደር ማሔቀመጄ ይፈልጋሉ? ዹግል ውሂቄዎ ይቀመጣል"</string>
     <string name="uninstall_application_text_all_users" msgid="575491774380227119">"ይህን መተግበáˆȘያ "<b>"ለሁሉም"</b>" ተጠቃሚዎቜ መጫን ይፈልጋሉ? መተግበáˆȘያው ኄና ውሂቡ በመሣáˆȘያው ላይ ካሉ "<b>"ሁሉም"</b>" ተጠቃሚዎቜ ይሰሹዛሉ፱"</string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"ይህን መተግበáˆȘያ ለተጠቃሚ <xliff:g id="USERNAME">%1$s</xliff:g> ማራገፍ ይፈልጋሉ?"</string>
     <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"ይህን መተግበáˆȘያ ኚሔራ መገለጫዎ ማራገፍ ይፈልጋሉ?"</string>
@@ -108,8 +102,7 @@
     <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"ዚኄርሔዎ ጡባዊ ኄና ዹግል ውሂቄ በማይታወቁ መተግበáˆȘያዎቜ ለሚደርሱ ጄቃቶቜ በይበልጄ ተጋላጭ ናቾው፱ ይህን መተግበáˆȘያ በመጫንዎ በኄርሔዎ ጡባዊ ላይ ለሚደርሔ ማናቾውም áŒ‰á‹łá‰” ወይም መተግበáˆȘያውን በመጠቀም ለሚኚሰቔ ዚውሂቄ መጄፋቔ ኃላፊነቱን ኄንደሚወሔዱ á‰°áˆ”áˆ›áˆá‰°á‹‹áˆáą"</string>
     <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"ዚኄርሔዎ ቮሌá‰Șዄን ኄና ዹግል ውሂቄ በማይታወቁ መተግበáˆȘያዎቜ ለሚደርሱ ጄቃቶቜ በይበልጄ ተጋላጭ ናቾው፱ ይህን መተግበáˆȘያ በመጫንዎ በኄርሔዎ ቮሌá‰Șዄን ላይ ለሚደርሔ ማናቾውም áŒ‰á‹łá‰” ወይም መተግበáˆȘያውን በመጠቀም ለሚኚሰቔ ዚውሂቄ መጄፋቔ ኃላፊነቱን ኄንደሚወሔዱ á‰°áˆ”áˆ›áˆá‰°á‹‹áˆáą"</string>
     <string name="cloned_app_label" msgid="7503612829833756160">"ዹተባዛ <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
-    <!-- no translation found for archiving_app_label (1127085259724124725) -->
-    <skip />
+    <string name="archiving_app_label" msgid="1127085259724124725">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> በማህደር ይቀመጄ?"</string>
     <string name="anonymous_source_continue" msgid="4375745439457209366">"ቀጄል"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"á‰…áŠ•á‰„áˆźá‰œ"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"ዹWear መተግበáˆȘያዎቜን መጫን/ማራገፍ"</string>
diff --git a/packages/PackageInstaller/res/values-ar/strings.xml b/packages/PackageInstaller/res/values-ar/strings.xml
index e4da5b7..224c6dc 100644
--- a/packages/PackageInstaller/res/values-ar/strings.xml
+++ b/packages/PackageInstaller/res/values-ar/strings.xml
@@ -44,8 +44,7 @@
     <string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"يŰȘŰč۰۱ Űčلى Ù‡Ű°Ű§ Ű§Ù„Ù…ŰłŰȘŰźŰŻÙ… ŰȘŰ«ŰšÙŠŰȘ Ű§Ù„ŰȘŰ·ŰšÙŠÙ‚Ű§ŰȘ ŰșÙŠŰ± Ű§Ù„Ù…ŰčŰ±ÙˆÙŰ©"</string>
     <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"ŰșÙŠŰ± Ù…ŰłÙ…ÙˆŰ­ Ù„Ù‡Ű°Ű§ Ű§Ù„Ù…ŰłŰȘŰźŰŻÙ… ŰšŰȘŰ«ŰšÙŠŰȘ Ű§Ù„ŰȘŰ·ŰšÙŠÙ‚Ű§ŰȘ"</string>
     <string name="ok" msgid="7871959885003339302">"Ű­ŰłÙ†Ù‹Ű§"</string>
-    <!-- no translation found for archive (4447791830199354721) -->
-    <skip />
+    <string name="archive" msgid="4447791830199354721">"ŰŁŰ±ŰŽÙŰ©"</string>
     <string name="update_anyway" msgid="8792432341346261969">"Ű§Ù„ŰȘŰ­ŰŻÙŠŰ« Űčلى ŰŁÙŠ Ű­Ű§Ù„"</string>
     <string name="manage_applications" msgid="5400164782453975580">"ۄۯۧ۱۩ Ű§Ù„ŰȘŰ·ŰšÙŠÙ‚Ű§ŰȘ"</string>
     <string name="out_of_space_dlg_title" msgid="4156690013884649502">"Ù†ÙŰŻŰȘ Ù…ŰłŰ§Ű­Ű© Ű§Ù„ŰȘŰźŰČين"</string>
@@ -60,16 +59,11 @@
     <string name="uninstall_update_title" msgid="824411791011583031">"Ű„ŰČŰ§Ù„Ű© Ű§Ù„ŰȘŰ­ŰŻÙŠŰ«"</string>
     <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> هو ŰŹŰČŰĄ من Ű§Ù„ŰȘŰ·ŰšÙŠÙ‚ Ű§Ù„ŰȘŰ§Ù„ÙŠ:"</string>
     <string name="uninstall_application_text" msgid="3816830743706143980">"هل ŰȘŰ±ÙŠŰŻ Ű„ŰČŰ§Ù„Ű© Ù‡Ű°Ű§ Ű§Ù„ŰȘŰ·ŰšÙŠÙ‚ŰŸ"</string>
-    <!-- no translation found for archive_application_text (8482325710714386348) -->
-    <skip />
-    <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
-    <skip />
-    <!-- no translation found for archive_application_text_user (2586558895535581451) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
-    <skip />
+    <string name="archive_application_text" msgid="8482325710714386348">"ŰłÙŠŰȘم Ű­ÙŰž ŰšÙŠŰ§Ù†Ű§ŰȘك Ű§Ù„ŰŽŰźŰ”ÙŠŰ©."</string>
+    <string name="archive_application_text_all_users" msgid="3151229641681672580">"هل ŰȘŰ±ÙŠŰŻ ŰŁŰ±ŰŽÙŰ© Ù‡Ű°Ű§ Ű§Ù„ŰȘŰ·ŰšÙŠÙ‚ Ù„ŰŹÙ…ÙŠŰč Ű§Ù„Ù…ŰłŰȘŰźŰŻÙ…ÙŠÙ†ŰŸ ŰłÙŠŰȘم Ű­ÙŰž ŰšÙŠŰ§Ù†Ű§ŰȘك Ű§Ù„ŰŽŰźŰ”ÙŠŰ©."</string>
+    <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"هل ŰȘŰ±ÙŠŰŻ ŰŁŰ±ŰŽÙŰ© Ù‡Ű°Ű§ Ű§Ù„ŰȘŰ·ŰšÙŠÙ‚ في ملف Ű§Ù„ŰčÙ…Ù„ŰŸ ŰłÙŠŰȘم Ű­ÙŰž ŰšÙŠŰ§Ù†Ű§ŰȘك Ű§Ù„ŰŽŰźŰ”ÙŠŰ©."</string>
+    <string name="archive_application_text_user" msgid="2586558895535581451">"هل ŰȘŰ±ÙŠŰŻ ŰŁŰ±ŰŽÙŰ© Ù‡Ű°Ű§ Ű§Ù„ŰȘŰ·ŰšÙŠÙ‚ لـ \"<xliff:g id="USERNAME">%1$s</xliff:g>\"۟ ŰłÙŠŰȘم Ű­ÙŰž ŰšÙŠŰ§Ù†Ű§ŰȘك Ű§Ù„ŰŽŰźŰ”ÙŠŰ©."</string>
+    <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"هل ŰȘŰ±ÙŠŰŻ ŰŁŰ±ŰŽÙŰ© Ù‡Ű°Ű§ Ű§Ù„ŰȘŰ·ŰšÙŠÙ‚ Ű§Ù„Ù…Ű­ÙÙˆŰž في Ű§Ù„Ù…ŰłŰ§Ű­Ű© Ű§Ù„ŰźŰ§Ű”Ù‘Ű©ŰŸ ŰłÙŠŰȘم Ű­ÙŰž ŰšÙŠŰ§Ù†Ű§ŰȘك Ű§Ù„ŰŽŰźŰ”ÙŠŰ©."</string>
     <string name="uninstall_application_text_all_users" msgid="575491774380227119">"هل ŰȘŰ±ÙŠŰŻ Ű„ŰČŰ§Ù„Ű© Ù‡Ű°Ű§ Ű§Ù„ŰȘŰ·ŰšÙŠÙ‚ "<b>"لكل"</b>" Ű§Ù„Ù…ŰłŰȘŰźŰŻÙ…ÙŠÙ†ŰŸ ŰłŰȘŰȘم Ű„ŰČŰ§Ù„Ű© Ű§Ù„ŰȘŰ·ŰšÙŠÙ‚ ÙˆŰšÙŠŰ§Ù†Ű§ŰȘه من "<b>"كل"</b>" Ű§Ù„Ù…ŰłŰȘŰźŰŻÙ…ÙŠÙ† Űčلى Ù‡Ű°Ű§ Ű§Ù„ŰŹÙ‡Ű§ŰČ."</string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"هل ŰȘŰ±ÙŠŰŻ Ű„ŰČŰ§Ù„Ű© Ù‡Ű°Ű§ Ű§Ù„ŰȘŰ·ŰšÙŠÙ‚ Ù„Ù„Ù…ŰłŰȘŰźŰŻÙ… <xliff:g id="USERNAME">%1$s</xliff:g>۟"</string>
     <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"هل ŰȘŰ±ÙŠŰŻ Ű„ŰČŰ§Ù„Ű© ŰȘŰ«ŰšÙŠŰȘ Ù‡Ű°Ű§ Ű§Ù„ŰȘŰ·ŰšÙŠÙ‚ من ملفك Ű§Ù„ŰŽŰźŰ”ÙŠ للŰčÙ…Ù„ŰŸ"</string>
@@ -108,8 +102,7 @@
     <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"يŰčŰȘۚ۱ Ű§Ù„ŰŹÙ‡Ű§ŰČ Ű§Ù„Ù„ÙˆŰ­ÙŠ ÙˆŰ§Ù„ŰšÙŠŰ§Ù†Ű§ŰȘ Ű§Ù„ŰŽŰźŰ”ÙŠŰ© ŰŁÙƒŰ«Ű± Űč۱۶۩ Ù„Ù‡ŰŹÙˆÙ… Ű§Ù„ŰȘŰ·ŰšÙŠÙ‚Ű§ŰȘ ŰșÙŠŰ± Ű§Ù„Ù…ŰčŰ±ÙˆÙŰ©. من ŰźÙ„Ű§Ù„ ŰȘŰ«ŰšÙŠŰȘ Ù‡Ű°Ű§ Ű§Ù„ŰȘŰ·ŰšÙŠÙ‚ŰŒ ŰȘÙˆŰ§ÙÙ‚ Űčلى ŰȘŰ­Ù…Ù„ Ù…ŰłŰ€ÙˆÙ„ÙŠŰ© ŰŁÙŠ ۶۱۱ ÙŠŰ­ŰŻŰ« Ù„Ù„ŰŹÙ‡Ű§ŰČ Ű§Ù„Ù„ÙˆŰ­ÙŠ ŰŁÙˆ ÙÙ‚ŰŻŰ§Ù† Ű§Ù„ŰšÙŠŰ§Ù†Ű§ŰȘ Ű§Ù„Ű°ÙŠ Ù‚ŰŻ ينŰȘŰŹ Űčن ۧ۳ŰȘŰźŰŻŰ§Ù…Ù‡."</string>
     <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"يŰčŰȘۚ۱ ŰŹÙ‡Ű§ŰČ Ű§Ù„ŰȘلفŰČيون ÙˆŰ§Ù„ŰšÙŠŰ§Ù†Ű§ŰȘ Ű§Ù„ŰŽŰźŰ”ÙŠŰ© ŰŁÙƒŰ«Ű± Űč۱۶۩ Ù„Ù‡ŰŹÙˆÙ… Ű§Ù„ŰȘŰ·ŰšÙŠÙ‚Ű§ŰȘ ŰșÙŠŰ± Ű§Ù„Ù…ŰčŰ±ÙˆÙŰ©. من ŰźÙ„Ű§Ù„ ŰȘŰ«ŰšÙŠŰȘ Ù‡Ű°Ű§ Ű§Ù„ŰȘŰ·ŰšÙŠÙ‚ŰŒ ŰȘÙˆŰ§ÙÙ‚ Űčلى ŰȘŰ­Ù…Ù„ Ù…ŰłŰ€ÙˆÙ„ÙŠŰ© ŰŁÙŠ ۶۱۱ ÙŠŰ­ŰŻŰ« Ù„ŰŹÙ‡Ű§ŰČ Ű§Ù„ŰȘلفŰČيون ŰŁÙˆ ÙÙ‚ŰŻŰ§Ù† Ű§Ù„ŰšÙŠŰ§Ù†Ű§ŰȘ Ű§Ù„Ű°ÙŠ Ù‚ŰŻ ينŰȘŰŹ Űčن ۧ۳ŰȘŰźŰŻŰ§Ù…Ù‡."</string>
     <string name="cloned_app_label" msgid="7503612829833756160">"Ù†ŰłŰźŰ© Ű·ŰšÙ‚ Ű§Ù„ŰŁŰ”Ù„ من \"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>\""</string>
-    <!-- no translation found for archiving_app_label (1127085259724124725) -->
-    <skip />
+    <string name="archiving_app_label" msgid="1127085259724124725">"هل ŰȘŰ±ÙŠŰŻ ŰŁŰ±ŰŽÙŰ© <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>۟"</string>
     <string name="anonymous_source_continue" msgid="4375745439457209366">"مŰȘۧۚŰčŰ©"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Ű§Ù„Ű„ŰčۯۧۯۧŰȘ"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"‏ŰȘŰ«ŰšÙŠŰȘ / Ű„Ù„Űșۧۥ ŰȘŰ«ŰšÙŠŰȘ ŰȘŰ·ŰšÙŠÙ‚Ű§ŰȘ Android Wear"</string>
diff --git a/packages/PackageInstaller/res/values-as/strings.xml b/packages/PackageInstaller/res/values-as/strings.xml
index 62a55dc..8a253a3 100644
--- a/packages/PackageInstaller/res/values-as/strings.xml
+++ b/packages/PackageInstaller/res/values-as/strings.xml
@@ -44,8 +44,7 @@
     <string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"àŠàŠ‡ àŠŹà§àŠŻà§±àŠčàŠŸà§°àŠ•àŠŸà§°à§€àŠŻàŠŒà§‡ àŠ…àŠœà§àŠžàŠŸàŠ€ àŠ‰à§ŽàŠžà§°àŠȘà§°àŠŸ àŠȘà§‹à§±àŠŸ àŠàŠȘ্‌àŠžàŠźà§‚àŠč àŠ‡àŠšàŠ·à§àŠŸàŠČ àŠ•à§°àŠżàŠŹ àŠšà§‹à§±àŠŸà§°à§‡"</string>
     <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"àŠàŠ‡ àŠŹà§àŠŻà§±àŠčàŠŸà§°àŠ•àŠŸà§°à§€àŠœàŠšà§° àŠàŠȘ্ àŠ‡àŠšàŠ·à§àŠŸàŠČ àŠ•à§°àŠŸà§° àŠ…àŠšà§àŠźàŠ€àŠż àŠšàŠŸàŠ‡"</string>
     <string name="ok" msgid="7871959885003339302">"àŠ àŠżàŠ• àŠ†àŠ›à§‡"</string>
-    <!-- no translation found for archive (4447791830199354721) -->
-    <skip />
+    <string name="archive" msgid="4447791830199354721">"àŠ†à§°à§àŠ•àŠŸàŠ‡àŠ­ àŠ•à§°àŠ•"</string>
     <string name="update_anyway" msgid="8792432341346261969">"àŠŻàŠżàŠ•à§‹àŠšà§‹ àŠȘà§à§°àŠ•àŠŸà§°à§‡ àŠ†àŠȘàŠĄà§‡’àŠŸ àŠ•à§°àŠ•"</string>
     <string name="manage_applications" msgid="5400164782453975580">"àŠàŠȘ্ àŠȘà§°àŠżàŠšàŠŸàŠČàŠšàŠŸ"</string>
     <string name="out_of_space_dlg_title" msgid="4156690013884649502">"àŠ–àŠŸàŠČী àŠ àŠŸàŠ‡ àŠšàŠŸàŠ‡"</string>
@@ -60,16 +59,11 @@
     <string name="uninstall_update_title" msgid="824411791011583031">"àŠ†àŠȘàŠĄà§‡’àŠŸ àŠ†àŠšàŠ‡àŠšàŠ·à§àŠŸàŠČ àŠ•à§°àŠ•"</string>
     <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> àŠčà§ˆàŠ›à§‡ àŠ€àŠČà§° àŠàŠȘ্‌àŠŸà§‹à§° àŠàŠŸàŠŸ àŠ…àŠ‚àŠ¶:"</string>
     <string name="uninstall_application_text" msgid="3816830743706143980">"àŠ†àŠȘà§àŠšàŠż àŠàŠ‡ àŠàŠȘ্‌àŠŸà§‹ àŠ†àŠšàŠ‡àŠšàŠ·à§àŠŸàŠČ àŠ•à§°àŠżàŠŹ àŠŹàŠżàŠšàŠŸà§°à§‡ àŠšà§‡àŠ•àŠż?"</string>
-    <!-- no translation found for archive_application_text (8482325710714386348) -->
-    <skip />
-    <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
-    <skip />
-    <!-- no translation found for archive_application_text_user (2586558895535581451) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
-    <skip />
+    <string name="archive_application_text" msgid="8482325710714386348">"àŠ†àŠȘà§‹àŠšàŠŸà§° àŠŹà§àŠŻàŠ•à§àŠ€àŠżàŠ—àŠ€ àŠĄà§‡àŠŸàŠŸ àŠ›à§‡àŠ­ àŠ•à§°àŠŸ àŠč’àŠŹ"</string>
+    <string name="archive_application_text_all_users" msgid="3151229641681672580">"àŠžàŠ•àŠČো àŠŹà§àŠŻà§±àŠčàŠŸà§°àŠ•àŠŸà§°à§€à§° àŠŹàŠŸàŠŹà§‡ àŠàŠ‡ àŠàŠȘ্‌àŠŸà§‹ àŠ†à§°à§àŠ•àŠŸàŠ‡àŠ­ àŠ•à§°àŠżàŠŹàŠšà§‡? àŠ†àŠȘà§‹àŠšàŠŸà§° àŠŹà§àŠŻàŠ•à§àŠ€àŠżàŠ—àŠ€ àŠĄà§‡àŠŸàŠŸ àŠ›à§‡àŠ­ àŠ•à§°àŠŸ àŠč’àŠŹ"</string>
+    <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"àŠ†àŠȘà§‹àŠšàŠŸà§° àŠ•à§°à§àŠźàŠžà§àŠ„àŠŸàŠšà§° àŠȘ্ৰ’àŠ«àŠŸàŠ‡àŠČàŠ€ àŠàŠ‡ àŠàŠȘ্‌àŠŸà§‹ àŠ†à§°à§àŠ•àŠŸàŠ‡àŠ­ àŠ•à§°àŠżàŠŹàŠšà§‡? àŠ†àŠȘà§‹àŠšàŠŸà§° àŠŹà§àŠŻàŠ•à§àŠ€àŠżàŠ—àŠ€ àŠĄà§‡àŠŸàŠŸ àŠ›à§‡àŠ­ àŠ•à§°àŠŸ àŠč’àŠŹ"</string>
+    <string name="archive_application_text_user" msgid="2586558895535581451">"<xliff:g id="USERNAME">%1$s</xliff:g>à§° àŠŹàŠŸàŠŹà§‡ àŠàŠ‡ àŠàŠȘ্‌àŠŸà§‹ àŠ†à§°à§àŠ•àŠŸàŠ‡àŠ­ àŠ•à§°àŠżàŠŹàŠšà§‡? àŠ†àŠȘà§‹àŠšàŠŸà§° àŠŹà§àŠŻàŠ•à§àŠ€àŠżàŠ—àŠ€ àŠĄà§‡àŠŸàŠŸ àŠ›à§‡àŠ­ àŠ•à§°àŠŸ àŠč’àŠŹ"</string>
+    <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"àŠ†àŠȘà§àŠšàŠż àŠ†àŠȘà§‹àŠšàŠŸà§° àŠŹà§àŠŻàŠ•à§àŠ€àŠżàŠ—àŠ€ àŠžà§àŠȘà§‡àŠšà§° àŠȘà§°àŠŸ àŠàŠ‡ àŠàŠȘ্‌àŠŸà§‹ àŠ†à§°à§àŠ•àŠŸàŠ‡àŠ­ àŠ•à§°àŠżàŠŹ àŠŹàŠżàŠšàŠŸà§°à§‡àŠšà§‡? àŠ†àŠȘà§‹àŠšàŠŸà§° àŠŹà§àŠŻàŠ•à§àŠ€àŠżàŠ—àŠ€ àŠĄà§‡àŠŸàŠŸ àŠ›à§‡àŠ­ àŠ•à§°àŠŸ àŠč’àŠŹ"</string>
     <string name="uninstall_application_text_all_users" msgid="575491774380227119">"àŠ†àŠȘà§àŠšàŠż "<b>"àŠžàŠ•àŠČো"</b>" àŠŹà§àŠŻà§±àŠčàŠŸà§°àŠ•àŠŸà§°à§€à§° àŠŹàŠŸàŠŹà§‡ àŠàŠ‡ àŠàŠȘ্‌àŠŸà§‹ àŠ†àŠšàŠ‡àŠšàŠ·à§àŠŸàŠČ àŠ•à§°àŠżàŠŹ àŠŹàŠżàŠšàŠŸà§°à§‡àŠšà§‡? àŠàŠȘ্àŠČàŠżàŠ•à§‡àŠ¶à§àŠŹàŠš àŠ†à§°à§ àŠ‡àŠŻàŠŒàŠŸà§° àŠĄà§‡àŠŸàŠŸ àŠĄàŠżàŠ­àŠŸàŠ‡àŠšàŠŸà§‹àŠ€ àŠ„àŠ•àŠŸ "<b>"àŠžàŠ•àŠČো"</b>" àŠŹà§àŠŻà§±àŠčàŠŸà§°àŠ•àŠŸà§°à§€à§° àŠȘà§°àŠŸ àŠ†àŠàŠ€à§°à§‹à§±àŠŸ àŠč\'àŠŹà§·"</string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"àŠ†àŠȘà§àŠšàŠż àŠŹà§àŠŻà§±àŠčàŠŸà§°àŠ•àŠŸà§°à§€à§° <xliff:g id="USERNAME">%1$s</xliff:g> àŠŹàŠŸàŠŹà§‡ àŠàŠ‡ àŠàŠȘ্‌àŠŸà§‹ àŠ†àŠšàŠ‡àŠšàŠ·à§àŠŸàŠČ àŠ•à§°àŠżàŠŹ àŠŹàŠżàŠšàŠŸà§°à§‡àŠšà§‡?"</string>
     <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"àŠ†àŠȘà§àŠšàŠż àŠšàŠżàŠœà§° àŠ•à§°à§àŠźàŠžà§àŠ„àŠŸàŠšà§° àŠȘ্ৰ’àŠ«àŠŸàŠ‡àŠČà§° àŠȘà§°àŠŸ àŠàŠ‡ àŠàŠȘ্‌àŠŸà§‹ àŠ†àŠšàŠ‡àŠšàŠ·à§àŠŸàŠČ àŠ•à§°àŠżàŠŹ àŠŹàŠżàŠšàŠŸà§°à§‡àŠšà§‡?"</string>
@@ -108,8 +102,7 @@
     <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"àŠ†àŠȘà§‹àŠšàŠŸà§° àŠŸà§‡àŠŹàŠČà§‡àŠŸ àŠ†à§°à§ àŠŹà§àŠŻàŠ•à§àŠ€àŠżàŠ—àŠ€ àŠĄà§‡àŠŸàŠŸ àŠ…àŠœà§àŠžàŠŸàŠ€ àŠàŠȘà§° àŠ†àŠ•à§à§°àŠźàŠŁà§° àŠŹàŠČàŠż àŠčà§‹à§±àŠŸà§° àŠžàŠźà§àŠ­àŠŸà§±àŠšàŠŸ àŠ…àŠ§àŠżàŠ•à„€ àŠ†àŠȘà§àŠšàŠż àŠàŠ‡ àŠàŠȘ্‌àŠŸà§‹ àŠ‡àŠšàŠ·à§àŠŸàŠČ àŠ•à§°àŠż àŠàŠȘ্‌àŠŸà§‹à§° àŠŹà§àŠŻà§±àŠčàŠŸà§°à§° àŠ«àŠČàŠ€ àŠ†àŠȘà§‹àŠšàŠŸà§° àŠŸàŠżàŠ­àŠżàŠ€ àŠč\'àŠŹ àŠȘà§°àŠŸ àŠŻàŠżàŠ•à§‹àŠšà§‹ àŠ•à§àŠ·àŠ€àŠż àŠŹàŠŸ àŠĄà§‡àŠŸàŠŸ àŠ•à§àŠ·àŠŻàŠŒà§° àŠŹàŠŸàŠŹà§‡ àŠ†àŠȘà§àŠšàŠż àŠšàŠżàŠœà§‡ àŠŠàŠŸàŠŻàŠŒà§€ àŠč\'àŠŹ àŠŹà§àŠČàŠż àŠžàŠšà§àŠźàŠ€àŠż àŠŠàŠżàŠŻàŠŒà§‡à„€"</string>
     <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"àŠ†àŠȘà§‹àŠšàŠŸà§° àŠŸàŠżàŠ­àŠż àŠ†à§°à§ àŠŹà§àŠŻàŠ•à§àŠ€àŠżàŠ—àŠ€ àŠĄà§‡àŠŸàŠŸ àŠ…àŠœà§àŠžàŠŸàŠ€ àŠàŠȘà§° àŠ†àŠ•à§à§°àŠźàŠŁà§° àŠŹàŠČàŠż àŠčà§‹à§±àŠŸà§° àŠžàŠźà§àŠ­àŠŸà§±àŠšàŠŸ àŠ…àŠ§àŠżàŠ•à„€ àŠ†àŠȘà§àŠšàŠż àŠàŠ‡ àŠàŠȘ্‌àŠŸà§‹ àŠ‡àŠšàŠ·à§àŠŸàŠČ àŠ•à§°àŠż àŠàŠȘ্‌àŠŸà§‹à§° àŠŹà§àŠŻà§±àŠčàŠŸà§°à§° àŠ«àŠČàŠ€ àŠ†àŠȘà§‹àŠšàŠŸà§° àŠŸàŠżàŠ­àŠżàŠ€ àŠč\'àŠŹ àŠȘà§°àŠŸ àŠŻàŠżàŠ•à§‹àŠšà§‹ àŠ•à§àŠ·àŠ€àŠż àŠŹàŠŸ àŠĄà§‡àŠŸàŠŸ àŠ•à§àŠ·àŠŻàŠŒà§° àŠŹàŠŸàŠŹà§‡ àŠ†àŠȘà§àŠšàŠż àŠšàŠżàŠœà§‡ àŠŠàŠŸàŠŻàŠŒà§€ àŠč\'àŠŹ àŠŹà§àŠČàŠż àŠžàŠšà§àŠźàŠ€àŠż àŠŠàŠżàŠŻàŠŒà§‡à„€"</string>
     <string name="cloned_app_label" msgid="7503612829833756160">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>à§° àŠ•à§àŠČ’àŠš"</string>
-    <!-- no translation found for archiving_app_label (1127085259724124725) -->
-    <skip />
+    <string name="archiving_app_label" msgid="1127085259724124725">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> àŠ†à§°à§àŠ•àŠŸàŠ‡àŠ­ àŠ•à§°àŠżàŠŹàŠšà§‡?"</string>
     <string name="anonymous_source_continue" msgid="4375745439457209366">"àŠ…àŠŹà§àŠŻàŠŸàŠčàŠ€ à§°àŠŸàŠ–àŠ•"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"àŠ›à§‡àŠŸàŠżàŠ‚"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"ৱেৰ àŠàŠȘ্‌àŠžàŠźà§‚àŠč àŠ‡àŠšàŠ·à§àŠŸàŠČ/àŠ†àŠšàŠ‡àŠšàŠ·à§àŠŸàŠČ àŠ•à§°àŠż àŠ„àŠ•àŠŸ àŠčà§ˆàŠ›à§‡"</string>
diff --git a/packages/PackageInstaller/res/values-az/strings.xml b/packages/PackageInstaller/res/values-az/strings.xml
index f5e3974..e946326 100644
--- a/packages/PackageInstaller/res/values-az/strings.xml
+++ b/packages/PackageInstaller/res/values-az/strings.xml
@@ -44,8 +44,7 @@
     <string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Naməlum tətbiqlər bu istifadəçi tərəfindən quraßdırıla bilməz"</string>
     <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Bu istifadəçinin tətbiqi quraßdırmaq üçün icazəsi yoxdur"</string>
     <string name="ok" msgid="7871959885003339302">"OK"</string>
-    <!-- no translation found for archive (4447791830199354721) -->
-    <skip />
+    <string name="archive" msgid="4447791830199354721">"Arxivə atın"</string>
     <string name="update_anyway" msgid="8792432341346261969">"İstənilən halda güncəlləyin"</string>
     <string name="manage_applications" msgid="5400164782453975580">"Tətbiqi idarə edin"</string>
     <string name="out_of_space_dlg_title" msgid="4156690013884649502">"Boß yer yoxdur"</string>
@@ -60,16 +59,11 @@
     <string name="uninstall_update_title" msgid="824411791011583031">"Güncəlləməni silin"</string>
     <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> bu tətbiqin hissəsidir:"</string>
     <string name="uninstall_application_text" msgid="3816830743706143980">"Bu tətbiqi sistemdən silmək istəyirsiniz?"</string>
-    <!-- no translation found for archive_application_text (8482325710714386348) -->
-    <skip />
-    <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
-    <skip />
-    <!-- no translation found for archive_application_text_user (2586558895535581451) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
-    <skip />
+    <string name="archive_application_text" msgid="8482325710714386348">"ƞəxsi məlumatlarınız yadda saxlanacaq"</string>
+    <string name="archive_application_text_all_users" msgid="3151229641681672580">"Bu tətbiq bütün istifadəçilər üçün arxivə atılsın? ƞəxsi məlumatlarınız yadda saxlanacaq"</string>
+    <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Bu tətbiq iß profilində arxivə atılsın? ƞəxsi məlumatlarınız yadda saxlanacaq"</string>
+    <string name="archive_application_text_user" msgid="2586558895535581451">"Bu tətbiq <xliff:g id="USERNAME">%1$s</xliff:g> üçün arxivə atılsın? ƞəxsi məlumatlarınız yadda saxlanacaq"</string>
+    <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Bu tətbiqi Ɵəxsi sahədən arxivə atmaq istəyirsiniz? ƞəxsi məlumatlarınız yadda saxlanacaq"</string>
     <string name="uninstall_application_text_all_users" msgid="575491774380227119">"Bu tətbiqi "<b>"bütün"</b>" istifadəçilər üçün silmək istəyirsiz? Tətbiq və onun datası cihazdakı "<b>"bütün"</b>" istifadəçilər üçün silinəcək."</string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"<xliff:g id="USERNAME">%1$s</xliff:g> adlı istifadəçi üçün bu tətbiqi sistemdən silmək istəyirsiniz?"</string>
     <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Bu tətbiqi iƟ profilinizdən silmək istəyirsiniz?"</string>
@@ -108,8 +102,7 @@
     <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Planßet və Ɵəxsi data naməlum tətbiqlərin hücumuna qarßı daha həssasdır. Bu tətbiqi quraßdırmaqla planßetə dəyə biləcək zərər və ya onun istifadəsi nəticəsində baß verə biləcək data itkisinə görə məsuliyyət daßıdığınızı qəbul edirsiniz."</string>
     <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Tv və Ɵəxsi data naməlum tətbiqlərin hücumuna qarßı daha həssasdır. Bu tətbiqi quraßdırmaqla Tv-yə dəyə biləcək zərər və ya onun istifadəsi nəticəsində baß verən data itkisinə görə məsuliyyət daßıdığınızı qəbul edirsiniz."</string>
     <string name="cloned_app_label" msgid="7503612829833756160">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> Kopyalayın"</string>
-    <!-- no translation found for archiving_app_label (1127085259724124725) -->
-    <skip />
+    <string name="archiving_app_label" msgid="1127085259724124725">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> arxivə atılsın?"</string>
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Davam edin"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Ayarlar"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Wear tətbiqləri quraßdırılır/sistemdən silinir"</string>
diff --git a/packages/PackageInstaller/res/values-b+sr+Latn/strings.xml b/packages/PackageInstaller/res/values-b+sr+Latn/strings.xml
index 111d90b..5627324 100644
--- a/packages/PackageInstaller/res/values-b+sr+Latn/strings.xml
+++ b/packages/PackageInstaller/res/values-b+sr+Latn/strings.xml
@@ -44,8 +44,7 @@
     <string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Ovaj korisnik ne moĆŸe da instalira nepoznate aplikacije"</string>
     <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Ovom korisniku nije dozvoljeno da instalira aplikacije"</string>
     <string name="ok" msgid="7871959885003339302">"Potvrdi"</string>
-    <!-- no translation found for archive (4447791830199354721) -->
-    <skip />
+    <string name="archive" msgid="4447791830199354721">"Arhiviraj"</string>
     <string name="update_anyway" msgid="8792432341346261969">"Ipak aĆŸuriraj"</string>
     <string name="manage_applications" msgid="5400164782453975580">"Upravljajte apl."</string>
     <string name="out_of_space_dlg_title" msgid="4156690013884649502">"Nema više prostora"</string>
@@ -60,16 +59,11 @@
     <string name="uninstall_update_title" msgid="824411791011583031">"Deinstaliraj aĆŸuriranje"</string>
     <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> je deo sledeće aplikacije:"</string>
     <string name="uninstall_application_text" msgid="3816830743706143980">"Ćœelite da deinstalirate ovu aplikaciju?"</string>
-    <!-- no translation found for archive_application_text (8482325710714386348) -->
-    <skip />
-    <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
-    <skip />
-    <!-- no translation found for archive_application_text_user (2586558895535581451) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
-    <skip />
+    <string name="archive_application_text" msgid="8482325710714386348">"Lični podaci će biti sačuvani"</string>
+    <string name="archive_application_text_all_users" msgid="3151229641681672580">"Ćœelite li da arhivirate ovu aplikaciju za sve korisnike? Lični podaci će biti sačuvani"</string>
+    <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Ćœelite li da arhivirate ovu aplikaciju sa poslovnog profila? Lični podaci će biti sačuvani"</string>
+    <string name="archive_application_text_user" msgid="2586558895535581451">"Ćœelite li da arhivirate ovu aplikaciju za korisnika <xliff:g id="USERNAME">%1$s</xliff:g>? Lični podaci će biti sačuvani"</string>
+    <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Ćœelite li da arhivirate ovu aplikaciju iz privatnog prostora? Lični podaci će biti sačuvani"</string>
     <string name="uninstall_application_text_all_users" msgid="575491774380227119">"Da li ĆŸelite da deinstalirate ovu aplikaciju za "<b>"sve"</b>" korisnike? Aplikacija i podaci uz nje biće uklonjeni za "<b>"sve"</b>" korisnike ovog uređaja."</string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Ćœelite li da deinstalirate ovu aplikaciju za korisnika <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
     <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Da li ĆŸelite da deinstalirate ovu aplikaciju sa poslovnog profila?"</string>
@@ -108,8 +102,7 @@
     <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Tablet i lični podaci su podloĆŸniji napadu nepoznatih aplikacija. Ako instalirate ovu aplikaciju, prihvatate da ste odgovorni za eventualna oštećenja tableta ili gubitak podataka do kojih moĆŸe da dođe zbog njenog korišÄ‡enja."</string>
     <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"TV i lični podaci su podloĆŸniji napadu nepoznatih aplikacija. Ako instalirate ovu aplikaciju, prihvatate da ste odgovorni za eventualna oštećenja TV-a ili gubitak podataka do kojih moĆŸe da dođe zbog njenog korišÄ‡enja."</string>
     <string name="cloned_app_label" msgid="7503612829833756160">"Klon aplikacije <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
-    <!-- no translation found for archiving_app_label (1127085259724124725) -->
-    <skip />
+    <string name="archiving_app_label" msgid="1127085259724124725">"Ćœelite li da arhivirate <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>?"</string>
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Nastavi"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Podešavanja"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Instaliranje/deinstaliranje Wear aplikac."</string>
diff --git a/packages/PackageInstaller/res/values-be/strings.xml b/packages/PackageInstaller/res/values-be/strings.xml
index d34ecb6..dcf3ad7 100644
--- a/packages/PackageInstaller/res/values-be/strings.xml
+++ b/packages/PackageInstaller/res/values-be/strings.xml
@@ -44,8 +44,7 @@
     <string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Гэты ĐșĐ°Ń€Ń‹ŃŃ‚Đ°Đ»ŃŒĐœŃ–Đș ĐœĐ” ĐŒĐŸĐ¶Đ° ўсталёўĐČаць ĐœĐ”ĐČŃĐŽĐŸĐŒŃ‹Ń ĐżŃ€Đ°ĐłŃ€Đ°ĐŒŃ‹"</string>
     <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Гэты ĐșĐ°Ń€Ń‹ŃŃ‚Đ°Đ»ŃŒĐœŃ–Đș ĐœĐ” ĐŒĐŸĐ¶Đ° ўсталёўĐČаць ĐżŃ€Đ°ĐłŃ€Đ°ĐŒŃ‹"</string>
     <string name="ok" msgid="7871959885003339302">"ОК"</string>
-    <!-- no translation found for archive (4447791830199354721) -->
-    <skip />
+    <string name="archive" msgid="4447791830199354721">"АрхіĐČаĐČаць"</string>
     <string name="update_anyway" msgid="8792432341346261969">"Усё Ń€ĐŸŃžĐœĐ° Đ°Đ±ĐœĐ°ĐČіць"</string>
     <string name="manage_applications" msgid="5400164782453975580">"КіраĐČаць"</string>
     <string name="out_of_space_dlg_title" msgid="4156690013884649502">"ĐĐ” хапаД ĐŒĐ”ŃŃ†Đ°"</string>
@@ -60,16 +59,11 @@
     <string name="uninstall_update_title" msgid="824411791011583031">"Đ’Ń‹ĐŽĐ°Đ»Ń–Ń†ŃŒ Đ°Đ±ĐœĐ°ŃžĐ»Đ”ĐœĐœĐ”"</string>
     <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> Đ·\'ŃŃžĐ»ŃĐ”Ń†Ń†Đ° частĐșаĐč ĐœĐ°ŃŃ‚ŃƒĐżĐœĐ°Đč ĐżŃ€Đ°ĐłŃ€Đ°ĐŒŃ‹:"</string>
     <string name="uninstall_application_text" msgid="3816830743706143980">"Đ’Ń‹ĐŽĐ°Đ»Ń–Ń†ŃŒ гэту ĐżŃ€Đ°ĐłŃ€Đ°ĐŒŃƒ?"</string>
-    <!-- no translation found for archive_application_text (8482325710714386348) -->
-    <skip />
-    <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
-    <skip />
-    <!-- no translation found for archive_application_text_user (2586558895535581451) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
-    <skip />
+    <string name="archive_application_text" msgid="8482325710714386348">"Вашы Đ°ŃĐ°Đ±Ń–ŃŃ‚Ń‹Ń ĐŽĐ°ĐœŃ‹Ń Đ±ŃƒĐŽŃƒŃ†ŃŒ захаĐČĐ°ĐœŃ‹"</string>
+    <string name="archive_application_text_all_users" msgid="3151229641681672580">"АрхіĐČаĐČаць гэту ĐżŃ€Đ°ĐłŃ€Đ°ĐŒŃƒ ĐŽĐ»Ń ўсіх ĐșĐ°Ń€Ń‹ŃŃ‚Đ°Đ»ŃŒĐœŃ–Đșаў? Вашы Đ°ŃĐ°Đ±Ń–ŃŃ‚Ń‹Ń ĐŽĐ°ĐœŃ‹Ń Đ±ŃƒĐŽŃƒŃ†ŃŒ захаĐČĐ°ĐœŃ‹."</string>
+    <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"АрхіĐČаĐČаць гэту ĐżŃ€Đ°ĐłŃ€Đ°ĐŒŃƒ Đ· ĐČашага ĐżŃ€Đ°Ń†ĐŸŃžĐœĐ°ĐłĐ° ĐżŃ€ĐŸŃ„Ń–Đ»ŃŽ? Вашы Đ°ŃĐ°Đ±Ń–ŃŃ‚Ń‹Ń ĐŽĐ°ĐœŃ‹Ń Đ±ŃƒĐŽŃƒŃ†ŃŒ захаĐČĐ°ĐœŃ‹."</string>
+    <string name="archive_application_text_user" msgid="2586558895535581451">"АрхіĐČаĐČаць гэту ĐżŃ€Đ°ĐłŃ€Đ°ĐŒŃƒ ĐŽĐ»Ń ĐșĐ°Ń€Ń‹ŃŃ‚Đ°Đ»ŃŒĐœŃ–Đșа <xliff:g id="USERNAME">%1$s</xliff:g>? Вашы Đ°ŃĐ°Đ±Ń–ŃŃ‚Ń‹Ń ĐŽĐ°ĐœŃ‹Ń Đ±ŃƒĐŽŃƒŃ†ŃŒ захаĐČĐ°ĐœŃ‹."</string>
+    <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"АрхіĐČаĐČаць гэту ĐżŃ€Đ°ĐłŃ€Đ°ĐŒŃƒ Đ· ĐČашаĐč прыĐČĐ°Ń‚ĐœĐ°Đč ĐżŃ€Đ°ŃŃ‚ĐŸŃ€Ń‹? Вашы Đ°ŃĐ°Đ±Ń–ŃŃ‚Ń‹Ń ĐŽĐ°ĐœŃ‹Ń Đ±ŃƒĐŽŃƒŃ†ŃŒ захаĐČĐ°ĐœŃ‹."</string>
     <string name="uninstall_application_text_all_users" msgid="575491774380227119">"Đ’Ń‹ĐŽĐ°Đ»Ń–Ń†ŃŒ гэту ĐżŃ€Đ°ĐłŃ€Đ°ĐŒŃƒ ĐŽĐ»Ń "<b>"ўсіх"</b>" ĐșĐ°Ń€Ń‹ŃŃ‚Đ°Đ»ŃŒĐœŃ–Đșаў? ĐŸŃ€Đ°ĐłŃ€Đ°ĐŒĐ° і ŃĐ” ĐŽĐ°ĐœŃ‹Ń Đ±ŃƒĐŽŃƒŃ†ŃŒ ĐČŃ‹ĐŽĐ°Đ»Đ”ĐœŃ‹ ĐŽĐ»Ń "<b>"ўсіх"</b>" ĐșĐ°Ń€Ń‹ŃŃ‚Đ°Đ»ŃŒĐœŃ–Đșаў прылаЎы."</string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Đ„ĐŸŃ‡Đ°Ń†Đ” ĐČŃ‹ĐŽĐ°Đ»Ń–Ń†ŃŒ гэту ĐżŃ€Đ°ĐłŃ€Đ°ĐŒŃƒ ĐŽĐ»Ń ĐșĐ°Ń€Ń‹ŃŃ‚Đ°Đ»ŃŒĐœŃ–Đșа <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
     <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Đ„ĐŸŃ‡Đ°Ń†Đ” ĐČŃ‹ĐŽĐ°Đ»Ń–Ń†ŃŒ гэту ĐżŃ€Đ°ĐłŃ€Đ°ĐŒŃƒ Đ· ĐżŃ€Đ°Ń†ĐŸŃžĐœĐ°ĐłĐ° ĐżŃ€ĐŸŃ„Ń–Đ»ŃŽ?"</string>
@@ -108,8 +102,7 @@
     <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Ваш ĐżĐ»Đ°ĐœŃˆŃŃ‚ і Đ°ŃĐ°Đ±Ń–ŃŃ‚Ń‹Ń ĐŽĐ°ĐœŃ‹Ń Đ±ĐŸĐ»ŃŒŃˆ ĐżŃ€Ń‹ŃŃ‚ŃƒĐżĐœŃ‹Ń ĐŽĐ»Ń атаĐș ĐœĐ”ĐČŃĐŽĐŸĐŒŃ‹Ń… ĐżŃ€Đ°ĐłŃ€Đ°ĐŒ. УсталёўĐČаючы гэту ĐżŃ€Đ°ĐłŃ€Đ°ĐŒŃƒ, ĐČы Đ·ĐłĐ°ĐŽĐ¶Đ°Đ”Ń†Đ”ŃŃ Đ· Ń‚Ń‹ĐŒ, ŃˆŃ‚ĐŸ ĐœĐ”ŃŃŃ†Đ” аЎĐșĐ°Đ·ĐœĐ°ŃŃ†ŃŒ за Đ»ŃŽĐ±Ń‹Ń пашĐșĐŸĐŽĐ¶Đ°ĐœĐœŃ– ĐżĐ»Đ°ĐœŃˆŃŃ‚Đ° ці страту ĐŽĐ°ĐœŃ‹Ń…, яĐșія ĐŒĐŸĐłŃƒŃ†ŃŒ аЎбыцца ў ĐČŃ‹ĐœŃ–Đșу ĐČыĐșĐ°Ń€Ń‹ŃŃ‚Đ°ĐœĐœŃ гэтаĐč ĐżŃ€Đ°ĐłŃ€Đ°ĐŒŃ‹."</string>
     <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Ваш Ń‚ŃĐ»Đ”ĐČізар і Đ°ŃĐ°Đ±Ń–ŃŃ‚Ń‹Ń ĐŽĐ°ĐœŃ‹Ń Đ±ĐŸĐ»ŃŒŃˆ ĐżŃ€Ń‹ŃŃ‚ŃƒĐżĐœŃ‹Ń ĐŽĐ»Ń атаĐș ĐœĐ”ĐČŃĐŽĐŸĐŒŃ‹Ń… ĐżŃ€Đ°ĐłŃ€Đ°ĐŒ. УсталёўĐČаючы гэту ĐżŃ€Đ°ĐłŃ€Đ°ĐŒŃƒ, ĐČы Đ·ĐłĐ°ĐŽĐ¶Đ°Đ”Ń†Đ”ŃŃ Đ· Ń‚Ń‹ĐŒ, ŃˆŃ‚ĐŸ ĐœĐ”ŃŃŃ†Đ” аЎĐșĐ°Đ·ĐœĐ°ŃŃ†ŃŒ за Đ»ŃŽĐ±Ń‹Ń пашĐșĐŸĐŽĐ¶Đ°ĐœĐœŃ– Ń‚ŃĐ»Đ”ĐČізара ці страту ĐŽĐ°ĐœŃ‹Ń…, яĐșія ĐŒĐŸĐłŃƒŃ†ŃŒ аЎбыцца ў ĐČŃ‹ĐœŃ–Đșу ĐČыĐșĐ°Ń€Ń‹ŃŃ‚Đ°ĐœĐœŃ гэтаĐč ĐżŃ€Đ°ĐłŃ€Đ°ĐŒŃ‹."</string>
     <string name="cloned_app_label" msgid="7503612829833756160">"ĐšĐ»ĐŸĐœ \"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>\""</string>
-    <!-- no translation found for archiving_app_label (1127085259724124725) -->
-    <skip />
+    <string name="archiving_app_label" msgid="1127085259724124725">"АрхіĐČаĐČаць ĐżŃ€Đ°ĐłŃ€Đ°ĐŒŃƒ \"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>\"?"</string>
     <string name="anonymous_source_continue" msgid="4375745439457209366">"ДалДĐč"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"ĐĐ°Đ»Đ°ĐŽŃ‹"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"ĐŁŃŃ‚Đ°Đ»ŃĐČĐ°ĐœĐœĐ” і ĐČŃ‹ĐŽĐ°Đ»Đ”ĐœĐœĐ” ĐżŃ€Đ°ĐłŃ€Đ°ĐŒ Wear"</string>
diff --git a/packages/PackageInstaller/res/values-bg/strings.xml b/packages/PackageInstaller/res/values-bg/strings.xml
index e0cd7a0..1d16074 100644
--- a/packages/PackageInstaller/res/values-bg/strings.xml
+++ b/packages/PackageInstaller/res/values-bg/strings.xml
@@ -44,8 +44,7 @@
     <string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"ĐąĐŸĐ·Đž ĐżĐŸŃ‚Ń€Đ”Đ±ĐžŃ‚Đ”Đ» ĐœĐ” ĐŒĐŸĐ¶Đ” Ўа ĐžĐœŃŃ‚Đ°Đ»ĐžŃ€Đ° ĐœĐ”ĐžĐ·ĐČĐ”ŃŃ‚ĐœĐž ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžŃ"</string>
     <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"ĐąĐŸĐ·Đž ĐżĐŸŃ‚Ń€Đ”Đ±ĐžŃ‚Đ”Đ» ĐœŃĐŒĐ° Ń€Đ°Đ·Ń€Đ”ŃˆĐ”ĐœĐžĐ” Ўа ĐžĐœŃŃ‚Đ°Đ»ĐžŃ€Đ° ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžŃ"</string>
     <string name="ok" msgid="7871959885003339302">"OK"</string>
-    <!-- no translation found for archive (4447791830199354721) -->
-    <skip />
+    <string name="archive" msgid="4447791830199354721">"АрхоĐČ"</string>
     <string name="update_anyway" msgid="8792432341346261969">"АĐșŃ‚ŃƒĐ°Đ»ĐžĐ·ĐžŃ€Đ°ĐœĐ” ĐČъпрДĐșĐž Ń‚ĐŸĐČа"</string>
     <string name="manage_applications" msgid="5400164782453975580">"ĐŸŃ€ĐžĐ».: УпраĐČĐ»."</string>
     <string name="out_of_space_dlg_title" msgid="4156690013884649502">"ĐŃĐŒĐ° ĐŒŃŃŃ‚ĐŸ"</string>
@@ -60,16 +59,11 @@
     <string name="uninstall_update_title" msgid="824411791011583031">"Đ”Đ”ĐžĐœŃŃ‚. ĐœĐ° аĐșŃ‚ŃƒĐ°Đ»ĐžĐ·Đ°Ń†ĐžŃŃ‚Đ°"</string>
     <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> Đ” част ĐŸŃ‚ ŃĐ»Đ”ĐŽĐœĐŸŃ‚ĐŸ ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžĐ”:"</string>
     <string name="uninstall_application_text" msgid="3816830743706143980">"ИсĐșатД лО Ўа ĐŽĐ”ĐžĐœŃŃ‚Đ°Đ»ĐžŃ€Đ°Ń‚Đ” Ń‚ĐŸĐČа ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžĐ”?"</string>
-    <!-- no translation found for archive_application_text (8482325710714386348) -->
-    <skip />
-    <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
-    <skip />
-    <!-- no translation found for archive_application_text_user (2586558895535581451) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
-    <skip />
+    <string name="archive_application_text" msgid="8482325710714386348">"Đ›ĐžŃ‡ĐœĐžŃ‚Đ” ĐČĐž ĐŽĐ°ĐœĐœĐž щД бъЎат Đ·Đ°ĐżĐ°Đ·Đ”ĐœĐž"</string>
+    <string name="archive_application_text_all_users" msgid="3151229641681672580">"Да сД архоĐČора лО Ń‚ĐŸĐČа ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžĐ” за ĐČсочĐșĐž ĐżĐŸŃ‚Ń€Đ”Đ±ĐžŃ‚Đ”Đ»Đž? Đ›ĐžŃ‡ĐœĐžŃ‚Đ” ĐČĐž ĐŽĐ°ĐœĐœĐž щД бъЎат Đ·Đ°ĐżĐ°Đ·Đ”ĐœĐž"</string>
+    <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Да сД архоĐČора лО Ń‚ĐŸĐČа ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžĐ” ĐČ ŃĐ»ŃƒĐ¶Đ”Đ±ĐœĐžŃ ĐČĐž ĐżĐŸŃ‚Ń€Đ”Đ±ĐžŃ‚Đ”Đ»ŃĐșĐž ĐżŃ€ĐŸŃ„ĐžĐ»? Đ›ĐžŃ‡ĐœĐžŃ‚Đ” ĐČĐž ĐŽĐ°ĐœĐœĐž щД бъЎат Đ·Đ°ĐżĐ°Đ·Đ”ĐœĐž"</string>
+    <string name="archive_application_text_user" msgid="2586558895535581451">"Да сД архоĐČора лО Ń‚ĐŸĐČа ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžĐ” за <xliff:g id="USERNAME">%1$s</xliff:g>? Đ›ĐžŃ‡ĐœĐžŃ‚Đ” ĐČĐž ĐŽĐ°ĐœĐœĐž щД бъЎат Đ·Đ°ĐżĐ°Đ·Đ”ĐœĐž"</string>
+    <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"ИсĐșатД лО Ўа архоĐČОратД Ń‚ĐŸĐČа ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžĐ” ĐŸŃ‚ Đ»ĐžŃ‡ĐœĐŸŃ‚ĐŸ сО ĐżŃ€ĐŸŃŃ‚Ń€Đ°ĐœŃŃ‚ĐČĐŸ? Đ›ĐžŃ‡ĐœĐžŃ‚Đ” ĐČĐž ĐŽĐ°ĐœĐœĐž щД бъЎат Đ·Đ°ĐżĐ°Đ·Đ”ĐœĐž"</string>
     <string name="uninstall_application_text_all_users" msgid="575491774380227119">"ИсĐșатД лО Ўа ĐŽĐ”ĐžĐœŃŃ‚Đ°Đ»ĐžŃ€Đ°Ń‚Đ” Ń‚ĐŸĐČа ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžĐ” за "<b>"ĐČсочĐșĐž"</b>" ĐżĐŸŃ‚Ń€Đ”Đ±ĐžŃ‚Đ”Đ»Đž? ĐŸŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžĐ”Ń‚ĐŸ Đž ĐŽĐ°ĐœĐœĐžŃ‚Đ” ĐŒŃƒ щД бъЎат ĐżŃ€Đ”ĐŒĐ°Ń…ĐœĐ°Ń‚Đž ĐŸŃ‚ "<b>"ĐČсочĐșĐž"</b>" ĐżĐŸŃ‚Ń€Đ”Đ±ĐžŃ‚Đ”Đ»Đž ĐœĐ° ŃƒŃŃ‚Ń€ĐŸĐčстĐČĐŸŃ‚ĐŸ."</string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"ИсĐșатД лО Ўа ĐŽĐ”ĐžĐœŃŃ‚Đ°Đ»ĐžŃ€Đ°Ń‚Đ” Ń‚ĐŸĐČа ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžĐ” за ĐżĐŸŃ‚Ń€Đ”Đ±ĐžŃ‚Đ”Đ»Ń <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
     <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"ИсĐșатД лО Ўа ĐŽĐ”ĐžĐœŃŃ‚Đ°Đ»ĐžŃ€Đ°Ń‚Đ” Ń‚ĐŸĐČа ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžĐ” ĐŸŃ‚ ŃĐ»ŃƒĐ¶Đ”Đ±ĐœĐžŃ сО ĐżĐŸŃ‚Ń€Đ”Đ±ĐžŃ‚Đ”Đ»ŃĐșĐž ĐżŃ€ĐŸŃ„ĐžĐ»?"</string>
@@ -108,8 +102,7 @@
     <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"йаблДтът Đž Đ»ĐžŃ‡ĐœĐžŃ‚Đ” ĐČĐž ĐŽĐ°ĐœĐœĐž са ĐżĐŸ-ŃƒŃĐ·ĐČĐžĐŒĐž ĐșŃŠĐŒ атаĐșĐž ĐŸŃ‚ ĐœĐ”ĐžĐ·ĐČĐ”ŃŃ‚ĐœĐž ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžŃ. ĐĄ ĐžĐœŃŃ‚Đ°Đ»ĐžŃ€Đ°ĐœĐ”Ń‚ĐŸ ĐœĐ° Ń‚ĐŸĐČа ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžĐ” ĐżŃ€ĐžĐ”ĐŒĐ°Ń‚Đ”, чД ĐœĐŸŃĐžŃ‚Đ” ĐŸŃ‚ĐłĐŸĐČĐŸŃ€ĐœĐŸŃŃ‚ про Đ”ĐČĐ”ĐœŃ‚ŃƒĐ°Đ»ĐœĐ° ĐżĐŸĐČрДЎа ĐœĐ° таблДта ОлО загуба ĐœĐ° ĐžĐœŃ„ĐŸŃ€ĐŒĐ°Ń†ĐžŃ ĐČслДЎстĐČОД ĐœĐ° ĐžĐ·ĐżĐŸĐ»Đ·ĐČĐ°ĐœĐ”Ń‚ĐŸ ĐœĐ° ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžĐ”Ń‚ĐŸ."</string>
     <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"йДлДĐČĐžĐ·ĐŸŃ€ŃŠŃ‚ Đž Đ»ĐžŃ‡ĐœĐžŃ‚Đ” ĐČĐž ĐŽĐ°ĐœĐœĐž са ĐżĐŸ-ŃƒŃĐ·ĐČĐžĐŒĐž ĐșŃŠĐŒ атаĐșĐž ĐŸŃ‚ ĐœĐ”ĐžĐ·ĐČĐ”ŃŃ‚ĐœĐž ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžŃ. ĐĄ ĐžĐœŃŃ‚Đ°Đ»ĐžŃ€Đ°ĐœĐ”Ń‚ĐŸ ĐœĐ° Ń‚ĐŸĐČа ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžĐ” ĐżŃ€ĐžĐ”ĐŒĐ°Ń‚Đ”, чД ĐœĐŸŃĐžŃ‚Đ” ĐŸŃ‚ĐłĐŸĐČĐŸŃ€ĐœĐŸŃŃ‚ про Đ”ĐČĐ”ĐœŃ‚ŃƒĐ°Đ»ĐœĐ° ĐżĐŸĐČрДЎа ĐœĐ° тДлДĐČĐžĐ·ĐŸŃ€Đ° ОлО загуба ĐœĐ° ĐžĐœŃ„ĐŸŃ€ĐŒĐ°Ń†ĐžŃ ĐČслДЎстĐČОД ĐœĐ° ĐžĐ·ĐżĐŸĐ»Đ·ĐČĐ°ĐœĐ”Ń‚ĐŸ ĐœĐ° ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžĐ”Ń‚ĐŸ."</string>
     <string name="cloned_app_label" msgid="7503612829833756160">"ĐšĐŸĐżĐžĐ” ĐœĐ° <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
-    <!-- no translation found for archiving_app_label (1127085259724124725) -->
-    <skip />
+    <string name="archiving_app_label" msgid="1127085259724124725">"Да сД архоĐČора лО <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>?"</string>
     <string name="anonymous_source_continue" msgid="4375745439457209366">"ĐĐ°ĐżŃ€Đ”ĐŽ"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"ĐĐ°ŃŃ‚Ń€ĐŸĐčĐșĐž"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Đ˜ĐœŃŃ‚Đ°Đ»ĐžŃ€./ĐŽĐ”ĐžĐœŃŃ‚Đ°Đ»ĐžŃ€. ĐœĐ° ĐżŃ€ĐžĐ»ĐŸĐ¶. за Wear"</string>
diff --git a/packages/PackageInstaller/res/values-bn/strings.xml b/packages/PackageInstaller/res/values-bn/strings.xml
index bab2d08..b6a36d2 100644
--- a/packages/PackageInstaller/res/values-bn/strings.xml
+++ b/packages/PackageInstaller/res/values-bn/strings.xml
@@ -44,8 +44,7 @@
     <string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"àŠàŠ‡ àŠŹà§àŠŻàŠŹàŠčàŠŸàŠ°àŠ•àŠŸàŠ°à§€ àŠ…àŠœàŠŸàŠšàŠŸ àŠ…à§àŠŻàŠŸàŠȘ àŠ‡àŠšàŠžà§àŠŸàŠČ àŠ•àŠ°àŠ€à§‡ àŠȘàŠŸàŠ°à§‡àŠš àŠšàŠŸ"</string>
     <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"àŠàŠ‡ àŠŹà§àŠŻàŠŹàŠčàŠŸàŠ°àŠ•àŠŸàŠ°à§€àŠ° àŠ…à§àŠŻàŠŸàŠȘ àŠ‡àŠšàŠžà§àŠŸàŠČ àŠ•àŠ°àŠŸàŠ° àŠ…àŠšà§àŠźàŠ€àŠż àŠšà§‡àŠ‡"</string>
     <string name="ok" msgid="7871959885003339302">"àŠ àŠżàŠ• àŠ†àŠ›à§‡"</string>
-    <!-- no translation found for archive (4447791830199354721) -->
-    <skip />
+    <string name="archive" msgid="4447791830199354721">"àŠ†àŠ°à§àŠ•àŠŸàŠ‡àŠ­ àŠ•àŠ°à§àŠš"</string>
     <string name="update_anyway" msgid="8792432341346261969">"àŠ€àŠŹà§àŠ“ àŠ†àŠȘàŠĄà§‡àŠŸ àŠ•àŠ°àŠ€à§‡ àŠšàŠŸàŠ‡"</string>
     <string name="manage_applications" msgid="5400164782453975580">"àŠ…à§àŠŻàŠŸàŠȘ àŠȘàŠ°àŠżàŠšàŠŸàŠČàŠšàŠŸ"</string>
     <string name="out_of_space_dlg_title" msgid="4156690013884649502">"àŠœàŠŸàŠŻàŠŒàŠ—àŠŸ àŠ–àŠŸàŠČàŠż àŠšà§‡àŠ‡"</string>
@@ -60,16 +59,11 @@
     <string name="uninstall_update_title" msgid="824411791011583031">"àŠ†àŠȘàŠĄà§‡àŠŸ àŠ†àŠšàŠ‡àŠšàŠžà§àŠŸàŠČ àŠ•àŠ°à§àŠš"</string>
     <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> àŠàŠ‡ àŠ…à§àŠŻàŠŸàŠȘàŠŸàŠżàŠ° àŠ…àŠ‚àŠ¶:"</string>
     <string name="uninstall_application_text" msgid="3816830743706143980">"àŠ†àŠȘàŠšàŠż àŠ•àŠż àŠàŠ‡ àŠ…à§àŠŻàŠŸàŠȘàŠŸàŠż àŠ†àŠšàŠ‡àŠšàŠžà§àŠŸàŠČ àŠ•àŠ°àŠ€à§‡ àŠšàŠŸàŠš?"</string>
-    <!-- no translation found for archive_application_text (8482325710714386348) -->
-    <skip />
-    <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
-    <skip />
-    <!-- no translation found for archive_application_text_user (2586558895535581451) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
-    <skip />
+    <string name="archive_application_text" msgid="8482325710714386348">"àŠ†àŠȘàŠšàŠŸàŠ° àŠŹà§àŠŻàŠ•à§àŠ€àŠżàŠ—àŠ€ àŠĄà§‡àŠŸàŠŸ àŠžà§‡àŠ­ àŠ•àŠ°àŠŸ àŠčàŠŹà§‡"</string>
+    <string name="archive_application_text_all_users" msgid="3151229641681672580">"àŠžàŠŹ àŠŹà§àŠŻàŠŹàŠčàŠŸàŠ°àŠ•àŠŸàŠ°à§€àŠ° àŠœàŠšà§àŠŻ àŠàŠ‡ àŠ…à§àŠŻàŠŸàŠȘ àŠ†àŠ°à§àŠ•àŠŸàŠ‡àŠ­ àŠ•àŠ°àŠ€à§‡ àŠšàŠŸàŠš? àŠ†àŠȘàŠšàŠŸàŠ° àŠŹà§àŠŻàŠ•à§àŠ€àŠżàŠ—àŠ€ àŠĄà§‡àŠŸàŠŸ àŠžà§‡àŠ­ àŠ•àŠ°àŠŸ àŠčàŠŹà§‡"</string>
+    <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"àŠ†àŠȘàŠšàŠŸàŠ° àŠ…àŠ«àŠżàŠž àŠȘà§àŠ°à§‹àŠ«àŠŸàŠ‡àŠČে àŠàŠ‡ àŠ…à§àŠŻàŠŸàŠȘ àŠ†àŠ°à§àŠ•àŠŸàŠ‡àŠ­ àŠ•àŠ°àŠ€à§‡ àŠšàŠŸàŠš? àŠ†àŠȘàŠšàŠŸàŠ° àŠŹà§àŠŻàŠ•à§àŠ€àŠżàŠ—àŠ€ àŠĄà§‡àŠŸàŠŸ àŠžà§‡àŠ­ àŠ•àŠ°àŠŸ àŠčàŠŹà§‡"</string>
+    <string name="archive_application_text_user" msgid="2586558895535581451">"<xliff:g id="USERNAME">%1$s</xliff:g>-àŠàŠ° àŠœàŠšà§àŠŻ àŠàŠ‡ àŠ…à§àŠŻàŠŸàŠȘ àŠ†àŠ°à§àŠ•àŠŸàŠ‡àŠ­ àŠ•àŠ°àŠ€à§‡ àŠšàŠŸàŠš? àŠ†àŠȘàŠšàŠŸàŠ° àŠŹà§àŠŻàŠ•à§àŠ€àŠżàŠ—àŠ€ àŠĄà§‡àŠŸàŠŸ àŠžà§‡àŠ­ àŠ•àŠ°àŠŸ àŠčàŠŹà§‡"</string>
+    <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"àŠ†àŠȘàŠšàŠŸàŠ° àŠŹà§àŠŻàŠ•à§àŠ€àŠżàŠ—àŠ€ àŠžà§àŠȘà§‡àŠž àŠ„à§‡àŠ•à§‡ àŠàŠ‡ àŠ…à§àŠŻàŠŸàŠȘ àŠ†àŠ°à§àŠ•àŠŸàŠ‡àŠ­ àŠ•àŠ°àŠ€à§‡ àŠšàŠŸàŠš? àŠ†àŠȘàŠšàŠŸàŠ° àŠŹà§àŠŻàŠ•à§àŠ€àŠżàŠ—àŠ€ àŠĄà§‡àŠŸàŠŸ àŠžà§‡àŠ­ àŠ•àŠ°àŠŸ àŠčàŠŹà§‡"</string>
     <string name="uninstall_application_text_all_users" msgid="575491774380227119">"àŠ†àŠȘàŠšàŠż àŠ•àŠż "<b>"àŠžàŠŹ"</b>" àŠŹà§àŠŻàŠŹàŠčàŠŸàŠ°àŠ•àŠŸàŠ°à§€àŠ° àŠœàŠšà§àŠŻ àŠàŠ‡ àŠ…à§àŠŻàŠŸàŠȘàŠŸàŠżàŠ•à§‡ àŠ†àŠšàŠ‡àŠšàŠžà§àŠŸàŠČ àŠ•àŠ°àŠ€à§‡ àŠšàŠŸàŠš? àŠàŠ‡ àŠĄàŠżàŠ­àŠŸàŠ‡àŠžà§‡àŠ° "<b>"àŠžàŠŹ"</b>" àŠŹà§àŠŻàŠŹàŠčàŠŸàŠ°àŠ•àŠŸàŠ°à§€àŠ° àŠĄà§‡àŠŸàŠŸ àŠžàŠč àŠàŠ‡ àŠ…à§àŠŻàŠŸàŠȘ্àŠČàŠżàŠ•à§‡àŠ¶àŠšàŠŸàŠż àŠžàŠ°àŠżàŠŻàŠŒà§‡ àŠŠà§‡àŠ“àŠŻàŠŒàŠŸ àŠčàŠŹà§‡à„€"</string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"àŠ†àŠȘàŠšàŠż àŠ•àŠż <xliff:g id="USERNAME">%1$s</xliff:g>-àŠàŠ° àŠœàŠšà§àŠŻ àŠàŠ‡ àŠ…à§àŠŻàŠŸàŠȘàŠŸàŠż àŠ†àŠšàŠ‡àŠšàŠžà§àŠŸàŠČ àŠ•àŠ°àŠ€à§‡ àŠšàŠŸàŠš?"</string>
     <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"àŠ†àŠȘàŠšàŠŸàŠ° àŠ…àŠ«àŠżàŠž àŠȘà§àŠ°à§‹àŠ«àŠŸàŠ‡àŠČ àŠ„à§‡àŠ•à§‡ àŠàŠ‡ àŠ…à§àŠŻàŠŸàŠȘ àŠ†àŠšàŠ‡àŠšàŠžà§àŠŸàŠČ àŠ•àŠ°àŠ€à§‡ àŠšàŠŸàŠš?"</string>
@@ -108,8 +102,7 @@
     <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"àŠ…àŠœàŠŸàŠšàŠŸ àŠ…à§àŠŻàŠŸàŠȘà§‡àŠ° àŠŠà§àŠŹàŠŸàŠ°àŠŸ àŠ†àŠȘàŠšàŠŸàŠ° àŠŸà§àŠŻàŠŸàŠŹàŠČà§‡àŠŸ àŠàŠŹàŠ‚ àŠŹà§àŠŻàŠ•à§àŠ€àŠżàŠ—àŠ€ àŠĄà§‡àŠŸàŠŸ àŠ†àŠ•à§àŠ°àŠŸàŠšà§àŠ€ àŠčàŠ“àŠŻàŠŒàŠŸàŠ° àŠžàŠźà§àŠ­àŠŸàŠŹàŠšàŠŸ àŠŹà§‡àŠ¶àŠż àŠ„àŠŸàŠ•à§‡à„€ àŠàŠ‡ àŠ…à§àŠŻàŠŸàŠȘàŠŸàŠż àŠ‡àŠšàŠžà§àŠŸàŠČ àŠ•àŠ°àŠŸàŠ° àŠźàŠŸàŠ§à§àŠŻàŠźà§‡ àŠ†àŠȘàŠšàŠż àŠžàŠźà§àŠźàŠ€ àŠčàŠšà§àŠ›à§‡àŠš àŠŻà§‡ àŠàŠŸàŠż àŠŹà§àŠŻàŠŹàŠčàŠŸàŠ°à§‡àŠ° àŠ«àŠČে àŠ†àŠȘàŠšàŠŸàŠ° àŠŸà§àŠŻàŠŸàŠŹàŠČà§‡àŠŸà§‡àŠ° àŠŹàŠŸ àŠĄà§‡àŠŸàŠŸàŠ° àŠ•à§‹àŠšàŠ“ àŠ•à§àŠ·àŠ€àŠż àŠčàŠČে àŠ€àŠŸàŠ° àŠœàŠšà§àŠŻ àŠ†àŠȘàŠšàŠżàŠ‡ àŠŠàŠŸàŠŻàŠŒà§€ àŠ„àŠŸàŠ•àŠŹà§‡àŠšà„€"</string>
     <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"àŠ…àŠœàŠŸàŠšàŠŸ àŠ…à§àŠŻàŠŸàŠȘà§‡àŠ° àŠŠà§àŠŹàŠŸàŠ°àŠŸ àŠ†àŠȘàŠšàŠŸàŠ° àŠŸàŠżàŠ­àŠż àŠàŠŹàŠ‚ àŠŹà§àŠŻàŠ•à§àŠ€àŠżàŠ—àŠ€ àŠĄà§‡àŠŸàŠŸ àŠ†àŠ•à§àŠ°àŠŸàŠšà§àŠ€ àŠčàŠ“àŠŻàŠŒàŠŸàŠ° àŠžàŠźà§àŠ­àŠŸàŠŹàŠšàŠŸ àŠŹà§‡àŠ¶àŠż àŠ„àŠŸàŠ•à§‡à„€ àŠàŠ‡ àŠ…à§àŠŻàŠŸàŠȘàŠŸàŠż àŠ‡àŠšàŠžà§àŠŸàŠČ àŠ•àŠ°àŠŸàŠ° àŠźàŠŸàŠ§à§àŠŻàŠźà§‡ àŠ†àŠȘàŠšàŠż àŠžàŠźà§àŠźàŠ€ àŠčàŠšà§àŠ›à§‡àŠš àŠŻà§‡ àŠàŠŸàŠż àŠŹà§àŠŻàŠŹàŠčàŠŸàŠ°à§‡àŠ° àŠ«àŠČে àŠ†àŠȘàŠšàŠŸàŠ° àŠŸàŠżàŠ­àŠż àŠŹàŠŸ àŠĄà§‡àŠŸàŠŸàŠ° àŠ•à§‹àŠšàŠ“ àŠ•à§àŠ·àŠ€àŠż àŠčàŠČে àŠ€àŠŸàŠ° àŠœàŠšà§àŠŻ àŠ†àŠȘàŠšàŠżàŠ‡ àŠŠàŠŸàŠŻàŠŒà§€ àŠ„àŠŸàŠ•àŠŹà§‡àŠšà„€"</string>
     <string name="cloned_app_label" msgid="7503612829833756160">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> àŠ•à§àŠČà§‹àŠš"</string>
-    <!-- no translation found for archiving_app_label (1127085259724124725) -->
-    <skip />
+    <string name="archiving_app_label" msgid="1127085259724124725">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> àŠ†àŠ°à§àŠ•àŠŸàŠ‡àŠ­ àŠ•àŠ°àŠ€à§‡ àŠšàŠŸàŠš?"</string>
     <string name="anonymous_source_continue" msgid="4375745439457209366">"àŠšàŠŸàŠČàŠżàŠŻàŠŒà§‡ àŠŻàŠŸàŠš"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"àŠžà§‡àŠŸàŠżàŠ‚àŠž"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Wear àŠ…à§àŠŻàŠŸàŠȘ àŠ‡àŠšàŠžà§àŠŸàŠČ/àŠ†àŠšàŠ‡àŠšàŠžà§àŠŸàŠČ àŠ•àŠ°àŠŸ àŠčàŠšà§àŠ›à§‡"</string>
diff --git a/packages/PackageInstaller/res/values-bs/strings.xml b/packages/PackageInstaller/res/values-bs/strings.xml
index defb388..9801e07 100644
--- a/packages/PackageInstaller/res/values-bs/strings.xml
+++ b/packages/PackageInstaller/res/values-bs/strings.xml
@@ -44,8 +44,7 @@
     <string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Ovaj korisnik ne moĆŸe instalirati nepoznate aplikacije"</string>
     <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Ovom korisniku nije dozvoljeno instaliranje aplikacija"</string>
     <string name="ok" msgid="7871959885003339302">"Uredu"</string>
-    <!-- no translation found for archive (4447791830199354721) -->
-    <skip />
+    <string name="archive" msgid="4447791830199354721">"Arhiviraj"</string>
     <string name="update_anyway" msgid="8792432341346261969">"Ipak aĆŸuriraj"</string>
     <string name="manage_applications" msgid="5400164782453975580">"Uprav. aplik."</string>
     <string name="out_of_space_dlg_title" msgid="4156690013884649502">"Nedostatak prostora"</string>
@@ -60,16 +59,11 @@
     <string name="uninstall_update_title" msgid="824411791011583031">"Deinstaliraj aĆŸuriranje"</string>
     <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> je dio sljedeće aplikacije:"</string>
     <string name="uninstall_application_text" msgid="3816830743706143980">"Ćœelite li deinstalirati ovu aplikaciju?"</string>
-    <!-- no translation found for archive_application_text (8482325710714386348) -->
-    <skip />
-    <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
-    <skip />
-    <!-- no translation found for archive_application_text_user (2586558895535581451) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
-    <skip />
+    <string name="archive_application_text" msgid="8482325710714386348">"Vaši lični podaci će se sačuvati"</string>
+    <string name="archive_application_text_all_users" msgid="3151229641681672580">"Arhivirati ovu aplikaciju za sve korisnike? Vaši lični podaci će se sačuvati"</string>
+    <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Arhivirati ovu aplikaciju na radni profil? Vaši lični podaci će se sačuvati"</string>
+    <string name="archive_application_text_user" msgid="2586558895535581451">"Arhivirati ovu aplikaciju za korisnika <xliff:g id="USERNAME">%1$s</xliff:g>? Vaši lični podaci će se sačuvati"</string>
+    <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Ćœelite li arhivirati ovu aplikaciju iz privatnog prostora? Vaši lični podaci će se sačuvati"</string>
     <string name="uninstall_application_text_all_users" msgid="575491774380227119">"Ćœelite li deinstalirati ovu aplikaciju za "<b>" sve "</b>" korisnike? Aplikacija i njeni podaci će biti uklonjeni iz "<b>" svih "</b>" korisničkih računa na uređaju."</string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Ćœelite li deinstalirati ovu aplikaciju za korisnika <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
     <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Ćœelite li deinstalirati ovu aplikaciju s radnog profila?"</string>
@@ -108,8 +102,7 @@
     <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Vaši podaci na tabletu i vaši lični podaci izloĆŸeniji su napadima nepoznatih aplikacija. Instaliranjem ove aplikacije, prihvatate odgovornost za bilo kakvu štetu na tabletu ili gubitak podataka do kojih moĆŸe doći korištenjem aplikacije."</string>
     <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Vaši podaci na TV-u i vaši lični podaci izloĆŸeniji su napadima nepoznatih aplikacija. Instaliranjem ove aplikacije, prihvatate odgovornost za bilo kakvu štetu na TV-u ili gubitak podataka do kojih moĆŸe doći korištenjem aplikacije."</string>
     <string name="cloned_app_label" msgid="7503612829833756160">"Klon aplikacije <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
-    <!-- no translation found for archiving_app_label (1127085259724124725) -->
-    <skip />
+    <string name="archiving_app_label" msgid="1127085259724124725">"Arhivirati aplikaciju <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>?"</string>
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Nastavi"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Postavke"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Instaliranje/deinstaliranje Wear aplik."</string>
diff --git a/packages/PackageInstaller/res/values-ca/strings.xml b/packages/PackageInstaller/res/values-ca/strings.xml
index 50e105c..e4da208 100644
--- a/packages/PackageInstaller/res/values-ca/strings.xml
+++ b/packages/PackageInstaller/res/values-ca/strings.xml
@@ -44,8 +44,7 @@
     <string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Aquest usuari no pot instal·lar aplicacions desconegudes"</string>
     <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Aquest usuari no té permís per instal·lar aplicacions"</string>
     <string name="ok" msgid="7871959885003339302">"D\'acord"</string>
-    <!-- no translation found for archive (4447791830199354721) -->
-    <skip />
+    <string name="archive" msgid="4447791830199354721">"Arxiva"</string>
     <string name="update_anyway" msgid="8792432341346261969">"Actualitza de tota manera"</string>
     <string name="manage_applications" msgid="5400164782453975580">"Gestiona apps"</string>
     <string name="out_of_space_dlg_title" msgid="4156690013884649502">"Espai esgotat"</string>
@@ -60,16 +59,11 @@
     <string name="uninstall_update_title" msgid="824411791011583031">"Desinstal·la l\'actualització"</string>
     <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> forma part de l\'aplicació següent:"</string>
     <string name="uninstall_application_text" msgid="3816830743706143980">"Vols desinstal·lar aquesta aplicació?"</string>
-    <!-- no translation found for archive_application_text (8482325710714386348) -->
-    <skip />
-    <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
-    <skip />
-    <!-- no translation found for archive_application_text_user (2586558895535581451) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
-    <skip />
+    <string name="archive_application_text" msgid="8482325710714386348">"Les teves dades personals es desaran"</string>
+    <string name="archive_application_text_all_users" msgid="3151229641681672580">"Vols arxivar aquesta aplicació per a tots els usuaris? Les teves dades personals es desaran."</string>
+    <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Vols arxivar aquesta aplicació al teu perfil de treball? Les teves dades personals es desaran."</string>
+    <string name="archive_application_text_user" msgid="2586558895535581451">"Vols arxivar aquesta aplicació per a <xliff:g id="USERNAME">%1$s</xliff:g>? Les teves dades personals es desaran."</string>
+    <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Vols arxivar aquesta aplicació del teu espai privat? Les teves dades personals es desaran."</string>
     <string name="uninstall_application_text_all_users" msgid="575491774380227119">"Vols desinstal·lar aquesta aplicació per a "<b>"tots"</b>" els usuaris? L\'aplicació i les seves dades se suprimiran per a "<b>"tots"</b>" els usuaris del dispositiu."</string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Vols desinstal·lar aquesta aplicació per a l\'usuari <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
     <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Vols desinstal·lar aquesta aplicació del teu perfil de treball?"</string>
@@ -108,8 +102,7 @@
     <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"La tauleta i les dades personals són més vulnerables als atacs d\'aplicacions desconegudes. En instal·lar aquesta aplicació, acceptes que ets responsable de qualsevol dany que es produeixi a la tauleta o de la pèrdua de dades que pugui resultar del seu ús."</string>
     <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"El televisor i les dades personals són més vulnerables als atacs d\'aplicacions desconegudes. En instal·lar aquesta aplicació, acceptes que ets responsable de qualsevol dany que es produeixi al televisor o de la pèrdua de dades que pugui resultar del seu ús."</string>
     <string name="cloned_app_label" msgid="7503612829833756160">"Clon de <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
-    <!-- no translation found for archiving_app_label (1127085259724124725) -->
-    <skip />
+    <string name="archiving_app_label" msgid="1127085259724124725">"Vols arxivar <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>?"</string>
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Continua"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Configuració"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Instal·lant o desinstal·lant apps de Wear"</string>
diff --git a/packages/PackageInstaller/res/values-cs/strings.xml b/packages/PackageInstaller/res/values-cs/strings.xml
index 3b204be..5bdc12d 100644
--- a/packages/PackageInstaller/res/values-cs/strings.xml
+++ b/packages/PackageInstaller/res/values-cs/strings.xml
@@ -44,8 +44,7 @@
     <string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Tento uĆŸivatel nemĆŻĆŸe instalovat neznámé aplikace"</string>
     <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Tento uĆŸivatel nesmí instalovat aplikace"</string>
     <string name="ok" msgid="7871959885003339302">"OK"</string>
-    <!-- no translation found for archive (4447791830199354721) -->
-    <skip />
+    <string name="archive" msgid="4447791830199354721">"Archivovat"</string>
     <string name="update_anyway" msgid="8792432341346261969">"Pƙesto aktualizovat"</string>
     <string name="manage_applications" msgid="5400164782453975580">"Správa aplikací"</string>
     <string name="out_of_space_dlg_title" msgid="4156690013884649502">"Nedostatek místa"</string>
@@ -60,16 +59,11 @@
     <string name="uninstall_update_title" msgid="824411791011583031">"Odinstalovat aktualizaci"</string>
     <string name="uninstall_activity_text" msgid="1928194674397770771">"Činnost <xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> je součástí následující aplikace:"</string>
     <string name="uninstall_application_text" msgid="3816830743706143980">"Chcete tuto aplikaci odinstalovat?"</string>
-    <!-- no translation found for archive_application_text (8482325710714386348) -->
-    <skip />
-    <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
-    <skip />
-    <!-- no translation found for archive_application_text_user (2586558895535581451) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
-    <skip />
+    <string name="archive_application_text" msgid="8482325710714386348">"Vaše osobní údaje budou uloĆŸeny"</string>
+    <string name="archive_application_text_all_users" msgid="3151229641681672580">"Archivovat tuto aplikaci pro všechny uĆŸivatele? Vaše osobní údaje budou uloĆŸeny"</string>
+    <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Archivovat tuto aplikaci v pracovním profilu? Vaše osobní údaje budou uloĆŸeny"</string>
+    <string name="archive_application_text_user" msgid="2586558895535581451">"Archivovat tuto aplikaci pro uĆŸivatele <xliff:g id="USERNAME">%1$s</xliff:g>? Vaše osobní údaje budou uloĆŸeny"</string>
+    <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Chcete tuto aplikaci archivovat ze soukromého prostoru? Vaše osobní údaje budou uloĆŸeny"</string>
     <string name="uninstall_application_text_all_users" msgid="575491774380227119">"Chcete tuto aplikaci odinstalovat "<b>"všem"</b>" uĆŸivatelĆŻm? Aplikace a její údaje budou odstraněny "<b>"všem"</b>" uĆŸivatelĆŻm tohoto zaƙízení."</string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Chcete tuto aplikaci pro uĆŸivatele <xliff:g id="USERNAME">%1$s</xliff:g> odinstalovat?"</string>
     <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Chcete tuto aplikaci odinstalovat ze svého pracovního profilu?"</string>
@@ -108,8 +102,7 @@
     <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Tablet a osobní údaje jsou zranitelnější vƯči útoku ze strany neznámých aplikací. Instalací této aplikace pƙijímáte odpovědnost za pƙípadné škody na tabletu nebo ztrátu dat, která mĆŻĆŸe být pouĆŸíváním aplikace zpĆŻsobena."</string>
     <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Televize a osobní údaje jsou zranitelnější vƯči útoku ze strany neznámých aplikací. Instalací této aplikace pƙijímáte odpovědnost za pƙípadné škody na televizi nebo ztrátu dat, která mĆŻĆŸe být pouĆŸíváním aplikace zpĆŻsobena."</string>
     <string name="cloned_app_label" msgid="7503612829833756160">"Klon <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
-    <!-- no translation found for archiving_app_label (1127085259724124725) -->
-    <skip />
+    <string name="archiving_app_label" msgid="1127085259724124725">"Archivovat aplikaci <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>?"</string>
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Pokračovat"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Nastavení"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Instalace/odinstalace aplikací pro Wear"</string>
diff --git a/packages/PackageInstaller/res/values-da/strings.xml b/packages/PackageInstaller/res/values-da/strings.xml
index d7819c7..fb4a1d2 100644
--- a/packages/PackageInstaller/res/values-da/strings.xml
+++ b/packages/PackageInstaller/res/values-da/strings.xml
@@ -26,9 +26,9 @@
     <string name="install_done" msgid="5987363587661783896">"Appen er installeret."</string>
     <string name="install_confirm_question" msgid="7663733664476363311">"Vil du installere denne app?"</string>
     <string name="install_confirm_question_update" msgid="3348888852318388584">"Vil du opdatere denne app?"</string>
-    <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="7994800761970572198">"&lt;p&gt;Vil du opdatere denne app via &lt;b&gt;<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>&lt;/b&gt;?&lt;/p&gt;&lt;p&gt;Denne app modtager normalt opdateriner fra &lt;b&gt;<xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>&lt;/b&gt;. Hvis du opdaterer fra en anden kilde, vil du kunne modtage opdateringer fra en hvilken som helst kilde på din tablet fremover. Dette kan påvirke appfunktionaliteten.&lt;/p&gt;"</string>
-    <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="2435174886412089791">"&lt;p&gt;Vil du opdatere denne app via &lt;b&gt;<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>&lt;/b&gt;?&lt;/p&gt;&lt;p&gt;Denne app modtager normalt opdateriner fra &lt;b&gt;<xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>&lt;/b&gt;. Hvis du opdaterer fra en anden kilde, vil du kunne modtage opdateringer fra en hvilken som helst kilde på dit fjernsyn fremover. Dette kan påvirke appfunktionaliteten.&lt;/p&gt;"</string>
-    <string name="install_confirm_question_update_owner_reminder" product="default" msgid="7155138616126795839">"&lt;p&gt;Vil du opdatere denne app via &lt;b&gt;<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>&lt;/b&gt;?&lt;/p&gt;&lt;p&gt;Denne app modtager normalt opdateriner fra &lt;b&gt;<xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>&lt;/b&gt;. Hvis du opdaterer fra en anden kilde, vil du kunne modtage opdateringer fra en hvilken som helst kilde på din telefon fremover. Dette kan påvirke appfunktionaliteten.&lt;/p&gt;"</string>
+    <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="7994800761970572198">"&lt;p&gt;Vil du opdatere denne app via &lt;b&gt;<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>&lt;/b&gt;?&lt;/p&gt;&lt;p&gt;Denne app modtager normalt opdateringer fra &lt;b&gt;<xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>&lt;/b&gt;. Hvis du opdaterer fra en anden kilde, vil du kunne modtage opdateringer fra en hvilken som helst kilde på din tablet fremover. Dette kan påvirke appfunktionaliteten.&lt;/p&gt;"</string>
+    <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="2435174886412089791">"&lt;p&gt;Vil du opdatere denne app via &lt;b&gt;<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>&lt;/b&gt;?&lt;/p&gt;&lt;p&gt;Denne app modtager normalt opdateringer fra &lt;b&gt;<xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>&lt;/b&gt;. Hvis du opdaterer fra en anden kilde, vil du kunne modtage opdateringer fra en hvilken som helst kilde på dit fjernsyn fremover. Dette kan påvirke appfunktionaliteten.&lt;/p&gt;"</string>
+    <string name="install_confirm_question_update_owner_reminder" product="default" msgid="7155138616126795839">"&lt;p&gt;Vil du opdatere denne app via &lt;b&gt;<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>&lt;/b&gt;?&lt;/p&gt;&lt;p&gt;Denne app modtager normalt opdateringer fra &lt;b&gt;<xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>&lt;/b&gt;. Hvis du opdaterer fra en anden kilde, vil du kunne modtage opdateringer fra en hvilken som helst kilde på din telefon fremover. Dette kan påvirke appfunktionaliteten.&lt;/p&gt;"</string>
     <string name="install_failed" msgid="5777824004474125469">"Appen blev ikke installeret."</string>
     <string name="install_failed_blocked" msgid="8512284352994752094">"Pakken blev forhindret i at blive installeret."</string>
     <string name="install_failed_conflict" msgid="3493184212162521426">"Appen blev ikke installeret, da pakken er i strid med en eksisterende pakke."</string>
@@ -44,8 +44,7 @@
     <string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Denne bruger kan ikke installere ukendte apps"</string>
     <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Denne bruger har ikke tilladelse til at installere apps"</string>
     <string name="ok" msgid="7871959885003339302">"OK"</string>
-    <!-- no translation found for archive (4447791830199354721) -->
-    <skip />
+    <string name="archive" msgid="4447791830199354721">"Arkivér"</string>
     <string name="update_anyway" msgid="8792432341346261969">"Opdater alligevel"</string>
     <string name="manage_applications" msgid="5400164782453975580">"Administrer apps"</string>
     <string name="out_of_space_dlg_title" msgid="4156690013884649502">"Der er ikke mere plads"</string>
@@ -60,16 +59,11 @@
     <string name="uninstall_update_title" msgid="824411791011583031">"Afinstaller opdatering"</string>
     <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> er en del af følgende app:"</string>
     <string name="uninstall_application_text" msgid="3816830743706143980">"Vil du afinstallere denne app?"</string>
-    <!-- no translation found for archive_application_text (8482325710714386348) -->
-    <skip />
-    <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
-    <skip />
-    <!-- no translation found for archive_application_text_user (2586558895535581451) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
-    <skip />
+    <string name="archive_application_text" msgid="8482325710714386348">"Dine private data gemmes"</string>
+    <string name="archive_application_text_all_users" msgid="3151229641681672580">"Vil du arkivere denne app for alle brugere? Dine private data gemmes"</string>
+    <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Vil du arkivere denne app fra din arbejdsprofil? Dine private data gemmes"</string>
+    <string name="archive_application_text_user" msgid="2586558895535581451">"Vil du arkivere denne app for <xliff:g id="USERNAME">%1$s</xliff:g>? Dine private data gemmes"</string>
+    <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Vil du arkivere denne app fra dit private område? Dine private data gemmes"</string>
     <string name="uninstall_application_text_all_users" msgid="575491774380227119">"Vil du afinstallere denne app for "<b>"alle"</b>" brugere? Appen og dens data fjernes fra "<b>"alle"</b>" brugere på denne enhed."</string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Vil du afinstallere denne app for brugeren <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
     <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Vil du afinstallere denne app fra din arbejdsprofil?"</string>
@@ -108,8 +102,7 @@
     <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Din tablet og dine personlige data er mere sårbare over for angreb fra ukendte apps. Når du installerer denne app, accepterer du, at du er ansvarlig for skader på din tablet eller tab af data, der kan skyldes brug af appen."</string>
     <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Dit fjernsyn og dine personlige data er mere sårbare over for angreb fra ukendte apps. Når du installerer denne app, accepterer du, at du er ansvarlig for skader på dit fjernsyn eller tab af data, der kan skyldes brug af appen."</string>
     <string name="cloned_app_label" msgid="7503612829833756160">"Klon af <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
-    <!-- no translation found for archiving_app_label (1127085259724124725) -->
-    <skip />
+    <string name="archiving_app_label" msgid="1127085259724124725">"Vil du arkivere <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>?"</string>
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Fortsæt"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Indstillinger"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Installerer/afinstallerer Wear-apps"</string>
diff --git a/packages/PackageInstaller/res/values-de/strings.xml b/packages/PackageInstaller/res/values-de/strings.xml
index 666ef45..6750008 100644
--- a/packages/PackageInstaller/res/values-de/strings.xml
+++ b/packages/PackageInstaller/res/values-de/strings.xml
@@ -44,8 +44,7 @@
     <string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Dieser Nutzer darf keine unbekannten Apps installieren"</string>
     <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Dieser Nutzer darf keine Apps installieren"</string>
     <string name="ok" msgid="7871959885003339302">"Ok"</string>
-    <!-- no translation found for archive (4447791830199354721) -->
-    <skip />
+    <string name="archive" msgid="4447791830199354721">"Archivieren"</string>
     <string name="update_anyway" msgid="8792432341346261969">"Trotzdem aktualisieren"</string>
     <string name="manage_applications" msgid="5400164782453975580">"Apps verwalten"</string>
     <string name="out_of_space_dlg_title" msgid="4156690013884649502">"Kein freier Speicher vorhanden"</string>
@@ -60,16 +59,11 @@
     <string name="uninstall_update_title" msgid="824411791011583031">"Update deinstallieren"</string>
     <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> gehört zu folgender App:"</string>
     <string name="uninstall_application_text" msgid="3816830743706143980">"Möchtest du diese App deinstallieren?"</string>
-    <!-- no translation found for archive_application_text (8482325710714386348) -->
-    <skip />
-    <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
-    <skip />
-    <!-- no translation found for archive_application_text_user (2586558895535581451) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
-    <skip />
+    <string name="archive_application_text" msgid="8482325710714386348">"Deine personenbezogenen Daten werden gespeichert"</string>
+    <string name="archive_application_text_all_users" msgid="3151229641681672580">"Diese App für alle Nutzer archivieren? Deine personenbezogenen Daten werden gespeichert."</string>
+    <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Diese in deinem Arbeitsprofil befindliche App archivieren? Deine personenbezogenen Daten werden gespeichert."</string>
+    <string name="archive_application_text_user" msgid="2586558895535581451">"Diese App für <xliff:g id="USERNAME">%1$s</xliff:g> archivieren? Deine personenbezogenen Daten werden gespeichert."</string>
+    <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Möchtest du diese in deinem privaten Bereich befindliche App archivieren? Deine personenbezogenen Daten werden gespeichert."</string>
     <string name="uninstall_application_text_all_users" msgid="575491774380227119">"Möchtest du diese App für "<b>"alle"</b>" Nutzer entfernen? Die App und alle zugehörigen Daten werden für "<b>"alle"</b>" Nutzer des Geräts entfernt."</string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Möchtest du diese App für den Nutzer <xliff:g id="USERNAME">%1$s</xliff:g> deinstallieren?"</string>
     <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Möchtest du diese App aus deinem Arbeitsprofil deinstallieren?"</string>
@@ -108,8 +102,7 @@
     <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Unbekannte Apps können gefährlich für dein Tablet und deine personenbezogenen Daten sein. Wenn du diese App installierst, erklärst du dich damit einverstanden, dass du die Verantwortung für alle Schäden an deinem Tablet und jegliche Datenverluste trägst, die aus der Verwendung dieser App entstehen können."</string>
     <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Unbekannte Apps können gefährlich für deinen Fernseher und deine personenbezogenen Daten sein. Wenn du diese App installierst, erklärst du dich damit einverstanden, dass du die Verantwortung für alle Schäden an deinem Fernseher und jegliche Datenverluste trägst, die aus der Verwendung dieser App entstehen können."</string>
     <string name="cloned_app_label" msgid="7503612829833756160">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>-Klon"</string>
-    <!-- no translation found for archiving_app_label (1127085259724124725) -->
-    <skip />
+    <string name="archiving_app_label" msgid="1127085259724124725">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> archivieren?"</string>
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Weiter"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Einstellungen"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Wear-Apps installieren/deinstallieren"</string>
diff --git a/packages/PackageInstaller/res/values-el/strings.xml b/packages/PackageInstaller/res/values-el/strings.xml
index 94e50b1..6f0db20 100644
--- a/packages/PackageInstaller/res/values-el/strings.xml
+++ b/packages/PackageInstaller/res/values-el/strings.xml
@@ -44,8 +44,7 @@
     <string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Δεν εÎŻναι δυνατÎź η εγκατÎŹσταση ÎŹγνωστων εφαρμογών από αυτόν τον χρÎźστη"</string>
     <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Δεν επιτρέπεται η εγκατÎŹσταση εφαρμογών σε αυτόν τον χρÎźστη"</string>
     <string name="ok" msgid="7871959885003339302">"ΟΚ"</string>
-    <!-- no translation found for archive (4447791830199354721) -->
-    <skip />
+    <string name="archive" msgid="4447791830199354721">"Αρχειοθέτηση"</string>
     <string name="update_anyway" msgid="8792432341346261969">"Να ενημερωθεÎŻ ούτως Îź ÎŹλλως"</string>
     <string name="manage_applications" msgid="5400164782453975580">"Διαχ. εφαρμογών"</string>
     <string name="out_of_space_dlg_title" msgid="4156690013884649502">"Δεν υπÎŹρχει χώρος"</string>
@@ -60,16 +59,11 @@
     <string name="uninstall_update_title" msgid="824411791011583031">"ΑπεγκατÎŹσταση ενημέρωσης"</string>
     <string name="uninstall_activity_text" msgid="1928194674397770771">"Η δραστηριότητα <xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> αποτελεÎŻ τμÎźμα της ακόλουθης εφαρμογÎźς:"</string>
     <string name="uninstall_application_text" msgid="3816830743706143980">"Θέλετε να απεγκαταστÎźσετε αυτÎź την εφαρμογÎź;"</string>
-    <!-- no translation found for archive_application_text (8482325710714386348) -->
-    <skip />
-    <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
-    <skip />
-    <!-- no translation found for archive_application_text_user (2586558895535581451) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
-    <skip />
+    <string name="archive_application_text" msgid="8482325710714386348">"Τα προσωπικÎŹ δεδομένα σας θα αποθηκευτούν"</string>
+    <string name="archive_application_text_all_users" msgid="3151229641681672580">"Αρχειοθέτηση αυτÎźς της εφαρμογÎźς για όλους τους χρÎźστες; Τα προσωπικÎŹ δεδομένα σας θα αποθηκευτούν"</string>
+    <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Αρχειοθέτηση αυτÎźς της εφαρμογÎźς στο προφÎŻλ εργασÎŻας σας; Τα προσωπικÎŹ δεδομένα σας θα αποθηκευτούν"</string>
+    <string name="archive_application_text_user" msgid="2586558895535581451">"Αρχειοθέτηση αυτÎźς της εφαρμογÎźς για τον χρÎźστη <xliff:g id="USERNAME">%1$s</xliff:g>; Τα προσωπικÎŹ δεδομένα σας θα αποθηκευτούν"</string>
+    <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Θέλετε να αρχειοθετÎźσετε αυτÎź την εφαρμογÎź από τον απόρρητο χώρο σας; Τα προσωπικÎŹ δεδομένα σας θα αποθηκευτούν"</string>
     <string name="uninstall_application_text_all_users" msgid="575491774380227119">"Θέλετε να απεγκαταστÎźσετε αυτÎź την εφαρμογÎź για "<b>"όλους"</b>" τους χρÎźστες; Η εφαρμογÎź και τα δεδομένα της θα καταργηθούν από "<b>"όλους"</b>" τους χρÎźστες στη συσκευÎź."</string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Θέλετε να απεγκαταστÎźσετε αυτÎź την εφαρμογÎź για τον χρÎźστη <xliff:g id="USERNAME">%1$s</xliff:g>;"</string>
     <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Θέλετε να καταργÎźσετε την εγκατÎŹσταση αυτÎźς της εφαρμογÎźς από το προφÎŻλ εργασÎŻας σας;"</string>
@@ -108,8 +102,7 @@
     <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Το tablet σας και τα προσωπικÎŹ δεδομένα σας εÎŻναι πιο ευÎŹλωτα σε επιθέσεις από ÎŹγνωστες εφαρμογές. Με την εγκατÎŹσταση αυτÎźς της εφαρμογÎźς, συμφωνεÎŻτε ότι εÎŻστε υπεύθυνοι για τυχόν βλÎŹβη που μπορεÎŻ να προκληθεÎŻ στο tablet Îź απώλεια δεδομένων που μπορεÎŻ να προκύψει από τη χρÎźση τους."</string>
     <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Η τηλεόρασÎź σας και τα προσωπικÎŹ δεδομένα σας εÎŻναι πιο ευÎŹλωτα σε επιθέσεις από ÎŹγνωστες εφαρμογές. Με την εγκατÎŹσταση αυτÎźς της εφαρμογÎźς, συμφωνεÎŻτε ότι εÎŻστε υπεύθυνοι για τυχόν βλÎŹβη που μπορεÎŻ να προκληθεÎŻ στην τηλεόρασÎź Îź απώλεια δεδομένων που μπορεÎŻ να προκύψει από τη χρÎźση τους."</string>
     <string name="cloned_app_label" msgid="7503612829833756160">"Διπλότυπο <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
-    <!-- no translation found for archiving_app_label (1127085259724124725) -->
-    <skip />
+    <string name="archiving_app_label" msgid="1127085259724124725">"Αρχειοθέτηση <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>;"</string>
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Συνέχεια"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"ΡυθμÎŻσεις"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"ΕγκατÎŹσταση/απεγκατÎŹσταση εφαρμογών Wear"</string>
diff --git a/packages/PackageInstaller/res/values-es-rUS/strings.xml b/packages/PackageInstaller/res/values-es-rUS/strings.xml
index b66976a..7537008 100644
--- a/packages/PackageInstaller/res/values-es-rUS/strings.xml
+++ b/packages/PackageInstaller/res/values-es-rUS/strings.xml
@@ -26,9 +26,9 @@
     <string name="install_done" msgid="5987363587661783896">"Se instaló la app."</string>
     <string name="install_confirm_question" msgid="7663733664476363311">"¿Deseas instalar esta app?"</string>
     <string name="install_confirm_question_update" msgid="3348888852318388584">"¿Deseas actualizar esta app?"</string>
-    <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="7994800761970572198">"&lt;p&gt;Actualiza esta app desde &lt;b&gt;<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>&lt;/b&gt;?&lt;/p&gt;&lt;p&gt;Esta app normalmente recibe actualizaciones desde &lt;b&gt;<xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>&lt;/b&gt;. Si actualizas a través de otra fuente, es posible que recibas las próximas actualizaciones de cualquier fuente en la tablet. Las funciones de la app puede cambiar.&lt;/p&gt;"</string>
-    <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="2435174886412089791">"&lt;p&gt;Actualiza esta app desde &lt;b&gt;<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>&lt;/b&gt;?&lt;/p&gt;&lt;p&gt;Esta app normalmente recibe actualizaciones desde &lt;b&gt;<xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>&lt;/b&gt;. Si actualizas a través de otra fuente, es posible que recibas las próximas actualizaciones de cualquier fuente en la TV. Las funciones de la app puede cambiar.&lt;/p&gt;"</string>
-    <string name="install_confirm_question_update_owner_reminder" product="default" msgid="7155138616126795839">"&lt;p&gt;Actualiza esta app desde &lt;b&gt;<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>&lt;/b&gt;?&lt;/p&gt;&lt;p&gt;Esta app normalmente recibe actualizaciones desde &lt;b&gt;<xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>&lt;/b&gt;. Si actualizas a través de otra fuente, es posible que recibas las próximas actualizaciones de cualquier fuente en el teléfono. Las funciones de la app puede cambiar.&lt;/p&gt;"</string>
+    <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="7994800761970572198">"&lt;p&gt;¿Actualizar esta app desde &lt;b&gt;<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>&lt;/b&gt;?&lt;/p&gt;&lt;p&gt;Esta app normalmente recibe actualizaciones desde &lt;b&gt;<xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>&lt;/b&gt;. Si actualizas a través de otra fuente, es posible que recibas las próximas actualizaciones de cualquier fuente en la tablet. Las funciones de la app pueden cambiar.&lt;/p&gt;"</string>
+    <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="2435174886412089791">"&lt;p&gt;¿Actualizar esta app desde &lt;b&gt;<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>&lt;/b&gt;?&lt;/p&gt;&lt;p&gt;Esta app normalmente recibe actualizaciones desde &lt;b&gt;<xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>&lt;/b&gt;. Si actualizas a través de otra fuente, es posible que recibas las próximas actualizaciones de cualquier fuente en la TV. Las funciones de la app pueden cambiar.&lt;/p&gt;"</string>
+    <string name="install_confirm_question_update_owner_reminder" product="default" msgid="7155138616126795839">"&lt;p&gt;¿Actualizar esta app desde &lt;b&gt;<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>&lt;/b&gt;?&lt;/p&gt;&lt;p&gt;Esta app normalmente recibe actualizaciones desde &lt;b&gt;<xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>&lt;/b&gt;. Si actualizas a través de otra fuente, es posible que recibas las próximas actualizaciones de cualquier fuente en el teléfono. Las funciones de la app pueden cambiar.&lt;/p&gt;"</string>
     <string name="install_failed" msgid="5777824004474125469">"No se instaló la app."</string>
     <string name="install_failed_blocked" msgid="8512284352994752094">"Se bloqueó el paquete para impedir la instalación."</string>
     <string name="install_failed_conflict" msgid="3493184212162521426">"No se instaló la app debido a un conflicto con un paquete."</string>
@@ -44,8 +44,7 @@
     <string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Este usuario no puede instalar apps desconocidas"</string>
     <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Este usuario no puede instalar apps"</string>
     <string name="ok" msgid="7871959885003339302">"Aceptar"</string>
-    <!-- no translation found for archive (4447791830199354721) -->
-    <skip />
+    <string name="archive" msgid="4447791830199354721">"Archivar"</string>
     <string name="update_anyway" msgid="8792432341346261969">"Actualizar de todas formas"</string>
     <string name="manage_applications" msgid="5400164782453975580">"Gestionar apps"</string>
     <string name="out_of_space_dlg_title" msgid="4156690013884649502">"Sin espacio"</string>
@@ -60,16 +59,11 @@
     <string name="uninstall_update_title" msgid="824411791011583031">"Desinstalar actualización"</string>
     <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> es parte de la siguiente app:"</string>
     <string name="uninstall_application_text" msgid="3816830743706143980">"¿Quieres desinstalar esta app?"</string>
-    <!-- no translation found for archive_application_text (8482325710714386348) -->
-    <skip />
-    <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
-    <skip />
-    <!-- no translation found for archive_application_text_user (2586558895535581451) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
-    <skip />
+    <string name="archive_application_text" msgid="8482325710714386348">"Se guardarán tus datos personales"</string>
+    <string name="archive_application_text_all_users" msgid="3151229641681672580">"¿Quieres archivar esta app para todos los usuarios? Se guardarán tus datos personales"</string>
+    <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"¿Quieres archivar esta app en tu perfil de trabajo? Se guardarán tus datos personales"</string>
+    <string name="archive_application_text_user" msgid="2586558895535581451">"¿Quieres archivar esta app para <xliff:g id="USERNAME">%1$s</xliff:g>? Se guardarán tus datos personales"</string>
+    <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"¿Quieres archivar esta app de tu espacio privado? Se guardarán tus datos personales"</string>
     <string name="uninstall_application_text_all_users" msgid="575491774380227119">"¿Quieres desinstalar esta app para "<b>"todos"</b>" los usuarios? Se quitarán la aplicación y sus datos de "<b>"todos"</b>" los usuarios del dispositivo."</string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"¿Quieres desinstalar esta app para el usuario <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
     <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"¿Deseas desinstalar esta app de tu perfil de trabajo?"</string>
@@ -108,8 +102,7 @@
     <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"La tablet y tus datos personales son más vulnerables a los ataques de apps desconocidas. Si instalas esta app, serás responsable de los daños que sufra la tablet y de la pérdida de datos que pueda ocasionar su uso."</string>
     <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"La TV y tus datos personales son más vulnerables a los ataques de apps desconocidas. Si instalas esta app, serás responsable de los daños que sufra la TV y de la pérdida de datos que pueda ocasionar su uso."</string>
     <string name="cloned_app_label" msgid="7503612829833756160">"Clon de <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
-    <!-- no translation found for archiving_app_label (1127085259724124725) -->
-    <skip />
+    <string name="archiving_app_label" msgid="1127085259724124725">"¿Quieres archivar <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>?"</string>
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Continuar"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Configuración"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Instalando/desinstalando apps para Wear"</string>
diff --git a/packages/PackageInstaller/res/values-es/strings.xml b/packages/PackageInstaller/res/values-es/strings.xml
index 2a0d063..9a7e523 100644
--- a/packages/PackageInstaller/res/values-es/strings.xml
+++ b/packages/PackageInstaller/res/values-es/strings.xml
@@ -44,8 +44,7 @@
     <string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Este usuario no puede instalar aplicaciones desconocidas"</string>
     <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Este usuario no tiene permiso para instalar aplicaciones"</string>
     <string name="ok" msgid="7871959885003339302">"Aceptar"</string>
-    <!-- no translation found for archive (4447791830199354721) -->
-    <skip />
+    <string name="archive" msgid="4447791830199354721">"Archivar"</string>
     <string name="update_anyway" msgid="8792432341346261969">"Actualizar igualmente"</string>
     <string name="manage_applications" msgid="5400164782453975580">"Gestionar aplicaciones"</string>
     <string name="out_of_space_dlg_title" msgid="4156690013884649502">"Sin espacio"</string>
@@ -60,16 +59,11 @@
     <string name="uninstall_update_title" msgid="824411791011583031">"Desinstalar actualización"</string>
     <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> forma parte de esta aplicación:"</string>
     <string name="uninstall_application_text" msgid="3816830743706143980">"¿Quieres desinstalar esta aplicación?"</string>
-    <!-- no translation found for archive_application_text (8482325710714386348) -->
-    <skip />
-    <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
-    <skip />
-    <!-- no translation found for archive_application_text_user (2586558895535581451) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
-    <skip />
+    <string name="archive_application_text" msgid="8482325710714386348">"Tus datos personales se guardarán."</string>
+    <string name="archive_application_text_all_users" msgid="3151229641681672580">"¿Archivar esta aplicación para todos los usuarios? Tus datos personales se guardarán."</string>
+    <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"¿Archivar esta aplicación en tu perfil de trabajo? Tus datos personales se guardarán."</string>
+    <string name="archive_application_text_user" msgid="2586558895535581451">"¿Archivar esta aplicación para <xliff:g id="USERNAME">%1$s</xliff:g>? Tus datos personales se guardarán."</string>
+    <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"¿Quieres archivar esta aplicación de tu espacio privado? Tus datos personales se guardarán."</string>
     <string name="uninstall_application_text_all_users" msgid="575491774380227119">"¿Quieres desinstalar esta aplicación para "<b>"todos"</b>" los usuarios? La aplicación y sus datos se borrarán de "<b>"todos"</b>" los usuarios del dispositivo."</string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"¿Quieres desinstalar esta aplicación para el usuario <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
     <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"¿Quieres desinstalar esta aplicación de tu perfil de trabajo?"</string>
@@ -108,8 +102,7 @@
     <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Tu tablet y tus datos personales son más vulnerables a los ataques de aplicaciones desconocidas. Al instalar esta aplicación, aceptas ser responsable de cualquier daño que sufra tu tablet o la pérdida de datos que se pueda derivar de su uso."</string>
     <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Tu TV y tus datos personales son más vulnerables a los ataques de aplicaciones desconocidas. Al instalar esta aplicación, aceptas ser responsable de cualquier daño que sufra tu TV o la pérdida de datos que se pueda derivar de su uso."</string>
     <string name="cloned_app_label" msgid="7503612829833756160">"Clon de <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
-    <!-- no translation found for archiving_app_label (1127085259724124725) -->
-    <skip />
+    <string name="archiving_app_label" msgid="1127085259724124725">"¿Archivar <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>?"</string>
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Continuar"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Ajustes"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Instalando/desinstalando apps para Wear"</string>
diff --git a/packages/PackageInstaller/res/values-et/strings.xml b/packages/PackageInstaller/res/values-et/strings.xml
index 5d5914e..b083932 100644
--- a/packages/PackageInstaller/res/values-et/strings.xml
+++ b/packages/PackageInstaller/res/values-et/strings.xml
@@ -44,8 +44,7 @@
     <string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"See kasutaja ei saa installida tundmatuid rakendusi"</string>
     <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Kasutajal ei ole lubatud rakendusi installida"</string>
     <string name="ok" msgid="7871959885003339302">"OK"</string>
-    <!-- no translation found for archive (4447791830199354721) -->
-    <skip />
+    <string name="archive" msgid="4447791830199354721">"Arhiiv"</string>
     <string name="update_anyway" msgid="8792432341346261969">"Värskenda ikkagi"</string>
     <string name="manage_applications" msgid="5400164782453975580">"Rakend. haldam."</string>
     <string name="out_of_space_dlg_title" msgid="4156690013884649502">"Pole ruumi"</string>
@@ -60,16 +59,11 @@
     <string name="uninstall_update_title" msgid="824411791011583031">"Värskenduse desinstallimine"</string>
     <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> on osa järgmisest rakendusest:"</string>
     <string name="uninstall_application_text" msgid="3816830743706143980">"Kas soovite selle rakenduse desinstallida?"</string>
-    <!-- no translation found for archive_application_text (8482325710714386348) -->
-    <skip />
-    <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
-    <skip />
-    <!-- no translation found for archive_application_text_user (2586558895535581451) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
-    <skip />
+    <string name="archive_application_text" msgid="8482325710714386348">"Teie isiklikud andmed salvestatakse"</string>
+    <string name="archive_application_text_all_users" msgid="3151229641681672580">"Kas arhiivida see rakendus kõigi kasutajate puhul? Teie isiklikud andmed salvestatakse"</string>
+    <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Kas arhiivida see rakendus teie tööprofiilil? Teie isiklikud andmed salvestatakse"</string>
+    <string name="archive_application_text_user" msgid="2586558895535581451">"Kas arhiivida see rakendus kasutaja <xliff:g id="USERNAME">%1$s</xliff:g> puhul? Teie isiklikud andmed salvestatakse"</string>
+    <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Kas soovite selle rakenduse oma privaatsest ruumist arhiivida? Teie isiklikud andmed salvestatakse"</string>
     <string name="uninstall_application_text_all_users" msgid="575491774380227119">"Kas soovite selle rakenduse "<b>"kõikide"</b>" kasutajate kontodelt desinstallida? Rakendus ja selle andmed eemaldatakse "<b>"kõikide"</b>" seadme kasutajate kontodelt."</string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Kas soovite selle rakenduse kasutaja <xliff:g id="USERNAME">%1$s</xliff:g> kontolt desinstallida?"</string>
     <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Kas soovite selle rakenduse oma tööprofiililt desinstallida?"</string>
@@ -108,8 +102,7 @@
     <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Teie tahvelarvuti ja isiklikud andmed on tundmatute rakenduste rünnakute suhtes haavatavamad. Selle rakenduse installimisel nõustute, et vastutate tahvelarvuti kahjude ja andmekao eest, mis võivad tuleneda selliste rakenduste kasutamisest."</string>
     <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Teie teler ja isiklikud andmed on tundmatute rakenduste rünnakute suhtes haavatavamad. Selle rakenduse installimisel nõustute, et vastutate teleri kahjude ja andmekao eest, mis võivad tuleneda selliste rakenduste kasutamisest."</string>
     <string name="cloned_app_label" msgid="7503612829833756160">"Rakenduse <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> kloon"</string>
-    <!-- no translation found for archiving_app_label (1127085259724124725) -->
-    <skip />
+    <string name="archiving_app_label" msgid="1127085259724124725">"Kas arhiivida <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>?"</string>
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Jätka"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Seaded"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Weari rak. installimine/desinstallimine"</string>
diff --git a/packages/PackageInstaller/res/values-eu/strings.xml b/packages/PackageInstaller/res/values-eu/strings.xml
index af25e52..9d1d536 100644
--- a/packages/PackageInstaller/res/values-eu/strings.xml
+++ b/packages/PackageInstaller/res/values-eu/strings.xml
@@ -44,8 +44,7 @@
     <string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Erabiltzaile honek ezin ditu instalatu aplikazio ezezagunak"</string>
     <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Erabiltzaile honek ez du baimenik aplikazioak instalatzeko"</string>
     <string name="ok" msgid="7871959885003339302">"Ados"</string>
-    <!-- no translation found for archive (4447791830199354721) -->
-    <skip />
+    <string name="archive" msgid="4447791830199354721">"Artxibatu"</string>
     <string name="update_anyway" msgid="8792432341346261969">"Eguneratu halere"</string>
     <string name="manage_applications" msgid="5400164782453975580">"Kudeatu aplikazioak"</string>
     <string name="out_of_space_dlg_title" msgid="4156690013884649502">"Ez dago behar adina toki"</string>
@@ -60,16 +59,11 @@
     <string name="uninstall_update_title" msgid="824411791011583031">"Desinstalatu eguneratzea"</string>
     <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> aplikazio honen zati da:"</string>
     <string name="uninstall_application_text" msgid="3816830743706143980">"Aplikazioa desinstalatu nahi duzu?"</string>
-    <!-- no translation found for archive_application_text (8482325710714386348) -->
-    <skip />
-    <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
-    <skip />
-    <!-- no translation found for archive_application_text_user (2586558895535581451) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
-    <skip />
+    <string name="archive_application_text" msgid="8482325710714386348">"Zure datu pertsonalak gorde egingo dira"</string>
+    <string name="archive_application_text_all_users" msgid="3151229641681672580">"Aplikazio hau erabiltzaile guztientzat artxibatu nahi duzu? Zure datu pertsonalak gorde egingo dira."</string>
+    <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Aplikazio hau laneko profiletik artxibatu nahi duzu? Zure datu pertsonalak gorde egingo dira."</string>
+    <string name="archive_application_text_user" msgid="2586558895535581451">"Aplikazio hau <xliff:g id="USERNAME">%1$s</xliff:g> erabiltzailearentzat artxibatu nahi duzu? Zure datu pertsonalak gorde egingo dira."</string>
+    <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Aplikazio hau eremu pribatutik artxibatu nahi duzu? Zure datu pertsonalak gorde egingo dira."</string>
     <string name="uninstall_application_text_all_users" msgid="575491774380227119">"Erabiltzaile "<b>"guztiei"</b>" desinstalatu nahi diezu aplikazioa? Aplikazioa eta haren datu guztiak ezabatuko zaizkie gailuko erabiltzaile "<b>"guztiei"</b>"."</string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"<xliff:g id="USERNAME">%1$s</xliff:g> erabiltzaileari desinstalatu nahi diozu aplikazioa?"</string>
     <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Aplikazioa laneko profiletik desinstalatu nahi duzu?"</string>
@@ -108,8 +102,7 @@
     <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Baliteke tabletak eta datu pertsonalek aplikazio ezezagunen erasoak jasatea. Aplikazio hau instalatzen baduzu, onartu egingo duzu hura erabiltzeagatik tabletari agian gertatuko zaizkion kalteen edo datu-galeren erantzulea zeu izango zarela."</string>
     <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Baliteke telebistak eta datu pertsonalek aplikazio ezezagunen erasoak jasatea. Aplikazio hau instalatzen baduzu, onartu egingo duzu hura erabiltzeagatik telebistari agian gertatuko zaizkion kalteen edo datu-galeren erantzulea zeu izango zarela."</string>
     <string name="cloned_app_label" msgid="7503612829833756160">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> aplikazioaren klona"</string>
-    <!-- no translation found for archiving_app_label (1127085259724124725) -->
-    <skip />
+    <string name="archiving_app_label" msgid="1127085259724124725">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> artxibatu nahi duzu?"</string>
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Egin aurrera"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Ezarpenak"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Wear aplikazioak instalatzea/desinstalatzea"</string>
diff --git a/packages/PackageInstaller/res/values-fa/strings.xml b/packages/PackageInstaller/res/values-fa/strings.xml
index a7dc36a..53bfd27 100644
--- a/packages/PackageInstaller/res/values-fa/strings.xml
+++ b/packages/PackageInstaller/res/values-fa/strings.xml
@@ -44,8 +44,7 @@
     <string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Ű§ÛŒÙ† کۧ۱ۚ۱ نمی‌ŰȘÙˆŰ§Ù†ŰŻ ŰšŰ±Ù†Ű§Ù…Ù‡‌Ù‡Ű§ÛŒ Ù†Ű§ŰŽÙ†Ű§Űł Ù†Ű”Űš Ú©Ù†ŰŻ"</string>
     <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Ű§ÛŒÙ† کۧ۱ۚ۱ Ù…ŰŹŰ§ŰČ ŰšÙ‡ Ù†Ű”Űš ŰšŰ±Ù†Ű§Ù…Ù‡‌ Ù†ÛŒŰłŰȘ"</string>
     <string name="ok" msgid="7871959885003339302">"ŰšŰłÛŒŰ§Ű± ŰźÙˆŰš"</string>
-    <!-- no translation found for archive (4447791830199354721) -->
-    <skip />
+    <string name="archive" msgid="4447791830199354721">"ŰšŰ§ÛŒÚŻŰ§Ù†ÛŒ"</string>
     <string name="update_anyway" msgid="8792432341346261969">"ŰŻŰ±Ù‡Ű±Ű”ÙˆŰ±ŰȘ ŰšÙ‡‌Ű±ÙˆŰČ ŰŽÙˆŰŻ"</string>
     <string name="manage_applications" msgid="5400164782453975580">"Ù…ŰŻÛŒŰ±ÛŒŰȘ ŰšŰ±Ù†Ű§Ù…Ù‡‌Ù‡Ű§"</string>
     <string name="out_of_space_dlg_title" msgid="4156690013884649502">"ÙŰ¶Ű§ Ú©Ű§ÙÛŒ Ù†ÛŒŰłŰȘ"</string>
@@ -60,16 +59,11 @@
     <string name="uninstall_update_title" msgid="824411791011583031">"Ű­Ű°Ù Ù†Ű”Űš ŰšÙ‡‌Ű±ÙˆŰČŰ±ŰłŰ§Ù†ÛŒ"</string>
     <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> Ù‚ŰłÙ…ŰȘی ۧŰČ ŰšŰ±Ù†Ű§Ù…Ù‡ ŰČÛŒŰ± ۧ۳ŰȘ:"</string>
     <string name="uninstall_application_text" msgid="3816830743706143980">"می‌ŰźÙˆŰ§Ù‡ÛŒŰŻ Ű§ÛŒÙ† ŰšŰ±Ù†Ű§Ù…Ù‡ ۱ۧ Ű­Ű°Ù Ù†Ű”Űš Ú©Ù†ÛŒŰŻŰŸ"</string>
-    <!-- no translation found for archive_application_text (8482325710714386348) -->
-    <skip />
-    <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
-    <skip />
-    <!-- no translation found for archive_application_text_user (2586558895535581451) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
-    <skip />
+    <string name="archive_application_text" msgid="8482325710714386348">"ŰŻŰ§ŰŻÙ‡‌Ù‡Ű§ÛŒ ŰŽŰźŰ”ÛŒ‌ŰȘŰ§Ù† Ű°ŰźÛŒŰ±Ù‡ ŰźÙˆŰ§Ù‡ŰŻ ŰŽŰŻ"</string>
+    <string name="archive_application_text_all_users" msgid="3151229641681672580">"Ű§ÛŒÙ† ŰšŰ±Ù†Ű§Ù…Ù‡ ŰšŰ±Ű§ÛŒ همه Ú©Ű§Ű±ŰšŰ±Ű§Ù† ŰšŰ§ÛŒÚŻŰ§Ù†ÛŒ ŰŽÙˆŰŻŰŸ ŰŻŰ§ŰŻÙ‡‌Ù‡Ű§ÛŒ ŰŽŰźŰ”ÛŒ‌ŰȘŰ§Ù† Ű°ŰźÛŒŰ±Ù‡ ŰźÙˆŰ§Ù‡ŰŻ ŰŽŰŻ"</string>
+    <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Ű§ÛŒÙ† ŰšŰ±Ù†Ű§Ù…Ù‡ ۯ۱ Ù†Ù…Ű§ÛŒÙ‡ Ú©Ű§Ű±ÛŒ ŰšŰ§ÛŒÚŻŰ§Ù†ÛŒ ŰŽÙˆŰŻŰŸ ŰŻŰ§ŰŻÙ‡‌Ù‡Ű§ÛŒ ŰŽŰźŰ”ÛŒ‌ŰȘŰ§Ù† Ű°ŰźÛŒŰ±Ù‡ ŰźÙˆŰ§Ù‡ŰŻ ŰŽŰŻ"</string>
+    <string name="archive_application_text_user" msgid="2586558895535581451">"Ű§ÛŒÙ† ŰšŰ±Ù†Ű§Ù…Ù‡ ŰšŰ±Ű§ÛŒ <xliff:g id="USERNAME">%1$s</xliff:g> ŰšŰ§ÛŒÚŻŰ§Ù†ÛŒ ŰŽÙˆŰŻŰŸ ŰŻŰ§ŰŻÙ‡‌Ù‡Ű§ÛŒ ŰŽŰźŰ”ÛŒ‌ŰȘŰ§Ù† Ű°ŰźÛŒŰ±Ù‡ ŰźÙˆŰ§Ù‡ŰŻ ŰŽŰŻ"</string>
+    <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"می‌ŰźÙˆŰ§Ù‡ÛŒŰŻ Ű§ÛŒÙ† ŰšŰ±Ù†Ű§Ù…Ù‡ ۧŰČ ÙŰ¶Ű§ÛŒ ŰźŰ”ÙˆŰ”ÛŒ‌ŰȘŰ§Ù† ۱ۧ ŰšŰ§ÛŒÚŻŰ§Ù†ÛŒ Ú©Ù†ÛŒŰŻŰŸ ŰŻŰ§ŰŻÙ‡‌Ù‡Ű§ÛŒ ŰŽŰźŰ”ÛŒ‌ŰȘŰ§Ù† Ű°ŰźÛŒŰ±Ù‡ ŰźÙˆŰ§Ù‡ŰŻ ŰŽŰŻ"</string>
     <string name="uninstall_application_text_all_users" msgid="575491774380227119">"ŰąÛŒŰ§ می‌ŰźÙˆŰ§Ù‡ÛŒŰŻ Ű§ÛŒÙ† ŰšŰ±Ù†Ű§Ù…Ù‡ ۱ۧ ŰšŰ±Ű§ÛŒ "<b>"همه"</b>" Ú©Ű§Ű±ŰšŰ±Ű§Ù† Ű­Ű°Ù Ú©Ù†ÛŒŰŻŰŸ Ű§ÛŒÙ† ŰšŰ±Ù†Ű§Ù…Ù‡ و ŰŻŰ§ŰŻÙ‡‌Ù‡Ű§ÛŒ ŰąÙ† ŰšŰ±Ű§ÛŒ "<b>"همه"</b>" Ú©Ű§Ű±ŰšŰ±Ű§Ù† Ű§ÛŒÙ† ŰŻŰłŰȘÚŻŰ§Ù‡ Ű­Ű°Ù ŰźÙˆŰ§Ù‡ŰŻ ŰŽŰŻ."</string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"ŰąÛŒŰ§ می‌ŰźÙˆŰ§Ù‡ÛŒŰŻ Ű§ÛŒÙ† ŰšŰ±Ù†Ű§Ù…Ù‡ ۱ۧ ŰšŰ±Ű§ÛŒ Ű§ÛŒÙ† کۧ۱ۚ۱ <xliff:g id="USERNAME">%1$s</xliff:g> Ű­Ű°Ù Ù†Ű”Űš Ú©Ù†ÛŒŰŻŰŸ"</string>
     <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"می‌ŰźÙˆŰ§Ù‡ÛŒŰŻ Ű§ÛŒÙ† ŰšŰ±Ù†Ű§Ù…Ù‡ ۱ۧ ۧŰČ Ù†Ù…Ű§ÛŒÙ‡ Ú©Ű§Ű±ÛŒ‌ŰȘŰ§Ù† Ű­Ű°Ù Ù†Ű”Űš Ú©Ù†ÛŒŰŻŰŸ"</string>
@@ -108,8 +102,7 @@
     <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Ű±Ű§ÛŒŰ§Ù†Ù‡ Ù„ÙˆŰ­ÛŒ و ŰŻŰ§ŰŻÙ‡‌Ù‡Ű§ÛŒ ŰŽŰźŰ”ÛŒ‌ŰȘŰ§Ù† ۯ۱ۚ۱ۧۚ۱ Ű­Ù…Ù„Ù‡ ŰšŰ±Ù†Ű§Ù…Ù‡‌Ù‡Ű§ÛŒ Ù†Ű§ŰŽÙ†Ű§Űł ŰąŰłÛŒŰš‌ÙŸŰ°ÛŒŰ±ŰȘ۱ Ù‡ŰłŰȘÙ†ŰŻ. ۚۧ Ù†Ű”Űš Ű§ÛŒÙ† ŰšŰ±Ù†Ű§Ù…Ù‡ŰŒ Ù…ÙˆŰ§ÙÙ‚ŰȘ می‌Ú©Ù†ÛŒŰŻ که Ù…ŰłŰŠÙˆÙ„ Ù‡Ű±ÚŻÙˆÙ†Ù‡ ŰąŰłÛŒŰš ŰšÙ‡ Ű±Ű§ÛŒŰ§Ù†Ù‡ Ù„ÙˆŰ­ÛŒ ÛŒŰ§ ۧŰČ ŰŻŰłŰȘ Ű±ÙŰȘن ŰŻŰ§ŰŻÙ‡‌Ű§ÛŒ Ù‡ŰłŰȘÛŒŰŻ که ممکن ۧ۳ŰȘ ŰŻŰ±Ù†ŰȘÛŒŰŹÙ‡ ۧ۳ŰȘÙŰ§ŰŻÙ‡ ۧŰČ ŰąÙ† ŰšÙ‡ ÙˆŰŹÙˆŰŻ ŰąÛŒŰŻ."</string>
     <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"ŰȘلویŰČیون و ŰŻŰ§ŰŻÙ‡‌Ù‡Ű§ÛŒ ŰŽŰźŰ”ÛŒ‌ŰȘŰ§Ù† ۯ۱ۚ۱ۧۚ۱ Ű­Ù…Ù„Ù‡ ŰšŰ±Ù†Ű§Ù…Ù‡‌Ù‡Ű§ÛŒ Ù†Ű§ŰŽÙ†Ű§Űł ŰąŰłÛŒŰš‌ÙŸŰ°ÛŒŰ±ŰȘ۱ Ù‡ŰłŰȘÙ†ŰŻ. ۚۧ Ù†Ű”Űš Ű§ÛŒÙ† ŰšŰ±Ù†Ű§Ù…Ù‡ŰŒ Ù…ÙˆŰ§ÙÙ‚ŰȘ می‌Ú©Ù†ÛŒŰŻ که Ù…ŰłŰŠÙˆÙ„ Ù‡Ű±ÚŻÙˆÙ†Ù‡ ŰąŰłÛŒŰš ŰšÙ‡ ŰȘلویŰČیون ÛŒŰ§ ۧŰČ ŰŻŰłŰȘ Ű±ÙŰȘن ŰŻŰ§ŰŻÙ‡‌Ű§ÛŒ Ù‡ŰłŰȘÛŒŰŻ که ممکن ۧ۳ŰȘ ŰŻŰ±Ù†ŰȘÛŒŰŹÙ‡ ۧ۳ŰȘÙŰ§ŰŻÙ‡ ۧŰČ ŰąÙ† ŰšÙ‡ ÙˆŰŹÙˆŰŻ ŰąÛŒŰŻ."</string>
     <string name="cloned_app_label" msgid="7503612829833756160">"Ù‡Ù…ŰłŰ§Ù†Ù‡ <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
-    <!-- no translation found for archiving_app_label (1127085259724124725) -->
-    <skip />
+    <string name="archiving_app_label" msgid="1127085259724124725">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ŰšŰ§ÛŒÚŻŰ§Ù†ÛŒ ŰŽÙˆŰŻŰŸ"</string>
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Ű§ŰŻŰ§Ù…Ù‡"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"ŰȘÙ†ŰžÛŒÙ…Ű§ŰȘ"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Ù†Ű”Űš/Ű­Ű°Ù Ù†Ű”Űš ŰšŰ±Ù†Ű§Ù…Ù‡‌Ù‡Ű§ÛŒ ÙŸÙˆŰŽÛŒŰŻÙ†ÛŒ"</string>
diff --git a/packages/PackageInstaller/res/values-fi/strings.xml b/packages/PackageInstaller/res/values-fi/strings.xml
index 3ef9807..c5bb501 100644
--- a/packages/PackageInstaller/res/values-fi/strings.xml
+++ b/packages/PackageInstaller/res/values-fi/strings.xml
@@ -44,8 +44,7 @@
     <string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Tämä käyttäjä ei voi asentaa tuntemattomia sovelluksia."</string>
     <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Tämä käyttäjä ei voi asentaa sovelluksia."</string>
     <string name="ok" msgid="7871959885003339302">"OK"</string>
-    <!-- no translation found for archive (4447791830199354721) -->
-    <skip />
+    <string name="archive" msgid="4447791830199354721">"Arkistoi"</string>
     <string name="update_anyway" msgid="8792432341346261969">"Päivitä silti"</string>
     <string name="manage_applications" msgid="5400164782453975580">"Sovellusvalinnat"</string>
     <string name="out_of_space_dlg_title" msgid="4156690013884649502">"Tallennustila ei riitä"</string>
@@ -60,16 +59,11 @@
     <string name="uninstall_update_title" msgid="824411791011583031">"Poista päivitys"</string>
     <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> on osa seuraavaa sovellusta:"</string>
     <string name="uninstall_application_text" msgid="3816830743706143980">"Haluatko poistaa tämän sovelluksen?"</string>
-    <!-- no translation found for archive_application_text (8482325710714386348) -->
-    <skip />
-    <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
-    <skip />
-    <!-- no translation found for archive_application_text_user (2586558895535581451) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
-    <skip />
+    <string name="archive_application_text" msgid="8482325710714386348">"Henkilökohtainen datasi tallennetaan"</string>
+    <string name="archive_application_text_all_users" msgid="3151229641681672580">"Arkistoidaanko tämä sovellus kaikilta käyttäjiltä? Henkilökohtainen datasi tallennetaan"</string>
+    <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Arkistoidaanko tämä sovellus työprofiilistasi? Henkilökohtainen datasi tallennetaan"</string>
+    <string name="archive_application_text_user" msgid="2586558895535581451">"Arkistoidaanko tämä sovellus käyttäjältä: <xliff:g id="USERNAME">%1$s</xliff:g>? Henkilökohtainen datasi tallennetaan"</string>
+    <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Haluatko arkistoida tämän sovelluksen yksityisestä tilasta? Henkilökohtainen datasi tallennetaan"</string>
     <string name="uninstall_application_text_all_users" msgid="575491774380227119">"Haluatko poistaa tämän sovelluksen "<b>"kaikilta"</b>" käyttäjiltä? Sovellus ja sen data poistetaan "<b>"kaikilta"</b>" laitteen käyttäjiltä."</string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Haluatko poistaa tämän sovelluksen käyttäjältä <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
     <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Haluatko poistaa sovelluksen työprofiilistasi?"</string>
@@ -108,8 +102,7 @@
     <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Tuntemattomat sovellukset voivat helpommin kaapata tablettisi ja henkilökohtaiset tietosi. Lataamalla sovelluksia tästä lähteestä hyväksyt, että olet itse vastuussa tabletillesi aiheutuvista vahingoista tai tietojen menetyksestä, jotka voivat johtua sovellusten käytöstä."</string>
     <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Tuntemattomat sovellukset voivat helpommin kaapata televisiosi ja henkilökohtaiset tietosi. Lataamalla sovelluksen hyväksyt, että olet itse vastuussa mahdollisista televisiolle aiheutuvista vahingoista tai tietojen menetyksestä, jotka voivat johtua sovellusten käytöstä."</string>
     <string name="cloned_app_label" msgid="7503612829833756160">"Klooni: <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
-    <!-- no translation found for archiving_app_label (1127085259724124725) -->
-    <skip />
+    <string name="archiving_app_label" msgid="1127085259724124725">"Arkistoidaanko <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>?"</string>
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Jatka"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Asetukset"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Wear-sovellusten asennus/poistaminen"</string>
diff --git a/packages/PackageInstaller/res/values-fr-rCA/strings.xml b/packages/PackageInstaller/res/values-fr-rCA/strings.xml
index 0bd5026..86ba34c 100644
--- a/packages/PackageInstaller/res/values-fr-rCA/strings.xml
+++ b/packages/PackageInstaller/res/values-fr-rCA/strings.xml
@@ -44,8 +44,7 @@
     <string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Cet utilisateur ne peut pas installer d\'applications inconnues"</string>
     <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Cet utilisateur n\'est pas autorisé à installer des applications"</string>
     <string name="ok" msgid="7871959885003339302">"OK"</string>
-    <!-- no translation found for archive (4447791830199354721) -->
-    <skip />
+    <string name="archive" msgid="4447791830199354721">"Archiver"</string>
     <string name="update_anyway" msgid="8792432341346261969">"Mettre à jour malgré tout"</string>
     <string name="manage_applications" msgid="5400164782453975580">"Gérer les applis"</string>
     <string name="out_of_space_dlg_title" msgid="4156690013884649502">"Espace insuffisant"</string>
@@ -60,16 +59,11 @@
     <string name="uninstall_update_title" msgid="824411791011583031">"Désinstaller mise à jour"</string>
     <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> fait partie de l\'application suivante :"</string>
     <string name="uninstall_application_text" msgid="3816830743706143980">"Voulez-vous désinstaller cette application?"</string>
-    <!-- no translation found for archive_application_text (8482325710714386348) -->
-    <skip />
-    <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
-    <skip />
-    <!-- no translation found for archive_application_text_user (2586558895535581451) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
-    <skip />
+    <string name="archive_application_text" msgid="8482325710714386348">"Vos données personnelles seront enregistrées"</string>
+    <string name="archive_application_text_all_users" msgid="3151229641681672580">"Archiver cette application pour tous les utilisateurs? Vos données personnelles seront enregistrées"</string>
+    <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Archiver cette application sur votre profil professionnel? Vos données personnelles seront enregistrées"</string>
+    <string name="archive_application_text_user" msgid="2586558895535581451">"Archiver cette application pour <xliff:g id="USERNAME">%1$s</xliff:g>? Vos données personnelles seront enregistrées"</string>
+    <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Voulez-vous archiver cette application de votre Espace privé? Vos données personnelles seront enregistrées"</string>
     <string name="uninstall_application_text_all_users" msgid="575491774380227119">"Voulez-vous désinstaller cette application pour "<b>"tous"</b>" les utilisateurs? L\'application et ses données seront supprimées pour "<b>"tous"</b>" les utilisateurs de l\'appareil."</string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Voulez-vous désinstaller cette application pour l\'utilisateur <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
     <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Voulez-vous désinstaller cette application de votre profil professionnel?"</string>
@@ -108,8 +102,7 @@
     <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Votre tablette et vos données personnelles sont plus vulnérables aux attaques provenant d\'applications inconnues. En installant cette application, vous acceptez d\'être le seul responsable de tout dommage causé à votre tablette ou de toute perte de données pouvant découler de l\'utilisation de telles applications."</string>
     <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Votre téléviseur et vos données personnelles sont plus vulnérables aux attaques d\'applications inconnues. En installant cette application, vous acceptez d\'être le seul responsable de tout dommage causé à votre téléviseur ou de toute perte de données pouvant découler de son utilisation."</string>
     <string name="cloned_app_label" msgid="7503612829833756160">"Clone de <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
-    <!-- no translation found for archiving_app_label (1127085259724124725) -->
-    <skip />
+    <string name="archiving_app_label" msgid="1127085259724124725">"Archiver <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>?"</string>
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Continuer"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Paramètres"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Installer/désinstaller applis Google Wear"</string>
diff --git a/packages/PackageInstaller/res/values-fr/strings.xml b/packages/PackageInstaller/res/values-fr/strings.xml
index 1c64613..0b74e19 100644
--- a/packages/PackageInstaller/res/values-fr/strings.xml
+++ b/packages/PackageInstaller/res/values-fr/strings.xml
@@ -26,9 +26,9 @@
     <string name="install_done" msgid="5987363587661783896">"Application installée."</string>
     <string name="install_confirm_question" msgid="7663733664476363311">"Voulez-vous installer cette appli ?"</string>
     <string name="install_confirm_question_update" msgid="3348888852318388584">"Voulez-vous mettre à jour cette appli ?"</string>
-    <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="7994800761970572198">"&lt;p&gt;Mettez à jour cette appli depuis &lt;b&gt;<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>&lt;/b&gt;?&lt;/p&gt;&lt;p&gt;Elle reçoit normalement des mises à jour depuis &lt;b&gt;<xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>&lt;/b&gt;. Si vous effectuez la mise à jour à partir d\'une autre source, vous recevrez peut-être les prochaines mises à jour depuis n\'importe quelle source sur votre tablette. Le fonctionnement de l\'application peut changer.&lt;/p&gt;"</string>
-    <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="2435174886412089791">"&lt;p&gt;Mettez à jour cette appli depuis &lt;b&gt;<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>&lt;/b&gt;?&lt;/p&gt;&lt;p&gt;Elle reçoit normalement des mises à jour depuis &lt;b&gt;<xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>&lt;/b&gt;. Si vous effectuez la mise à jour à partir d\'une autre source, vous recevrez peut-être les prochaines mises à jour depuis n\'importe quelle source sur votre téléviseur. Le fonctionnement de l\'application peut changer.&lt;/p&gt;"</string>
-    <string name="install_confirm_question_update_owner_reminder" product="default" msgid="7155138616126795839">"&lt;p&gt;Mettez à jour cette appli depuis &lt;b&gt;<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>&lt;/b&gt;?&lt;/p&gt;&lt;p&gt;Elle reçoit normalement des mises à jour depuis &lt;b&gt;<xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>&lt;/b&gt;. Si vous effectuez la mise à jour à partir d\'une autre source, vous recevrez peut-être les prochaines mises à jour depuis n\'importe quelle source sur votre téléphone. Le fonctionnement de l\'application peut changer.&lt;/p&gt;"</string>
+    <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="7994800761970572198">"&lt;p&gt;Mettre à jour cette appli depuis &lt;b&gt;<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>&lt;/b&gt; ?&lt;/p&gt;&lt;p&gt;Elle reçoit normalement des mises à jour depuis &lt;b&gt;<xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>&lt;/b&gt;. Si vous effectuez la mise à jour à partir d\'une autre source, vous recevrez peut-être les prochaines mises à jour depuis n\'importe quelle source sur votre tablette. Le fonctionnement de l\'application peut changer.&lt;/p&gt;"</string>
+    <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="2435174886412089791">"&lt;p&gt;Mettre à jour cette appli depuis &lt;b&gt;<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>&lt;/b&gt; ?&lt;/p&gt;&lt;p&gt;Elle reçoit normalement des mises à jour depuis &lt;b&gt;<xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>&lt;/b&gt;. Si vous effectuez la mise à jour à partir d\'une autre source, vous recevrez peut-être les prochaines mises à jour depuis n\'importe quelle source sur votre téléviseur. Le fonctionnement de l\'application peut changer.&lt;/p&gt;"</string>
+    <string name="install_confirm_question_update_owner_reminder" product="default" msgid="7155138616126795839">"&lt;p&gt;Mettre à jour cette appli depuis &lt;b&gt;<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>&lt;/b&gt; ?&lt;/p&gt;&lt;p&gt;Elle reçoit normalement des mises à jour depuis &lt;b&gt;<xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>&lt;/b&gt;. Si vous effectuez la mise à jour à partir d\'une autre source, vous recevrez peut-être les prochaines mises à jour depuis n\'importe quelle source sur votre téléphone. Le fonctionnement de l\'application peut changer.&lt;/p&gt;"</string>
     <string name="install_failed" msgid="5777824004474125469">"Application non installée."</string>
     <string name="install_failed_blocked" msgid="8512284352994752094">"L\'installation du package a été bloquée."</string>
     <string name="install_failed_conflict" msgid="3493184212162521426">"L\'application n\'a pas été installée, car le package est en conflit avec un package déjà présent."</string>
@@ -44,8 +44,7 @@
     <string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Cet utilisateur ne peut pas installer d\'applications inconnues"</string>
     <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Cet utilisateur n\'est pas autorisé à installer des applications"</string>
     <string name="ok" msgid="7871959885003339302">"OK"</string>
-    <!-- no translation found for archive (4447791830199354721) -->
-    <skip />
+    <string name="archive" msgid="4447791830199354721">"Archiver"</string>
     <string name="update_anyway" msgid="8792432341346261969">"Mettre à jour quand même"</string>
     <string name="manage_applications" msgid="5400164782453975580">"Gérer applis"</string>
     <string name="out_of_space_dlg_title" msgid="4156690013884649502">"Mémoire insuffisante"</string>
@@ -60,16 +59,11 @@
     <string name="uninstall_update_title" msgid="824411791011583031">"Désinstaller la mise à jour"</string>
     <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> fait partie de l\'application suivante :"</string>
     <string name="uninstall_application_text" msgid="3816830743706143980">"Voulez-vous désinstaller cette application ?"</string>
-    <!-- no translation found for archive_application_text (8482325710714386348) -->
-    <skip />
-    <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
-    <skip />
-    <!-- no translation found for archive_application_text_user (2586558895535581451) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
-    <skip />
+    <string name="archive_application_text" msgid="8482325710714386348">"Vos données personnelles seront enregistrées."</string>
+    <string name="archive_application_text_all_users" msgid="3151229641681672580">"Archiver cette appli pour tous les utilisateurs ? Vos données personnelles seront enregistrées."</string>
+    <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Archiver cette appli sur votre profil professionnel ? Vos données personnelles seront enregistrées."</string>
+    <string name="archive_application_text_user" msgid="2586558895535581451">"Archiver cette appli pour <xliff:g id="USERNAME">%1$s</xliff:g> ? Vos données personnelles seront enregistrées."</string>
+    <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Souhaitez-vous archiver cette application de votre espace privé ? Vos données personnelles seront enregistrées."</string>
     <string name="uninstall_application_text_all_users" msgid="575491774380227119">"Voulez-vous désinstaller cette application pour "<b>"tous"</b>" les utilisateurs ? L\'application et ses données seront supprimées pour "<b>"tous"</b>" les utilisateurs de l\'appareil."</string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Voulez-vous désinstaller cette application pour l\'utilisateur <xliff:g id="USERNAME">%1$s</xliff:g> ?"</string>
     <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Voulez-vous désinstaller cette appli de votre profil professionnel ?"</string>
@@ -108,8 +102,7 @@
     <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Votre tablette et vos données à caractère personnel sont plus vulnérables aux attaques d\'applications inconnues. En installant cette application, vous acceptez d\'être le seul responsable de tout dommage causé à votre tablette ou de toute perte de données pouvant découler de son utilisation."</string>
     <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Votre téléviseur et vos données à caractère personnel sont plus vulnérables aux attaques d\'applications inconnues. En installant cette application, vous acceptez d\'être le seul responsable de tout dommage causé à votre téléviseur ou de toute perte de données pouvant découler de son utilisation."</string>
     <string name="cloned_app_label" msgid="7503612829833756160">"Clone de <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
-    <!-- no translation found for archiving_app_label (1127085259724124725) -->
-    <skip />
+    <string name="archiving_app_label" msgid="1127085259724124725">"Archiver <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ?"</string>
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Continuer"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Paramètres"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Installer/Désinstaller les applis Wear"</string>
diff --git a/packages/PackageInstaller/res/values-gl/strings.xml b/packages/PackageInstaller/res/values-gl/strings.xml
index 38e79ba..96f0fdb 100644
--- a/packages/PackageInstaller/res/values-gl/strings.xml
+++ b/packages/PackageInstaller/res/values-gl/strings.xml
@@ -44,8 +44,7 @@
     <string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Este usuario non pode instalar aplicacións descoñecidas"</string>
     <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Este usuario non ten permiso para instalar aplicacións"</string>
     <string name="ok" msgid="7871959885003339302">"Aceptar"</string>
-    <!-- no translation found for archive (4447791830199354721) -->
-    <skip />
+    <string name="archive" msgid="4447791830199354721">"Arquivar"</string>
     <string name="update_anyway" msgid="8792432341346261969">"Actualizar de todas formas"</string>
     <string name="manage_applications" msgid="5400164782453975580">"Xestionar apps"</string>
     <string name="out_of_space_dlg_title" msgid="4156690013884649502">"Esgotouse o espazo"</string>
@@ -60,16 +59,11 @@
     <string name="uninstall_update_title" msgid="824411791011583031">"Desinstalar actualización"</string>
     <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> forma parte da seguinte aplicación:"</string>
     <string name="uninstall_application_text" msgid="3816830743706143980">"Queres desinstalar esta aplicación?"</string>
-    <!-- no translation found for archive_application_text (8482325710714386348) -->
-    <skip />
-    <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
-    <skip />
-    <!-- no translation found for archive_application_text_user (2586558895535581451) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
-    <skip />
+    <string name="archive_application_text" msgid="8482325710714386348">"Gardaranse os teus datos persoais"</string>
+    <string name="archive_application_text_all_users" msgid="3151229641681672580">"Queres arquivar esta aplicación para todos os usuarios? Gardaranse os teus datos persoais"</string>
+    <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Queres arquivar esta aplicación no teu perfil de traballo? Gardaranse os teus datos persoais"</string>
+    <string name="archive_application_text_user" msgid="2586558895535581451">"Queres arquivar esta aplicación para <xliff:g id="USERNAME">%1$s</xliff:g>? Gardaranse os teus datos persoais"</string>
+    <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Queres arquivar esta aplicación do teu espazo privado? Gardaranse os teus datos persoais"</string>
     <string name="uninstall_application_text_all_users" msgid="575491774380227119">"Queres desinstalar esta aplicación para "<b>"todos"</b>" os usuarios? A aplicación e os seus datos quitaranse de "<b>"todos"</b>" os usuarios do dispositivo."</string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Queres desinstalar esta aplicación para o usuario que se chama <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
     <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Queres desinstalar esta aplicación do perfil de traballo?"</string>
@@ -108,8 +102,7 @@
     <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"A tableta e os datos persoais son máis vulnerables aos ataques de aplicacións descoñecidas. Ao instalar esta aplicación, aceptas que es responsable dos danos ocasionados na tableta ou da perda dos datos que se poidan derivar do seu uso."</string>
     <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"A televisión e os datos persoais son máis vulnerables aos ataques de aplicacións descoñecidas. Ao instalar esta aplicación, aceptas que es responsable dos danos ocasionados na televisión ou da perda dos datos que se poidan derivar do seu uso."</string>
     <string name="cloned_app_label" msgid="7503612829833756160">"Clon de <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
-    <!-- no translation found for archiving_app_label (1127085259724124725) -->
-    <skip />
+    <string name="archiving_app_label" msgid="1127085259724124725">"Queres arquivar <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>?"</string>
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Continuar"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Configuración"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Instalando/desinstalando apps para Wear"</string>
diff --git a/packages/PackageInstaller/res/values-gu/strings.xml b/packages/PackageInstaller/res/values-gu/strings.xml
index 3362a16..18db939 100644
--- a/packages/PackageInstaller/res/values-gu/strings.xml
+++ b/packages/PackageInstaller/res/values-gu/strings.xml
@@ -44,8 +44,7 @@
     <string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"àȘ† àȘ”àȘȘàȘ°àȘŸàȘ¶àȘ•àȘ°à«àȘ€àȘŸ àȘ…àȘœàȘŸàȘŁà«€ àȘàȘȘ્àȘČàȘżàȘ•ેàȘ¶àȘšà«‹àȘšà«‡ àȘ‡àȘšà«àȘžà«àȘŸà«‰àȘČ àȘ•àȘ°à«€ àȘ¶àȘ•àȘ€àȘŸ àȘšàȘ„ી"</string>
     <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"àȘ† àȘ”àȘȘàȘ°àȘŸàȘ¶àȘ•àȘ°à«àȘ€àȘŸàȘšà«‡ àȘàȘȘ્àȘČàȘżàȘ•ેàȘ¶àȘšà«‹ àȘ‡àȘšà«àȘžà«àȘŸà«‰àȘČ àȘ•àȘ°àȘ”àȘŸàȘšà«€ àȘźàȘ‚àȘœà«‚àȘ°à«€ àȘšàȘ„ી"</string>
     <string name="ok" msgid="7871959885003339302">"àȘ“àȘ•ે"</string>
-    <!-- no translation found for archive (4447791830199354721) -->
-    <skip />
+    <string name="archive" msgid="4447791830199354721">"àȘ†àȘ°à«àȘ•àȘŸàȘ‡àȘ” àȘ•àȘ°à«‹"</string>
     <string name="update_anyway" msgid="8792432341346261969">"àȘ€à«‹ àȘȘàȘŁ àȘ…àȘȘàȘĄà«‡àȘŸ àȘ•àȘ°à«‹"</string>
     <string name="manage_applications" msgid="5400164782453975580">"àȘàȘȘàȘšà«‡ àȘźà«‡àȘšà«‡àȘœ àȘ•àȘ°à«‹"</string>
     <string name="out_of_space_dlg_title" msgid="4156690013884649502">"àȘžà«àȘȘેàȘž àȘšàȘ„ી"</string>
@@ -60,16 +59,11 @@
     <string name="uninstall_update_title" msgid="824411791011583031">"àȘ…àȘȘàȘĄà«‡àȘŸ àȘ…àȘšàȘ‡àȘšà«àȘžà«àȘŸà«‰àȘČ àȘ•àȘ°à«‹"</string>
     <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g>, àȘšà«€àȘšà«‡àȘšà«€ àȘàȘȘ્àȘČàȘżàȘ•ેàȘ¶àȘšàȘšà«‹ àȘ­àȘŸàȘ— àȘ›à«‡:"</string>
     <string name="uninstall_application_text" msgid="3816830743706143980">"àȘ¶à«àȘ‚ àȘ€àȘźà«‡ àȘ† àȘàȘȘàȘšà«‡ àȘ…àȘšàȘ‡àȘšà«àȘžà«àȘŸà«‰àȘČ àȘ•àȘ°àȘ”àȘŸ àȘźàȘŸàȘ‚àȘ—à«‹ àȘ›à«‹?"</string>
-    <!-- no translation found for archive_application_text (8482325710714386348) -->
-    <skip />
-    <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
-    <skip />
-    <!-- no translation found for archive_application_text_user (2586558895535581451) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
-    <skip />
+    <string name="archive_application_text" msgid="8482325710714386348">"àȘ€àȘźàȘŸàȘ°à«‹ àȘ”્àȘŻàȘ•્àȘ€àȘżàȘ—àȘ€ àȘĄà«‡àȘŸàȘŸ àȘžàȘŸàȘšàȘ”àȘ”àȘŸàȘźàȘŸàȘ‚ àȘ†àȘ”àȘ¶à«‡"</string>
+    <string name="archive_application_text_all_users" msgid="3151229641681672580">"àȘ¶à«àȘ‚ àȘ† àȘàȘȘàȘšà«‡ àȘ€àȘźàȘŸàȘź àȘ”àȘȘàȘ°àȘŸàȘ¶àȘ•àȘ°à«àȘ€àȘŸàȘ“ àȘźàȘŸàȘŸà«‡ àȘ†àȘ°à«àȘ•àȘŸàȘ‡àȘ” àȘ•àȘ°à«€àȘ? àȘ€àȘźàȘŸàȘ°à«‹ àȘ”્àȘŻàȘ•્àȘ€àȘżàȘ—àȘ€ àȘĄà«‡àȘŸàȘŸ àȘžàȘŸàȘšàȘ”àȘ”àȘŸàȘźàȘŸàȘ‚ àȘ†àȘ”àȘ¶à«‡"</string>
+    <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"àȘ¶à«àȘ‚ àȘ† àȘàȘȘàȘšà«‡ àȘ€àȘźàȘŸàȘ°à«€ àȘ‘àȘ«àȘżàȘžàȘšà«€ àȘȘ્àȘ°à«‹àȘ«àȘŸàȘ‡àȘČ àȘȘàȘ° àȘ†àȘ°à«àȘ•àȘŸàȘ‡àȘ” àȘ•àȘ°à«€àȘ? àȘ€àȘźàȘŸàȘ°à«‹ àȘ”્àȘŻàȘ•્àȘ€àȘżàȘ—àȘ€ àȘĄà«‡àȘŸàȘŸ àȘžàȘŸàȘšàȘ”àȘ”àȘŸàȘźàȘŸàȘ‚ àȘ†àȘ”àȘ¶à«‡"</string>
+    <string name="archive_application_text_user" msgid="2586558895535581451">"àȘ¶à«àȘ‚ àȘ† àȘàȘȘàȘšà«‡ <xliff:g id="USERNAME">%1$s</xliff:g> àȘźàȘŸàȘŸà«‡ àȘ†àȘ°à«àȘ•àȘŸàȘ‡àȘ” àȘ•àȘ°à«€àȘ? àȘ€àȘźàȘŸàȘ°à«‹ àȘ”્àȘŻàȘ•્àȘ€àȘżàȘ—àȘ€ àȘĄà«‡àȘŸàȘŸ àȘžàȘŸàȘšàȘ”àȘ”àȘŸàȘźàȘŸàȘ‚ àȘ†àȘ”àȘ¶à«‡"</string>
+    <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"àȘ¶à«àȘ‚ àȘ€àȘźà«‡ àȘ€àȘźàȘŸàȘ°à«€ àȘ–àȘŸàȘšàȘ—à«€ àȘžà«àȘȘેàȘžàȘźàȘŸàȘ‚àȘ„ી àȘ† àȘàȘȘàȘšà«‡ àȘ†àȘ°à«àȘ•àȘŸàȘ‡àȘ” àȘ•àȘ°àȘ”àȘŸ àȘźàȘŸàȘ—à«‹ àȘ›à«‹? àȘ€àȘźàȘŸàȘ°à«‹ àȘ”્àȘŻàȘ•્àȘ€àȘżàȘ—àȘ€ àȘĄà«‡àȘŸàȘŸ àȘžàȘŸàȘšàȘ”àȘ”àȘŸàȘźàȘŸàȘ‚ àȘ†àȘ”àȘ¶à«‡"</string>
     <string name="uninstall_application_text_all_users" msgid="575491774380227119">"àȘ¶à«àȘ‚ àȘ€àȘźà«‡ "<b>"àȘŹàȘ§àȘŸ"</b>" àȘ”àȘȘàȘ°àȘŸàȘ¶àȘ•àȘ°à«àȘ€àȘŸàȘ“ àȘźàȘŸàȘŸà«‡ àȘ† àȘàȘȘ્àȘČàȘżàȘ•ેàȘ¶àȘšàȘšà«‡ àȘ…àȘšàȘ‡àȘšà«àȘžà«àȘŸà«‰àȘČ àȘ•àȘ°àȘ”àȘŸ àȘźàȘŸàȘ—à«‹ àȘ›à«‹? àȘĄàȘżàȘ”àȘŸàȘ‡àȘž àȘȘàȘ°àȘšàȘŸ "<b>"àȘŹàȘ§àȘŸ"</b>" àȘ”àȘȘàȘ°àȘŸàȘ¶àȘ•àȘ°à«àȘ€àȘŸàȘ“àȘšà«€ àȘàȘȘ્àȘČàȘżàȘ•ેàȘ¶àȘš àȘ…àȘšà«‡ àȘ€à«‡àȘšà«‹ àȘĄà«‡àȘŸàȘŸ àȘ•àȘŸàȘąà«€ àȘšàȘŸàȘ–àȘ”àȘŸàȘźàȘŸàȘ‚ àȘ†àȘ”àȘ¶à«‡."</string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"àȘ¶à«àȘ‚ àȘ€àȘźà«‡ <xliff:g id="USERNAME">%1$s</xliff:g> àȘ”àȘȘàȘ°àȘŸàȘ¶àȘ•àȘ°à«àȘ€àȘŸ àȘźàȘŸàȘŸà«‡ àȘ† àȘàȘȘ્àȘČàȘżàȘ•ેàȘ¶àȘšàȘšà«‡ àȘ…àȘšàȘ‡àȘšà«àȘžà«àȘŸà«‰àȘČ àȘ•àȘ°àȘ”àȘŸ àȘźàȘŸàȘ—à«‹ àȘ›à«‹?"</string>
     <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"àȘ¶à«àȘ‚ àȘ€àȘźà«‡ àȘ€àȘźàȘŸàȘ°à«€ àȘ‘àȘ«àȘżàȘžàȘšà«€ àȘȘ્àȘ°à«‹àȘ«àȘŸàȘ‡àȘČàȘźàȘŸàȘ‚àȘ„ી àȘ† àȘàȘȘàȘšà«‡ àȘ…àȘšàȘ‡àȘšà«àȘžà«àȘŸà«‰àȘČ àȘ•àȘ°àȘ”àȘŸ àȘźàȘŸàȘ—à«‹ àȘ›à«‹?"</string>
@@ -108,8 +102,7 @@
     <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"àȘ€àȘźàȘŸàȘ°à«àȘ‚ àȘŸà«…àȘŹà«àȘČેàȘŸ àȘ…àȘšà«‡ àȘ”્àȘŻàȘ•્àȘ€àȘżàȘ—àȘ€ àȘĄà«‡àȘŸàȘŸ àȘ…àȘœàȘŸàȘŁà«€ àȘàȘȘ્àȘČàȘżàȘ•ેàȘ¶àȘšà«‹ àȘŠà«àȘ”àȘŸàȘ°àȘŸ àȘčુàȘźàȘČàȘŸ àȘźàȘŸàȘŸà«‡ àȘ”àȘ§à« àȘžàȘ‚àȘ”ેàȘŠàȘšàȘ¶à«€àȘČ àȘ›à«‡. àȘ† àȘàȘȘ્àȘČàȘżàȘ•ેàȘ¶àȘš àȘ‡àȘšà«àȘžà«àȘŸà«‰àȘČ àȘ•àȘ°à«€àȘšà«‡ àȘ€àȘźà«‡ àȘžàȘ‚àȘźàȘ€ àȘ„àȘŸàȘ“ àȘ›à«‹ àȘ•ે àȘ†àȘšà«‹ àȘ‰àȘȘàȘŻà«‹àȘ— àȘ•àȘ°àȘ”àȘŸàȘ„ી àȘ€àȘźàȘŸàȘ°àȘŸ àȘŸà«…àȘŹà«àȘČેàȘŸàȘšà«‡ àȘ„àȘ€à«€ àȘ•à«‹àȘˆàȘȘàȘŁ àȘčàȘŸàȘšàȘż àȘ…àȘ„àȘ”àȘŸ àȘĄà«‡àȘŸàȘŸàȘšàȘŸ àȘšà«àȘ•àȘžàȘŸàȘš àȘźàȘŸàȘŸà«‡ àȘ€àȘźà«‡ àȘœàȘ”àȘŸàȘŹàȘŠàȘŸàȘ° àȘ›à«‹."</string>
     <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"àȘ€àȘźàȘŸàȘ°à«àȘ‚ àȘŸà«€àȘ”ી àȘ…àȘšà«‡ àȘ”્àȘŻàȘ•્àȘ€àȘżàȘ—àȘ€ àȘĄà«‡àȘŸàȘŸ àȘ…àȘœàȘŸàȘŁà«€ àȘàȘȘ્àȘČàȘżàȘ•ેàȘ¶àȘšà«‹ àȘŠà«àȘ”àȘŸàȘ°àȘŸ àȘčુàȘźàȘČàȘŸ àȘźàȘŸàȘŸà«‡ àȘ”àȘ§à« àȘžàȘ‚àȘ”ેàȘŠàȘšàȘ¶à«€àȘČ àȘ›à«‡. àȘ† àȘàȘȘ્àȘČàȘżàȘ•ેàȘ¶àȘš àȘ‡àȘšà«àȘžà«àȘŸà«‰àȘČ àȘ•àȘ°à«€àȘšà«‡ àȘ€àȘźà«‡ àȘžàȘ‚àȘźàȘ€ àȘ„àȘŸàȘ“ àȘ›à«‹ àȘ•ે àȘ†àȘšà«‹ àȘ‰àȘȘàȘŻà«‹àȘ— àȘ•àȘ°àȘ”àȘŸàȘ„ી àȘ€àȘźàȘŸàȘ°àȘŸ àȘŸà«€àȘ”ીàȘšà«‡ àȘ„àȘ€à«€ àȘ•à«‹àȘˆàȘȘàȘŁ àȘčàȘŸàȘšàȘż àȘ…àȘ„àȘ”àȘŸ àȘĄà«‡àȘŸàȘŸàȘšàȘŸ àȘšà«àȘ•àȘžàȘŸàȘš àȘźàȘŸàȘŸà«‡ àȘ€àȘźà«‡ àȘœàȘ”àȘŸàȘŹàȘŠàȘŸàȘ° àȘ›à«‹."</string>
     <string name="cloned_app_label" msgid="7503612829833756160">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>àȘšà«€ àȘ•્àȘČોàȘš"</string>
-    <!-- no translation found for archiving_app_label (1127085259724124725) -->
-    <skip />
+    <string name="archiving_app_label" msgid="1127085259724124725">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> àȘ†àȘ°à«àȘ•àȘŸàȘ‡àȘ” àȘ•àȘ°à«€àȘ?"</string>
     <string name="anonymous_source_continue" msgid="4375745439457209366">"àȘ†àȘ—àȘł àȘ”àȘ§à«‹"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"àȘžà«‡àȘŸàȘżàȘ‚àȘ—"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"àȘàȘźà«àȘŹà«‡àȘĄ àȘàȘȘ્àȘČàȘżàȘ•ેàȘ¶àȘšà«‹ àȘ‡àȘšà«àȘžà«àȘŸà«‰àȘČ/àȘ…àȘšàȘ‡àȘšà«àȘžà«àȘŸà«‰àȘČ"</string>
diff --git a/packages/PackageInstaller/res/values-hi/strings.xml b/packages/PackageInstaller/res/values-hi/strings.xml
index e774291..73cbc33 100644
--- a/packages/PackageInstaller/res/values-hi/strings.xml
+++ b/packages/PackageInstaller/res/values-hi/strings.xml
@@ -44,8 +44,7 @@
     <string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"à€Żà€č à€‰à€Șà€Żà„‹à€—à€•à€°à„à€€à€Ÿ à€…à€šà€œà€Ÿà€š à€à€Șà„à€Čà€żà€•à„‡à€¶à€š à€‡à€‚à€žà„à€Ÿà„‰à€Č à€šà€čà„€à€‚ à€•à€° à€žà€•à€€à€Ÿ"</string>
     <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"à€‡à€ž à€‰à€Șà€Żà„‹à€—à€•à€°à„à€€à€Ÿ à€•à„‹ à€à€Șà„à€Čà€żà€•à„‡à€¶à€š à€‡à€‚à€žà„à€Ÿà„‰à€Č à€•à€°à€šà„‡ à€•à„€ à€…à€šà„à€źà€€à€ż à€šà€čà„€à€‚ à€čà„ˆ"</string>
     <string name="ok" msgid="7871959885003339302">"à€ à„€à€• à€čà„ˆ"</string>
-    <!-- no translation found for archive (4447791830199354721) -->
-    <skip />
+    <string name="archive" msgid="4447791830199354721">"à€žà€‚à€—à„à€°à€čà€żà€€ à€•à€°à„‡à€‚"</string>
     <string name="update_anyway" msgid="8792432341346261969">"à€«à€żà€° à€­à„€ à€…à€Șà€Ąà„‡à€Ÿ à€•à€°à„‡à€‚"</string>
     <string name="manage_applications" msgid="5400164782453975580">"à€à€Șà„à€Čà€żà€•à„‡à€¶à€š à€Șà„à€°à€Źà€‚à€§à€żà€€ à€•à€°à„‡à€‚"</string>
     <string name="out_of_space_dlg_title" msgid="4156690013884649502">"à€œà€—à€č à€šà€čà„€à€‚ à€čà„ˆ"</string>
@@ -60,16 +59,11 @@
     <string name="uninstall_update_title" msgid="824411791011583031">"à€…à€Șà€Ąà„‡à€Ÿ à€…à€šà€‡à€‚à€žà„‍à€Ÿà„‰à€Č à€•à€°à„‡à€‚"</string>
     <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> à€‡à€ž à€à€Șà„à€Čà€żà€•à„‡à€¶à€š à€•à€Ÿ à€­à€Ÿà€— à€čà„ˆ:"</string>
     <string name="uninstall_application_text" msgid="3816830743706143980">"à€•à„‍à€Żà€Ÿ à€†à€Șà€•à„‹ à€‡à€ž à€à€Șà„à€Čà€żà€•à„‡à€¶à€š à€•à„‹ à€…à€šà€‡à€‚à€žà„‍à€Ÿà„‰à€Č à€•à€°à€šà€Ÿ à€čà„ˆ?"</string>
-    <!-- no translation found for archive_application_text (8482325710714386348) -->
-    <skip />
-    <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
-    <skip />
-    <!-- no translation found for archive_application_text_user (2586558895535581451) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
-    <skip />
+    <string name="archive_application_text" msgid="8482325710714386348">"à€†à€Șà€•à€Ÿ à€šà€żà€œà„€ à€Ąà„‡à€Ÿà€Ÿ à€žà„‡à€” à€•à€żà€Żà€Ÿ à€œà€Ÿà€à€—à€Ÿ"</string>
+    <string name="archive_application_text_all_users" msgid="3151229641681672580">"à€•à„à€Żà€Ÿ à€‡à€ž à€à€Șà„à€Čà€żà€•à„‡à€¶à€š à€•à„‹ à€žà€­à„€ à€•à„‡ à€Čà€żà€ à€žà€‚à€—à„à€°à€čà€żà€€ à€•à€°à€šà€Ÿ à€čà„ˆ? à€†à€Șà€•à€Ÿ à€šà€żà€œà„€ à€Ąà„‡à€Ÿà€Ÿ à€žà„‡à€” à€•à€żà€Żà€Ÿ à€œà€Ÿà€à€—à€Ÿ"</string>
+    <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"à€•à„à€Żà€Ÿ à€†à€Șà€•à„‹ à€…à€Șà€šà„€ à€”à€°à„à€• à€Șà„à€°à„‹à€«à€Œà€Ÿà€‡à€Č à€žà„‡ à€Żà€č à€à€Șà„à€Čà€żà€•à„‡à€¶à€š à€žà€‚à€—à„à€°à€čà€żà€€ à€•à€°à€šà€Ÿ à€čà„ˆ? à€†à€Șà€•à€Ÿ à€šà€żà€œà„€ à€Ąà„‡à€Ÿà€Ÿ à€žà„‡à€” à€•à€żà€Żà€Ÿ à€œà€Ÿà€à€—à€Ÿ"</string>
+    <string name="archive_application_text_user" msgid="2586558895535581451">"à€•à„à€Żà€Ÿ <xliff:g id="USERNAME">%1$s</xliff:g> à€•à„‡ à€Čà€żà€ à€Żà€č à€à€Șà„à€Čà€żà€•à„‡à€¶à€š à€žà€‚à€—à„à€°à€čà€żà€€ à€•à€°à€šà€Ÿ à€čà„ˆ? à€†à€Șà€•à€Ÿ à€šà€żà€œà„€ à€Ąà„‡à€Ÿà€Ÿ à€žà„‡à€” à€•à€żà€Żà€Ÿ à€œà€Ÿà€à€—à€Ÿ"</string>
+    <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"à€•à„à€Żà€Ÿ à€†à€Șà€•à„‹ à€…à€Șà€šà„‡ à€Șà„à€°à€Ÿà€‡à€”à„‡à€Ÿ à€žà„à€Șà„‡à€ž à€žà„‡ à€Żà€č à€à€Șà„à€Čà€żà€•à„‡à€¶à€š à€žà€‚à€—à„à€°à€čà€żà€€ à€•à€°à€šà€Ÿ à€čà„ˆ? à€†à€Șà€•à€Ÿ à€šà€żà€œà„€ à€Ąà„‡à€Ÿà€Ÿ à€žà„‡à€” à€•à€żà€Żà€Ÿ à€œà€Ÿà€à€—à€Ÿ"</string>
     <string name="uninstall_application_text_all_users" msgid="575491774380227119">"à€•à„à€Żà€Ÿ à€†à€Ș à€‡à€ž à€à€Șà„à€Čà€żà€•à„‡à€¶à€š à€•à„‹ "<b>"à€žà€­à„€"</b>" à€‰à€Șà€Żà„‹à€—à€•à€°à„à€€à€Ÿà€“à€‚ à€•à„‡ à€Čà€żà€ à€…à€šà€‡à€‚à€žà„à€Ÿà„‰à€Č à€•à€°à€šà€Ÿ à€šà€Ÿà€čà€€à„‡ à€čà„ˆà€‚? à€à€Șà„à€Čà€żà€•à„‡à€¶à€š à€”à€° à€‰à€žà€•à„‡ à€Ąà„‡à€Ÿà€Ÿ à€•à„‹ à€Ąà€żà€”à€Ÿà€‡à€ž à€Șà€° "<b>"à€žà€­à„€"</b>" à€‰à€Șà€Żà„‹à€—à€•à€°à„à€€à€Ÿà€“à€‚ à€žà„‡ à€čà€Ÿà€Ÿ à€Šà€żà€Żà€Ÿ à€œà€Ÿà€à€—à€Ÿ."</string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"à€•à„à€Żà€Ÿ à€†à€Ș à€‰à€Șà€Żà„‹à€—à€•à€°à„à€€à€Ÿ <xliff:g id="USERNAME">%1$s</xliff:g> à€•à„‡ à€Čà€żà€ à€‡à€ž à€à€Șà„à€Čà€żà€•à„‡à€¶à€š à€•à„‹ à€…à€šà€‡à€‚à€žà„à€Ÿà„‰à€Č à€•à€°à€šà€Ÿ à€šà€Ÿà€čà€€à„‡ à€čà„ˆà€‚?"</string>
     <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"à€•à„à€Żà€Ÿ à€…à€Șà€šà„€ à€”à€°à„à€• à€Șà„à€°à„‹à€«à€Œà€Ÿà€‡à€Č à€žà„‡ à€‡à€ž à€à€Șà„à€Čà€żà€•à„‡à€¶à€š à€•à„‹ à€…à€šà€‡à€‚à€žà„à€Ÿà„‰à€Č à€•à€°à€šà€Ÿ à€čà„ˆ?"</string>
@@ -108,8 +102,7 @@
     <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"à€†à€Șà€•à€Ÿ à€Ÿà„ˆà€Źà€Čà„‡à€Ÿ à€”à€° à€šà€żà€œà„€ à€Ąà„‡à€Ÿà€Ÿ à€…à€šà€œà€Ÿà€š à€à€Șà„à€Čà€żà€•à„‡à€¶à€š à€•à„‡ à€čà€źà€Čà„‡ à€•à„‹ à€Čà„‡à€•à€° à€œà€Œà„à€Żà€Ÿà€Šà€Ÿ à€žà€‚à€”à„‡à€Šà€šà€¶à„€à€Č à€čà„ˆà€‚. à€‡à€ž à€à€Șà„à€Čà€żà€•à„‡à€¶à€š à€•à„‹ à€‡à€‚à€žà„à€Ÿà„‰à€Č à€•à€°à€•à„‡, à€†à€Ș à€žà€čà€źà€€à€ż à€Šà„‡à€€à„‡ à€čà„ˆà€‚ à€•à€ż à€‡à€žà€•à„‡ à€‡à€žà„à€€à„‡à€źà€Ÿà€Č à€•à„‡ à€šà€Čà€€à„‡ à€†à€Șà€•à„‡ à€Ÿà„ˆà€Źà€Čà„‡à€Ÿ à€•à„‹ à€čà„‹à€šà„‡ à€”à€Ÿà€Čà„‡ à€•à€żà€žà„€ à€­à„€ à€šà„à€•à€žà€Ÿà€š à€Żà€Ÿ à€Ąà„‡à€Ÿà€Ÿ à€•à„‡ à€źà€żà€Ÿ à€œà€Ÿà€šà„‡ à€Șà€°, à€†à€Ș à€œà€Œà€żà€źà„à€źà„‡à€Šà€Ÿà€° à€čà„‹à€‚à€—à„‡."</string>
     <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"à€†à€Șà€•à€Ÿ à€Ÿà„€à€”à„€ à€”à€° à€šà€żà€œà„€ à€Ąà„‡à€Ÿà€Ÿ à€…à€šà€œà€Ÿà€š à€à€Șà„à€Čà€żà€•à„‡à€¶à€š à€•à„‡ à€čà€źà€Čà„‡ à€•à„‹ à€Čà„‡à€•à€° à€œà€Œà„à€Żà€Ÿà€Šà€Ÿ à€žà€‚à€”à„‡à€Šà€šà€¶à„€à€Č à€čà„ˆà€‚. à€‡à€ž à€à€Șà„à€Čà€żà€•à„‡à€¶à€š à€•à„‹ à€‡à€‚à€žà„à€Ÿà„‰à€Č à€•à€°à€•à„‡, à€†à€Ș à€žà€čà€źà€€à€ż à€Šà„‡à€€à„‡ à€čà„ˆà€‚ à€•à€ż à€‡à€žà€•à„‡ à€‡à€žà„à€€à„‡à€źà€Ÿà€Č à€•à„‡ à€šà€Čà€€à„‡ à€†à€Șà€•à„‡ à€Ÿà„€à€”à„€ à€•à„‹ à€čà„‹à€šà„‡ à€”à€Ÿà€Čà„‡ à€•à€żà€žà„€ à€­à„€ à€šà„à€•à€žà€Ÿà€š à€Żà€Ÿ à€Ąà„‡à€Ÿà€Ÿ à€•à„‡ à€źà€żà€Ÿ à€œà€Ÿà€šà„‡ à€Șà€°, à€†à€Ș à€œà€Œà€żà€źà„à€źà„‡à€Šà€Ÿà€° à€čà„‹à€‚à€—à„‡."</string>
     <string name="cloned_app_label" msgid="7503612829833756160">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> à€•à€Ÿ à€•à„à€Čà„‹à€š"</string>
-    <!-- no translation found for archiving_app_label (1127085259724124725) -->
-    <skip />
+    <string name="archiving_app_label" msgid="1127085259724124725">"à€•à„à€Żà€Ÿ <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> à€žà€‚à€—à„à€°à€čà€żà€€ à€•à€°à€šà€Ÿ à€čà„ˆ?"</string>
     <string name="anonymous_source_continue" msgid="4375745439457209366">"à€œà€Ÿà€°à„€ à€°à€–à„‡à€‚"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"à€žà„‡à€Ÿà€żà€‚à€—"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Wear à€à€Șà„à€Čà€żà€•à„‡à€¶à€š à€‡à€‚à€žà„à€Ÿà„‰à€Č/à€…à€šà€‡à€‚à€žà„à€Ÿà„‰à€Č à€čà„‹ à€°à€čà„‡ à€čà„ˆà€‚"</string>
diff --git a/packages/PackageInstaller/res/values-hr/strings.xml b/packages/PackageInstaller/res/values-hr/strings.xml
index 5df276e..27ba153 100644
--- a/packages/PackageInstaller/res/values-hr/strings.xml
+++ b/packages/PackageInstaller/res/values-hr/strings.xml
@@ -44,8 +44,7 @@
     <string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Ovaj korisnik ne moĆŸe instalirati nepoznate aplikacije"</string>
     <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Ovaj korisnik nema dopuštenje za instaliranje aplikacija"</string>
     <string name="ok" msgid="7871959885003339302">"U redu"</string>
-    <!-- no translation found for archive (4447791830199354721) -->
-    <skip />
+    <string name="archive" msgid="4447791830199354721">"Arhiviraj"</string>
     <string name="update_anyway" msgid="8792432341346261969">"Ipak aĆŸuriraj"</string>
     <string name="manage_applications" msgid="5400164782453975580">"Upravljanje apl."</string>
     <string name="out_of_space_dlg_title" msgid="4156690013884649502">"Nema dovoljno mjesta"</string>
@@ -60,16 +59,11 @@
     <string name="uninstall_update_title" msgid="824411791011583031">"Deinstalacija aĆŸuriranja"</string>
     <string name="uninstall_activity_text" msgid="1928194674397770771">"Aktivnost <xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> dio je sljedeće aplikacije:"</string>
     <string name="uninstall_application_text" msgid="3816830743706143980">"Ćœelite li deinstalirati ovu aplikaciju?"</string>
-    <!-- no translation found for archive_application_text (8482325710714386348) -->
-    <skip />
-    <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
-    <skip />
-    <!-- no translation found for archive_application_text_user (2586558895535581451) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
-    <skip />
+    <string name="archive_application_text" msgid="8482325710714386348">"Vaši će se osobni podaci spremiti"</string>
+    <string name="archive_application_text_all_users" msgid="3151229641681672580">"Ćœelite li arhivirati ovu aplikaciju za sve korisnike? Vaši će se osobni podaci spremiti"</string>
+    <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Ćœelite li arhivirati ovu aplikaciju na poslovnom profilu? Vaši će se osobni podaci spremiti"</string>
+    <string name="archive_application_text_user" msgid="2586558895535581451">"Ćœelite li arhivirati ovu aplikaciju za korisnika <xliff:g id="USERNAME">%1$s</xliff:g>? Vaši će se osobni podaci spremiti"</string>
+    <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Ćœelite li arhivirati ovu aplikaciju iz svojeg privatnog prostora? Vaši će se osobni podaci spremiti"</string>
     <string name="uninstall_application_text_all_users" msgid="575491774380227119">"Ćœelite li deinstalirati tu aplikaciju za "<b>"sve"</b>" korisnike? Aplikacija i njezini podaci bit će uklonjeni sa "<b>"svih"</b>" korisnika na uređaju."</string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Ćœelite li deinstalirati tu aplikaciju za korisnika <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
     <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Ćœelite li deinstalirati tu aplikaciju s poslovnog profila?"</string>
@@ -108,8 +102,7 @@
     <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Vaš tablet i osobni podaci podloĆŸniji su napadima nepoznatih aplikacija. Instaliranjem te aplikacije prihvaćate odgovornost za oštećenje tableta ili gubitak podataka do kojih moĆŸe doći uslijed njezine upotrebe."</string>
     <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Vaš TV i osobni podaci podloĆŸniji su napadima nepoznatih aplikacija. Instaliranjem te aplikacije prihvaćate odgovornost za oštećenje televizora ili gubitak podataka do kojih moĆŸe doći uslijed njezine upotrebe."</string>
     <string name="cloned_app_label" msgid="7503612829833756160">"Klon <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
-    <!-- no translation found for archiving_app_label (1127085259724124725) -->
-    <skip />
+    <string name="archiving_app_label" msgid="1127085259724124725">"Ćœelite li arhivirati aplikaciju <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>?"</string>
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Nastavi"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Postavke"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Instaliranje/deinstaliranje Wear apl."</string>
diff --git a/packages/PackageInstaller/res/values-hu/strings.xml b/packages/PackageInstaller/res/values-hu/strings.xml
index 6de5bac..b2c7c1a 100644
--- a/packages/PackageInstaller/res/values-hu/strings.xml
+++ b/packages/PackageInstaller/res/values-hu/strings.xml
@@ -44,8 +44,7 @@
     <string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Ez a felhasználó nem telepíthet ismeretlen alkalmazásokat"</string>
     <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Ez a felhasználó nem telepíthet alkalmazásokat"</string>
     <string name="ok" msgid="7871959885003339302">"OK"</string>
-    <!-- no translation found for archive (4447791830199354721) -->
-    <skip />
+    <string name="archive" msgid="4447791830199354721">"Archiválás"</string>
     <string name="update_anyway" msgid="8792432341346261969">"Frissítés"</string>
     <string name="manage_applications" msgid="5400164782453975580">"Alkalmazáskezelés"</string>
     <string name="out_of_space_dlg_title" msgid="4156690013884649502">"Nincs elég hely"</string>
@@ -60,16 +59,11 @@
     <string name="uninstall_update_title" msgid="824411791011583031">"Frissítés eltávolítása"</string>
     <string name="uninstall_activity_text" msgid="1928194674397770771">"A(z) <xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> a következƑ alkalmazás része:"</string>
     <string name="uninstall_application_text" msgid="3816830743706143980">"Eltávolítja ezt az alkalmazást?"</string>
-    <!-- no translation found for archive_application_text (8482325710714386348) -->
-    <skip />
-    <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
-    <skip />
-    <!-- no translation found for archive_application_text_user (2586558895535581451) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
-    <skip />
+    <string name="archive_application_text" msgid="8482325710714386348">"Személyes adatait menteni fogja a rendszer."</string>
+    <string name="archive_application_text_all_users" msgid="3151229641681672580">"Valamennyi felhasználó számára archiválja az alkalmazást? Személyes adatait menteni fogja a rendszer."</string>
+    <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Archiválja ezt az alkalmazást a munkaprofilján? Személyes adatait menteni fogja a rendszer."</string>
+    <string name="archive_application_text_user" msgid="2586558895535581451">"Archiválja ezt az alkalmazást <xliff:g id="USERNAME">%1$s</xliff:g> számára? Személyes adatait menteni fogja a rendszer."</string>
+    <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Archiválja ezt az alkalmazást a privát területrƑl? Személyes adatait menteni fogja a rendszer."</string>
     <string name="uninstall_application_text_all_users" msgid="575491774380227119">"Szeretné eltávolítani ezt az alkalmazást "<b>"minden"</b>" felhasználónál? Az alkalmazást és adatait az eszköz "<b>"minden"</b>" felhasználójánál töröljük."</string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Eltávolítja ezt az alkalmazást <xliff:g id="USERNAME">%1$s</xliff:g> felhasználó esetében?"</string>
     <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Biztosan eltávolítja ezt az alkalmazást a munkaprofiljából?"</string>
@@ -108,8 +102,7 @@
     <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Táblagépe és személyes adatai fokozott kockázatnak vannak kitéve az ismeretlen alkalmazások támadásaival szemben. Az alkalmazás telepítésével elfogadja, hogy Ön a felelƑs az alkalmazás használatából eredƑ esetleges adatvesztésért és a táblagépet ért károkért."</string>
     <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Tévéje és személyes adatai fokozott kockázatnak vannak kitéve az ismeretlen alkalmazások támadásaival szemben. Az alkalmazás telepítésével elfogadja, hogy Ön a felelƑs az alkalmazás használatából eredƑ esetleges adatvesztésért és a tévét ért károkért."</string>
     <string name="cloned_app_label" msgid="7503612829833756160">"Klónozott <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
-    <!-- no translation found for archiving_app_label (1127085259724124725) -->
-    <skip />
+    <string name="archiving_app_label" msgid="1127085259724124725">"Archiválja ezt: <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>?"</string>
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Tovább"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Beállítások"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Wear-alkalmazások telepítése/törlése"</string>
diff --git a/packages/PackageInstaller/res/values-hy/strings.xml b/packages/PackageInstaller/res/values-hy/strings.xml
index 4bb3915..bbc1974 100644
--- a/packages/PackageInstaller/res/values-hy/strings.xml
+++ b/packages/PackageInstaller/res/values-hy/strings.xml
@@ -44,8 +44,7 @@
     <string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Ô±Ő”Őœ Ö…ŐŁŐżŐĄŐżŐ„Ö€Őš ŐčŐ« կարվŐČ ŐĄŐ¶Ő°ŐĄŐ”Őż Ő°ŐĄŐŸŐ„ŐŹŐŸŐĄŐźŐ¶Ő„Ö€ ŐżŐ„ŐČŐĄŐ€Ö€Ő„ŐŹ"</string>
     <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Ô±Ő”Őœ Ö…ŐŁŐżŐĄŐżŐ«Ö€ŐžŐ»Őš ŐčŐ« Ő©ŐžÖ‚Ő”ŐŹŐĄŐżÖ€ŐŸŐžÖ‚ŐŽ ŐżŐ„ŐČŐĄŐ€Ö€Ő„ŐŹ Ő°ŐĄŐŸŐ„ŐŹŐŸŐĄŐźŐ¶Ő„Ö€"</string>
     <string name="ok" msgid="7871959885003339302">"Ô”ŐČŐĄŐŸ"</string>
-    <!-- no translation found for archive (4447791830199354721) -->
-    <skip />
+    <string name="archive" msgid="4447791830199354721">"Ô±Ö€Ő­Ő«ŐŸŐĄÖŐ¶Ő„ŐŹ"</string>
     <string name="update_anyway" msgid="8792432341346261969">"ÔčերՎեցնՄՏ"</string>
     <string name="manage_applications" msgid="5400164782453975580">"ÔżŐĄŐŒŐĄŐŸŐĄÖ€Ő„ŐŹ Ő°ŐĄŐŸŐ„ŐŹŐŸŐĄŐźŐ¶Ő„Ö€Őš"</string>
     <string name="out_of_space_dlg_title" msgid="4156690013884649502">"ÔČŐĄŐŸŐĄÖ€ŐĄÖ€ տարառք ŐčŐŻŐĄ"</string>
@@ -60,16 +59,11 @@
     <string name="uninstall_update_title" msgid="824411791011583031">"Ô±ŐșŐĄŐżŐ„ŐČŐĄŐ€Ö€Ő„ŐŹ թերՎեց՞ւՎ՚"</string>
     <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> գվրռվŐČŐžÖ‚Ő©Ő”ŐžÖ‚Ő¶Őš Ő°Ő„ŐżÖ‡Ő”ŐĄŐŹ Ő°ŐĄŐŸŐ„ŐŹŐŸŐĄŐźŐ« ŐŽŐĄŐœŐ¶ Ő§`"</string>
     <string name="uninstall_application_text" msgid="3816830743706143980">"Ութվ՞ւծ Ő„Ö„ ŐĄŐșŐĄŐżŐ„ŐČŐĄŐ€Ö€Ő„ŐŹ ŐĄŐ”Őœ Ő°ŐĄŐŸŐ„ŐŹŐŸŐĄŐźŐšÖ‰"</string>
-    <!-- no translation found for archive_application_text (8482325710714386348) -->
-    <skip />
-    <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
-    <skip />
-    <!-- no translation found for archive_application_text_user (2586558895535581451) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
-    <skip />
+    <string name="archive_application_text" msgid="8482325710714386348">"ՁՄր ŐĄŐ¶Ő±Ő¶ŐĄŐŻŐĄŐ¶ ŐżŐŸŐ”ŐĄŐŹŐ¶Ő„Ö€Őš ŐŻŐșŐĄŐ°ŐŸŐ„Ő¶"</string>
+    <string name="archive_application_text_all_users" msgid="3151229641681672580">"Ô±Ö€Ő­Ő«ŐŸŐĄÖŐ¶Ő„ŐžŐŹ ŐĄŐ”Őœ Ő°ŐĄŐŸŐ„ŐŹŐŸŐĄŐźŐš ձվլվր Ö…ŐŁŐżŐĄŐżŐ„Ö€Ő„Ö€Ő« հածար։ ՁՄր ŐĄŐ¶Ő±Ő¶ŐĄŐŻŐĄŐ¶ ŐżŐŸŐ”ŐĄŐŹŐ¶Ő„Ö€Őš ŐŻŐșŐĄŐ°ŐŸŐ„Ő¶"</string>
+    <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Ô±Ö€Ő­Ő«ŐŸŐĄÖŐ¶Ő„ŐžŐŹ ŐĄŐ”Őœ Ő°ŐĄŐŸŐ„ŐŹŐŸŐĄŐźŐš Ő±Ő„Ö€ ŐĄŐ·Ő­ŐĄŐżŐĄŐ¶Ö„ŐĄŐ”Ő«Ő¶ ŐșÖ€ŐžÖ†Ő«ŐŹŐžÖ‚ŐŽÖ‰ ՁՄր ŐĄŐ¶Ő±Ő¶ŐĄŐŻŐĄŐ¶ ŐżŐŸŐ”ŐĄŐŹŐ¶Ő„Ö€Őš ŐŻŐșŐĄŐ°ŐŸŐ„Ő¶"</string>
+    <string name="archive_application_text_user" msgid="2586558895535581451">"Ô±Ö€Ő­Ő«ŐŸŐĄÖŐ¶Ő„ŐžŐŹ ŐĄŐ”Őœ Ő°ŐĄŐŸŐ„ŐŹŐŸŐĄŐźŐš <xliff:g id="USERNAME">%1$s</xliff:g> Ö…ŐŁŐżŐĄŐżŐ«Ö€ŐžŐ» հածար։ ՁՄր ŐĄŐ¶Ő±Ő¶ŐĄŐŻŐĄŐ¶ ŐżŐŸŐ”ŐĄŐŹŐ¶Ő„Ö€Őš ŐŻŐșŐĄŐ°ŐŸŐ„Ő¶"</string>
+    <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Ô±Ö€Ő­Ő«ŐŸŐĄÖŐ¶Ő„ŐžŐŹ ŐĄŐ”Őœ Ő°ŐĄŐŸŐ„ŐŹŐŸŐĄŐźŐš Ő±Ő„Ö€ ŐĄŐ¶Ő±Ő¶ŐĄŐŻŐĄŐ¶ տերե՟քից։ ՁՄր ŐĄŐ¶Ő±Ő¶ŐĄŐŻŐĄŐ¶ ŐżŐŸŐ”ŐĄŐŹŐ¶Ő„Ö€Őš ŐŻŐșŐĄŐ°ŐŸŐ„Ő¶"</string>
     <string name="uninstall_application_text_all_users" msgid="575491774380227119">"Ô±ŐșŐĄŐżŐ„ŐČŐĄŐ€Ö€Ő„ŐžŐŹ ŐĄŐ”Őœ Ő°ŐĄŐŸŐ„ŐŹŐŸŐĄŐźŐš "<b>"ձվլվր"</b>" Ö…ŐŁŐżŐĄŐżŐ„Ö€Ő„Ö€Ő« հածար: Ő€ŐĄŐŸŐ„ŐŹŐŸŐĄŐźŐš և Ő€Ö€ŐĄ ŐżŐŸŐ”ŐĄŐŹŐ¶Ő„Ö€Őš ŐŻŐ°Ő„ŐŒŐĄÖŐŸŐ„Ő¶ ŐœŐĄÖ€Ö„Ő« "<b>"ձվլվր"</b>" օգտետՄրՄրից:"</string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Ô±ŐșŐĄŐżŐ„ŐČŐĄŐ€Ö€Ő„ŐžŐŹ ŐĄŐ”Őœ Ő°ŐĄŐŸŐ„ŐŹŐŸŐĄŐźŐš <xliff:g id="USERNAME">%1$s</xliff:g> Ö…ŐŁŐżŐĄŐżŐ«Ö€ŐžŐ» հածար:"</string>
     <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Ő€Ő„ŐŒŐĄÖŐ¶Ő„ŐžŐŹ ŐĄŐ”Őœ Ő°ŐĄŐŸŐ„ŐŹŐŸŐĄŐźŐš Ő±Ő„Ö€ ŐĄŐ·Ő­ŐĄŐżŐĄŐ¶Ö„ŐĄŐ”Ő«Ő¶ Őșր՞ֆիՏից"</string>
@@ -108,8 +102,7 @@
     <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"ՁՄր ŐșŐŹŐĄŐ¶Ő·Ő„ŐżŐš և ŐĄŐ¶Ő±Ő¶ŐĄŐŻŐĄŐ¶ ŐżŐŸŐ”ŐĄŐŹŐ¶Ő„Ö€Ő¶ ŐĄŐŒŐĄŐŸŐ„ŐŹ խ՞ցՄՏի Ő„Ő¶ ŐĄŐ¶Ő°ŐĄŐ”Őż Ő°ŐĄŐŸŐ„ŐŹŐŸŐĄŐźŐ¶Ő„Ö€Ő« ŐŁÖ€ŐžŐ°Ő¶Ő„Ö€Ő« Ő¶ŐŻŐĄŐżŐŽŐĄŐŽŐą: ŐŐ„ŐČŐĄŐ€Ö€Ő„ŐŹŐžŐŸ ŐĄŐ”Őœ Ő°ŐĄŐŸŐ„ŐŹŐŸŐĄŐźŐšŐ Ő€ŐžÖ‚Ö„ ŐšŐ¶Ő€ŐžÖ‚Ő¶ŐžÖ‚ŐŽ Ő„Ö„, վր ŐșŐĄŐżŐĄŐœŐ­ŐĄŐ¶ŐĄŐżŐŸŐžÖ‚Ő©Ő”ŐžÖ‚Ő¶ Ő„Ö„ կրվւծ Ő°ŐĄŐŸŐ„ŐŹŐŸŐĄŐźŐ« Ö…ŐŁŐżŐĄŐŁŐžÖ€ŐźŐŽŐĄŐ¶ Ő°Ő„ŐżÖ‡ŐĄŐ¶Ö„ŐžŐŸ Ő±Ő„Ö€ ŐșŐŹŐĄŐ¶Ő·Ő„ŐżŐ«Ő¶ Ő°ŐĄŐœÖŐŸŐĄŐź ցենկեցե՟ ŐŸŐ¶ŐĄŐœŐ« ŐŻŐĄŐŽ ŐżŐŸŐ”ŐĄŐŹŐ¶Ő„Ö€Ő« ŐŻŐžÖ€ŐœŐżŐ« հածար:"</string>
     <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"ՁՄր Ő°Ő„ŐŒŐžÖ‚ŐœŐżŐĄÖŐžÖ‚Ő”ÖŐš և ŐĄŐ¶Ő±Ő¶ŐĄŐŻŐĄŐ¶ ŐżŐŸŐ”ŐĄŐŹŐ¶Ő„Ö€Ő¶ ŐĄŐŒŐĄŐŸŐ„ŐŹ խ՞ցՄՏի Ő„Ő¶ ŐĄŐ¶Ő°ŐĄŐ”Őż Ő°ŐĄŐŸŐ„ŐŹŐŸŐĄŐźŐ¶Ő„Ö€Ő« ŐŁÖ€ŐžŐ°Ő¶Ő„Ö€Ő« Ő¶ŐŻŐĄŐżŐŽŐĄŐŽŐą: ŐŐ„ŐČŐĄŐ€Ö€Ő„ŐŹŐžŐŸ ŐĄŐ”Őœ Ő°ŐĄŐŸŐ„ŐŹŐŸŐĄŐźŐšŐ Ő€ŐžÖ‚Ö„ ŐšŐ¶Ő€ŐžÖ‚Ő¶ŐžÖ‚ŐŽ Ő„Ö„, վր ŐșŐĄŐżŐĄŐœŐ­ŐĄŐ¶ŐĄŐżŐŸŐžÖ‚Ő©Ő”ŐžÖ‚Ő¶ Ő„Ö„ կրվւծ Ő°ŐĄŐŸŐ„ŐŹŐŸŐĄŐźŐ« Ö…ŐŁŐżŐĄŐŁŐžÖ€ŐźŐŽŐĄŐ¶ Ő°Ő„ŐżÖ‡ŐĄŐ¶Ö„ŐžŐŸ Ő±Ő„Ö€ Ő°Ő„ŐŒŐžÖ‚ŐœŐżŐĄÖŐžÖ‚Ő”ÖŐ«Ő¶ Ő°ŐĄŐœÖŐŸŐĄŐź ցենկեցե՟ ŐŸŐ¶ŐĄŐœŐ« ŐŻŐĄŐŽ ŐżŐŸŐ”ŐĄŐŹŐ¶Ő„Ö€Ő« ŐŻŐžÖ€ŐœŐżŐ« հածար:"</string>
     <string name="cloned_app_label" msgid="7503612829833756160">"«<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>» Ő°ŐĄŐŸŐ„ŐŹŐŸŐĄŐźŐ« ŐŻŐŹŐžŐ¶"</string>
-    <!-- no translation found for archiving_app_label (1127085259724124725) -->
-    <skip />
+    <string name="archiving_app_label" msgid="1127085259724124725">"Ô±Ö€Ő­Ő«ŐŸŐĄÖŐ¶Ő„ŐžŐŹ <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> Ő°ŐĄŐŸŐ„ŐŹŐŸŐĄŐźŐš"</string>
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Ő‡ŐĄÖ€ŐžÖ‚Ő¶ŐĄŐŻŐ„ŐŹ"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"ÔżŐĄÖ€ŐŁŐĄŐŸŐžÖ€ŐžÖ‚ŐŽŐ¶Ő„Ö€"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Wear Ő°ŐĄŐŸŐ„ŐŹŐŸŐĄŐźŐ¶Ő„Ö€Ő« ŐżŐ„ŐČŐĄŐ€Ö€ŐžÖ‚ŐŽ/ŐĄŐșŐĄŐżŐ„ŐČŐĄŐ€Ö€ŐžÖ‚ŐŽ"</string>
diff --git a/packages/PackageInstaller/res/values-in/strings.xml b/packages/PackageInstaller/res/values-in/strings.xml
index 58db084..d4f3e76 100644
--- a/packages/PackageInstaller/res/values-in/strings.xml
+++ b/packages/PackageInstaller/res/values-in/strings.xml
@@ -44,8 +44,7 @@
     <string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Aplikasi yang tidak dikenal tidak dapat diinstal oleh pengguna ini"</string>
     <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Pengguna ini tidak diizinkan menginstal aplikasi"</string>
     <string name="ok" msgid="7871959885003339302">"Oke"</string>
-    <!-- no translation found for archive (4447791830199354721) -->
-    <skip />
+    <string name="archive" msgid="4447791830199354721">"Mengarsipkan"</string>
     <string name="update_anyway" msgid="8792432341346261969">"Tetap update"</string>
     <string name="manage_applications" msgid="5400164782453975580">"Kelola aplikasi"</string>
     <string name="out_of_space_dlg_title" msgid="4156690013884649502">"Kehabisan ruang penyimpanan"</string>
@@ -60,16 +59,11 @@
     <string name="uninstall_update_title" msgid="824411791011583031">"Uninstal update"</string>
     <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> adalah bagian dari aplikasi berikut:"</string>
     <string name="uninstall_application_text" msgid="3816830743706143980">"Apakah Anda ingin meng-uninstal aplikasi ini?"</string>
-    <!-- no translation found for archive_application_text (8482325710714386348) -->
-    <skip />
-    <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
-    <skip />
-    <!-- no translation found for archive_application_text_user (2586558895535581451) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
-    <skip />
+    <string name="archive_application_text" msgid="8482325710714386348">"Data pribadi Anda akan disimpan"</string>
+    <string name="archive_application_text_all_users" msgid="3151229641681672580">"Arsipkan aplikasi ini untuk semua pengguna? Data pribadi Anda akan disimpan"</string>
+    <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Arsipkan aplikasi ini di profil kerja? Data pribadi Anda akan disimpan"</string>
+    <string name="archive_application_text_user" msgid="2586558895535581451">"Arsipkan aplikasi untuk <xliff:g id="USERNAME">%1$s</xliff:g>? Data pribadi Anda akan disimpan"</string>
+    <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Ingin mengarsipkan aplikasi ini dari ruang pribadi? Data pribadi Anda akan disimpan"</string>
     <string name="uninstall_application_text_all_users" msgid="575491774380227119">"Apakah Anda ingin meng-uninstal aplikasi ini untuk "<b>"semua"</b>" pengguna? Aplikasi dan datanya akan dihapus dari "<b>"semua"</b>" pengguna pada perangkat."</string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Apakah Anda ingin meng-uninstal aplikasi ini untuk pengguna <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
     <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Ingin meng-uninstal aplikasi ini dari profil kerja Anda?"</string>
@@ -108,8 +102,7 @@
     <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Tablet dan data pribadi Anda lebih rentan terhadap serangan oleh aplikasi yang tidak dikenal. Dengan menginstal aplikasi ini, Anda setuju bahwa Anda bertanggung jawab atas kerusakan tablet atau kehilangan data yang mungkin diakibatkan oleh penggunaannya."</string>
     <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"TV dan data pribadi Anda lebih rentan terhadap serangan oleh aplikasi yang tidak dikenal. Dengan menginstal aplikasi ini, Anda setuju bahwa Anda bertanggung jawab atas kerusakan TV atau kehilangan data yang mungkin diakibatkan oleh penggunaannya."</string>
     <string name="cloned_app_label" msgid="7503612829833756160">"Clone <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
-    <!-- no translation found for archiving_app_label (1127085259724124725) -->
-    <skip />
+    <string name="archiving_app_label" msgid="1127085259724124725">"Arsipkan <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>?"</string>
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Lanjutkan"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Setelan"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Melakukan instal/uninstal aplikasi Wear"</string>
diff --git a/packages/PackageInstaller/res/values-is/strings.xml b/packages/PackageInstaller/res/values-is/strings.xml
index 44fc208..32b686e 100644
--- a/packages/PackageInstaller/res/values-is/strings.xml
+++ b/packages/PackageInstaller/res/values-is/strings.xml
@@ -44,8 +44,7 @@
     <string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Þessi notandi getur ekki sett upp óþekkt forrit"</string>
     <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Þessi notandi hefur ekki leyfi til að setja upp forrit"</string>
     <string name="ok" msgid="7871959885003339302">"Í lagi"</string>
-    <!-- no translation found for archive (4447791830199354721) -->
-    <skip />
+    <string name="archive" msgid="4447791830199354721">"Setja í geymslu"</string>
     <string name="update_anyway" msgid="8792432341346261969">"Uppfæra samt"</string>
     <string name="manage_applications" msgid="5400164782453975580">"Stj. forritum"</string>
     <string name="out_of_space_dlg_title" msgid="4156690013884649502">"Ekkert pláss eftir"</string>
@@ -60,16 +59,11 @@
     <string name="uninstall_update_title" msgid="824411791011583031">"Fjarlægja uppfærslu"</string>
     <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> er hluti af þessu forriti:"</string>
     <string name="uninstall_application_text" msgid="3816830743706143980">"Viltu fjarlægja þetta forrit?"</string>
-    <!-- no translation found for archive_application_text (8482325710714386348) -->
-    <skip />
-    <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
-    <skip />
-    <!-- no translation found for archive_application_text_user (2586558895535581451) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
-    <skip />
+    <string name="archive_application_text" msgid="8482325710714386348">"Persónuupplýsingarnar þínar verða vistaðar"</string>
+    <string name="archive_application_text_all_users" msgid="3151229641681672580">"Setja þetta forrit í geymslu fyrir alla notendur? Persónuupplýsingarnar þínar verða vistaðar"</string>
+    <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Setja þetta forrit á geymslu á vinnusniðinu þínu? Persónuupplýsingarnar þínar verða vistaðar"</string>
+    <string name="archive_application_text_user" msgid="2586558895535581451">"Setja þetta forrit í geymslu fyrir <xliff:g id="USERNAME">%1$s</xliff:g>? Persónuupplýsingarnar þínar verða vistaðar"</string>
+    <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Viltu setja þetta forrit úr einkarýminu í geymslu? Persónuupplýsingarnar þínar verða vistaðar"</string>
     <string name="uninstall_application_text_all_users" msgid="575491774380227119">"Viltu fjarlægja þetta forrit hjá "<b>"öllum"</b>" notendum? Forritið og gögn þess verða fjarlægð hjá "<b>"öllum"</b>" notendum tækisins."</string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Viltu fjarlægja þetta forrit fyrir notandann <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
     <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Viltu fjarlægja þetta forrit af vinnuprófílnum þínum?"</string>
@@ -108,8 +102,7 @@
     <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Spjaldtölvan þín og persónuleg gögn eru berskjaldaðri fyrir árásum forrita af óþekktum uppruna. Með uppsetningu þessa forrits samþykkirðu að bera fulla ábyrgð á hverju því tjóni sem verða kann á spjaldtölvunni eða gagnatapi sem leiða kann af notkun þess."</string>
     <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Sjónvarpið þitt og persónuleg gögn eru berskjaldaðri fyrir árásum forrita af óþekktum uppruna. Með uppsetningu þessa forrits samþykkirðu að bera fulla ábyrgð á hverju því tjóni sem verða kann á sjónvarpinu eða gagnatapi sem leiða kann af notkun þess."</string>
     <string name="cloned_app_label" msgid="7503612829833756160">"Afrit af <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
-    <!-- no translation found for archiving_app_label (1127085259724124725) -->
-    <skip />
+    <string name="archiving_app_label" msgid="1127085259724124725">"Setja <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> í geymslu?"</string>
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Áfram"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Stillingar"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Uppsetning/fjarlæging Wear forrita"</string>
diff --git a/packages/PackageInstaller/res/values-it/strings.xml b/packages/PackageInstaller/res/values-it/strings.xml
index 136a537..3fb3846 100644
--- a/packages/PackageInstaller/res/values-it/strings.xml
+++ b/packages/PackageInstaller/res/values-it/strings.xml
@@ -44,8 +44,7 @@
     <string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Questo utente non può installare app sconosciute"</string>
     <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"L\'utente non è autorizzato a installare app"</string>
     <string name="ok" msgid="7871959885003339302">"OK"</string>
-    <!-- no translation found for archive (4447791830199354721) -->
-    <skip />
+    <string name="archive" msgid="4447791830199354721">"Archivia"</string>
     <string name="update_anyway" msgid="8792432341346261969">"Aggiorna comunque"</string>
     <string name="manage_applications" msgid="5400164782453975580">"Gestisci app"</string>
     <string name="out_of_space_dlg_title" msgid="4156690013884649502">"Spazio esaurito"</string>
@@ -60,16 +59,11 @@
     <string name="uninstall_update_title" msgid="824411791011583031">"Disinstalla aggiornamento"</string>
     <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> fa parte della seguente applicazione:"</string>
     <string name="uninstall_application_text" msgid="3816830743706143980">"Vuoi disinstallare questa app?"</string>
-    <!-- no translation found for archive_application_text (8482325710714386348) -->
-    <skip />
-    <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
-    <skip />
-    <!-- no translation found for archive_application_text_user (2586558895535581451) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
-    <skip />
+    <string name="archive_application_text" msgid="8482325710714386348">"I tuoi dati personali verranno salvati"</string>
+    <string name="archive_application_text_all_users" msgid="3151229641681672580">"Vuoi archiviare questa app per tutti gli utenti? I tuoi dati personali verranno salvati"</string>
+    <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Vuoi archiviare questa app che si trova nel tuo profilo di lavoro? I tuoi dati personali verranno salvati"</string>
+    <string name="archive_application_text_user" msgid="2586558895535581451">"Vuoi archiviare questa app per <xliff:g id="USERNAME">%1$s</xliff:g>? I tuoi dati personali verranno salvati"</string>
+    <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Vuoi archiviare questa app che si trova nel tuo spazio privato? I tuoi dati personali verranno salvati"</string>
     <string name="uninstall_application_text_all_users" msgid="575491774380227119">"Vuoi disinstallare questa applicazione per "<b>"tutti"</b>" gli utenti? L\'applicazione e i relativi dati verranno rimossi da "<b>"tutti"</b>" gli utenti configurati sul dispositivo."</string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Disinstallare l\'app per l\'utente <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
     <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Vuoi disinstallare questa app dal tuo profilo di lavoro?"</string>
@@ -108,8 +102,7 @@
     <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"I dati del tablet e i dati personali sono più vulnerabili agli attacchi di app sconosciute. Se installi questa app, accetti di essere responsabile degli eventuali danni al tablet o dell\'eventuale perdita di dati derivanti dall\'uso dell\'app."</string>
     <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"I dati della TV e i dati personali sono più vulnerabili agli attacchi di app sconosciute. Se installi questa app, accetti di essere responsabile degli eventuali danni alla TV o dell\'eventuale perdita di dati derivanti dall\'uso dell\'app."</string>
     <string name="cloned_app_label" msgid="7503612829833756160">"Clone di <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
-    <!-- no translation found for archiving_app_label (1127085259724124725) -->
-    <skip />
+    <string name="archiving_app_label" msgid="1127085259724124725">"Vuoi archiviare <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>?"</string>
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Continua"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Impostazioni"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Installazione/disinstallazione app Wear"</string>
diff --git a/packages/PackageInstaller/res/values-iw/strings.xml b/packages/PackageInstaller/res/values-iw/strings.xml
index 2c6f127..dcec0e0 100644
--- a/packages/PackageInstaller/res/values-iw/strings.xml
+++ b/packages/PackageInstaller/res/values-iw/strings.xml
@@ -44,8 +44,7 @@
     <string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"ŚœŚžŚ©ŚȘŚžŚ© ڔږڔ ŚŚ™ŚŸ Ś”ŚšŚ©ŚŚ” ŚœŚ”ŚȘŚ§Ś™ŚŸ ŚŚ€ŚœŚ™Ś§ŚŠŚ™Ś•ŚȘ Ś©ŚŚ™Ś ŚŸ ŚžŚ•Ś›ŚšŚ•ŚȘ"</string>
     <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"ŚœŚžŚ©ŚȘŚžŚ© ڔږڔ ŚŚ™ŚŸ Ś”ŚšŚ©ŚŚ” ŚœŚ”ŚȘŚ§Ś™ŚŸ ŚŚ€ŚœŚ™Ś§ŚŠŚ™Ś•ŚȘ"</string>
     <string name="ok" msgid="7871959885003339302">"ŚŚ™Ś©Ś•Śš"</string>
-    <!-- no translation found for archive (4447791830199354721) -->
-    <skip />
+    <string name="archive" msgid="4447791830199354721">"Ś”ŚąŚ‘ŚšŚ” ŚœŚŚšŚ›Ś™Ś•ŚŸ"</string>
     <string name="update_anyway" msgid="8792432341346261969">"ڐڠڙ ŚšŚ•ŚŠŚ” ŚœŚąŚ“Ś›ŚŸ Ś‘Ś›Śœ ږڐŚȘ"</string>
     <string name="manage_applications" msgid="5400164782453975580">"Ś Ś™Ś”Ś•Śœ ŚŚ€ŚœŚ™Ś§ŚŠŚ™Ś•ŚȘ"</string>
     <string name="out_of_space_dlg_title" msgid="4156690013884649502">"ŚŚ™ŚŸ ŚžŚĄŚ€Ś™Ś§ ŚžŚ§Ś•Ś"</string>
@@ -60,16 +59,11 @@
     <string name="uninstall_update_title" msgid="824411791011583031">"Ś”ŚĄŚšŚȘ Ś”ŚȘڧڠڔ کڜ ŚąŚ“Ś›Ś•ŚŸ"</string>
     <string name="uninstall_activity_text" msgid="1928194674397770771">"Ś”Ś€ŚąŚ™ŚœŚ•ŚȘ <xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> ڔڙڐ Ś—ŚœŚ§ ŚžŚ”ŚŚ€ŚœŚ™Ś§ŚŠŚ™Ś” ڔڑڐڔ:"</string>
     <string name="uninstall_application_text" msgid="3816830743706143980">"ŚœŚ”ŚĄŚ™Śš ڐŚȘ ڔڔŚȘڧڠڔ کڜ Ś”ŚŚ€ŚœŚ™Ś§ŚŠŚ™Ś” ڔږڕ?"</string>
-    <!-- no translation found for archive_application_text (8482325710714386348) -->
-    <skip />
-    <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
-    <skip />
-    <!-- no translation found for archive_application_text_user (2586558895535581451) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
-    <skip />
+    <string name="archive_application_text" msgid="8482325710714386348">"Ś”ŚžŚ™Ś“Śą ڔڐڙکڙ کڜښ Ś™Ś™Ś©ŚžŚš"</string>
+    <string name="archive_application_text_all_users" msgid="3151229641681672580">"ŚœŚ”ŚąŚ‘Ś™Śš ŚœŚŚšŚ›Ś™Ś•ŚŸ ڐŚȘ Ś”ŚŚ€ŚœŚ™Ś§ŚŠŚ™Ś” ڔږڕ ŚœŚ›Śœ Ś”ŚžŚ©ŚȘŚžŚ©Ś™Ś? Ś”ŚžŚ™Ś“Śą ڔڐڙکڙ کڜښ Ś™Ś™Ś©ŚžŚš"</string>
+    <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"ŚœŚ”ŚąŚ‘Ś™Śš ڐŚȘ Ś”ŚŚ€ŚœŚ™Ś§ŚŠŚ™Ś” ڔږڕ ŚžŚ€ŚšŚ•Ś€Ś™Śœ Ś”ŚąŚ‘Ś•Ś“Ś” ŚœŚŚšŚ›Ś™Ś•ŚŸ? Ś”ŚžŚ™Ś“Śą ڔڐڙکڙ کڜښ Ś™Ś™Ś©ŚžŚš"</string>
+    <string name="archive_application_text_user" msgid="2586558895535581451">"ŚœŚ”ŚąŚ‘Ś™Śš ڐŚȘ Ś”ŚŚ€ŚœŚ™Ś§ŚŠŚ™Ś” ڔږڕ کڜ <xliff:g id="USERNAME">%1$s</xliff:g> ŚœŚŚšŚ›Ś™Ś•ŚŸ? Ś”ŚžŚ™Ś“Śą ڔڐڙکڙ کڜښ Ś™Ś™Ś©ŚžŚš"</string>
+    <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"ŚœŚ”ŚąŚ‘Ś™Śš ڐŚȘ Ś”ŚŚ€ŚœŚ™Ś§ŚŠŚ™Ś” ڔږڕ ŚžŚ”ŚžŚšŚ—Ś‘ Ś”Ś€ŚšŚ˜Ś™ ŚœŚŚšŚ›Ś™Ś•ŚŸ? Ś”ŚžŚ™Ś“Śą ڔڐڙکڙ کڜښ Ś™Ś™Ś©ŚžŚš"</string>
     <string name="uninstall_application_text_all_users" msgid="575491774380227119">"ŚœŚ”ŚĄŚ™Śš ڐŚȘ Ś”ŚŚ€ŚœŚ™Ś§ŚŠŚ™Ś” ڔږڕ ŚąŚ‘Ś•Śš "<b>"Ś›Śœ"</b>" Ś”ŚžŚ©ŚȘŚžŚ©Ś™Ś? Ś”ŚŚ€ŚœŚ™Ś§ŚŠŚ™Ś” ڕڔڠŚȘŚ•Ś Ś™Ś Ś©ŚœŚ” Ś™Ś•ŚĄŚšŚ• ŚąŚ‘Ś•Śš "<b>"Ś›Śœ"</b>" Ś”ŚžŚ©ŚȘŚžŚ©Ś™Ś Ś‘ŚžŚ›Ś©Ś™Śš."</string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"ŚœŚ”ŚĄŚ™Śš ڐŚȘ ڔڔŚȘڧڠڔ کڜ Ś”ŚŚ€ŚœŚ™Ś§ŚŠŚ™Ś” ڔږڕ ŚąŚ‘Ś•Śš <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
     <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"ŚšŚ•ŚŠŚ” ŚœŚ”ŚĄŚ™Śš ڐŚȘ Ś”ŚŚ€ŚœŚ™Ś§ŚŠŚ™Ś” ڔږڕ ŚžŚ€ŚšŚ•Ś€Ś™Śœ Ś”ŚąŚ‘Ś•Ś“Ś”?"</string>
@@ -108,8 +102,7 @@
     <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Ś ŚȘڕڠڙ Ś”Ś˜ŚŚ‘ŚœŚ˜ ڕڔڠŚȘŚ•Ś Ś™Ś Ś”ŚŚ™Ś©Ś™Ś™Ś کڜښ Ś—Ś©Ś•Ś€Ś™Ś ڙڕŚȘŚš ڑڀڠڙ Ś”ŚȘڧڀڕŚȘ ŚąŚœ ڙړڙ ŚŚ€ŚœŚ™Ś§ŚŠŚ™Ś•ŚȘ ŚžŚžŚ§Ś•ŚšŚ•ŚȘ ڜڐ Ś™Ś“Ś•ŚąŚ™Ś. Ś”ŚȘڧڠŚȘ Ś”ŚŚ€ŚœŚ™Ś§ŚŠŚ™Ś” ڔږڕ ŚžŚ”Ś•Ś•Ś” ڐŚȘ Ś”ŚĄŚ›ŚžŚȘښ ŚœŚ›Śš Ś©Ś”ŚŚ—ŚšŚ™Ś•ŚȘ Ś”Ś‘ŚœŚąŚ“Ś™ŚȘ ڔڙڐ کڜښ Ś‘ŚžŚ§ŚšŚ” کڜ ŚŚ•Ś‘Ś“ŚŸ Ś ŚȘŚ•Ś Ś™Ś ڐڕ Ś’ŚšŚ™ŚžŚȘ ڠږڧ ŚœŚ˜ŚŚ‘ŚœŚ˜ Ś‘ŚąŚ§Ś‘Ś•ŚȘ Ś”Ś©Ś™ŚžŚ•Ś© Ś‘ŚŚ€ŚœŚ™Ś§ŚŠŚ™Ś”."</string>
     <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Ś ŚȘڕڠڙ Ś”Ś˜ŚœŚ•Ś•Ś™Ś–Ś™Ś” ڕڔڠŚȘŚ•Ś Ś™Ś Ś”ŚŚ™Ś©Ś™Ś™Ś کڜښ Ś—Ś©Ś•Ś€Ś™Ś ڙڕŚȘŚš ڑڀڠڙ Ś”ŚȘڧڀڕŚȘ ŚąŚœ ڙړڙ ŚŚ€ŚœŚ™Ś§ŚŠŚ™Ś•ŚȘ ŚžŚžŚ§Ś•ŚšŚ•ŚȘ ڜڐ Ś™Ś“Ś•ŚąŚ™Ś. Ś”ŚȘڧڠŚȘ Ś”ŚŚ€ŚœŚ™Ś§ŚŠŚ™Ś” ڔږڕ ŚžŚ”Ś•Ś•Ś” ڐŚȘ Ś”ŚĄŚ›ŚžŚȘښ ŚœŚ›Śš Ś©Ś”ŚŚ—ŚšŚ™Ś•ŚȘ Ś”Ś‘ŚœŚąŚ“Ś™ŚȘ ڔڙڐ کڜښ Ś‘ŚžŚ§ŚšŚ” کڜ ŚŚ•Ś‘Ś“ŚŸ Ś ŚȘŚ•Ś Ś™Ś ڐڕ Ś’ŚšŚ™ŚžŚȘ ڠږڧ ŚœŚ˜ŚœŚ•Ś•Ś™Ś–Ś™Ś” کڜښ Ś‘ŚąŚ§Ś‘Ś•ŚȘ Ś”Ś©Ś™ŚžŚ•Ś© Ś‘ŚŚ€ŚœŚ™Ś§ŚŠŚ™Ś”."</string>
     <string name="cloned_app_label" msgid="7503612829833756160">"Ś©Ś›Ś€Ś•Śœ کڜ <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
-    <!-- no translation found for archiving_app_label (1127085259724124725) -->
-    <skip />
+    <string name="archiving_app_label" msgid="1127085259724124725">"ŚœŚ”ŚąŚ‘Ś™Śš ڐŚȘ <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ŚœŚŚšŚ›Ś™Ś•ŚŸ?"</string>
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Ś”ŚžŚ©Śš"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Ś”Ś’Ś“ŚšŚ•ŚȘ"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"‏ŚȘŚ”ŚœŚ™Śš Ś”ŚȘڧڠڔ/Ś”ŚĄŚšŚȘ Ś”ŚȘڧڠڔ کڜ ŚŚ€ŚœŚ™Ś§ŚŠŚ™Ś•ŚȘ Wear"</string>
diff --git a/packages/PackageInstaller/res/values-ja/strings.xml b/packages/PackageInstaller/res/values-ja/strings.xml
index dd0d00e..4d16b1a 100644
--- a/packages/PackageInstaller/res/values-ja/strings.xml
+++ b/packages/PackageInstaller/res/values-ja/strings.xml
@@ -44,8 +44,7 @@
     <string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"ă“ăźăƒŠăƒŒă‚¶ăƒŒăŻäžæ˜ŽăȘケプăƒȘă‚’ă‚€ăƒłă‚čăƒˆăƒŒăƒ«ă§ăăŸă›ă‚“"</string>
     <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"ă“ăźăƒŠăƒŒă‚¶ăƒŒăŻă‚ąăƒ—ăƒȘă‚’ă‚€ăƒłă‚čăƒˆăƒŒăƒ«ă§ăăŸă›ă‚“"</string>
     <string name="ok" msgid="7871959885003339302">"OK"</string>
-    <!-- no translation found for archive (4447791830199354721) -->
-    <skip />
+    <string name="archive" msgid="4447791830199354721">"ă‚ąăƒŒă‚«ă‚€ăƒ–"</string>
     <string name="update_anyway" msgid="8792432341346261969">"æ›Žæ–°ă™ă‚‹"</string>
     <string name="manage_applications" msgid="5400164782453975580">"ケプăƒȘぼ缡理"</string>
     <string name="out_of_space_dlg_title" msgid="4156690013884649502">"ćźčé‡äžè¶ł"</string>
@@ -60,16 +59,11 @@
     <string name="uninstall_update_title" msgid="824411791011583031">"ă‚ąăƒƒăƒ—ăƒ‡ăƒŒăƒˆăźă‚ąăƒłă‚€ăƒłă‚čăƒˆăƒŒăƒ«"</string>
     <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> ăŻæŹĄăźă‚ąăƒ—ăƒȘぼ侀郹です。"</string>
     <string name="uninstall_application_text" msgid="3816830743706143980">"こぼケプăƒȘă‚’ă‚ąăƒłă‚€ăƒłă‚čăƒˆăƒŒăƒ«ă—ăŸă™ă‹ïŒŸ"</string>
-    <!-- no translation found for archive_application_text (8482325710714386348) -->
-    <skip />
-    <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
-    <skip />
-    <!-- no translation found for archive_application_text_user (2586558895535581451) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
-    <skip />
+    <string name="archive_application_text" msgid="8482325710714386348">"怋äșșăƒ‡ăƒŒă‚żăŒäżć­˜ă•ă‚ŒăŸă™"</string>
+    <string name="archive_application_text_all_users" msgid="3151229641681672580">"すăčăŠăźăƒŠăƒŒă‚¶ăƒŒă«ćŻŸă—ă“ăźă‚ąăƒ—ăƒȘă‚’ă‚ąăƒŒă‚«ă‚€ăƒ–ă—ăŸă™ă‹ïŒŸć€‹äșșăƒ‡ăƒŒă‚żăŒäżć­˜ă•ă‚ŒăŸă™"</string>
+    <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"仕äș‹ç”šăƒ—ăƒ­ăƒ•ă‚Ąă‚€ăƒ«ă§ă“ăźă‚ąăƒ—ăƒȘă‚’ă‚ąăƒŒă‚«ă‚€ăƒ–ă—ăŸă™ă‹ïŒŸć€‹äșșăƒ‡ăƒŒă‚żăŒäżć­˜ă•ă‚ŒăŸă™"</string>
+    <string name="archive_application_text_user" msgid="2586558895535581451">"<xliff:g id="USERNAME">%1$s</xliff:g> ă«ćŻŸă—ă“ăźă‚ąăƒ—ăƒȘă‚’ă‚ąăƒŒă‚«ă‚€ăƒ–ă—ăŸă™ă‹ïŒŸć€‹äșșăƒ‡ăƒŒă‚żăŒäżć­˜ă•ă‚ŒăŸă™"</string>
+    <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"ăƒ—ăƒ©ă‚€ăƒ™ăƒŒăƒˆ ă‚čăƒšăƒŒă‚čからこぼケプăƒȘă‚’ă‚ąăƒŒă‚«ă‚€ăƒ–ă—ăŸă™ă‹ïŒŸć€‹äșșăƒ‡ăƒŒă‚żăŒäżć­˜ă•ă‚ŒăŸă™"</string>
     <string name="uninstall_application_text_all_users" msgid="575491774380227119">"こぼケプăƒȘを"<b>"すăčおた"</b>"ăƒŠăƒŒă‚¶ăƒŒă‹ă‚‰ă‚ąăƒłă‚€ăƒłă‚čăƒˆăƒŒăƒ«ă—ăŸă™ă‹ïŒŸă“ăźă‚ąăƒ—ăƒȘăšăăźăƒ‡ăƒŒă‚żăŻăƒ‡ăƒă‚€ă‚čた"<b>"すăčおた"</b>"ăƒŠăƒŒă‚¶ăƒŒă‹ă‚‰ć‰Šé™€ă•ă‚ŒăŸă™ă€‚"</string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"<xliff:g id="USERNAME">%1$s</xliff:g> さんぼケプăƒȘă‚’ă‚ąăƒłă‚€ăƒłă‚čăƒˆăƒŒăƒ«ă—ăŸă™ă‹ïŒŸ"</string>
     <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"こぼケプăƒȘを仕äș‹ç”šăƒ—ăƒ­ăƒ•ă‚Ąă‚€ăƒ«ă‹ă‚‰ă‚ąăƒłă‚€ăƒłă‚čăƒˆăƒŒăƒ«ă—ăŸă™ă‹ïŒŸ"</string>
@@ -108,8 +102,7 @@
     <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"䞍明ăȘケプăƒȘă‚’ă‚€ăƒłă‚čăƒˆăƒŒăƒ«ă™ă‚‹ăšă‚żăƒ–ăƒŹăƒƒăƒˆă‚„ć€‹äșșăƒ‡ăƒŒă‚żăźäŸ”ćźłă«ćŻŸă™ă‚‹ćź‰ć…šæ€§ăŒäœŽäž‹ă—ăŸă™ă€‚ă“ăźă‚ąăƒ—ăƒȘă‚’ă‚€ăƒłă‚čăƒˆăƒŒăƒ«ă™ă‚‹ă“ăšă§ă€ă‚ąăƒ—ăƒȘăźäœżç”šă«ă‚ˆă‚Šç”Ÿă˜ă‚‹ćŻèƒœæ€§ăŒă‚ă‚‹ă‚żăƒ–ăƒŹăƒƒăƒˆăžăźäŸ”ćźłă‚„ăƒ‡ăƒŒă‚żăźæć€±ă«ă€ă„ăŠă€ăƒŠăƒŒă‚¶ăƒŒă”è‡Șèș«ăŒć˜ç‹Źă§èȏ任をèČ ă†ă“ăšă«ćŒæ„ă™ă‚‹ă“ăšă«ăȘă‚ŠăŸă™ă€‚"</string>
     <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"䞍明ăȘケプăƒȘă‚’ă‚€ăƒłă‚čăƒˆăƒŒăƒ«ă™ă‚‹ăšăƒ†ăƒŹăƒ“ă‚„ć€‹äșșăƒ‡ăƒŒă‚żăźäŸ”ćźłă«ćŻŸă™ă‚‹ćź‰ć…šæ€§ăŒäœŽäž‹ă—ăŸă™ă€‚ă“ăźă‚ąăƒ—ăƒȘă‚’ă‚€ăƒłă‚čăƒˆăƒŒăƒ«ă™ă‚‹ă“ăšă§ă€ă‚ąăƒ—ăƒȘăźäœżç”šă«ă‚ˆă‚Šç”Ÿă˜ă‚‹ćŻèƒœæ€§ăŒă‚ă‚‹ăƒ†ăƒŹăƒ“ăžăźäŸ”ćźłă‚„ăƒ‡ăƒŒă‚żăźæć€±ă«ă€ă„ăŠă€ăƒŠăƒŒă‚¶ăƒŒă”è‡Șèș«ăŒć˜ç‹Źă§èȏ任をèČ ă†ă“ăšă«ćŒæ„ă™ă‚‹ă“ăšă«ăȘă‚ŠăŸă™ă€‚"</string>
     <string name="cloned_app_label" msgid="7503612829833756160">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ăźă‚Żăƒ­ăƒŒăƒł"</string>
-    <!-- no translation found for archiving_app_label (1127085259724124725) -->
-    <skip />
+    <string name="archiving_app_label" msgid="1127085259724124725">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ă‚’ă‚ąăƒŒă‚«ă‚€ăƒ–ă—ăŸă™ă‹ïŒŸ"</string>
     <string name="anonymous_source_continue" msgid="4375745439457209366">"æŹĄăž"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"èš­ćźš"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"WearケプăƒȘ ă‚€ăƒłă‚čăƒˆăƒŒăƒ«/ă‚ąăƒłă‚€ăƒłă‚čăƒˆăƒŒăƒ«"</string>
diff --git a/packages/PackageInstaller/res/values-ka/strings.xml b/packages/PackageInstaller/res/values-ka/strings.xml
index 7d4c1fd..1a459af 100644
--- a/packages/PackageInstaller/res/values-ka/strings.xml
+++ b/packages/PackageInstaller/res/values-ka/strings.xml
@@ -44,8 +44,7 @@
     <string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"ამ მომჼმარებელს არ áƒšáƒ”áƒŁáƒ«áƒšáƒ˜áƒ უáƒȘნობი აპების ინსჱალაáƒȘია"</string>
     <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"ამ მომჼმარებელს არ áƒáƒ„áƒ•áƒĄ აპების ინსჱალაáƒȘიიქ áƒŁáƒ€áƒšáƒ”áƒ‘áƒ"</string>
     <string name="ok" msgid="7871959885003339302">"კარგი"</string>
-    <!-- no translation found for archive (4447791830199354721) -->
-    <skip />
+    <string name="archive" msgid="4447791830199354721">"არჄივი"</string>
     <string name="update_anyway" msgid="8792432341346261969">"მაინáƒȘ განაჼლდეს"</string>
     <string name="manage_applications" msgid="5400164782453975580">"აპების მართვა"</string>
     <string name="out_of_space_dlg_title" msgid="4156690013884649502">"მეჼსიერება არასაკმარისია"</string>
@@ -60,16 +59,11 @@
     <string name="uninstall_update_title" msgid="824411791011583031">"განაჼლების დეინსჱალაáƒȘია"</string>
     <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> არის ჹემდეგი აპის ნაწილი:"</string>
     <string name="uninstall_application_text" msgid="3816830743706143980">"გსურთ ამ აპის დეინსჱალაáƒȘია?"</string>
-    <!-- no translation found for archive_application_text (8482325710714386348) -->
-    <skip />
-    <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
-    <skip />
-    <!-- no translation found for archive_application_text_user (2586558895535581451) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
-    <skip />
+    <string name="archive_application_text" msgid="8482325710714386348">"თჄვენი პერსონალური მონაáƒȘემები ჹეინაჼება"</string>
+    <string name="archive_application_text_all_users" msgid="3151229641681672580">"გსურთ ამ აპის ყველა მომჼმარებლისთვის დაარჄივება? თჄვენი პერსონალური მონაáƒȘემები ჹეინაჼება"</string>
+    <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"გსურთ ამ აპის დაარჄივება áƒ—áƒ„áƒ•áƒ”áƒœáƒĄ სამსაჼურის პროჀილლი? თჄვენი პერსონალური მონაáƒȘემები ჹეინაჼება"</string>
+    <string name="archive_application_text_user" msgid="2586558895535581451">"გსურთ ამ აპის დაარჄივება <xliff:g id="USERNAME">%1$s</xliff:g>-ისთვის? თჄვენი პერსონალური მონაáƒȘემები ჹეინაჼება"</string>
+    <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"გსურთ ამ აპის დაარჄივება თჄვენი კერძო სივრáƒȘიდან? თჄვენი პერსონალური მონაáƒȘემები ჹეინაჼება"</string>
     <string name="uninstall_application_text_all_users" msgid="575491774380227119">"გსურთ ამ აპის დეინსჱალაáƒȘია "<b>"ყველა"</b>" მომჼმარებლისთვის? აპლიკაáƒȘია და მისი მონაáƒȘემები ამოიჹლება "<b>"ყველა"</b>" მომჼმარებლის პროჀილიდან მოწყობილობაზე."</string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"გსურთ ამ აპის დეინსჱალაáƒȘია <xliff:g id="USERNAME">%1$s</xliff:g>-ისთვის?"</string>
     <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"გსურთ ამ აპის დეინსჱალაáƒȘია თჄვენი სამსაჼურის პროჀილიდან?"</string>
@@ -108,8 +102,7 @@
     <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"თჄვენი ჱაბლეჱი და პერსონალური მონაáƒȘემები მეჱად დაუáƒȘველია უáƒȘნობი აპების მჼრიდან ჹეჱევების წინაჹე. ამ აპის ინსჱალაáƒȘიიქ ჹემთჼვევაჹი, თჄვენ თანაჼმა ჼართ, პასუჼისმგებელი იყოთ მისი გამოყენების ჹედეგად ჱაბლეჱისთვის მიყენებულ ზიანსა თუ მონაáƒȘემების დაკარგვაზე."</string>
     <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"თჄვენი ჱელევიზორი და პერსონალური მონაáƒȘემები მეჱად დაუáƒȘველია უáƒȘნობი აპების მჼრიდან ჹეჱევების წინაჹე. ამ აპის ინსჱალაáƒȘიიქ ჹემთჼვევაჹი, თჄვენ თანაჼმა ჼართ, პასუჼისმგებელი იყოთ მისი გამოყენების ჹედეგად ჱელევიზორისთვის მიყენებულ ზიანსა თუ მონაáƒȘემების დაკარგვაზე."</string>
     <string name="cloned_app_label" msgid="7503612829833756160">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> კლონის ლეჄმნა"</string>
-    <!-- no translation found for archiving_app_label (1127085259724124725) -->
-    <skip />
+    <string name="archiving_app_label" msgid="1127085259724124725">"გსურთ <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>-იქ დაარჄივება?"</string>
     <string name="anonymous_source_continue" msgid="4375745439457209366">"გაგრძელება"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"პარამეჱრები"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Wear აპების ინსჱალაáƒȘია/დეინსჱალაáƒȘია"</string>
diff --git a/packages/PackageInstaller/res/values-kk/strings.xml b/packages/PackageInstaller/res/values-kk/strings.xml
index f731bbc..718ca1d 100644
--- a/packages/PackageInstaller/res/values-kk/strings.xml
+++ b/packages/PackageInstaller/res/values-kk/strings.xml
@@ -44,8 +44,7 @@
     <string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Đ‘Ò±Đ» паĐčĐŽĐ°Đ»Đ°ĐœŃƒŃˆŃ‹ бДлгісіз Ò›ĐŸĐ»ĐŽĐ°ĐœĐ±Đ°Đ»Đ°Ń€ĐŽŃ‹ ĐŸŃ€ĐœĐ°Ń‚Đ° Đ°Đ»ĐŒĐ°ĐčЮы"</string>
     <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Đ‘Ò±Đ» паĐčĐŽĐ°Đ»Đ°ĐœŃƒŃˆŃ‹ĐœŃ‹ÒŁ Ò›ĐŸĐ»ĐŽĐ°ĐœĐ±Đ°Đ»Đ°Ń€ĐŽŃ‹ ĐŸŃ€ĐœĐ°Ń‚Ńƒ Ń€Ò±Ò›ŃĐ°Ń‚Ń‹ Đ¶ĐŸÒ›"</string>
     <string name="ok" msgid="7871959885003339302">"ЖараĐčЮы"</string>
-    <!-- no translation found for archive (4447791830199354721) -->
-    <skip />
+    <string name="archive" msgid="4447791830199354721">"ĐœÒ±Ń€Đ°Ò“Đ°Ń‚Ń‚Đ°Ńƒ"</string>
     <string name="update_anyway" msgid="8792432341346261969">"Бәрібір Đ¶Đ°ÒŁĐ°Ń€Ń‚Ńƒ"</string>
     <string name="manage_applications" msgid="5400164782453975580">"ÒšĐŸĐ»ĐŽĐ°ĐœĐ±Đ°Đ»Đ°Ń€ĐŽŃ‹ Đ±Đ°ŃÒ›Đ°Ń€Ńƒ"</string>
     <string name="out_of_space_dlg_title" msgid="4156690013884649502">"ĐžŃ€Ń‹Đœ Đ¶ĐŸÒ›"</string>
@@ -60,16 +59,11 @@
     <string name="uninstall_update_title" msgid="824411791011583031">"Đ–Đ°ÒŁĐ° ĐœÒ±ŃÒ›Đ°ĐœŃ‹ Đ¶ĐŸŃŽ"</string>
     <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> ĐșДлДсі Ò›ĐŸĐ»ĐŽĐ°ĐœĐ±Đ°ĐœŃ‹ÒŁ бөлігі:"</string>
     <string name="uninstall_application_text" msgid="3816830743706143980">"Осы Ò›ĐŸĐ»ĐŽĐ°ĐœĐ±Đ° Đ¶ĐŸĐčŃ‹Đ»ŃŃ‹Đœ ба?"</string>
-    <!-- no translation found for archive_application_text (8482325710714386348) -->
-    <skip />
-    <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
-    <skip />
-    <!-- no translation found for archive_application_text_user (2586558895535581451) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
-    <skip />
+    <string name="archive_application_text" msgid="8482325710714386348">"Đ–Đ”ĐșĐ” ЎДрДĐșŃ‚Đ”Ń€Ń–ÒŁŃ–Đ· ŃĐ°Ò›Ń‚Đ°Đ»Đ°ĐŽŃ‹."</string>
+    <string name="archive_application_text_all_users" msgid="3151229641681672580">"Đ‘Ò±Đ» Ò›ĐŸĐ»ĐŽĐ°ĐœĐ±Đ°ĐœŃ‹ Đ±Đ°Ń€Đ»Ń‹Ò› паĐčĐŽĐ°Đ»Đ°ĐœŃƒŃˆŃ‹ ÒŻŃˆŃ–Đœ ĐŒÒ±Ń€Đ°Ò“Đ°Ń‚Ń‚Đ°Ńƒ ĐșДрДĐș пД? Đ–Đ”ĐșĐ” ЎДрДĐșŃ‚Đ”Ń€Ń–ÒŁŃ–Đ· ŃĐ°Ò›Ń‚Đ°Đ»Đ°ĐŽŃ‹."</string>
+    <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Đ‘Ò±Đ» Ò›ĐŸĐ»ĐŽĐ°ĐœĐ±Đ°ĐœŃ‹ Đ¶Ò±ĐŒŃ‹Ń ĐżŃ€ĐŸŃ„ĐžĐ»Ń–ÒŁŃ–Đ·ĐŽĐ” ĐŒÒ±Ń€Đ°Ò“Đ°Ń‚Ń‚Đ°Ńƒ ĐșДрДĐș пД? Đ–Đ”ĐșĐ” ЎДрДĐșŃ‚Đ”Ń€Ń–ÒŁŃ–Đ· ŃĐ°Ò›Ń‚Đ°Đ»Đ°ĐŽŃ‹."</string>
+    <string name="archive_application_text_user" msgid="2586558895535581451">"Đ‘Ò±Đ» Ò›ĐŸĐ»ĐŽĐ°ĐœĐ±Đ°ĐœŃ‹ <xliff:g id="USERNAME">%1$s</xliff:g> ÒŻŃˆŃ–Đœ ĐŒÒ±Ń€Đ°Ò“Đ°Ń‚Ń‚Đ°Ńƒ ĐșДрДĐș пД? Đ–Đ”ĐșĐ” ЎДрДĐșŃ‚Đ”Ń€Ń–ÒŁŃ–Đ· ŃĐ°Ò›Ń‚Đ°Đ»Đ°ĐŽŃ‹."</string>
+    <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Осы Ò›Ò±Ń€Ń‹Đ»Ò“Ń‹ĐœŃ‹ жДĐșĐ” Đ±Ó©Đ»ĐŒĐ”ÒŁŃ–Đ·ĐŽĐ” ĐŒÒ±Ń€Đ°Ò“Đ°Ń‚Ń‚Đ°Ò“Ń‹ÒŁŃ‹Đ· ĐșДлД ĐŒĐ”? Đ–Đ”ĐșĐ” ЎДрДĐșŃ‚Đ”Ń€Ń–ÒŁŃ–Đ· ŃĐ°Ò›Ń‚Đ°Đ»Đ°ĐŽŃ‹."</string>
     <string name="uninstall_application_text_all_users" msgid="575491774380227119">"Đ‘Ò±Đ» Ò›ĐŸĐ»ĐŽĐ°ĐœĐ±Đ° "<b>"Đ±Đ°Ń€Đ»Ń‹Ò›"</b>" паĐčĐŽĐ°Đ»Đ°ĐœŃƒŃˆŃ‹Đ»Đ°Ń€ ÒŻŃˆŃ–Đœ Đ¶ĐŸĐčŃ‹Đ»ŃŃ‹Đœ ба? ÒšĐŸĐ»ĐŽĐ°ĐœĐ±Đ° Đ¶Ó™ĐœĐ” ĐŸĐœŃ‹ÒŁ ЎДрДĐșтДрі Ò›Ò±Ń€Ń‹Đ»Ò“Ń‹ĐŽĐ°Ò“Ń‹ "<b>"Đ±Đ°Ń€Đ»Ń‹Ò›"</b>" паĐčĐŽĐ°Đ»Đ°ĐœŃƒŃˆŃ‹Đ»Đ°Ń€ĐŽĐ°Đœ Ó©ŃˆŃ–Ń€Ń–Đ»Đ”ĐŽŃ–."</string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"<xliff:g id="USERNAME">%1$s</xliff:g> ÒŻŃˆŃ–Đœ ĐŸŃŃ‹ Ò›ĐŸĐ»ĐŽĐ°ĐœĐ±Đ° Đ¶ĐŸĐčŃ‹Đ»ŃŃ‹Đœ ба?"</string>
     <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Đ‘Ò±Đ» Ò›ĐŸĐ»ĐŽĐ°ĐœĐ±Đ° Đ¶Ò±ĐŒŃ‹Ń ĐżŃ€ĐŸŃ„ĐžĐ»Ń–ÒŁŃ–Đ·ĐŽĐ”Đœ Đ¶ĐŸĐčŃ‹Đ»ŃŃ‹Đœ ба?"</string>
@@ -108,8 +102,7 @@
     <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"ĐŸĐ»Đ°ĐœŃˆĐ”Ń‚ Đ¶Ó™ĐœĐ” жДĐșĐ” ЎДрДĐșтДр бДлгісіз Ò›ĐŸĐ»ĐŽĐ°ĐœĐ±Đ°Đ»Đ°Ń€ĐŽŃ‹ÒŁ ŃˆĐ°Đ±ŃƒŃ‹Đ»Ń‹ĐœĐ° Ò±ŃˆŃ‹Ń€Đ°ŃƒŃ‹ ĐŒÒŻĐŒĐșŃ–Đœ. Đ‘Ò±Đ» Ò›ĐŸĐ»ĐŽĐ°ĐœĐ±Đ°ĐœŃ‹ ĐŸŃ€ĐœĐ°Ń‚Ńƒ Đ°Ń€Ò›Ń‹Đ»Ń‹ ĐŸĐœŃ‹ паĐčĐŽĐ°Đ»Đ°ĐœŃƒ ĐœÓ™Ń‚ĐžĐ¶Đ”ŃŃ–ĐœĐŽĐ”ĐłŃ– ĐżĐ»Đ°ĐœŃˆĐ”Ń‚ĐșĐ” ĐșĐ”Đ»Đ”Ń‚Ń–Đœ Đ·Đ°Đ»Đ°Đ»Ò“Đ° ĐœĐ”ĐŒĐ”ŃĐ” ЎДрДĐșŃ‚Đ”Ń€ĐŽŃ–ÒŁ Đ¶ĐŸÒ“Đ°Đ»ŃƒŃ‹ĐœĐ° Ó©Đ·Ń–ÒŁŃ–Đ· Ò“Đ°ĐœĐ° Đ¶Đ°ŃƒĐ°ĐżŃ‚Ń‹ Đ±ĐŸĐ»Đ°Ń‚Ń‹ĐœŃ‹ÒŁŃ‹Đ·Ò“Đ° ĐșДлісДсіз."</string>
     <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"йДлДЎОЎар Đ¶Ó™ĐœĐ” жДĐșĐ” ЎДрДĐșтДр бДлгісіз Ò›ĐŸĐ»ĐŽĐ°ĐœĐ±Đ°Đ»Đ°Ń€ĐŽŃ‹ÒŁ ŃˆĐ°Đ±ŃƒŃ‹Đ»Ń‹ĐœĐ° Ò±ŃˆŃ‹Ń€Đ°ŃƒŃ‹ ĐŒÒŻĐŒĐșŃ–Đœ. Đ‘Ò±Đ» Ò›ĐŸĐ»ĐŽĐ°ĐœĐ±Đ°ĐœŃ‹ ĐŸŃ€ĐœĐ°Ń‚Ńƒ Đ°Ń€Ò›Ń‹Đ»Ń‹ ĐŸĐœŃ‹ паĐčĐŽĐ°Đ»Đ°ĐœŃƒ ĐœÓ™Ń‚ĐžĐ¶Đ”ŃŃ–ĐœĐŽĐ”ĐłŃ– Ń‚Đ”Đ»Đ”ĐŽĐžĐŽĐ°Ń€Ò“Đ° ĐșĐ”Đ»Đ”Ń‚Ń–Đœ Ò›Đ°ĐœĐŽĐ°Đč Ўа бір Đ·Đ°Đ»Đ°Đ»Ò“Đ° ĐœĐ”ĐŒĐ”ŃĐ” ЎДрДĐșŃ‚Đ”Ń€ĐŽŃ–ÒŁ Đ¶ĐŸÒ“Đ°Đ»ŃƒŃ‹ĐœĐ° Ó©Đ·Ń–ÒŁŃ–Đ· Ò“Đ°ĐœĐ° Đ¶Đ°ŃƒĐ°ĐżŃ‚Ń‹ Đ±ĐŸĐ»Đ°Ń‚Ń‹ĐœŃ‹ÒŁŃ‹Đ·Ò“Đ° ĐșДлісДсіз."</string>
     <string name="cloned_app_label" msgid="7503612829833756160">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ĐșĐ»ĐŸĐœŃ‹"</string>
-    <!-- no translation found for archiving_app_label (1127085259724124725) -->
-    <skip />
+    <string name="archiving_app_label" msgid="1127085259724124725">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> Đ±Đ”Đ»ĐłŃ–ŃŃ–Đœ ĐŒÒ±Ń€Đ°Ò“Đ°Ń‚Ń‚Đ°Ńƒ ĐșДрДĐș пД?"</string>
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Đ–Đ°Đ»Ò“Đ°ŃŃ‚Ń‹Ń€Ńƒ"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"ĐŸĐ°Ń€Đ°ĐŒĐ”Ń‚Ń€Đ»Đ”Ń€"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Wear Ò›ĐŸĐ»ĐŽĐ°ĐœĐ±Đ°ŃŃ‹Đœ ĐŸŃ€ĐœĐ°Ń‚Ńƒ/Đ¶ĐŸŃŽ"</string>
diff --git a/packages/PackageInstaller/res/values-km/strings.xml b/packages/PackageInstaller/res/values-km/strings.xml
index 7ae0ce7..3034f88 100644
--- a/packages/PackageInstaller/res/values-km/strings.xml
+++ b/packages/PackageInstaller/res/values-km/strings.xml
@@ -44,8 +44,7 @@
     <string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"ážąáŸ’áž“áž€áž”áŸ’ážšážŸáž”áŸ’ážšáž¶ážŸáŸ‹â€‹áž“áŸáŸ‡â€‹áž˜áž·áž“ážąáž¶áž…â€‹ážŠáŸ†ážĄážŸáž„áž€áž˜áŸ’áž˜ážœáž·áž’ážžáž˜áž·áž“ážŸáŸ’áž‚áž¶áž›áŸ‹â€‹â€‹áž”áž¶áž“â€‹áž‘áŸ"</string>
     <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"áž˜áž·áž“â€‹ážąáž“áž»áž‰áŸ’áž‰áž¶ážâ€‹áž±áŸ’áž™ážąáŸ’áž“áž€â€‹áž”áŸ’ážšážŸâ€‹áž”áŸ’ážšáž¶ážŸáŸ‹áž“áŸáŸ‡â€‹ážŠáŸ†ážĄážŸáž„áž€áž˜áŸ’áž˜ážœáž·áž’ážžáž‘áŸ"</string>
     <string name="ok" msgid="7871959885003339302">"យល់ព្រម"</string>
-    <!-- no translation found for archive (4447791830199354721) -->
-    <skip />
+    <string name="archive" msgid="4447791830199354721">"ទុក​ក្នុង​បណ្ណសារ"</string>
     <string name="update_anyway" msgid="8792432341346261969">"áž˜áž·áž“ážąážžáž‘áŸ ážŠáŸ†ážĄážŸáž„â€‹áž€áŸ†ážŽáŸ‚â€‹áž…áž»áŸ‡"</string>
     <string name="manage_applications" msgid="5400164782453975580">"គ្រប់គ្រង​កម្មវិធឞ"</string>
     <string name="out_of_space_dlg_title" msgid="4156690013884649502">"ážąážŸáŸ‹â€‹áž‘áŸ†áž áŸ†áž•áŸ’áž‘áž»áž€"</string>
@@ -60,16 +59,11 @@
     <string name="uninstall_update_title" msgid="824411791011583031">"លុប​កំណែ​ថ្មឞ"</string>
     <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g>​ ​ជា​ផ្នែក​មវយ​នៃ​កម្មវិធឞ​ដឌច​ខាង​ក្រោម​នេះ​៖"</string>
     <string name="uninstall_application_text" msgid="3816830743706143980">"ážážŸâ€‹ážąáŸ’áž“áž€â€‹áž…áž„áŸ‹â€‹áž›áž»áž”â€‹áž€áž˜áŸ’áž˜ážœáž·áž’ážžâ€‹áž“áŸáŸ‡â€‹ážŠáŸ‚ážšáž‘áŸ?"</string>
-    <!-- no translation found for archive_application_text (8482325710714386348) -->
-    <skip />
-    <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
-    <skip />
-    <!-- no translation found for archive_application_text_user (2586558895535581451) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
-    <skip />
+    <string name="archive_application_text" msgid="8482325710714386348">"áž‘áž·áž“áŸ’áž“áž“áŸáž™â€‹áž•áŸ’áž‘áž¶áž›áŸ‹ážáŸ’áž›ážœáž“ážšáž”ážŸáŸ‹ážąáŸ’áž“áž€áž“ážčងត្រឌវបានរក្សាទុក"</string>
+    <string name="archive_application_text_all_users" msgid="3151229641681672580">"áž‘áž»áž€áž€áž˜áŸ’áž˜ážœáž·áž’ážžáž“áŸáŸ‡áž€áŸ’áž“áž»áž„áž”ážŽáŸ’ážŽážŸáž¶ážšážŸáž˜áŸ’ážšáž¶áž”áŸ‹ážąáŸ’áž“áž€áž”áŸ’ážšážŸáž”áŸ’ážšáž¶ážŸáŸ‹áž‘áž¶áŸ†áž„ážąážŸáŸ‹ážŹ? áž‘áž·áž“áŸ’áž“áž“áŸáž™â€‹áž•áŸ’áž‘áž¶áž›áŸ‹ážáŸ’áž›ážœáž“ážšáž”ážŸáŸ‹ážąáŸ’áž“áž€áž“ážčងត្រឌវបានរក្សាទុក"</string>
+    <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"áž‘áž»áž€áž€áž˜áŸ’áž˜ážœáž·áž’ážžáž“áŸáŸ‡áž€áŸ’áž“áž»áž„áž”ážŽáŸ’ážŽážŸáž¶ážšáž“áŸ…áž›ážŸáž€áž˜áŸ’ážšáž„áž–áŸážáŸŒáž˜áž¶áž“áž€áž¶ážšáž„áž¶ážšážšáž”ážŸáŸ‹ážąáŸ’áž“áž€ážŹ? áž‘áž·áž“áŸ’áž“áž“áŸáž™â€‹áž•áŸ’áž‘áž¶áž›áŸ‹ážáŸ’áž›ážœáž“ážšáž”ážŸáŸ‹ážąáŸ’áž“áž€áž“ážčងត្រឌវបានរក្សាទុក"</string>
+    <string name="archive_application_text_user" msgid="2586558895535581451">"ទុកកម្មវិធឞនេះក្នុងបណ្ណសារសម្រាប់ <xliff:g id="USERNAME">%1$s</xliff:g> ឬ? áž‘áž·áž“áŸ’áž“áž“áŸáž™â€‹áž•áŸ’áž‘áž¶áž›áŸ‹ážáŸ’áž›ážœáž“ážšáž”ážŸáŸ‹ážąáŸ’áž“áž€áž“ážčងត្រឌវបានរក្សាទុក"</string>
+    <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"ážážŸážąáŸ’áž“áž€áž…áž„áŸ‹áž‘áž»áž€áž€áž˜áŸ’áž˜ážœáž·áž’ážžáž“áŸáŸ‡áž€áŸ’áž“áž»áž„áž”ážŽáŸ’ážŽážŸáž¶ážšáž–ážžáž›áŸ†áž ážŻáž€áž‡áž“ážšáž”ážŸáŸ‹ážąáŸ’áž“áž€ážŠáŸ‚ážšáž‘áŸ? áž‘áž·áž“áŸ’áž“áž“áŸáž™â€‹áž•áŸ’áž‘áž¶áž›áŸ‹ážáŸ’áž›ážœáž“ážšáž”ážŸáŸ‹ážąáŸ’áž“áž€áž“ážčងត្រឌវបានរក្សាទុក"</string>
     <string name="uninstall_application_text_all_users" msgid="575491774380227119">"ážážŸâ€‹ážąáŸ’áž“áž€â€‹áž…áž„áŸ‹â€‹áž›áž»áž”â€‹áž€áž˜áŸ’áž˜ážœáž·áž’ážžâ€‹áž“áŸáŸ‡â€‹ážŸáž˜áŸ’ážšáž¶áž”áŸ‹â€‹ážąáŸ’áž“áž€áž”áŸ’ážšážŸáž”áŸ’ážšáž¶ážŸáŸ‹"<b>"áž‘áž¶áŸ†áž„ážąážŸáŸ‹"</b>"ដែរទេ? កម្មវិធឞនេះ និង​ទិន្នន័យ​របស់​វា​នážčáž„â€‹ážáŸ’ážšážŒážœâ€‹áž”áž¶áž“â€‹áž›áž»áž”â€‹áž…áŸáž‰â€‹áž–ážžâ€‹ážąáŸ’áž“áž€áž”áŸ’ážšážŸáž”áŸ’ážšáž¶ážŸáŸ‹"<b>"áž‘áž¶áŸ†áž„ážąážŸáŸ‹"</b>"នៅលស​ឧបករណ៍​នេះ។"</string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"ážážŸâ€‹ážąáŸ’áž“áž€â€‹áž…áž„áŸ‹â€‹áž›áž»áž”â€‹áž€áž˜áŸ’áž˜ážœáž·áž’ážžâ€‹áž“áŸáŸ‡â€‹ážŸáž˜áŸ’ážšáž¶áž”áŸ‹â€‹ážąáŸ’áž“áž€áž”áŸ’ážšážŸáž”áŸ’ážšáž¶ážŸáŸ‹ <xliff:g id="USERNAME">%1$s</xliff:g> ដែរទេ?"</string>
     <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"ážážŸážąáŸ’áž“áž€â€‹áž…áž„áŸ‹áž›áž»áž”â€‹áž€áž˜áŸ’áž˜ážœáž·áž’ážžáž“áŸáŸ‡â€‹áž–ážžáž€áž˜áŸ’ážšáž„áž–áŸážáŸŒáž˜áž¶áž“â€‹áž€áž¶ážšáž„áž¶ážšážšáž”ážŸáŸ‹ážąáŸ’áž“áž€â€‹ážŠáŸ‚ážšáž‘áŸ?"</string>
@@ -108,8 +102,7 @@
     <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"ថេប្លេត áž“áž·áž„áž‘áž·áž“áŸ’áž“áž“áŸáž™áž•áŸ’áž‘áž¶áž›áŸ‹ážáŸ’áž›ážœáž“ážšáž”ážŸáŸ‹ážąáŸ’áž“áž€áž„áž¶áž™áž“ážčងរងគ្រោះពឞការវាយប្រហារពឞកម្មវិធឞដែលមិនស្គាល់។ áž”áŸ’ážšážŸáž·áž“áž”ážŸážŠáŸ†ážĄážŸáž„áž€áž˜áŸ’áž˜ážœáž·áž’ážžáž“áŸáŸ‡ áž˜áž¶áž“áž“áŸáž™ážáž¶ážąáŸ’áž“áž€áž‘áž‘ážœáž›ážáž»ážŸážáŸ’ážšážŒážœáž›ážŸáž€áž¶ážšážážŒáž…ážáž¶ážáž‘áž¶áŸ†áž„ážĄáž¶áž™áž…áŸ†áž–áŸ„áŸ‡ážáŸáž”áŸ’áž›áŸáž ážŹáž€áž¶ážšáž”áž¶ážáŸ‹áž”áž„áŸ‹áž‘áž·áž“áŸ’áž“áž“áŸáž™ážšáž”ážŸáŸ‹ážąáŸ’áž“áž€ ážŠáŸ‚áž›ážąáž¶áž…áž”ážŽáŸ’ážŠáž¶áž›áž˜áž€áž–ážžáž€áž¶ážšáž”áŸ’ážšážŸáž”áŸ’ážšáž¶ážŸáŸ‹áž€áž˜áŸ’áž˜ážœáž·áž’ážžáž“áŸáŸ‡áŸ”"</string>
     <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"ទឌរទស្សន៍ áž“áž·áž„áž‘áž·áž“áŸ’áž“áž“áŸáž™áž•áŸ’áž‘áž¶áž›áŸ‹ážáŸ’áž›ážœáž“ážšáž”ážŸáŸ‹ážąáŸ’áž“áž€áž„áž¶áž™áž“ážčងរងគ្រោះពឞការវាយប្រហារពឞកម្មវិធឞដែលមិនស្គាល់។ áž”áŸ’ážšážŸáž·áž“áž”ážŸážŠáŸ†ážĄážŸáž„áž€áž˜áŸ’áž˜ážœáž·áž’ážžáž“áŸáŸ‡ áž˜áž¶áž“áž“áŸáž™ážáž¶ážąáŸ’áž“áž€áž‘áž‘ážœáž›ážáž»ážŸážáŸ’ážšážŒážœáž›ážŸáž€áž¶ážšážážŒáž…ážáž¶ážáž‘áž¶áŸ†áž„ážĄáž¶áž™áž…áŸ†áž–áŸ„áŸ‡áž‘ážŒážšáž‘ážŸáŸ’ážŸáž“áŸ ážŹáž€áž¶ážšáž”áž¶ážáŸ‹áž”áž„áŸ‹áž‘áž·áž“áŸ’áž“áž“áŸáž™ážšáž”ážŸáŸ‹ážąáŸ’áž“áž€ ážŠáŸ‚áž›ážąáž¶áž…áž”ážŽáŸ’ážŠáž¶áž›áž˜áž€áž–ážžáž€áž¶ážšáž”áŸ’ážšážŸáž”áŸ’ážšáž¶ážŸáŸ‹áž€áž˜áŸ’áž˜ážœáž·áž’ážžáž“áŸáŸ‡áŸ”"</string>
     <string name="cloned_app_label" msgid="7503612829833756160">"ក្លឌន <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
-    <!-- no translation found for archiving_app_label (1127085259724124725) -->
-    <skip />
+    <string name="archiving_app_label" msgid="1127085259724124725">"ទុក <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> áž€áŸ’áž“áž»áž„áž”ážŽáŸ’ážŽážŸáž¶ážšážŹ?"</string>
     <string name="anonymous_source_continue" msgid="4375745439457209366">"បន្ត"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"ការកំណត់"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"áž€áž¶ážšážŠáŸ†ážĄážŸáž„/ការលុបកម្មវិធឞឧបករណ៍​ពាក់​"</string>
diff --git a/packages/PackageInstaller/res/values-kn/strings.xml b/packages/PackageInstaller/res/values-kn/strings.xml
index ec0746d..e65770f 100644
--- a/packages/PackageInstaller/res/values-kn/strings.xml
+++ b/packages/PackageInstaller/res/values-kn/strings.xml
@@ -44,8 +44,7 @@
     <string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"àȈ àČŹàČłàČ•àł†àČŠàČŸàȰàČ°àł àȅàČȘàȰàČżàȚàČżàČ€ àČ†àłàČŻàČȘàł‌àȗàČłàČšàłàČšàł àȅàČšàł‌àȇàČšàł‌àČžàłàȟàČŸàČČàł‌ àČźàČŸàČĄàČČàł àČžàČŸàČ§àłàČŻàČ”àČżàČČàłàČČ"</string>
     <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"àČ†àłàČŻàČȘàł‌àȗàČłàČšàłàČšàł àȇàČšàł‌àČžàłàȟàČŸàČČàł‌ àČźàČŸàČĄàČČàł àȈ àČŹàČłàČ•àł†àČŠàČŸàȰàȰàČšàłàČšàł àȅàČšàłàČźàČ€àČżàČžàČČàČŸàČ—àłàČ”àłàČŠàČżàČČàłàČČ"</string>
     <string name="ok" msgid="7871959885003339302">"àČžàȰàČż"</string>
-    <!-- no translation found for archive (4447791830199354721) -->
-    <skip />
+    <string name="archive" msgid="4447791830199354721">"àȆàČ°àłàČ•àłˆàČ”àł àČźàČŸàČĄàČż"</string>
     <string name="update_anyway" msgid="8792432341346261969">"àČȘàȰàČ”àČŸàȗàČżàČČàłàČČ, àȅàČȘàł‌àČĄàł‡àČŸàł àČźàČŸàČĄàČż"</string>
     <string name="manage_applications" msgid="5400164782453975580">"àČ†àłàČŻàČȘàł àČšàČżàČ°àłàČ”àČčàČżàČžàČż"</string>
     <string name="out_of_space_dlg_title" msgid="4156690013884649502">"àČžàȂàČ—àłàȰàČčàČŁàł† àȖàČŸàČČàČż àȇàČČàłàČČ"</string>
@@ -60,16 +59,11 @@
     <string name="uninstall_update_title" msgid="824411791011583031">"àȅàČȘàł‌àČĄàł‡àČŸàł‌ àȅàČšàł‌àȇàČšàł‌àČžàłàȟàČŸàČČàł àČźàČŸàČĄàČż"</string>
     <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> àȎàȂàČŹàłàČŠàł àČ•àł†àČłàȗàČżàČš àČ†àłàČŻàČȘàł‌àČš àČ­àČŸàȗàČ”àČŸàȗàČżàČŠàł†:"</string>
     <string name="uninstall_application_text" msgid="3816830743706143980">"àČšàł€àČ”àł àȈ àČ†àłàČŻàČȘàł‌ àȅàČšàłàČšàł àȅàČšàł‌àȇàČšàł‌àČžàłàȟàČŸàČČàł‌ àČźàČŸàČĄàČČàł àČŹàČŻàČžàłàČ”àČżàȰàČŸ?"</string>
-    <!-- no translation found for archive_application_text (8482325710714386348) -->
-    <skip />
-    <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
-    <skip />
-    <!-- no translation found for archive_application_text_user (2586558895535581451) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
-    <skip />
+    <string name="archive_application_text" msgid="8482325710714386348">"àČšàČżàČźàłàČź àČ”àłˆàČŻàČ•àłàČ€àČżàȕ àČĄàł‡àȟàČŸàČ”àČšàłàČšàł àČžàł‡àČ”àł àČźàČŸàČĄàČČàČŸàČ—àłàČ€àłàČ€àČŠàł†"</string>
+    <string name="archive_application_text_all_users" msgid="3151229641681672580">"àȎàČČàłàČČàČŸ àČŹàČłàČ•àł†àČŠàČŸàȰàȰàČżàȗàČŸàȗàČż àȈ àČ†àłàČŻàČȘàł àȅàČšàłàČšàł àȆàČ°àłàČ•àłˆàČ”àł àČźàČŸàČĄàČŹàł‡àČ•àł†? àČšàČżàČźàłàČź àČ”àłˆàČŻàČ•àłàČ€àČżàȕ àČĄàł‡àȟàČŸàČ”àČšàłàČšàł àČžàł‡àČ”àł àČźàČŸàČĄàČČàČŸàČ—àłàČ€àłàČ€àČŠàł†"</string>
+    <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"àČšàČżàČźàłàČź àȉàČŠàłàČŻàł‹àȗ àČȘàłàČ°àłŠàČ«àłˆàČČàł‌àČšàČČàłàČČàČż àȈ àČ†àłàČŻàČȘàł àȅàČšàłàČšàł àȆàČ°àłàČ•àłˆàČ”àł àČźàČŸàČĄàČŹàł‡àČ•àł†? àČšàČżàČźàłàČź àČ”àłˆàČŻàČ•àłàČ€àČżàȕ àČĄàł‡àȟàČŸàČ”àČšàłàČšàł àČžàł‡àČ”àł àČźàČŸàČĄàČČàČŸàČ—àłàČ€àłàČ€àČŠàł†"</string>
+    <string name="archive_application_text_user" msgid="2586558895535581451">"àȈ àČ†àłàČŻàČȘàł àȅàČšàłàČšàł <xliff:g id="USERNAME">%1$s</xliff:g> àȗàČŸàȗàČż àȆàČ°àłàČ•àłˆàČ”àł àČźàČŸàČĄàČŹàł‡àČ•àł†? àČšàČżàČźàłàČź àČ”àłˆàČŻàČ•àłàČ€àČżàȕ àČĄàł‡àȟàČŸàČ”àČšàłàČšàł àČžàł‡àČ”àł àČźàČŸàČĄàČČàČŸàČ—àłàČ€àłàČ€àČŠàł†"</string>
+    <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"àČšàČżàČźàłàČź àȖàČŸàČžàȗàČż àČžàłàČȘàł‡àČžàł‌àČšàČżàȂàČŠ àȈ àČ†àłàČŻàČȘàł àȅàČšàłàČšàł àȆàČ°àłàČ•àłˆàČ”àł àČźàČŸàČĄàČČàł àČšàł€àČ”àł àČŹàČŻàČžàłàČ€àłàČ€àł€àȰàČŸ? àČšàČżàČźàłàČź àČ”àłˆàČŻàČ•àłàČ€àČżàȕ àČĄàł‡àȟàČŸàČ”àČšàłàČšàł àČžàł‡àČ”àł àČźàČŸàČĄàČČàČŸàČ—àłàČ€àłàČ€àČŠàł†"</string>
     <string name="uninstall_application_text_all_users" msgid="575491774380227119">"àČšàł€àČ”àł "<b>"àȎàČČàłàČČàČŸ"</b>" àČŹàČłàČ•àł†àČŠàČŸàȰàȰàČżàČ—àł‚ àȈ àČ†àłàČŻàČȘàł‌ àȅàČšàłàČšàł àȅàČšàł‌àȇàČšàł‌àČžàłàȟàČŸàČČàł‌ àČźàČŸàČĄàČČàł àČŹàČŻàČžàłàČ”àČżàȰàČŸ? àČžàČŸàȧàČšàČŠàČČàłàČČàČżàČš "<b>"àȎàČČàłàČČàČŸ"</b>" àČŹàČłàČ•àł†àČŠàČŸàȰàȰàČżàȂàČŠ àČ†àłàČŻàČȘàł‌ àČźàČ€àłàČ€àł àȅàČŠàȰ àČĄàł‡àȟàČŸàČ”àČšàłàČšàł àČ€àł†àČ—àł†àČŠàłàČčàČŸàȕàČČàČŸàČ—àłàČ”àłàČŠàł."</string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"<xliff:g id="USERNAME">%1$s</xliff:g> àČŹàČłàČ•àł†àČŠàČŸàȰàȰàČżàČ—àł† àȈ àČ†àłàČŻàČȘàł‌ àȅàČšàłàČšàł àȅàČšàł‌àȇàČšàł‌àČžàłàȟàČŸàČČàł‌ àČźàČŸàČĄàČČàł àČšàł€àČ”àł àČŹàČŻàČžàłàČ”àČżàȰàČŸ?"</string>
     <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"àČšàČżàČźàłàČź àȉàČŠàłàČŻàł‹àȗàČŠ àČȘàłàČ°àłŠàČ«àłˆàČČàł‌àČšàČżàȂàČŠ àȈ àČ†àłàČŻàČȘàł‌ àȅàČšàłàČšàł àȅàČšàł‌àȇàČšàł‌àČžàłàȟàČŸàČČàł àČźàČŸàČĄàČČàł àČšàł€àČ”àł àČŹàČŻàČžàłàČ”àČżàȰàČŸ?"</string>
@@ -108,8 +102,7 @@
     <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"àČšàČżàČźàłàČź àČŸàłàČŻàČŸàČŹàłàČČàł†àČŸàł àČčàČŸàČ—àł‚ àČ”àłˆàČŻàČ•àłàČ€àČżàȕ àČĄàł‡àȟàČŸ, àȅàČȘàȰàČżàȚàČżàČ€ àČ†àłàČŻàČȘàł‌àȗàČł àČŠàČŸàČłàČżàČ—àł† àČ€àłàČ€àłàČ€àČŸàČ—àłàČ” àČžàČŸàČ§àłàČŻàČ€àł† àČčàł†àČšàłàȚàČŸàȗàČżàČŠàł†. àȈ àČ†àłàČŻàČȘàł‌ àȅàČšàłàČšàł àȇàČšàł‌àČžàłàȟàČŸàČČàł‌ àČźàČŸàČĄàłàČ” àČźàł‚àČČàȕ, àČšàČżàČźàłàČź àČ«àł‹àČšàł‌àČ—àł† àČŻàČŸàČ”àłàČŠàł‡ àČčàČŸàČšàČż àȉàȂàȟàČŸàČŠàČ°àł† àȅàČ„àČ”àČŸ àȅàČŠàȰ àČŹàČłàČ•àł†àČŻàČżàȂàČŠ àČĄàł‡àȟàČŸ àČšàČ·àłàȟàČ”àČŸàČŠàČ°àł†, àȅàČŠàČ•àłàČ•àł† àČšàł€àČ”àł‡ àȜàČ”àČŸàČŹàłàČŠàČŸàȰàČ°àł àȎàČšàłàČšàłàČ”àłàČŠàČšàłàČšàł àȒàČȘàłàČȘàČżàČ•àłŠàČłàłàČłàłàČ€àłàČ€àł€àȰàČż."</string>
     <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"àČšàČżàČźàłàČź àȟàČżàČ”àČż àČčàČŸàČ—àł‚ àČ”àłˆàČŻàČ•àłàČ€àČżàȕ àČĄàł‡àȟàČŸ, àȅàČȘàȰàČżàȚàČżàČ€ àČ†àłàČŻàČȘàł‌àȗàČł àČŠàČŸàČłàČżàČ—àł† àČ€àłàČ€àłàČ€àČŸàČ—àłàČ” àČžàČŸàČ§àłàČŻàČ€àł† àČčàł†àČšàłàȚàČŸàȗàČżàČŠàł†. àȈ àČ†àłàČŻàČȘàł‌ àȅàČšàłàČšàł àȇàČšàł‌àČžàłàȟàČŸàČČàł‌ àČźàČŸàČĄàłàČ” àČźàł‚àČČàȕ, àČšàČżàČźàłàČź àȟàČżàČ”àČżàČ—àł† àČŻàČŸàČ”àłàČŠàł‡ àČčàČŸàČšàČż àȉàȂàȟàČŸàČŠàČ°àł† àȅàČ„àČ”àČŸ àȅàČŠàȰ àČŹàČłàČ•àł†àČŻàČżàȂàČŠ àČĄàł‡àȟàČŸ àČšàČ·àłàȟàČ”àČŸàČŠàČ°àł†, àȅàČŠàČ•àłàČ•àł† àČšàł€àČ”àł‡ àȜàČ”àČŸàČŹàłàČŠàČŸàȰàČ°àł àȎàČšàłàČšàłàČ”àłàČŠàČšàłàČšàł àȒàČȘàłàČȘàČżàČ•àłŠàČłàłàČłàłàČ€àłàČ€àł€àȰàČż."</string>
     <string name="cloned_app_label" msgid="7503612829833756160">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> àČ•àłàČČàł‹àČšàł"</string>
-    <!-- no translation found for archiving_app_label (1127085259724124725) -->
-    <skip />
+    <string name="archiving_app_label" msgid="1127085259724124725">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> àȅàČšàłàČšàł àȆàČ°àłàČ•àłˆàČ”àł àČźàČŸàČĄàČŹàł‡àČ•àł†?"</string>
     <string name="anonymous_source_continue" msgid="4375745439457209366">"àČźàłàȂàČŠàłàČ”àȰàČżàČžàČż"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"àČžàł†àČŸàłàȟàČżàȂàČ—àł‌àȗàČłàł"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"wear àČ†àłàČŻàČȘàł‌àȗàČłàČšàłàČšàł àȇàČšàł‌àČžàłàȟàČŸàČČàł‌/àȅàČšàł‌àȇàČšàł‌àČžàłàȟàČŸàČČàł‌ àČźàČŸàČĄàČČàČŸàČ—àłàČ€àłàČ€àČżàČŠàł†"</string>
diff --git a/packages/PackageInstaller/res/values-ko/strings.xml b/packages/PackageInstaller/res/values-ko/strings.xml
index 9fbff25..fbe8969 100644
--- a/packages/PackageInstaller/res/values-ko/strings.xml
+++ b/packages/PackageInstaller/res/values-ko/strings.xml
@@ -28,7 +28,7 @@
     <string name="install_confirm_question_update" msgid="3348888852318388584">"읎 앱을 업데읎튞하시êČ ìŠ”ë‹ˆêčŒ?"</string>
     <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="7994800761970572198">"&lt;p&gt;&lt;b&gt;<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>에서&lt;/b&gt;?&lt;/p&gt;&lt;p&gt;읎 앱을 업데읎튞하섞요. 읎 앱은 볎톔&lt;b&gt;<xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>&lt;/b&gt;에서 업데읎튞넌 받슔니닀. 닀넞 출ìČ˜ì—ì„œ 업데읎튞넌 받윌멎 햄후 íƒœëž”ëŠżì— 있는 ì–Žë–€ 출ìČ˜ì—ì„œë“ ì§€ 업데읎튞넌 받을 수 있슔니닀. 앱 Ʞ늄읎 변êČœë  수 있슔니닀.&lt;/p&gt;"</string>
     <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="2435174886412089791">"&lt;p&gt;&lt;b&gt;<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>에서&lt;/b&gt;?&lt;/p&gt;&lt;p&gt;읎 앱을 업데읎튞하섞요. 읎 앱은 볎톔&lt;b&gt;<xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>&lt;/b&gt;에서 업데읎튞넌 받슔니닀. 닀넞 출ìČ˜ì—ì„œ 앱을 업데읎튞하멎 햄후 TV에 있는 ì–Žë–€ 출ìČ˜ì—ì„œë“ ì§€ 업데읎튞넌 받을 수 있슔니닀. 앱 Ʞ늄읎 변êČœë  수 있슔니닀.&lt;/p&gt;"</string>
-    <string name="install_confirm_question_update_owner_reminder" product="default" msgid="7155138616126795839">"&lt;p&gt;&lt;b&gt;<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>에서&lt;/b&gt;?&lt;/p&gt;&lt;p&gt;읎 앱을 업데읎튞하섞요. 읎 앱은 볎톔&lt;b&gt;<xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>&lt;/b&gt;에서 업데읎튞넌 받슔니닀. 닀넞 출ìČ˜ì—ì„œ 업데읎튞넌 받윌멎 햄후 휎대전화에 있는 ì–Žë–€ 출ìČ˜ë“ ì§€ 업데읎튞넌 받을 수 있슔니닀. 앱 Ʞ늄읎 변êČœë  수 있슔니닀.&lt;/p&gt;"</string>
+    <string name="install_confirm_question_update_owner_reminder" product="default" msgid="7155138616126795839">"&lt;p&gt;&lt;b&gt;<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>&lt;/b&gt;에서 읎 앱을 업데읎튞하시êČ ì–Žìš”?&lt;/p&gt;&lt;p&gt;읎 앱은 볎톔 &lt;b&gt;<xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>&lt;/b&gt;에서 업데읎튞넌 받슔니닀. 닀넞 출ìČ˜ì—ì„œ 앱을 업데읎튞하멎 햄후 휎대전화에 있는 ì–Žë–€ 출ìČ˜ì—ì„œë‚˜ 업데읎튞넌 받을 수 있윌며, 앱 Ʞ늄읎 변êČœë  수 있슔니닀.&lt;/p&gt;"</string>
     <string name="install_failed" msgid="5777824004474125469">"앱읎 ì„€ìč˜ë˜ì§€ 않았슔니닀."</string>
     <string name="install_failed_blocked" msgid="8512284352994752094">"팚킀지 ì„€ìč˜ê°€ 찚닚되었슔니닀."</string>
     <string name="install_failed_conflict" msgid="3493184212162521426">"팚킀지가 êž°ìĄŽ 팚킀지와 ì¶©ëŒí•˜ì—Ź 앱읎 ì„€ìč˜ë˜ì§€ 않았슔니닀."</string>
@@ -44,8 +44,7 @@
     <string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"읎 ì‚Źìš©ìžëŠ” 알 수 없는 앱을 ì„€ìč˜í•  수 없슔니닀."</string>
     <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"읎 ì‚Źìš©ìžëŠ” 앱을 ì„€ìč˜í•  권한읎 없슔니닀."</string>
     <string name="ok" msgid="7871959885003339302">"확읞"</string>
-    <!-- no translation found for archive (4447791830199354721) -->
-    <skip />
+    <string name="archive" msgid="4447791830199354721">"ëłŽêŽ€ìȘ늏"</string>
     <string name="update_anyway" msgid="8792432341346261969">"업데읎튞"</string>
     <string name="manage_applications" msgid="5400164782453975580">"앱 êŽ€ëŠŹ"</string>
     <string name="out_of_space_dlg_title" msgid="4156690013884649502">"ì—Źìœ  êł”ê°„ìŽ 없음"</string>
@@ -60,16 +59,11 @@
     <string name="uninstall_update_title" msgid="824411791011583031">"업데읎튞 제거"</string>
     <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g>은(는) 닀음 앱의 음부입니닀."</string>
     <string name="uninstall_application_text" msgid="3816830743706143980">"읎 앱을 제거하시êČ ìŠ”ë‹ˆêčŒ?"</string>
-    <!-- no translation found for archive_application_text (8482325710714386348) -->
-    <skip />
-    <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
-    <skip />
-    <!-- no translation found for archive_application_text_user (2586558895535581451) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
-    <skip />
+    <string name="archive_application_text" msgid="8482325710714386348">"낮 개읞 ì •ëłŽê°€ 저임됩니닀."</string>
+    <string name="archive_application_text_all_users" msgid="3151229641681672580">"ëȘšë“  ì‚Źìš©ìžë„Œ ëŒ€ìƒìœŒëĄœ 읎 앱을 ëłŽêŽ€ìČ˜ëŠŹí•˜ì‹œêČ ìŠ”ë‹ˆêčŒ? 낮 개읞 ì •ëłŽê°€ 저임됩니닀."</string>
+    <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"직임 í”„ëĄœí•„ì—ì„œ 읎 앱을 ëłŽêŽ€ìČ˜ëŠŹí•˜ì‹œêČ ìŠ”ë‹ˆêčŒ? 낮 개읞 ì •ëłŽê°€ 저임됩니닀."</string>
+    <string name="archive_application_text_user" msgid="2586558895535581451">"<xliff:g id="USERNAME">%1$s</xliff:g>님을 ëŒ€ìƒìœŒëĄœ 읎 앱을 ëłŽêŽ€ìČ˜ëŠŹí•˜ì‹œêČ ìŠ”ë‹ˆêčŒ? 낮 개읞 ì •ëłŽê°€ 저임됩니닀."</string>
+    <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"ëč„êł”개 슀페읎슀에서 읎 앱을 ëłŽêŽ€ìČ˜ëŠŹí•˜ì‹œêČ ìŠ”ë‹ˆêčŒ? 낮 개읞 ì •ëłŽê°€ 저임됩니닀."</string>
     <string name="uninstall_application_text_all_users" msgid="575491774380227119"><b>"ëȘšë“ "</b>" ì‚Źìš©ìžì— 대핮 읎 앱을 제거하시êČ ìŠ”ë‹ˆêčŒ? ꞰꞰ넌 ì‚Źìš©í•˜ëŠ” "<b>"ëȘšë“ "</b>" ì‚Źìš©ìžì— 대핮 ì• í”ŒëŠŹìŒ€ìŽì…˜ 및 데읎터가 삭제됩니닀."</string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"<xliff:g id="USERNAME">%1$s</xliff:g>님의 ꞰꞰ에 ì„€ìč˜ëœ 앱을 제거하시êČ ìŠ”ë‹ˆêčŒ?"</string>
     <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"직임 í”„ëĄœí•„ì—ì„œ 읎 앱을 제거하시êČ ìŠ”ë‹ˆêčŒ?"</string>
@@ -108,8 +102,7 @@
     <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"íƒœëž”ëŠżêłŒ 개읞 데읎터는 알 수 없는 앱의 êł”êČ©ì— 더욱 췚앜합니닀. 읎 앱을 ì„€ìč˜í•˜ë©Ž 앱 ì‚Źìš©ìœŒëĄœ 읞핎 발생할 수 있는 ëȘšë“  태뾔멿 손상읎나 데읎터 손싀에 ì‚Źìš©ìžê°€ 책임을 진닀는 êČƒì— 동의하êȌ 됩니닀."</string>
     <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"TV와 개읞 데읎터는 알 수 없는 앱의 êł”êČ©ì— 더욱 췚앜합니닀. 읎 앱을 ì„€ìč˜í•˜ë©Ž 앱 ì‚Źìš©ìœŒëĄœ 읞핎 발생할 수 있는 ëȘšë“  TV 손상읎나 데읎터 손싀에 ì‚Źìš©ìžê°€ 책임을 진닀는 êČƒì— 동의하êȌ 됩니닀."</string>
     <string name="cloned_app_label" msgid="7503612829833756160">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ëł”ì œ"</string>
-    <!-- no translation found for archiving_app_label (1127085259724124725) -->
-    <skip />
+    <string name="archiving_app_label" msgid="1127085259724124725">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> 앱을 ëłŽêŽ€ìČ˜ëŠŹí•˜ì‹œêČ ìŠ”ë‹ˆêčŒ?"</string>
     <string name="anonymous_source_continue" msgid="4375745439457209366">"êł„ì†"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"섀정"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Wear 앱 ì„€ìč˜/제거"</string>
diff --git a/packages/PackageInstaller/res/values-ky/strings.xml b/packages/PackageInstaller/res/values-ky/strings.xml
index b6d3e21..8b2d425 100644
--- a/packages/PackageInstaller/res/values-ky/strings.xml
+++ b/packages/PackageInstaller/res/values-ky/strings.xml
@@ -44,8 +44,7 @@
     <string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Đ‘ŃƒĐ» ĐșĐŸĐ»ĐŽĐŸĐœŃƒŃƒŃ‡Ńƒ бДлгОсОз ĐșĐŸĐ»ĐŽĐŸĐœĐŒĐŸĐ»ĐŸŃ€ĐŽŃƒ ĐŸŃ€ĐœĐŸŃ‚ĐŸ албаĐčт"</string>
     <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Đ‘ŃƒĐ» ĐșĐŸĐ»ĐŽĐŸĐœŃƒŃƒŃ‡Ńƒ ĐșĐŸĐ»ĐŽĐŸĐœĐŒĐŸĐ»ĐŸŃ€ĐŽŃƒ ĐŸŃ€ĐœĐŸŃ‚ĐŸ албаĐčт"</string>
     <string name="ok" msgid="7871959885003339302">"ЖАРАЙб"</string>
-    <!-- no translation found for archive (4447791830199354721) -->
-    <skip />
+    <string name="archive" msgid="4447791830199354721">"АрхоĐČĐŽÓ©Ó©"</string>
     <string name="update_anyway" msgid="8792432341346261969">"Баары бОр Đ¶Đ°ÒŁŃ‹Ń€Ń‚Ń‹Đ»ŃŃ‹Đœ"</string>
     <string name="manage_applications" msgid="5400164782453975580">"ĐšĐŸĐ»ĐŽ. башĐșаруу"</string>
     <string name="out_of_space_dlg_title" msgid="4156690013884649502">"Đ‘ĐŸŃˆ ĐŸŃ€ŃƒĐœ Đ¶ĐŸĐș"</string>
@@ -60,16 +59,11 @@
     <string name="uninstall_update_title" msgid="824411791011583031">"Đ–Đ°ÒŁŃ‹Ń€Ń‚ŃƒŃƒĐœŃƒ чыгарып салуу"</string>
     <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> Ń‚Ó©ĐŒÓ©ĐœĐșÒŻ ĐșĐŸĐ»ĐŽĐŸĐœĐŒĐŸĐœŃƒĐœ Đ±Ó©Đ»ÒŻĐłÒŻ:"</string>
     <string name="uninstall_application_text" msgid="3816830743706143980">"Đ‘ŃƒĐ» ĐșĐŸĐ»ĐŽĐŸĐœĐŒĐŸĐœŃƒ чыгарып саласызбы?"</string>
-    <!-- no translation found for archive_application_text (8482325710714386348) -->
-    <skip />
-    <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
-    <skip />
-    <!-- no translation found for archive_application_text_user (2586558895535581451) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
-    <skip />
+    <string name="archive_application_text" msgid="8482325710714386348">"Đ–Đ”ĐșĐ” ĐŒĐ°Đ°Đ»Ń‹ĐŒĐ°Ń‚Ń‹ÒŁŃ‹Đ· саĐșталат"</string>
+    <string name="archive_application_text_all_users" msgid="3151229641681672580">"Đ‘ŃƒĐ» ĐșĐŸĐ»ĐŽĐŸĐœĐŒĐŸ барЎыĐș ĐșĐŸĐ»ĐŽĐŸĐœŃƒŃƒŃ‡ŃƒĐ»Đ°Ń€ ÒŻŃ‡ÒŻĐœ архоĐČĐŽĐ”Đ»ŃĐžĐœĐ±Đž? Đ–Đ”ĐșĐ” ĐŒĐ°Đ°Đ»Ń‹ĐŒĐ°Ń‚Ń‹ÒŁŃ‹Đ· саĐșталат"</string>
+    <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Đ‘ŃƒĐ» ĐșĐŸĐ»ĐŽĐŸĐœĐŒĐŸ Đ¶ŃƒĐŒŃƒŃˆ ĐżŃ€ĐŸŃ„ĐžĐ»ĐžÒŁĐžĐ·ĐŽĐ” архоĐČĐŽĐ”Đ»ŃĐžĐœĐ±Đž? Đ–Đ”ĐșĐ” ĐŒĐ°Đ°Đ»Ń‹ĐŒĐ°Ń‚Ń‹ÒŁŃ‹Đ· саĐșталат"</string>
+    <string name="archive_application_text_user" msgid="2586558895535581451">"Đ‘ŃƒĐ» ĐșĐŸĐ»ĐŽĐŸĐœĐŒĐŸ <xliff:g id="USERNAME">%1$s</xliff:g> ÒŻŃ‡ÒŻĐœ архоĐČĐŽĐ”Đ»ŃĐžĐœĐ±Đž? Đ–Đ”ĐșĐ” ĐŒĐ°Đ°Đ»Ń‹ĐŒĐ°Ń‚Ń‹ÒŁŃ‹Đ· саĐșталат"</string>
+    <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Đ‘ŃƒĐ» ĐșĐŸĐ»ĐŽĐŸĐœĐŒĐŸ жДĐșĐ” чөĐčŃ€Ó©ĐŽÓ©Đœ архоĐČĐŽĐ”Đ»ŃĐžĐœĐ±Đž? Đ–Đ”ĐșĐ” ĐŒĐ°Đ°Đ»Ń‹ĐŒĐ°Ń‚Ń‹ÒŁŃ‹Đ· саĐșталат"</string>
     <string name="uninstall_application_text_all_users" msgid="575491774380227119">"Đ‘ŃƒĐ» ĐșĐŸĐ»ĐŽĐŸĐœĐŒĐŸ "<b>"барЎыĐș"</b>" ĐșĐŸĐ»ĐŽĐŸĐœŃƒŃƒŃ‡ŃƒĐ»Đ°Ń€ĐŽĐ°Đœ Đ°Đ»Ń‹ĐœŃ‹Đż ŃĐ°Đ»Ń‹ĐœŃŃ‹ĐœĐ±Ń‹? Đ‘ŃƒĐ» ĐșĐŸĐ»ĐŽĐŸĐœĐŒĐŸ Đ¶Đ°ĐœĐ° Đ°ĐœŃ‹Đœ ЎаĐčŃ‹ĐœĐŽĐ°Ń€Ń‹ бул Ń‚ÒŻĐ·ĐŒÓ©ĐșŃ‚ÒŻĐœ "<b>"барЎыĐș"</b>" ĐșĐŸĐ»ĐŽĐŸĐœŃƒŃƒŃ‡ŃƒĐ»Đ°Ń€Ń‹ĐœĐ°Đœ Ó©Ń‡ÒŻŃ€ÒŻĐ»Ó©Ń‚."</string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Đ‘ŃƒĐ» ĐșĐŸĐ»ĐŽĐŸĐœĐŒĐŸĐœŃƒ <xliff:g id="USERNAME">%1$s</xliff:g> ÒŻŃ‡ÒŻĐœ чыгарып саласызбы?"</string>
     <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Đ‘ŃƒĐ» ĐșĐŸĐ»ĐŽĐŸĐœĐŒĐŸĐœŃƒ Đ¶ŃƒĐŒŃƒŃˆ ĐżŃ€ĐŸŃ„ĐžĐ»ĐžÒŁĐžĐ·ĐŽĐ”Đœ чыгарып саласызбы?"</string>
@@ -108,8 +102,7 @@
     <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"ĐŸĐ»Đ°ĐœŃˆĐ”Ń‚ĐžÒŁĐžĐ· Đ¶Đ°ĐœĐ° жДĐșĐ” ЎаĐčŃ‹Đœ-ЎарДĐșŃ‚Đ”Ń€ĐžÒŁĐžĐ· бДлгОсОз ĐșĐŸĐ»ĐŽĐŸĐœĐŒĐŸĐ»ĐŸŃ€ĐŽĐŸĐœ Đ·Ń‹ŃĐœ тартып ĐșĐ°Đ»Ń‹ŃˆŃ‹ ĐŒÒŻĐŒĐșÒŻĐœ. Đ‘ŃƒĐ» ĐșĐŸĐ»ĐŽĐŸĐœĐŒĐŸĐœŃƒ ĐŸŃ€ĐœĐŸŃ‚ŃƒĐż, Đ°ĐœŃ‹ паĐčĐŽĐ°Đ»Đ°ĐœŃƒŃƒĐŽĐ°Đœ ŃƒĐ»Đ°ĐŒ ĐżĐ»Đ°ĐœŃˆĐ”Ń‚ĐžÒŁĐžĐ·ĐłĐ” ĐșĐ°ĐœĐŽĐ°ĐčЮыр бОр Đ·Ń‹ŃĐœ ĐșДлтОрОлсД жД ЎаĐčŃ‹Đœ-ЎарДĐșŃ‚Đ”Ń€ĐžÒŁĐžĐ·ĐŽŃ‹ Đ¶ĐŸĐłĐŸŃ‚ŃƒĐż Đ°Đ»ŃĐ°ÒŁŃ‹Đ·, Ó©Đ·ÒŻÒŁÒŻĐ· Đ¶ĐŸĐŸĐżŃ‚ŃƒŃƒ Đ±ĐŸĐ»ĐŸŃŃƒĐ·."</string>
     <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"ĐĄŃ‹ĐœĐ°Đ»ĐłŃ‹ÒŁŃ‹Đ· Đ¶Đ°ĐœĐ° жДĐșĐ” ЎаĐčŃ‹Đœ-ЎарДĐșŃ‚Đ”Ń€ĐžÒŁĐžĐ· бДлгОсОз ĐșĐŸĐ»ĐŽĐŸĐœĐŒĐŸĐ»ĐŸŃ€ĐŽĐŸĐœ Đ·Ń‹ŃĐœ тартып ĐșĐ°Đ»Ń‹ŃˆŃ‹ ĐŒÒŻĐŒĐșÒŻĐœ. Đ‘ŃƒĐ» ĐșĐŸĐ»ĐŽĐŸĐœĐŒĐŸĐœŃƒ ĐŸŃ€ĐœĐŸŃ‚ŃƒĐż, Đ°ĐœŃ‹ паĐčĐŽĐ°Đ»Đ°ĐœŃƒŃƒĐŽĐ°Đœ ŃƒĐ»Đ°ĐŒ ŃŃ‹ĐœĐ°Đ»ĐłŃ‹ÒŁŃ‹Đ·ĐłĐ° ĐșĐ°ĐœĐŽĐ°ĐčЮыр бОр Đ·Ń‹ŃĐœ ĐșДлтОрОлсД жД ЎаĐčŃ‹Đœ-ЎарДĐșŃ‚Đ”Ń€ĐžÒŁĐžĐ·ĐŽŃ‹ Đ¶ĐŸĐłĐŸŃ‚ŃƒĐż Đ°Đ»ŃĐ°ÒŁŃ‹Đ·, Ó©Đ·ÒŻÒŁÒŻĐ· Đ¶ĐŸĐŸĐżŃ‚ŃƒŃƒ Đ±ĐŸĐ»ĐŸŃŃƒĐ·."</string>
     <string name="cloned_app_label" msgid="7503612829833756160">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ĐșĐ»ĐŸĐœŃƒ"</string>
-    <!-- no translation found for archiving_app_label (1127085259724124725) -->
-    <skip />
+    <string name="archiving_app_label" msgid="1127085259724124725">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> архоĐČĐŽĐ”Đ»ŃĐžĐœĐ±Đž?"</string>
     <string name="anonymous_source_continue" msgid="4375745439457209366">"ĐŁĐ»Đ°ĐœŃ‚ŃƒŃƒ"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"ĐŸĐ°Ń€Đ°ĐŒĐ”Ń‚Ń€Đ»Đ”Ń€"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"ĐąĐ°ĐłŃ‹ĐœĐŒĐ° ĐșĐŸĐ»ĐŽĐŸĐœĐŒĐŸĐ»ĐŸŃ€ĐŽŃƒ ĐŸŃ€ĐœĐŸŃ‚ŃƒŃƒ/чыгаруу"</string>
diff --git a/packages/PackageInstaller/res/values-lo/strings.xml b/packages/PackageInstaller/res/values-lo/strings.xml
index d9b33ff..8a95398 100644
--- a/packages/PackageInstaller/res/values-lo/strings.xml
+++ b/packages/PackageInstaller/res/values-lo/strings.xml
@@ -44,8 +44,7 @@
     <string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"àșœàșč້ໃàșŠà»‰àș™àș”້àșšà»à»ˆàșȘàșČàșĄàșČàș”àș•àșŽàș”àș•àș±à»‰àș‡à»àș­àș±àșšàș—àș”່àșšà»à»ˆàșźàșč້àșˆàș±àșà»„àș”້"</string>
     <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"àșœàșč້ໃàșŠà»‰àș™àș”້àșšà»à»ˆà»„àș”້àșźàș±àșšàș­àș°àș™àșžàșàșČàș”ໃàș«à»‰àș•àșŽàș”àș•àș±à»‰àș‡à»àș­àș±àșšà»„àș”້"</string>
     <string name="ok" msgid="7871959885003339302">"àș•àș»àșàș„àș»àș‡"</string>
-    <!-- no translation found for archive (4447791830199354721) -->
-    <skip />
+    <string name="archive" msgid="4447791830199354721">"ເàșàș±àșšà»„àș§à»‰à»ƒàș™à»àșŸà»‰àșĄ"</string>
     <string name="update_anyway" msgid="8792432341346261969">"àșąàș·àș™àșąàș±àș™àșàșČàș™àș­àș±àșšà»€àș”àș”"</string>
     <string name="manage_applications" msgid="5400164782453975580">"àșˆàș±àș”àșàșČàș™à»àș­àș±àșš"</string>
     <string name="out_of_space_dlg_title" msgid="4156690013884649502">"àșžàș·à»‰àș™àș—àș”່àș«àș§à»ˆàșČàș‡àșšà»à»ˆàșžàșœàș‡àșžà»"</string>
@@ -60,16 +59,11 @@
     <string name="uninstall_update_title" msgid="824411791011583031">"àș–àș­àș™àșàșČàș™àș•àșŽàș”àș•àș±à»‰àș‡àș­àș±àșšà»€àș”àș”"</string>
     <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> ແàșĄà»ˆàș™â€‹àșȘ່àș§àș™â€‹à»œàș¶à»ˆàș‡àș‚àș­àș‡à»àș­àș±àșšàș•ໍ່ໄàș›àș™àș”້:"</string>
     <string name="uninstall_application_text" msgid="3816830743706143980">"àș—່àșČàș™àș•້àș­àș‡àșàșČàș™àș–àș­àș™àșàșČàș™àș•àșŽàș”àș•àș±à»‰àș‡à»àș­àș±àșšàș™àș”້àșšà»à»ˆ?"</string>
-    <!-- no translation found for archive_application_text (8482325710714386348) -->
-    <skip />
-    <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
-    <skip />
-    <!-- no translation found for archive_application_text_user (2586558895535581451) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
-    <skip />
+    <string name="archive_application_text" msgid="8482325710714386348">"àș„àș°àșšàș»àșšàșˆàș°àșšàș±àș™àș—àș¶àșàș‚ໍ້àșĄàșčàș™àșȘ່àș§àș™àș•àș»àș§àș‚àș­àș‡àș—່àșČàș™à»„àș§à»‰"</string>
+    <string name="archive_application_text_all_users" msgid="3151229641681672580">"ເàșàș±àșšà»àș­àș±àșšàș™àș”້ໄàș§à»‰à»ƒàș™à»àșŸà»‰àșĄàșȘàșłàș„àș±àșšàșœàșč້ໃàșŠà»‰àș—àșžàșàș„àș»àș™àșšà»? àș„àș°àșšàș»àșšàșˆàș°àșšàș±àș™àș—àș¶àșàș‚ໍ້àșĄàșčàș™àșȘ່àș§àș™àș•àș»àș§àș‚àș­àș‡àș—່àșČàș™à»„àș§à»‰"</string>
+    <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"ເàșàș±àșšà»àș­àș±àșšàș™àș”້ໄàș§à»‰à»ƒàș™à»àșŸà»‰àșĄàșąàșč່ໂàș›àșŁà»„àșŸàș„໌àșšà»ˆàș­àș™à»€àșźàș±àș”àș§àșœàșàș‚àș­àș‡àș—່àșČàș™àșšà»? àș„àș°àșšàș»àșšàșˆàș°àșšàș±àș™àș—àș¶àșàș‚ໍ້àșĄàșčàș™àșȘ່àș§àș™àș•àș»àș§àș‚àș­àș‡àș—່àșČàș™à»„àș§à»‰"</string>
+    <string name="archive_application_text_user" msgid="2586558895535581451">"ເàșàș±àșšà»àș­àș±àșšàș™àș”້ໄàș§à»‰à»ƒàș™à»àșŸà»‰àșĄàșȘàșłàș„àș±àșš <xliff:g id="USERNAME">%1$s</xliff:g> àșšà»? àș„àș°àșšàș»àșšàșˆàș°àșšàș±àș™àș—àș¶àșàș‚ໍ້àșĄàșčàș™àșȘ່àș§àș™àș•àș»àș§àș‚àș­àș‡àș—່àșČàș™à»„àș§à»‰"</string>
+    <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"àș—່àșČàș™àș•້àș­àș‡àșàșČàș™à»€àșàș±àșšà»àș­àș±àșšàș™àș”້ໄàș§à»‰à»ƒàș™à»àșŸà»‰àșĄà»àșàșàșˆàșČàșàșžàș·à»‰àș™àș—àș”່àșȘ່àș§àș™àș•àș»àș§àș‚àș­àș‡àș—່àșČàș™àșšà»? àș„àș°àșšàș»àșšàșˆàș°àșšàș±àș™àș—àș¶àșàș‚ໍ້àșĄàșčàș™àșȘ່àș§àș™àș•àș»àș§àș‚àș­àș‡àș—່àșČàș™à»„àș§à»‰"</string>
     <string name="uninstall_application_text_all_users" msgid="575491774380227119">"àș—່àșČàș™àș•້àș­àș‡àșàșČàș™àș—àș”່àșˆàș°àș–àș­àș™àșàșČàș™àș•àșŽàș”àș•àș±à»‰àș‡à»àș­àș±àșšàș™àș”້àșȘàșłàș„àș±àș‡àșœàșč້ໃàșŠà»‰"<b>"àș—àșžàșàș„àș»àș™"</b>"àșšà»à»ˆ? ແàș­àș±àșšàșžàș„àșŽà»€àș„àșŠàș±àș™ ແàș„àș° àș‚ໍ້àșĄàșčàș™àș‚àș­àș‡àșĄàș±àș™àșˆàș°àș–àș·àșàș„àș¶àșšàș­àș­àșàșˆàșČàșàșœàșč້ໃàșŠà»‰"<b>"àș—àș±àș‡à»àș»àș”"</b>"ໃàș™àș­àșžàș›àș°àșàș­àș™àș™àș”້."</string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"àș—່àșČàș™â€‹àș•້àș­àș‡â€‹àșàșČàș™â€‹àș–àș­àș™â€‹àșàșČàș™â€‹àș•àșŽàș”​àș•àș±à»‰àș‡â€‹à»àș­àș±àșšâ€‹àș™àș”້​àșȘàșłâ€‹àș„àș±àșšâ€‹àșœàșč້​ໃàșŠà»‰ <xliff:g id="USERNAME">%1$s</xliff:g> àșšà»à»ˆ?"</string>
     <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"àș—່àșČàș™àș•້àș­àș‡àșàșČàș™àș–àș­àș™àșàșČàș™àș•àșŽàș”àș•àș±à»‰àș‡à»àș­àș±àșšàș™àș”້àșˆàșČàșâ€‹à»‚àș›àșŁâ€‹à»„àșŸàș„໌​àșšà»ˆàș­àș™â€‹à»€àșźàș±àș”​àș§àșœàșàșšà»?"</string>
@@ -108,8 +102,7 @@
     <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"ແàș—àș±àșšà»€àș„àș±àș” ແàș„àș° àș‚ໍ້àșĄàșčàș™àșȘ່àș§àș™àș•àș»àș§àș‚àș­àș‡àș—່àșČàș™àș­àșČàș”àș–àș·àșà»‚àșˆàșĄàș•àș”ໄàș”້ໂàș”àșà»àș­àș±àșšàș—àș”່àșšà»à»ˆàșźàșč້àșˆàș±àș. ໂàș”àșàșàșČàș™àș•àșŽàș”àș•àș±à»‰àș‡à»àș­àș±àșšàș™àș”້, ແàșĄà»ˆàș™àș—່àșČàș™àșàș­àșĄàșźàș±àșšàș§à»ˆàșČàș—່àșČàș™àșˆàș°àșźàș±àșšàșœàșŽàș”àșŠàș­àșšàș•ໍ່àș„àș§àșČàșĄà»€àșȘàșàș«àșČàșà»ƒàș”ໆàșà»àș•àșČàșĄàș—àș”່ເàșàș”àș”àș‚àș¶à»‰àș™àș•ໍ່ໂàș—àș„àș°àș—àș±àș”àș‚àș­àș‡àș—່àșČàș™ àș«àșŒàș· àșàșČàș™àșȘàșčàș™à»€àșȘàșàș‚ໍ້àșĄàșčàș™àș—àș”່àș­àșČàș”ເàșàș”àș”àșˆàșČàșàșàșČàș™àș™àșłà»ƒàșŠà»‰àșĄàș±àș™."</string>
     <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"ໂàș—àș„àș°àș—àș±àș” ແàș„àș° àș‚ໍ້àșĄàșčàș™àșȘ່àș§àș™àș•àș»àș§àș‚àș­àș‡àș—່àșČàș™àș­àșČàș”àș–àș·àșà»‚àșˆàșĄàș•àș”ໄàș”້ໂàș”àșà»àș­àș±àșšàș—àș”່àșšà»à»ˆàșźàșč້àșˆàș±àș. ໂàș”àșàșàșČàș™àș•àșŽàș”àș•àș±à»‰àș‡à»àș­àș±àșšàș™àș”້, ແàșĄà»ˆàș™àș—່àșČàș™àșàș­àșĄàșźàș±àșšàș§à»ˆàșČàș—່àșČàș™àșˆàș°àșźàș±àșšàșœàșŽàș”àșŠàș­àșšàș•ໍ່àș„àș§àșČàșĄà»€àșȘàșàș«àșČàșà»ƒàș”ໆàșà»àș•àșČàșĄàș—àș”່ເàșàș”àș”àș‚àș¶à»‰àș™àș•ໍ່ໂàș—àș„àș°àș—àș±àș”àș‚àș­àș‡àș—່àșČàș™ àș«àșŒàș· àșàșČàș™àșȘàșčàș™à»€àșȘàșàș‚ໍ້àșĄàșčàș™àș—àș”່àș­àșČàș”ເàșàș”àș”àșˆàșČàșàșàșČàș™àș™àșłà»ƒàșŠà»‰àșĄàș±àș™."</string>
     <string name="cloned_app_label" msgid="7503612829833756160">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ໂàș„àș„àș™"</string>
-    <!-- no translation found for archiving_app_label (1127085259724124725) -->
-    <skip />
+    <string name="archiving_app_label" msgid="1127085259724124725">"ເàșàș±àșš <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ໄàș§à»‰à»ƒàș™à»àșŸà»‰àșĄàșšà»?"</string>
     <string name="anonymous_source_continue" msgid="4375745439457209366">"àșȘàș·àșšàș•ໍ່"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"àșàșČàș™àș•àș±à»‰àș‡àș„່àșČ"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"àșàșČàș™àș•àșŽàș”àș•àș±à»‰àș‡/àș–àș­àș™àșàșČàș™àș•àșŽàș”àș•àș±à»‰àș‡à»àș­àș±àșš Wear"</string>
diff --git a/packages/PackageInstaller/res/values-lt/strings.xml b/packages/PackageInstaller/res/values-lt/strings.xml
index b5ae480..47778ec 100644
--- a/packages/PackageInstaller/res/values-lt/strings.xml
+++ b/packages/PackageInstaller/res/values-lt/strings.xml
@@ -44,8 +44,7 @@
     <string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Šis naudotojas negali diegti neĆŸinomĆł programĆł"</string>
     <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Šiam naudotojui neleidĆŸiama diegti programĆł"</string>
     <string name="ok" msgid="7871959885003339302">"Gerai"</string>
-    <!-- no translation found for archive (4447791830199354721) -->
-    <skip />
+    <string name="archive" msgid="4447791830199354721">"Archyvuoti"</string>
     <string name="update_anyway" msgid="8792432341346261969">"Vis tiek atnaujinti"</string>
     <string name="manage_applications" msgid="5400164782453975580">"Tvark. progr."</string>
     <string name="out_of_space_dlg_title" msgid="4156690013884649502">"Nėra vietos"</string>
@@ -60,16 +59,11 @@
     <string name="uninstall_update_title" msgid="824411791011583031">"Pašalinti naujinÄŻ"</string>
     <string name="uninstall_activity_text" msgid="1928194674397770771">"Veikla „<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g>“ yra toliau nurodytos programos dalis."</string>
     <string name="uninstall_application_text" msgid="3816830743706143980">"Ar norite pašalinti šią programą?"</string>
-    <!-- no translation found for archive_application_text (8482325710714386348) -->
-    <skip />
-    <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
-    <skip />
-    <!-- no translation found for archive_application_text_user (2586558895535581451) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
-    <skip />
+    <string name="archive_application_text" msgid="8482325710714386348">"JĆ«sĆł asmens duomenys bus išsaugoti"</string>
+    <string name="archive_application_text_all_users" msgid="3151229641681672580">"Archyvuoti šią programą visiems naudotojams? JĆ«sĆł asmens duomenys bus išsaugoti"</string>
+    <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Archyvuoti šią programą darbo profilyje? JĆ«sĆł asmens duomenys bus išsaugoti"</string>
+    <string name="archive_application_text_user" msgid="2586558895535581451">"Archyvuoti šią programą naudotojui (-ai) (<xliff:g id="USERNAME">%1$s</xliff:g>)? JĆ«sĆł asmens duomenys bus išsaugoti"</string>
+    <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Ar norite archyvuoti šią programą iš privačios erdvės? JĆ«sĆł asmens duomenys bus išsaugoti"</string>
     <string name="uninstall_application_text_all_users" msgid="575491774380227119">"Ar norite pašalinti šią programą "<b>"visiems"</b>" naudotojams? Programa ir jos duomenys bus pašalinti "<b>"visiems"</b>" ÄŻrenginio naudotojams."</string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Ar norite pašalinti šią naudotojo <xliff:g id="USERNAME">%1$s</xliff:g> programą?"</string>
     <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Ar norite pašalinti šią programą iš savo darbo profilio?"</string>
@@ -108,8 +102,7 @@
     <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Planšetinis kompiuteris ir asmens duomenys labiau paĆŸeidĆŸiami ÄŻdiegus neĆŸinomĆł programĆł. Äźdiegdami šią programą sutinkate, kad patys esate atsakingi uĆŸ ĆŸalą planšetiniam kompiuteriui arba prarastus duomenis dėl šios programos naudojimo."</string>
     <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"TV ir asmens duomenys labiau paĆŸeidĆŸiami ÄŻdiegus neĆŸinomĆł programĆł. Äźdiegdami šią programą sutinkate, kad patys esate atsakingi uĆŸ ĆŸalą TV arba prarastus duomenis dėl šios programos naudojimo."</string>
     <string name="cloned_app_label" msgid="7503612829833756160">"„<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>“ kopija"</string>
-    <!-- no translation found for archiving_app_label (1127085259724124725) -->
-    <skip />
+    <string name="archiving_app_label" msgid="1127085259724124725">"Archyvuoti „<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>“?"</string>
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Tęsti"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Nustatymai"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Äźdiegiamos / pašalinamos „Wear“ program."</string>
diff --git a/packages/PackageInstaller/res/values-lv/strings.xml b/packages/PackageInstaller/res/values-lv/strings.xml
index 61b5392..b129706 100644
--- a/packages/PackageInstaller/res/values-lv/strings.xml
+++ b/packages/PackageInstaller/res/values-lv/strings.xml
@@ -44,8 +44,7 @@
     <string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Šis lietotājs nevar instalēt nezināmas lietotnes"</string>
     <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Šim lietotājam nav atÄŒauts instalēt lietotnes"</string>
     <string name="ok" msgid="7871959885003339302">"Labi"</string>
-    <!-- no translation found for archive (4447791830199354721) -->
-    <skip />
+    <string name="archive" msgid="4447791830199354721">"Arhivēt"</string>
     <string name="update_anyway" msgid="8792432341346261969">"Tik un tā atjaunināt"</string>
     <string name="manage_applications" msgid="5400164782453975580">"Pārv. lietotnes"</string>
     <string name="out_of_space_dlg_title" msgid="4156690013884649502">"Nav brīvas vietas"</string>
@@ -60,16 +59,11 @@
     <string name="uninstall_update_title" msgid="824411791011583031">"Atinstalēt atjauninājumu"</string>
     <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> ir daÄŒa no šÄdas lietotnes:"</string>
     <string name="uninstall_application_text" msgid="3816830743706143980">"Vai vēlaties atinstalēt šo lietotni?"</string>
-    <!-- no translation found for archive_application_text (8482325710714386348) -->
-    <skip />
-    <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
-    <skip />
-    <!-- no translation found for archive_application_text_user (2586558895535581451) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
-    <skip />
+    <string name="archive_application_text" msgid="8482325710714386348">"Jƫsu personas dati tiks saglabāti."</string>
+    <string name="archive_application_text_all_users" msgid="3151229641681672580">"Vai arhivēt šo lietotni visiem lietotājiem? JĆ«su personas dati tiks saglabāti."</string>
+    <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Vai arhivēt šo lietotni jĆ«su darba profilā? JĆ«su personas dati tiks saglabāti."</string>
+    <string name="archive_application_text_user" msgid="2586558895535581451">"Vai arhivēt šo lietotāja <xliff:g id="USERNAME">%1$s</xliff:g> lietotni? JĆ«su personas dati tiks saglabāti."</string>
+    <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Vai vēlaties arhivēt šo lietotni no savas privātās telpas? JĆ«su personas dati tiks saglabāti."</string>
     <string name="uninstall_application_text_all_users" msgid="575491774380227119">"Vai vēlaties atinstalēt šo lietotni "<b>"visiem"</b>" lietotājiem? ŠÄ« lietojumprogramma un tās dati tiks noƆemti no "<b>"visiem"</b>" ierÄ«ces lietotāju kontiem."</string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Vai vēlaties atinstalēt šo lietotni lietotājam <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
     <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Vai vēlaties atinstalēt šo lietotni no sava darba profila?"</string>
@@ -108,8 +102,7 @@
     <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"JĆ«su planšetdators un personas dati ir neaizsargātāki pret uzbrukumiem no nezināmām lietotnēm. Instalējot šo lietotni, jĆ«s piekrÄ«tat, ka esat atbildÄ«gs par planšetdatora bojājumiem vai datu zudumu, kas var rasties lietotnes dēČ."</string>
     <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"JĆ«su televizors un personas dati ir neaizsargātāki pret uzbrukumiem no nezināmām lietotnēm. Instalējot šo lietotni, jĆ«s piekrÄ«tat, ka esat atbildÄ«gs par televizora bojājumiem vai datu zudumu, kas var rasties lietotnes dēČ."</string>
     <string name="cloned_app_label" msgid="7503612829833756160">"Lietotnes <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> klons"</string>
-    <!-- no translation found for archiving_app_label (1127085259724124725) -->
-    <skip />
+    <string name="archiving_app_label" msgid="1127085259724124725">"Vai arhivēt lietotni <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>?"</string>
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Tālāk"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Iestatījumi"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Wear lietotƆu instalēšana/atinstalēšana"</string>
diff --git a/packages/PackageInstaller/res/values-mk/strings.xml b/packages/PackageInstaller/res/values-mk/strings.xml
index 300ef67..f453f57 100644
--- a/packages/PackageInstaller/res/values-mk/strings.xml
+++ b/packages/PackageInstaller/res/values-mk/strings.xml
@@ -44,8 +44,7 @@
     <string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"ĐšĐŸŃ€ĐžŃĐœĐžĐșĐŸĐČ ĐœĐ” ĐŒĐŸĐ¶Đ” Ўа ĐžĐœŃŃ‚Đ°Đ»ĐžŃ€Đ° ĐœĐ”ĐżĐŸĐ·ĐœĐ°Ń‚Đž аплОĐșацоо"</string>
     <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"На ĐșĐŸŃ€ĐžŃĐœĐžĐșĐŸĐČ ĐœĐ” ĐŒŃƒ Đ” ĐŽĐŸĐ·ĐČĐŸĐ»Đ”ĐœĐŸ Ўа ĐžĐœŃŃ‚Đ°Đ»ĐžŃ€Đ° аплОĐșацоо"</string>
     <string name="ok" msgid="7871959885003339302">"Đ’ĐŸ рДЎ"</string>
-    <!-- no translation found for archive (4447791830199354721) -->
-    <skip />
+    <string name="archive" msgid="4447791830199354721">"АрхоĐČорај"</string>
     <string name="update_anyway" msgid="8792432341346261969">"ХДпаĐș Đ°Đ¶ŃƒŃ€ĐžŃ€Đ°Ń˜"</string>
     <string name="manage_applications" msgid="5400164782453975580">"УпраĐČуĐČањД ŃĐŸ аплОĐșацООтД"</string>
     <string name="out_of_space_dlg_title" msgid="4156690013884649502">"ĐĐ”ĐŒĐ° ĐżŃ€ĐŸŃŃ‚ĐŸŃ€"</string>
@@ -60,16 +59,11 @@
     <string name="uninstall_update_title" msgid="824411791011583031">"Đ”Đ”ĐžĐœŃŃ‚Đ°Đ»ĐžŃ€Đ°Ń˜Ń‚Đ” Đ°Đ¶ŃƒŃ€ĐžŃ€Đ°ŃšĐ”"</string>
     <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> Đ” ЎДл ĐŸĐŽ ŃĐ»Đ”ĐŽĐœĐ°Ń‚Đ° аплОĐșацоја:"</string>
     <string name="uninstall_application_text" msgid="3816830743706143980">"ДалО саĐșатД Ўа ја ĐŽĐ”ĐžĐœŃŃ‚Đ°Đ»ĐžŃ€Đ°Ń‚Đ” ĐŸĐČаа аплОĐșацоја?"</string>
-    <!-- no translation found for archive_application_text (8482325710714386348) -->
-    <skip />
-    <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
-    <skip />
-    <!-- no translation found for archive_application_text_user (2586558895535581451) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
-    <skip />
+    <string name="archive_application_text" msgid="8482325710714386348">"Đ’Đ°ŃˆĐžŃ‚Đ” Đ»ĐžŃ‡ĐœĐž ĐżĐŸĐŽĐ°Ń‚ĐŸŃ†Đž ŃœĐ” сД Đ·Đ°Ń‡ŃƒĐČаат"</string>
+    <string name="archive_application_text_all_users" msgid="3151229641681672580">"Да сД архоĐČора аплОĐșацојаĐČа за сОтД ĐșĐŸŃ€ĐžŃĐœĐžŃ†Đž? Đ’Đ°ŃˆĐžŃ‚Đ” Đ»ĐžŃ‡ĐœĐž ĐżĐŸĐŽĐ°Ń‚ĐŸŃ†Đž ŃœĐ” сД Đ·Đ°Ń‡ŃƒĐČаат"</string>
+    <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Да сД архоĐČора аплОĐșацојаĐČа ĐœĐ° ĐČĐ°ŃˆĐžĐŸŃ‚ Ń€Đ°Đ±ĐŸŃ‚Đ”Đœ ĐżŃ€ĐŸŃ„ĐžĐ»? Đ’Đ°ŃˆĐžŃ‚Đ” Đ»ĐžŃ‡ĐœĐž ĐżĐŸĐŽĐ°Ń‚ĐŸŃ†Đž ŃœĐ” сД Đ·Đ°Ń‡ŃƒĐČаат"</string>
+    <string name="archive_application_text_user" msgid="2586558895535581451">"Да сД архоĐČора аплОĐșацојаĐČа за <xliff:g id="USERNAME">%1$s</xliff:g>? Đ’Đ°ŃˆĐžŃ‚Đ” Đ»ĐžŃ‡ĐœĐž ĐżĐŸĐŽĐ°Ń‚ĐŸŃ†Đž ŃœĐ” сД Đ·Đ°Ń‡ŃƒĐČаат"</string>
+    <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"ДалО саĐșатД Ўа ја архоĐČОратД аплОĐșацојаĐČа ĐŸĐŽ ĐČĐ°ŃˆĐžĐŸŃ‚ „ПроĐČĐ°Ń‚Đ”Đœ ĐżŃ€ĐŸŃŃ‚ĐŸŃ€“? Đ’Đ°ŃˆĐžŃ‚Đ” Đ»ĐžŃ‡ĐœĐž ĐżĐŸĐŽĐ°Ń‚ĐŸŃ†Đž ŃœĐ” сД Đ·Đ°Ń‡ŃƒĐČаат"</string>
     <string name="uninstall_application_text_all_users" msgid="575491774380227119">"ДалО саĐșатД Ўа ја ĐŽĐ”ĐžĐœŃŃ‚Đ°Đ»ĐžŃ€Đ°Ń‚Đ” ĐŸĐČаа аплОĐșацоја за "<b>"сОтД"</b>" ĐșĐŸŃ€ĐžŃĐœĐžŃ†Đž? АплОĐșацојата Đž ĐœĐ”Ń˜Đ·ĐžĐœĐžŃ‚Đ” ĐżĐŸĐŽĐ°Ń‚ĐŸŃ†Đž ŃœĐ” сД ĐŸŃ‚ŃŃ‚Ń€Đ°ĐœĐ°Ń‚ ĐŸĐŽ "<b>"сОтД"</b>" ĐșĐŸŃ€ĐžŃĐœĐžŃ†Đž ĐœĐ° ŃƒŃ€Đ”ĐŽĐŸŃ‚."</string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"ДалО саĐșатД Ўа ја ĐŽĐ”ĐžĐœŃŃ‚Đ°Đ»ĐžŃ€Đ°Ń‚Đ” аплОĐșацојаĐČа за ĐșĐŸŃ€ĐžŃĐœĐžĐșĐŸŃ‚ <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
     <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"ДалО саĐșатД Ўа ја ĐŽĐ”ĐžĐœŃŃ‚Đ°Đ»ĐžŃ€Đ°Ń‚Đ” аплОĐșацојаĐČа ĐŸĐŽ Ń€Đ°Đ±ĐŸŃ‚ĐœĐžĐŸŃ‚ ĐżŃ€ĐŸŃ„ĐžĐ»?"</string>
@@ -108,8 +102,7 @@
     <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"ĐąĐ°Đ±Đ»Đ”Ń‚ĐŸŃ‚ Đž Đ»ĐžŃ‡ĐœĐžŃ‚Đ” ĐżĐŸĐŽĐ°Ń‚ĐŸŃ†Đž сД ĐżĐŸĐżĐŸĐŽĐ»ĐŸĐ¶ĐœĐž ĐœĐ° ĐœĐ°ĐżĐ°ĐŽĐž ĐŸĐŽ аплОĐșацоо ĐŸĐŽ ĐœĐ”ĐżĐŸĐ·ĐœĐ°Ń‚Đž ОзĐČĐŸŃ€Đž. АĐșĐŸ ја ĐžĐœŃŃ‚Đ°Đ»ĐžŃ€Đ°Ń‚Đ” аплОĐșацојаĐČа, сД ŃĐŸĐłĐ»Đ°ŃŃƒĐČатД ĐŽĐ”Đșа стД ĐŸĐŽĐłĐŸĐČĐŸŃ€ĐœĐž за ĐșаĐșĐČа Đ±ĐžĐ»ĐŸ ŃˆŃ‚Đ”Ń‚Đ° ĐœĐ° Ń‚Đ°Đ±Đ»Đ”Ń‚ĐŸŃ‚ ОлО загуба ĐœĐ° ĐżĐŸĐŽĐ°Ń‚ĐŸŃ†Đž ĐżĐŸŃ€Đ°ĐŽĐž ĐœĐ”Ń˜Đ·ĐžĐœĐŸŃ‚ĐŸ ĐșĐŸŃ€ĐžŃŃ‚Đ”ŃšĐ”."</string>
     <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"йДлДĐČĐžĐ·ĐŸŃ€ĐŸŃ‚ Đž Đ»ĐžŃ‡ĐœĐžŃ‚Đ” ĐżĐŸĐŽĐ°Ń‚ĐŸŃ†Đž сД ĐżĐŸĐżĐŸĐŽĐ»ĐŸĐ¶ĐœĐž ĐœĐ° ĐœĐ°ĐżĐ°ĐŽĐž ĐŸĐŽ аплОĐșацоо ĐŸĐŽ ĐœĐ”ĐżĐŸĐ·ĐœĐ°Ń‚Đž ОзĐČĐŸŃ€Đž. АĐșĐŸ ја ĐžĐœŃŃ‚Đ°Đ»ĐžŃ€Đ°Ń‚Đ” аплОĐșацојаĐČа, сД ŃĐŸĐłĐ»Đ°ŃŃƒĐČатД ĐŽĐ”Đșа стД ĐŸĐŽĐłĐŸĐČĐŸŃ€ĐœĐž за ĐșаĐșĐČа Đ±ĐžĐ»ĐŸ ŃˆŃ‚Đ”Ń‚Đ° ĐœĐ° тДлДĐČĐžĐ·ĐŸŃ€ĐŸŃ‚ ОлО загуба ĐœĐ° ĐżĐŸĐŽĐ°Ń‚ĐŸŃ†Đž ĐżĐŸŃ€Đ°ĐŽĐž ĐœĐ”Ń˜Đ·ĐžĐœĐŸŃ‚ĐŸ ĐșĐŸŃ€ĐžŃŃ‚Đ”ŃšĐ”."</string>
     <string name="cloned_app_label" msgid="7503612829833756160">"ĐšĐ»ĐŸĐœ ĐœĐ° <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
-    <!-- no translation found for archiving_app_label (1127085259724124725) -->
-    <skip />
+    <string name="archiving_app_label" msgid="1127085259724124725">"Да сД архоĐČора <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>?"</string>
     <string name="anonymous_source_continue" msgid="4375745439457209366">"ĐŸŃ€ĐŸĐŽĐŸĐ»Đ¶Đž"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"ĐŸĐŸŃŃ‚Đ°ĐČĐșĐž"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"ĐĄĐ” ĐžĐœŃŃ‚Đ°Đ»ĐžŃ€Đ°Đ°Ń‚/ĐŽĐ”ĐžĐœŃŃ‚Đ°Đ»ĐžŃ€Đ°Đ°Ń‚ аплОĐș. Wear"</string>
diff --git a/packages/PackageInstaller/res/values-ml/strings.xml b/packages/PackageInstaller/res/values-ml/strings.xml
index 7f071f6..f193acb 100644
--- a/packages/PackageInstaller/res/values-ml/strings.xml
+++ b/packages/PackageInstaller/res/values-ml/strings.xml
@@ -44,8 +44,7 @@
     <string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"àŽˆ àŽ‰àŽȘàŽŻà”‹àŽ•à”àŽ€àŽŸàŽ”àŽżàŽšà”, àŽ…àŽœà”àŽžàŽŸàŽ€ àŽ†àŽȘà”àŽȘà”àŽ•à”Ÿ àŽ‡à”»àŽžà”‌àŽ±à”àŽ±àŽŸà”Ÿ àŽšà”†àŽŻà”àŽŻàŽŸàŽšàŽŸàŽ”àŽżàŽČà”àŽČ"</string>
     <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"àŽ†àŽȘà”àŽȘà”àŽ•à”Ÿ àŽ‡à”»‌àŽžà”‌àŽ±à”àŽ±àŽŸà”Ÿ àŽšà”†àŽŻà”àŽŻàŽŸà”» àŽˆ àŽ‰àŽȘàŽŻà”‹àŽ•à”àŽ€àŽŸàŽ”àŽżàŽšà” àŽ…àŽšà”àŽ”àŽŸàŽŠàŽźàŽżàŽČà”àŽČ"</string>
     <string name="ok" msgid="7871959885003339302">"àŽ¶àŽ°àŽż"</string>
-    <!-- no translation found for archive (4447791830199354721) -->
-    <skip />
+    <string name="archive" msgid="4447791830199354721">"àŽ†à”ŒàŽ•à”àŽ•à”ˆàŽ”à” àŽšà”†àŽŻà”àŽŻà”àŽ•"</string>
     <string name="update_anyway" msgid="8792432341346261969">"àŽŽàŽšà”àŽ€àŽŸàŽŻàŽŸàŽČà”àŽ‚ àŽ…àŽȘà”‌àŽĄà”‡àŽ±à”àŽ±à” àŽšà”†àŽŻà”àŽŻà”àŽ•"</string>
     <string name="manage_applications" msgid="5400164782453975580">"àŽ†àŽȘà”àŽȘà”àŽ•à”Ÿ àŽźàŽŸàŽšà”‡àŽœà” àŽšà”†àŽŻà”àŽŻà”àŽ•"</string>
     <string name="out_of_space_dlg_title" msgid="4156690013884649502">"àŽ‡àŽŸàŽźàŽżàŽČà”àŽČ"</string>
@@ -60,16 +59,11 @@
     <string name="uninstall_update_title" msgid="824411791011583031">"àŽ…àŽȘà”‌àŽĄà”‡àŽ±à”àŽ±à”, àŽ…à”ș àŽ‡‌à”»àŽžà”‌àŽ±à”àŽ±àŽŸà”Ÿ àŽšà”†àŽŻà”àŽŻà”àŽ•"</string>
     <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g>, àŽ‡àŽšàŽżàŽȘà”àŽȘàŽ±àŽŻà”àŽšà”àŽš àŽ†àŽȘà”àŽȘàŽżàŽšà”àŽ±à”† àŽ­àŽŸàŽ—àŽźàŽŸàŽŁà” :"</string>
     <string name="uninstall_application_text" msgid="3816830743706143980">"àŽˆ àŽ†àŽȘà”àŽȘà” àŽ…à”ș àŽ‡à”»àŽžà”‌àŽ±à”àŽ±àŽŸà”Ÿ àŽšà”†àŽŻà”àŽŻàŽŁà”‹?"</string>
-    <!-- no translation found for archive_application_text (8482325710714386348) -->
-    <skip />
-    <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
-    <skip />
-    <!-- no translation found for archive_application_text_user (2586558895535581451) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
-    <skip />
+    <string name="archive_application_text" msgid="8482325710714386348">"àŽšàŽżàŽ™à”àŽ™àŽłà”àŽŸà”† àŽ”à”àŽŻàŽ•à”àŽ€àŽżàŽȘàŽ°àŽźàŽŸàŽŻ àŽĄàŽŸàŽ±à”àŽ± àŽžàŽ‚àŽ°àŽ•à”àŽ·àŽżàŽ•à”àŽ•à”àŽ‚"</string>
+    <string name="archive_application_text_all_users" msgid="3151229641681672580">"àŽŽàŽČà”àŽČàŽŸ àŽ‰àŽȘàŽŻà”‹àŽ•à”àŽ€àŽŸàŽ•à”àŽ•à”ŸàŽ•à”àŽ•à”àŽźàŽŸàŽŻàŽż àŽˆ àŽ†àŽȘà”àŽȘà” àŽ†à”ŒàŽ•à”àŽ•à”ˆàŽ”à” àŽšà”†àŽŻà”àŽŻàŽŁà”‹? àŽšàŽżàŽ™à”àŽ™àŽłà”àŽŸà”† àŽ”à”àŽŻàŽ•à”àŽ€àŽżàŽȘàŽ°àŽźàŽŸàŽŻ àŽĄàŽŸàŽ±à”àŽ± àŽžàŽ‚àŽ°àŽ•à”àŽ·àŽżàŽ•à”àŽ•à”àŽ‚"</string>
+    <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"àŽšàŽżàŽ™à”àŽ™àŽłà”àŽŸà”† àŽ”àŽŠà”àŽŻà”‹àŽ—àŽżàŽ• àŽȘà”àŽ°à”ŠàŽ«à”ˆàŽČàŽżà”œ àŽˆ àŽ†àŽȘà”àŽȘà” àŽ†à”ŒàŽ•à”àŽ•à”ˆàŽ”à” àŽšà”†àŽŻà”àŽŻàŽŁà”‹? àŽšàŽżàŽ™à”àŽ™àŽłà”àŽŸà”† àŽ”à”àŽŻàŽ•à”àŽ€àŽżàŽȘàŽ°àŽźàŽŸàŽŻ àŽĄàŽŸàŽ±à”àŽ± àŽžàŽ‚àŽ°àŽ•à”àŽ·àŽżàŽ•à”àŽ•à”àŽ‚"</string>
+    <string name="archive_application_text_user" msgid="2586558895535581451">"<xliff:g id="USERNAME">%1$s</xliff:g> àŽŽàŽšà”àŽšàŽŻàŽŸà”ŸàŽ•à”àŽ•àŽŸàŽŻàŽż àŽˆ àŽ†àŽȘà”àŽȘà” àŽ†à”ŒàŽ•à”àŽ•à”ˆàŽ”à” àŽšà”†àŽŻà”àŽŻàŽŁà”‹? àŽšàŽżàŽ™à”àŽ™àŽłà”àŽŸà”† àŽ”à”àŽŻàŽ•à”àŽ€àŽżàŽȘàŽ°àŽźàŽŸàŽŻ àŽĄàŽŸàŽ±à”àŽ± àŽžàŽ‚àŽ°àŽ•à”àŽ·àŽżàŽ•à”àŽ•à”àŽ‚"</string>
+    <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"àŽšàŽżàŽ™à”àŽ™àŽłà”àŽŸà”† àŽžà”àŽ”àŽ•àŽŸàŽ°à”àŽŻ àŽžà”‌àŽȘà”‡àŽžàŽżà”œ àŽšàŽżàŽšà”àŽšà” àŽˆ àŽ†àŽȘà”àŽȘà” àŽ†à”ŒàŽ•à”àŽ•à”ˆàŽ”à” àŽšà”†àŽŻà”àŽŻàŽŁà”‹? àŽšàŽżàŽ™à”àŽ™àŽłà”àŽŸà”† àŽ”à”àŽŻàŽ•à”àŽ€àŽżàŽȘàŽ°àŽźàŽŸàŽŻ àŽĄàŽŸàŽ±à”àŽ± àŽžàŽ‚àŽ°àŽ•à”àŽ·àŽżàŽ•à”àŽ•à”àŽ‚"</string>
     <string name="uninstall_application_text_all_users" msgid="575491774380227119">"àŽˆ àŽ…àŽȘà”àŽȘà” "<b>"àŽŽàŽČà”àŽČàŽŸ"</b>" àŽ‰àŽȘàŽŻà”‹àŽ•à”àŽ€àŽŸàŽ•à”àŽ•à”ŸàŽ•à”àŽ•à”àŽźàŽŸàŽŻàŽż àŽ…à”ș àŽ‡à”»àŽžà”‌àŽ±à”àŽ±àŽŸà”Ÿ àŽšà”†àŽŻà”àŽŻàŽŁà”‹? àŽ‰àŽȘàŽ•àŽ°àŽŁàŽ€à”àŽ€àŽżàŽČà”† "<b>"àŽŽàŽČà”àŽČàŽŸ"</b>" àŽ‰àŽȘàŽŻà”‹àŽ•à”àŽ€àŽŸàŽ•à”àŽ•àŽłàŽżà”œ àŽšàŽżàŽšà”àŽšà”àŽ‚ àŽ†àŽȘà”àŽȘà”àŽ‚ àŽ…àŽ€àŽżàŽšà”àŽ±à”† àŽĄàŽŸàŽ±à”àŽ±àŽŻà”àŽ‚ àŽšà”€àŽ•à”àŽ•àŽ‚ àŽšà”†àŽŻà”àŽŻàŽȘà”àŽȘà”†àŽŸà”àŽ‚."</string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"<xliff:g id="USERNAME">%1$s</xliff:g> àŽŽàŽšà”àŽš àŽ‰àŽȘàŽŻà”‹àŽ•à”àŽ€àŽŸàŽ”àŽżàŽšàŽŸàŽŻàŽż àŽˆ àŽ†àŽȘà”àŽȘà” àŽ…à”ș àŽ‡à”»àŽžà”‌àŽ±à”àŽ±àŽŸà”Ÿ àŽšà”†àŽŻà”àŽŻàŽŁà”‹?"</string>
     <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"àŽšàŽżàŽ™à”àŽ™àŽłà”àŽŸà”† àŽ”àŽŠà”àŽŻà”‹àŽ—àŽżàŽ• àŽȘà”àŽ°à”ŠàŽ«à”ˆàŽČàŽżà”œ àŽšàŽżàŽšà”àŽšà” àŽˆ àŽ†àŽȘà”àŽȘà” àŽ…à”ș àŽ‡à”»àŽžà”‌àŽ±à”àŽ±àŽŸà”Ÿ àŽšà”†àŽŻà”àŽŻàŽŁà”‹?"</string>
@@ -108,8 +102,7 @@
     <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"àŽ…àŽœà”àŽžàŽŸàŽ€àŽźàŽŸàŽŻ àŽ†àŽȘà”àŽȘà”àŽ•àŽłàŽŸà”œ àŽšàŽżàŽ™à”àŽ™àŽłà”àŽŸà”† àŽŸàŽŸàŽŹà”‌àŽČà”†àŽ±à”àŽ±à”àŽ‚ àŽ”à”àŽŻàŽ•à”àŽ€àŽżàŽ—àŽ€ àŽĄàŽŸàŽ±à”àŽ±àŽŻà”àŽ‚ àŽ†àŽ•à”àŽ°àŽźàŽżàŽ•à”àŽ•àŽȘà”àŽȘà”†àŽŸàŽŸàŽšà”àŽłà”àŽł àŽžàŽŸàŽ§à”àŽŻàŽ€ àŽ”àŽłàŽ°à”† àŽ•à”‚àŽŸà”àŽ€àŽČàŽŸàŽŁà”. àŽˆ àŽ†àŽȘà”àŽȘà” àŽ‡à”»àŽžà”‌àŽ±à”àŽ±àŽŸà”Ÿ àŽšà”†àŽŻà”àŽŻà”àŽšà”àŽšàŽ€àŽżàŽČà”‚àŽŸà”†, àŽ‡àŽ€à” àŽ‰àŽȘàŽŻà”‹àŽ—àŽżàŽ•à”àŽ•à”àŽšà”àŽšàŽ€àŽżàŽšàŽŸà”œ àŽšàŽżàŽ™à”àŽ™àŽłà”àŽŸà”† àŽŸàŽŸàŽŹà”‌àŽČà”†àŽ±à”àŽ±àŽżàŽšà” àŽžàŽ‚àŽ­àŽ”àŽżàŽšà”àŽšà”‡àŽ•à”àŽ•àŽŸàŽ”à”àŽšà”àŽš àŽàŽ€à” àŽšàŽŸàŽ¶àŽšàŽ·à”‌àŽŸàŽ€à”àŽ€àŽżàŽšà”àŽ±à”†àŽŻà”àŽ‚ àŽ…àŽČà”àŽČà”†àŽ™à”àŽ•àŽżà”œ àŽĄàŽŸàŽ±à”àŽ±àŽŸ àŽšàŽ·à”‌àŽŸàŽ€à”àŽ€àŽżàŽšà”àŽ±à”†àŽŻà”àŽ‚ àŽ‰àŽ€à”àŽ€àŽ°àŽ”àŽŸàŽŠàŽżàŽ€à”àŽ€àŽ‚ àŽšàŽżàŽ™à”àŽ™à”ŸàŽ•à”àŽ•àŽŸàŽŻàŽżàŽ°àŽżàŽ•à”àŽ•à”àŽźà”†àŽšà”àŽšà” àŽ…àŽ‚àŽ—à”€àŽ•àŽ°àŽżàŽ•à”àŽ•à”àŽšà”àŽšà”."</string>
     <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"àŽ…àŽœà”àŽžàŽŸàŽ€àŽźàŽŸàŽŻ àŽ†àŽȘà”àŽȘà”àŽ•àŽłàŽŸà”œ àŽšàŽżàŽ™à”àŽ™àŽłà”àŽŸà”† àŽŸàŽżàŽ”àŽżàŽŻà”àŽ‚ àŽ”à”àŽŻàŽ•à”àŽ€àŽżàŽ—àŽ€ àŽĄàŽŸàŽ±à”àŽ±àŽŻà”àŽ‚ àŽ†àŽ•à”àŽ°àŽźàŽżàŽ•à”àŽ•àŽȘà”àŽȘà”†àŽŸàŽŸàŽšà”àŽłà”àŽł àŽžàŽŸàŽ§à”àŽŻàŽ€ àŽ”àŽłàŽ°à”† àŽ•à”‚àŽŸà”àŽ€àŽČàŽŸàŽŁà”. àŽˆ àŽ†àŽȘà”àŽȘà” àŽ‡à”»àŽžà”‌àŽ±à”àŽ±àŽŸà”Ÿ àŽšà”†àŽŻà”àŽŻà”àŽšà”àŽšàŽ€àŽżàŽČà”‚àŽŸà”†, àŽ‡àŽ€à” àŽ‰àŽȘàŽŻà”‹àŽ—àŽżàŽ•à”àŽ•à”àŽšà”àŽšàŽ€àŽżàŽšàŽŸà”œ àŽšàŽżàŽ™à”àŽ™àŽłà”àŽŸà”† àŽŸàŽżàŽ”àŽżàŽ•à”àŽ•à” àŽžàŽ‚àŽ­àŽ”àŽżàŽšà”àŽšà”‡àŽ•à”àŽ•àŽŸàŽ”à”àŽšà”àŽš àŽàŽ€à” àŽšàŽŸàŽ¶àŽšàŽ·à”‌àŽŸàŽ€à”àŽ€àŽżàŽšà”àŽ±à”†àŽŻà”àŽ‚ àŽ…àŽČà”àŽČà”†àŽ™à”àŽ•àŽżà”œ àŽĄàŽŸàŽ±à”àŽ±àŽŸ àŽšàŽ·à”‌àŽŸàŽ€à”àŽ€àŽżàŽšà”àŽ±à”†àŽŻà”àŽ‚ àŽ‰àŽ€à”àŽ€àŽ°àŽ”àŽŸàŽŠàŽżàŽ€à”àŽ€àŽ‚ àŽšàŽżàŽ™à”àŽ™à”ŸàŽ•à”àŽ•àŽŸàŽŻàŽżàŽ°àŽżàŽ•à”àŽ•à”àŽźà”†àŽšà”àŽšà” àŽ…àŽ‚àŽ—à”€àŽ•àŽ°àŽżàŽ•à”àŽ•à”àŽšà”àŽšà”."</string>
     <string name="cloned_app_label" msgid="7503612829833756160">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> àŽ•à”àŽČà”‹à”ș àŽšà”†àŽŻà”àŽŻà”œ"</string>
-    <!-- no translation found for archiving_app_label (1127085259724124725) -->
-    <skip />
+    <string name="archiving_app_label" msgid="1127085259724124725">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> àŽ†à”ŒàŽ•à”àŽ•à”ˆàŽ”à” àŽšà”†àŽŻà”àŽŻàŽŁà”‹?"</string>
     <string name="anonymous_source_continue" msgid="4375745439457209366">"àŽ€à”àŽŸàŽ°à”àŽ•"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"àŽ•à”àŽ°àŽźà”€àŽ•àŽ°àŽŁàŽ‚"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Wear àŽ†àŽȘà”àŽȘà” àŽ‡à”»àŽžà”‌àŽ±à”àŽ±àŽŸà”Ÿ/àŽ…à”ș àŽ‡à”»àŽžà”‌àŽ±à”àŽ±àŽŸà”Ÿ àŽšà”†àŽŻà”àŽŻà”àŽšà”àŽšà”"</string>
diff --git a/packages/PackageInstaller/res/values-mn/strings.xml b/packages/PackageInstaller/res/values-mn/strings.xml
index 35beb14..091778f 100644
--- a/packages/PackageInstaller/res/values-mn/strings.xml
+++ b/packages/PackageInstaller/res/values-mn/strings.xml
@@ -44,8 +44,7 @@
     <string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Đ­ĐœŃ Ń…ŃŃ€ŃĐłĐ»ŃĐłŃ‡ Ń‚ĐŸĐŽĐŸŃ€Ń…ĐŸĐčĐłÒŻĐč апп ŃŃƒŃƒĐ»ĐłĐ°Ń… Đ±ĐŸĐ»ĐŸĐŒĐ¶ĐłÒŻĐč"</string>
     <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Đ­ĐœŃ Ń…ŃŃ€ŃĐłĐ»ŃĐłŃ‡ ĐœŃŒ апп ŃŃƒŃƒĐ»ĐłĐ°Ń… Đ·Ó©ĐČŃˆÓ©Ó©Ń€Ó©Đ»ĐłÒŻĐč баĐčĐœĐ°"</string>
     <string name="ok" msgid="7871959885003339302">"ОК"</string>
-    <!-- no translation found for archive (4447791830199354721) -->
-    <skip />
+    <string name="archive" msgid="4447791830199354721">"АрхоĐČлах"</string>
     <string name="update_anyway" msgid="8792432341346261969">"ĐŻĐŒĐ°Ń€Ń‚Đ°Đč ч ŃˆĐžĐœŃŃ‡Đ»ŃŃ…"</string>
     <string name="manage_applications" msgid="5400164782453975580">"АппууЮыг уЮорЮах"</string>
     <string name="out_of_space_dlg_title" msgid="4156690013884649502">"ĐžŃ€ĐŸĐœ заĐč ĐŽŃƒŃ‚Đ°ĐłĐŽĐ°Đ¶ баĐčĐœĐ°"</string>
@@ -60,16 +59,11 @@
     <string name="uninstall_update_title" msgid="824411791011583031">"ĐšĐžĐœŃŃ‡Đ»ŃĐ»Ń‚ĐžĐčĐł устгах"</string>
     <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> ĐœŃŒ Юараах Đ°ĐżĐżŃ‹Đœ хэсэг Đ±ĐŸĐ»ĐœĐŸ:"</string>
     <string name="uninstall_application_text" msgid="3816830743706143980">"йа ŃĐœŃ аппыг устгахыг Ń…ÒŻŃŃĐ¶ баĐčĐœĐ° уу?"</string>
-    <!-- no translation found for archive_application_text (8482325710714386348) -->
-    <skip />
-    <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
-    <skip />
-    <!-- no translation found for archive_application_text_user (2586558895535581451) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
-    <skip />
+    <string name="archive_application_text" msgid="8482325710714386348">"ĐąĐ°ĐœŃ‹ хуĐČĐžĐčĐœ өгөгЎлОĐčĐł Ń…Đ°ĐŽĐłĐ°Đ»ĐœĐ°"</string>
+    <string name="archive_application_text_all_users" msgid="3151229641681672580">"Đ­ĐœŃ аппыг Đ±ÒŻŃ… Ń…ŃŃ€ŃĐłĐ»ŃĐłŃ‡ĐžĐŽ архоĐČлах уу? ĐąĐ°ĐœŃ‹ хуĐČĐžĐčĐœ өгөгЎлОĐčĐł Ń…Đ°ĐŽĐłĐ°Đ»ĐœĐ°"</string>
+    <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Đ­ĐœŃ аппыг Ń‚Đ°ĐœŃ‹ Đ°Đ¶Đ»Ń‹Đœ ĐżŃ€ĐŸŃ„Đ°ĐčлЎ архоĐČлах уу? ĐąĐ°ĐœŃ‹ хуĐČĐžĐčĐœ өгөгЎлОĐčĐł Ń…Đ°ĐŽĐłĐ°Đ»ĐœĐ°"</string>
+    <string name="archive_application_text_user" msgid="2586558895535581451">"Đ­ĐœŃ аппыг <xliff:g id="USERNAME">%1$s</xliff:g>-ĐŽ архоĐČлах уу? ĐąĐ°ĐœŃ‹ хуĐČĐžĐčĐœ өгөгЎлОĐčĐł Ń…Đ°ĐŽĐłĐ°Đ»ĐœĐ°"</string>
+    <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"йа ŃĐœŃ аппыг хуĐČĐžĐčĐœ ĐŸŃ€ĐŸĐœ заĐčĐœĐ°Đ°ŃĐ°Đ° архоĐČлахыг Ń…ÒŻŃŃĐ¶ баĐčĐœĐ° уу? ĐąĐ°ĐœŃ‹ хуĐČĐžĐčĐœ өгөгЎлОĐčĐł Ń…Đ°ĐŽĐłĐ°Đ»ĐœĐ°"</string>
     <string name="uninstall_application_text_all_users" msgid="575491774380227119">"йа ŃĐœŃ аппыг "<b>"Đ±ÒŻŃ…"</b>" Ń…ŃŃ€ŃĐłĐ»ŃĐłŃ‡ŃŃŃ устгахыг Ń…ÒŻŃŃĐ¶ баĐčĐœĐ° уу? Апп Đ±ĐŸĐ»ĐŸĐœ ĐŽĐŸŃ‚ĐŸŃ€Ń… өгөгЎлОĐčĐł Ń‚Ó©Ń…Ó©Ó©Ń€Ó©ĐŒĐ¶ Юээрх "<b>"Đ±ÒŻŃ…"</b>" Ń…ŃŃ€ŃĐłĐ»ŃĐłŃ‡ŃŃŃ ŃƒŃŃ‚ĐłĐ°ĐœĐ°."</string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"йа ŃĐœŃ аппыг <xliff:g id="USERNAME">%1$s</xliff:g> Ń…ŃŃ€ŃĐłĐ»ŃĐłŃ‡ĐžĐčĐœ Ó©ĐŒĐœÓ©Ó©Ń устгахыг Ń…ÒŻŃŃĐ¶ баĐčĐœĐ° уу?"</string>
     <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"йа ŃĐœŃ аппыг Đ°Đ¶Đ»Ń‹Đœ ĐżŃ€ĐŸŃ„Đ°Đčлаасаа устгахыг Ń…ÒŻŃŃĐ¶ баĐčĐœĐ° уу?"</string>
@@ -108,8 +102,7 @@
     <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"ĐąĐ°ĐœŃ‹ таблДт Đ±ĐŸĐ»ĐŸĐœ хуĐČĐžĐčĐœ өгөгЎөл Ń‚ĐŸĐŽĐŸŃ€Ń…ĐŸĐčĐłÒŻĐč апп ŃŃƒŃƒĐ»ĐłĐ°ŃĐ°Đœ Ń‚ĐŸŃ…ĐžĐŸĐ»ĐŽĐŸĐ»ĐŽ ĐłŃĐŒŃ‚ŃĐ¶ Đ±ĐŸĐ»Đ·ĐŸŃˆĐłÒŻĐč. Đ­ĐœŃ аппыг ŃŃƒŃƒĐ»ĐłĐ°ŃĐœĐ°Đ°Ń€ ÒŻÒŻĐœĐžĐčĐł Đ°ŃˆĐžĐłĐ»Đ°ŃĐœĐ°Đ°Ń ÒŻÒŻĐŽŃĐœ Ń‚Đ°ĐœŃ‹ таблДтаЎ ĐłŃĐŒŃ‚ŃĐ» гарах, эсĐČŃĐ» өгөгЎөл устах Đ·ŃŃ€ŃĐł ŃŃ€ŃĐŽŃĐ»ĐžĐčĐł Ń…Đ°Ń€ĐžŃƒŃ†ĐœĐ° гэЮгээ Đ·Ó©ĐČŃˆÓ©Ó©Ń€Ń‡ баĐčĐœĐ°."</string>
     <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"ĐąĐ°ĐœŃ‹ бВ Đ±ĐŸĐ»ĐŸĐœ хуĐČĐžĐčĐœ өгөгЎөл Ń‚ĐŸĐŽĐŸŃ€Ń…ĐŸĐčĐłÒŻĐč апп ŃŃƒŃƒĐ»ĐłĐ°ŃĐ°Đœ Ń‚ĐŸŃ…ĐžĐŸĐ»ĐŽĐŸĐ»ĐŽ ĐłŃĐŒŃ‚ŃĐ¶ Đ±ĐŸĐ»Đ·ĐŸŃˆĐłÒŻĐč. Đ­ĐœŃ аппыг ŃŃƒŃƒĐ»ĐłĐ°ŃĐœĐ°Đ°Ń€ ÒŻÒŻĐœĐžĐčĐł Đ°ŃˆĐžĐłĐ»Đ°ŃĐœĐ°Đ°Ń ÒŻÒŻĐŽŃĐœ Ń‚Đ°ĐœŃ‹ бВ-ĐŽ ĐłŃĐŒŃ‚ŃĐ» гарах, эсĐČŃĐ» өгөгЎөл устах Đ·ŃŃ€ŃĐł ŃŃ€ŃĐŽŃĐ»ĐžĐčĐł Ń…Đ°Ń€ĐžŃƒŃ†ĐœĐ° гэЮгээ Đ·Ó©ĐČŃˆÓ©Ó©Ń€Ń‡ баĐčĐœĐ°."</string>
     <string name="cloned_app_label" msgid="7503612829833756160">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ĐșĐ»ĐŸĐœ"</string>
-    <!-- no translation found for archiving_app_label (1127085259724124725) -->
-    <skip />
+    <string name="archiving_app_label" msgid="1127085259724124725">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>-Đł архоĐČлах уу?"</string>
     <string name="anonymous_source_continue" msgid="4375745439457209366">"ÒźŃ€ĐłŃĐ»Đ¶Đ»ÒŻÒŻĐ»ŃŃ…"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"ĐąĐŸŃ…ĐžŃ€ĐłĐŸĐŸ"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Wear аппууЮыг суулгаж/ŃƒŃŃ‚ĐłĐ°Đ¶ баĐčĐœĐ°"</string>
diff --git a/packages/PackageInstaller/res/values-mr/strings.xml b/packages/PackageInstaller/res/values-mr/strings.xml
index 44a0e27..a8c9bd0 100644
--- a/packages/PackageInstaller/res/values-mr/strings.xml
+++ b/packages/PackageInstaller/res/values-mr/strings.xml
@@ -44,8 +44,7 @@
     <string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"à€Żà€Ÿ à€”à€Ÿà€Șà€°à€•à€°à„à€€à„à€Żà€Ÿà€Šà„à€”à€Ÿà€°à„‡ à€…à€œà„à€žà€Ÿà€€ à€…‍à„…à€Șà„à€ž à€‡à€‚à€žà„à€Ÿà„‰à€Č à€•à„‡à€Čà„€ à€œà€Ÿà€Š à€¶à€•à€€ à€šà€Ÿà€čà„€à€€"</string>
     <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"à€Żà€Ÿ à€”à€Ÿà€Șà€°à€•à€°à„à€€à„à€Żà€Ÿà€Čà€Ÿ à€…‍à„…à€Șà„à€ž à€‡à€‚à€žà„à€Ÿà„‰à€Č à€•à€°à€Łà„à€Żà€Ÿà€šà„€ à€…à€šà„à€źà€€à„€ à€šà€Ÿà€čà„€"</string>
     <string name="ok" msgid="7871959885003339302">"à€“à€•à„‡"</string>
-    <!-- no translation found for archive (4447791830199354721) -->
-    <skip />
+    <string name="archive" msgid="4447791830199354721">"à€žà€‚à€—à„à€°à€čà€żà€€ à€•à€°à€Ÿ"</string>
     <string name="update_anyway" msgid="8792432341346261969">"à€€à€°à„€à€čà„€ à€…à€Șà€Ąà„‡à€Ÿ à€•à€°à€Ÿ"</string>
     <string name="manage_applications" msgid="5400164782453975580">"à€…‍à„…à€Șà„à€ž à€”à„à€Żà€”à€žà„à€„à€Ÿà€Șà€š"</string>
     <string name="out_of_space_dlg_title" msgid="4156690013884649502">"à€œà€Ÿà€—à€Ÿ à€žà€‚à€Șà€Čà„€"</string>
@@ -60,16 +59,11 @@
     <string name="uninstall_update_title" msgid="824411791011583031">"à€…à€Șà€Ąà„‡à€Ÿ à€…à€šà€‡à€‚à€žà„à€Ÿà„‰à€Č à€•à€°à€Ÿ"</string>
     <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> à€–à€Ÿà€Čà„€à€Č à„Čà€Șà€šà€Ÿ à€­à€Ÿà€— à€†à€čà„‡:"</string>
     <string name="uninstall_application_text" msgid="3816830743706143980">"à€€à„à€źà„à€čà€Ÿà€Čà€Ÿ à€čà„‡ à€…‍à„…à€Ș à€…à€šà€‡à€‚à€žà„à€Ÿà„‰à€Č à€•à€°à€Ÿà€Żà€šà„‡ à€†à€čà„‡ à€•à€Ÿ?"</string>
-    <!-- no translation found for archive_application_text (8482325710714386348) -->
-    <skip />
-    <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
-    <skip />
-    <!-- no translation found for archive_application_text_user (2586558895535581451) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
-    <skip />
+    <string name="archive_application_text" msgid="8482325710714386348">"à€€à„à€źà€šà€Ÿ à€”à„ˆà€Żà€•à„à€€à€żà€• à€Ąà„‡à€Ÿà€Ÿ à€žà„‡à€”à„à€č à€•à„‡à€Čà€Ÿ à€œà€Ÿà€ˆà€Č"</string>
+    <string name="archive_application_text_all_users" msgid="3151229641681672580">"à€žà€°à„à€” à€”à€Ÿà€Șà€°à€•à€°à„à€€à„à€Żà€Ÿà€‚à€žà€Ÿà€ à„€ à€čà„‡ à„Čà€Ș à€žà€‚à€—à„à€°à€čà€żà€€ à€•à€°à€Ÿà€Żà€šà„‡ à€†à€čà„‡ à€•à€Ÿ? à€€à„à€źà€šà€Ÿ à€”à„ˆà€Żà€•à„à€€à€żà€• à€Ąà„‡à€Ÿà€Ÿ à€žà„‡à€”à„à€č à€•à„‡à€Čà€Ÿ à€œà€Ÿà€ˆà€Č"</string>
+    <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"à€€à„à€źà€šà„à€Żà€Ÿ à€•à€Ÿà€°à„à€Ż à€Șà„à€°à„‹à€«à€Ÿà€‡à€Čà€”à€° à€čà„‡ à„Čà€Ș à€žà€‚à€—à„à€°à€čà€żà€€ à€•à€°à€Ÿà€Żà€šà„‡ à€†à€čà„‡ à€•à€Ÿ? à€€à„à€źà€šà€Ÿ à€”à„ˆà€Żà€•à„à€€à€żà€• à€Ąà„‡à€Ÿà€Ÿ à€žà„‡à€”à„à€č à€•à„‡à€Čà€Ÿ à€œà€Ÿà€ˆà€Č"</string>
+    <string name="archive_application_text_user" msgid="2586558895535581451">"<xliff:g id="USERNAME">%1$s</xliff:g> à€žà€Ÿà€ à„€ à€čà„‡ à„Čà€Ș à€žà€‚à€—à„à€°à€čà€żà€€ à€•à€°à€Ÿà€Żà€šà„‡ à€†à€čà„‡ à€•à€Ÿ? à€€à„à€źà€šà€Ÿ à€”à„ˆà€Żà€•à„à€€à€żà€• à€Ąà„‡à€Ÿà€Ÿ à€žà„‡à€”à„à€č à€•à„‡à€Čà€Ÿ à€œà€Ÿà€ˆà€Č"</string>
+    <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"à€€à„à€źà„à€čà€Ÿà€Čà€Ÿ à€€à„à€źà€šà„à€Żà€Ÿ à€–à€Ÿà€œà€—à„€ à€žà„à€Șà„‡à€žà€źà€§à„‚à€š à€čà„‡ à„Čà€Ș à€žà€‚à€—à„à€°à€čà€żà€€ à€•à€°à€Ÿà€Żà€šà„‡ à€†à€čà„‡ à€•à€Ÿ? à€€à„à€źà€šà€Ÿ à€”à„ˆà€Żà€•à„à€€à€żà€• à€Ąà„‡à€Ÿà€Ÿ à€žà„‡à€”à„à€č à€•à„‡à€Čà€Ÿ à€œà€Ÿà€ˆà€Č"</string>
     <string name="uninstall_application_text_all_users" msgid="575491774380227119">"à€€à„à€źà„à€čà€Ÿà€Čà€Ÿ à€čà„‡ à€…‍à„…à€Ș "<b>"à€žà€°à„à€”"</b>" à€”à€Ÿà€Șà€°à€•à€°à„à€€à„à€Żà€Ÿà€‚à€žà€Ÿà€ à„€ à€…à€šà€‡à€‚à€žà„à€Ÿà„‰à€Č à€•à€°à€Ÿà€Żà€šà„‡ à€†à€čà„‡ à€•à€Ÿ? à€…‍à„…à€Șà„à€Čà€żà€•à„‡à€¶à€š à€†à€Łà€ż à€€à„à€Żà€Ÿà€šà€Ÿ à€Ąà„‡à€Ÿà€Ÿ à€Ąà€żà€”à„à€čà€Ÿà€‡à€žà€”à€°à„€à€Č "<b>"à€žà€°à„à€”"</b>" à€”à€Ÿà€Șà€°à€•à€°à„à€€à„à€Żà€Ÿà€‚à€•à€Ąà„‚à€š à€•à€Ÿà€ąà€Čà€Ÿ à€œà€Ÿà€ˆà€Č."</string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"à€€à„à€źà„à€čà€Ÿà€Čà€Ÿ <xliff:g id="USERNAME">%1$s</xliff:g> à€”à€Ÿà€Șà€°à€•à€°à„à€€à„à€Żà€Ÿà€žà€Ÿà€ à„€ à€čà„‡ à€…‍à„…à€Ș à€…à€šà€‡à€‚à€žà„à€Ÿà„‰à€Č à€•à€°à€Ÿà€Żà€šà„‡ à€†à€čà„‡ à€•à€Ÿ?"</string>
     <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"à€€à„à€źà„à€čà€Ÿà€Čà€Ÿ à€€à„à€źà€šà„à€Żà€Ÿ à€•à€Ÿà€°à„à€Ż à€Șà„à€°à„‹à€«à€Ÿà€‡à€Čà€źà€§à„‚à€š à€čà„‡ à„Čà€Ș à€…à€šà€‡à€‚à€žà„à€Ÿà„‰à€Č à€•à€°à€Ÿà€Żà€šà„‡ à€†à€čà„‡ à€•à€Ÿ?"</string>
@@ -108,8 +102,7 @@
     <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"à€€à„à€źà€šà€Ÿ à€Ÿà„…à€Źà€Čà„‡à€Ÿ à€†à€Łà€ż à€”à„ˆà€Żà€•à„à€€à€żà€• à€Ąà„‡à€Ÿà€Ÿ à€…à€œà„à€žà€Ÿà€€ à€…‍à„…à€Șà„‍à€žà€•à€Ąà„‚à€š à€čà„‹à€Łà€Ÿà€°à„‍à€Żà€Ÿ à€…à€Ÿà„…à€•à€źà„à€łà„‡ à€…à€§à€żà€• à€…à€žà„à€°à€•à„à€·à€żà€€ à€†à€čà„‡. à€čà„‡ à€…‍à„…à€Ș à€‡à€‚à€žà„à€Ÿà„‰à€Č à€•à€°à„‚à€š, à€€à„à€źà„‍à€čà„€ à€žà€čà€źà€€à„€ à€Šà„‡à€€à€Ÿ à€•à„€ à€€à„‡ à€”à€Ÿà€Șà€°à€Čà„‍à€Żà€Ÿà€šà„‡ à€€à„à€źà€šà„‍à€Żà€Ÿ à€Ÿà„…à€Źà€Čà„‡à€Ÿà€šà„‡ à€•à„‹à€Łà€€à„‍à€Żà€Ÿà€čà„€ à€Șà„à€°à€•à€Ÿà€°à„‡ à€čà„‹à€Łà€Ÿà€°à„‡ à€šà„à€•à€žà€Ÿà€š à€•à€żà€‚à€”à€Ÿ à€Ąà„‡à€Ÿà€Ÿ à€čà€Ÿà€šà„€à€žà€Ÿà€ à„€ à€€à„à€źà„‍à€čà„€ à€œà€Źà€Ÿà€Źà€Šà€Ÿà€° à€†à€čà€Ÿà€€."</string>
     <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"à€€à„à€źà€šà€Ÿ à€Ÿà„€à€”à„‍à€čà„€ à€†à€Łà€ż à€”à„ˆà€Żà€•à„à€€à€żà€• à€Ąà„‡à€Ÿà€Ÿ à€…à€œà„à€žà€Ÿà€€ à€…‍à„…à€Șà„‍à€žà€•à€Ąà„‚à€š à€čà„‹à€Łà€Ÿà€°à„‍à€Żà€Ÿ à€…à€Ÿà„…à€•à€źà„à€łà„‡ à€…à€§à€żà€• à€…à€žà„à€°à€•à„à€·à€żà€€ à€†à€čà„‡. à€čà„‡ à€…‍à„…à€Ș à€‡à€‚à€žà„à€Ÿà„‰à€Č à€•à€°à„‚à€š, à€€à„à€źà„à€čà„€ à€žà€čà€źà€€à„€ à€Šà„‡à€€à€Ÿ à€•à„€ à€€à„‡ à€”à€Ÿà€Șà€°à€Čà„‍à€Żà€Ÿà€šà„‡ à€€à„à€źà€šà„‍à€Żà€Ÿ à€Ÿà„€à€”à„‍à€čà„€à€šà„‡ à€•à„‹à€Łà€€à„‍à€Żà€Ÿà€čà„€ à€Șà„à€°à€•à€Ÿà€°à„‡ à€čà„‹à€Łà€Ÿà€°à„‡ à€šà„à€•à€žà€Ÿà€š à€•à€żà€‚à€”à€Ÿ à€Ąà„‡à€Ÿà€Ÿ à€čà€Ÿà€šà„€à€žà€Ÿà€ à„€ à€€à„à€źà„‍à€čà„€ à€œà€Źà€Ÿà€Źà€Šà€Ÿà€° à€†à€čà€Ÿà€€."</string>
     <string name="cloned_app_label" msgid="7503612829833756160">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> à€•à„à€Čà„‹à€š à€•à„‡à€Čà„‡à€Čà„‡"</string>
-    <!-- no translation found for archiving_app_label (1127085259724124725) -->
-    <skip />
+    <string name="archiving_app_label" msgid="1127085259724124725">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> à€žà€‚à€—à„à€°à€čà€żà€€ à€•à€°à€Ÿà€Żà€šà„‡ à€†à€čà„‡ à€•à€Ÿ?"</string>
     <string name="anonymous_source_continue" msgid="4375745439457209366">"à€žà„à€°à„‚ à€ à„‡à€”à€Ÿ"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"à€žà„‡à€Ÿà€żà€‚à€—à„à€œ"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"wear à€…‍à„…à€Șà„à€ž à€‡à€‚à€žà„à€Ÿà„‰à€Č/à€…à€šà€‡à€‚à€žà„à€Ÿà„‰à€Č à€•à€°à€€ à€†à€čà„‡"</string>
diff --git a/packages/PackageInstaller/res/values-ms/strings.xml b/packages/PackageInstaller/res/values-ms/strings.xml
index 489c74e..5b4ed96 100644
--- a/packages/PackageInstaller/res/values-ms/strings.xml
+++ b/packages/PackageInstaller/res/values-ms/strings.xml
@@ -44,8 +44,7 @@
     <string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Apl yang tidak diketahui tidak boleh dipasang oleh pengguna ini"</string>
     <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Pengguna ini tidak dibenarkan memasang apl"</string>
     <string name="ok" msgid="7871959885003339302">"OK"</string>
-    <!-- no translation found for archive (4447791830199354721) -->
-    <skip />
+    <string name="archive" msgid="4447791830199354721">"Arkib"</string>
     <string name="update_anyway" msgid="8792432341346261969">"Kemas kinikan juga"</string>
     <string name="manage_applications" msgid="5400164782453975580">"Urus apl"</string>
     <string name="out_of_space_dlg_title" msgid="4156690013884649502">"Kehabisan ruang"</string>
@@ -60,16 +59,11 @@
     <string name="uninstall_update_title" msgid="824411791011583031">"Nyahpasang kemas kini"</string>
     <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> merupakan sebahagian daripada apl berikut:"</string>
     <string name="uninstall_application_text" msgid="3816830743706143980">"Adakah anda mahu menyahpasang apl ini?"</string>
-    <!-- no translation found for archive_application_text (8482325710714386348) -->
-    <skip />
-    <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
-    <skip />
-    <!-- no translation found for archive_application_text_user (2586558895535581451) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
-    <skip />
+    <string name="archive_application_text" msgid="8482325710714386348">"Data peribadi anda akan disimpan"</string>
+    <string name="archive_application_text_all_users" msgid="3151229641681672580">"Arkibkan apl ini untuk semua pengguna? Data peribadi anda akan disimpan"</string>
+    <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Arkibkan apl ini dalam profil kerja anda? Data peribadi anda akan disimpan"</string>
+    <string name="archive_application_text_user" msgid="2586558895535581451">"Arkibkan apl ini untuk <xliff:g id="USERNAME">%1$s</xliff:g>? Data peribadi anda akan disimpan"</string>
+    <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Adakah anda mahu mengarkibkan apl ini daripada ruang peribadi anda? Data peribadi anda akan disimpan"</string>
     <string name="uninstall_application_text_all_users" msgid="575491774380227119">"Adakah anda mahu menyahpasang apl ini untuk "<b>"semua"</b>" pengguna? Aplikasi dan datanya akan dialih keluar daripada "<b>"semua"</b>" pengguna pada peranti."</string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Adakah anda ingin menyahpasang apl ini untuk pengguna <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
     <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Adakah anda mahu menyahpasang apl ini daripada profil kerja anda?"</string>
@@ -108,8 +102,7 @@
     <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Tablet dan data peribadi anda lebih mudah diserang oleh apl yang tidak diketahui. Dengan memasang apl ini, anda bersetuju bahawa anda bertanggungjawab atas sebarang kerosakan pada tablet anda atau kehilangan data yang mungkin disebabkan oleh penggunaan apl tersebut."</string>
     <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"TV dan data peribadi anda lebih mudah diserang oleh apl yang tidak diketahui. Dengan memasang apl ini, anda bersetuju bahawa anda bertanggungjawab atas sebarang kerosakan pada TV anda atau kehilangan data yang mungkin disebabkan oleh penggunaan apl tersebut."</string>
     <string name="cloned_app_label" msgid="7503612829833756160">"Klon <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
-    <!-- no translation found for archiving_app_label (1127085259724124725) -->
-    <skip />
+    <string name="archiving_app_label" msgid="1127085259724124725">"Arkibkan <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Teruskan"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Tetapan"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Memasang/menyahpasang apl wear"</string>
diff --git a/packages/PackageInstaller/res/values-my/strings.xml b/packages/PackageInstaller/res/values-my/strings.xml
index bc2cb8e..d34d363 100644
--- a/packages/PackageInstaller/res/values-my/strings.xml
+++ b/packages/PackageInstaller/res/values-my/strings.xml
@@ -44,8 +44,7 @@
     <string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"အရငá€șá€žá€Ąá€™á€Œá€…á€șမသိသေဏ အကá€șပá€șမျဏသကို á€€á€Ąá€žá€Żá€¶á€žá€•á€Œá€Żá€žá€°á€€ ထည့á€șသလငá€șသခလင့á€șမရဟိပါ"</string>
     <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"á€€á€Ąá€žá€Żá€¶á€žá€•á€Œá€Żá€žá€°á€žá€Šá€ș အကá€șပá€șမျဏသကို ထည့á€șသလငá€șသခလင့á€șမရဟိပါ"</string>
     <string name="ok" msgid="7871959885003339302">"OK"</string>
-    <!-- no translation found for archive (4447791830199354721) -->
-    <skip />
+    <string name="archive" msgid="4447791830199354721">"သိမá€șှရနá€ș"</string>
     <string name="update_anyway" msgid="8792432341346261969">"á€˜á€Źá€–á€Œá€…á€șဖဌစá€ș အပá€șဒိတá€șလုပá€șရနá€ș"</string>
     <string name="manage_applications" msgid="5400164782453975580">"အကá€șပá€șá€…á€źá€™á€¶á€á€Œá€„á€șှ"</string>
     <string name="out_of_space_dlg_title" msgid="4156690013884649502">"á€”á€±á€›á€Źá€œá€œá€á€șမရဟိပါ"</string>
@@ -60,16 +59,11 @@
     <string name="uninstall_update_title" msgid="824411791011583031">"အပá€șဒိတá€șကို ဖယá€șá€›á€Ÿá€Źá€žá€›á€”á€ș"</string>
     <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> သညá€ș ဥေဏကá€șပါဥကá€șပá€ș၏ တစá€șစိတá€șတစá€șဒေသဖဌစá€șသညá€ș−"</string>
     <string name="uninstall_application_text" msgid="3816830743706143980">"ကဥကá€șပá€șကို ဖယá€șá€›á€Ÿá€Źá€žá€œá€­á€Żá€•á€«á€žá€œá€Źá€žá‹"</string>
-    <!-- no translation found for archive_application_text (8482325710714386348) -->
-    <skip />
-    <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
-    <skip />
-    <!-- no translation found for archive_application_text_user (2586558895535581451) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
-    <skip />
+    <string name="archive_application_text" msgid="8482325710714386348">"သင့á€șကိုယá€șရေသဥချကá€șအလကá€șကို သိမá€șသပါမညá€ș"</string>
+    <string name="archive_application_text_all_users" msgid="3151229641681672580">"ကဥကá€șပá€șကို á€Ąá€žá€Żá€¶á€žá€•á€Œá€Żá€žá€°á€Ąá€Źá€žá€œá€Żá€¶á€žá€Ąá€á€œá€€á€ș သိမá€șှမလာှ။ သင့á€șကိုယá€șရေသဥချကá€șအလကá€șကို သိမá€șသပါမညá€ș"</string>
+    <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"ကဥကá€șပá€șကို သင့á€șအလုပá€șပရိုဖိုငá€șတလငá€ș သိမá€șှမလာှ။ သင့á€șကိုယá€șရေသဥချကá€șအလကá€șကို သိမá€șသပါမညá€ș"</string>
+    <string name="archive_application_text_user" msgid="2586558895535581451">"ကဥကá€șပá€șကို <xliff:g id="USERNAME">%1$s</xliff:g> á€Ąá€á€œá€€á€ș သိမá€șှမလာှ။ သင့á€șကိုယá€șရေသဥချကá€șအလကá€șကို သိမá€șသပါမညá€ș"</string>
+    <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"ကဥကá€șပá€șကို သင့á€șသဟသသန့á€șá€”á€±á€›á€Źá€á€œá€„á€ș သိမá€șှလိုသလာှ။ သင့á€șကိုယá€șရေသဥချကá€șအလကá€șကို သိမá€șသပါမညá€ș"</string>
     <string name="uninstall_application_text_all_users" msgid="575491774380227119">"á€€á€Ąá€•á€œá€źá€€á€±á€žá€›á€Ÿá€„á€șှကို á€Ąá€žá€Żá€¶á€žá€•á€Œá€Żá€žá€° "<b>"á€Ąá€Źá€žá€œá€Żá€¶á€ž"</b>" á€Ąá€á€œá€€á€ș ဖယá€șá€›á€Ÿá€Źá€žá€œá€­á€Żá€•á€«á€žá€œá€Źá€žá‹ á€€á€Ąá€•á€œá€źá€€á€±á€žá€›á€Ÿá€„á€șသနဟင့á€ș သကá€șဆိုငá€șရာ ဥချကá€șအလကá€șမျဏသ á€Ąá€Źá€žá€œá€Żá€¶á€žá€€á€­á€Ż "<b>" က "</b>" စကá€șá€Ąá€žá€Żá€¶á€žá€•á€Œá€Żá€žá€°á€™á€»á€Źá€žá€Ąá€á€œá€€á€ș ဖယá€șá€›á€Ÿá€Źá€žá€œá€­á€Żá€€á€șပါမညá€ș။"</string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"သငá€șသညá€ș á€Ąá€žá€Żá€¶á€žá€•á€Œá€Żá€žá€° <xliff:g id="USERNAME">%1$s</xliff:g> á€Ąá€á€œá€€á€ș ကဥကေဏင့á€șကို ဖယá€șá€›á€Ÿá€Źá€žá€œá€­á€Żá€•á€«á€žá€œá€Źá€žá‹"</string>
     <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"သင့á€șအလုပá€șပရိုဖိုငá€șကနေ ကဥကá€șပá€șကို ဖယá€șá€›á€Ÿá€Źá€žá€œá€­á€Żá€á€Ź á€žá€±á€á€»á€Źá€•á€«á€žá€œá€Źá€žá‹"</string>
@@ -108,8 +102,7 @@
     <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"သငá€ș၏ တကá€șဘလကá€șနဟင့á€ș ကိုယá€șရေသကိုယá€șတာ ဥချကá€șအလကá€șမျဏသသညá€ș ဥမျိုသဥမညá€ș မသိသေဏဥကá€șပá€șá€™á€»á€Źá€žá တိုကá€șခိုကá€șခဌငá€șှကို ပိုမိုခံရနိုငá€șပါသညá€ș။ ကဥကá€șပá€șကို ထည့á€șသလငá€șá€žá€Ąá€žá€Żá€¶á€žá€•á€Œá€Żá€á€Œá€„á€șá€žá€Ąá€Źá€žá€–á€Œá€„á€·á€ș ဖဌစá€șပေါá€șလာနိုငá€șသေဏ တကá€șဘလကá€șပျကá€șá€…á€źá€žá€™á€Ÿá€Ż á€žá€­á€Żá€·á€™á€Ÿá€Żá€á€ș á€’á€±á€á€Źá€†á€Żá€¶á€žá€›á€Ÿá€Żá€¶á€žá€™á€Ÿá€Żá€™á€»á€Źá€žá€Ąá€á€œá€€á€ș သင့á€șá€‘á€¶áŒá€žá€Ź တာဝနá€șá€›á€Ÿá€­á€€á€Œá€±á€Źá€„á€șှ á€žá€˜á€±á€Źá€á€°á€›á€Źá€›á€±á€Źá€€á€șပါသညá€ș။"</string>
     <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"သငá€ș၏ TV နဟင့á€ș ကိုယá€șရေသကိုယá€șတာ ဥချကá€șအလကá€șမျဏသသညá€ș ဥမျိုသဥမညá€ș မသိသေဏဥကá€șပá€șá€™á€»á€Źá€žá တိုကá€șခိုကá€șခဌငá€șှကို ပိုမိုခံရနိုငá€șပါသညá€ș။ ကဥကá€șပá€șကို ထည့á€șသလငá€șá€žá€Ąá€žá€Żá€¶á€žá€•á€Œá€Żá€á€Œá€„á€șá€žá€Ąá€Źá€žá€–á€Œá€„á€·á€ș ဖဌစá€șပေါá€șလာနိုငá€șသေဏ TV ပျကá€șá€…á€źá€žá€™á€Ÿá€Ż á€žá€­á€Żá€·á€™á€Ÿá€Żá€á€ș á€’á€±á€á€Źá€†á€Żá€¶á€žá€›á€Ÿá€Żá€¶á€žá€™á€Ÿá€Żá€™á€»á€Źá€žá€Ąá€á€œá€€á€ș သင့á€șá€‘á€¶áŒá€žá€Ź တာဝနá€șá€›á€Ÿá€­á€€á€Œá€±á€Źá€„á€șှ á€žá€˜á€±á€Źá€á€°á€›á€Źá€›á€±á€Źá€€á€șပါသညá€ș။"</string>
     <string name="cloned_app_label" msgid="7503612829833756160">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> á€•á€Żá€¶á€á€°á€•á€œá€Źá€ž"</string>
-    <!-- no translation found for archiving_app_label (1127085259724124725) -->
-    <skip />
+    <string name="archiving_app_label" msgid="1127085259724124725">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ကို သိမá€șှမလာှ။"</string>
     <string name="anonymous_source_continue" msgid="4375745439457209366">"ရဟေ့ဆကá€șရနá€ș"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"ဆကá€șတငá€șမျဏသ"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"wear အကá€șပá€șကိုထည့á€șသလငá€șသခဌငá€șှ/ဖယá€șá€›á€Ÿá€Źá€žá€á€Œá€„á€șှ"</string>
diff --git a/packages/PackageInstaller/res/values-nb/strings.xml b/packages/PackageInstaller/res/values-nb/strings.xml
index 2a24daa..0a5ea5f 100644
--- a/packages/PackageInstaller/res/values-nb/strings.xml
+++ b/packages/PackageInstaller/res/values-nb/strings.xml
@@ -44,8 +44,7 @@
     <string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Ukjente apper kan ikke installeres av denne brukeren"</string>
     <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Brukeren har ikke tillatelse til å installere apper"</string>
     <string name="ok" msgid="7871959885003339302">"OK"</string>
-    <!-- no translation found for archive (4447791830199354721) -->
-    <skip />
+    <string name="archive" msgid="4447791830199354721">"Arkivér"</string>
     <string name="update_anyway" msgid="8792432341346261969">"Oppdater likevel"</string>
     <string name="manage_applications" msgid="5400164782453975580">"Administrer apper"</string>
     <string name="out_of_space_dlg_title" msgid="4156690013884649502">"Tom for plass"</string>
@@ -60,16 +59,11 @@
     <string name="uninstall_update_title" msgid="824411791011583031">"Avinstaller oppdateringen"</string>
     <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> er del av følgende app:"</string>
     <string name="uninstall_application_text" msgid="3816830743706143980">"Vil du avinstallere denne appen?"</string>
-    <!-- no translation found for archive_application_text (8482325710714386348) -->
-    <skip />
-    <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
-    <skip />
-    <!-- no translation found for archive_application_text_user (2586558895535581451) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
-    <skip />
+    <string name="archive_application_text" msgid="8482325710714386348">"De personlige dataene dine blir lagret"</string>
+    <string name="archive_application_text_all_users" msgid="3151229641681672580">"Vil du arkivere denne appen for alle brukere? De personlige dataene dine blir lagret"</string>
+    <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Vil du arkivere denne appen på jobbprofilen din? De personlige dataene dine blir lagret"</string>
+    <string name="archive_application_text_user" msgid="2586558895535581451">"Vil du arkivere denne appen for <xliff:g id="USERNAME">%1$s</xliff:g>? De personlige dataene dine blir lagret"</string>
+    <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Vil du arkivere denne appen fra det private området ditt? De personlige dataene dine blir lagret"</string>
     <string name="uninstall_application_text_all_users" msgid="575491774380227119">"Vil du avinstallere denne appen for "<b>"alle"</b>" brukere? Appen og tilhørende data blir fjernet fra "<b>"alle"</b>" brukere på enheten."</string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Ønsker du å avinstallere denne appen for brukeren <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
     <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Vil du avinstallere denne appen fra jobbprofilen din?"</string>
@@ -108,8 +102,7 @@
     <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Nettbrettet ditt og de personlige dataene dine er mer sårbare for angrep fra ukjente apper. Når du installerer denne appen, samtykker du i at du er ansvarlig for eventuelle skader på nettbrettet eller tap av data som kan skyldes bruk av appen."</string>
     <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"TV-en din og de personlige dataene dine er mer sårbare for angrep fra ukjente apper. Når du installerer denne appen, samtykker du i at du er ansvarlig for eventuelle skader på TV-en eller tap av data som kan skyldes bruk av appen."</string>
     <string name="cloned_app_label" msgid="7503612829833756160">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>-klon"</string>
-    <!-- no translation found for archiving_app_label (1127085259724124725) -->
-    <skip />
+    <string name="archiving_app_label" msgid="1127085259724124725">"Vil du arkivere <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>?"</string>
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Fortsett"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Innstillinger"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Installerer/avinstallerer Wear-apper"</string>
diff --git a/packages/PackageInstaller/res/values-ne/strings.xml b/packages/PackageInstaller/res/values-ne/strings.xml
index 7dbe141..6ead922 100644
--- a/packages/PackageInstaller/res/values-ne/strings.xml
+++ b/packages/PackageInstaller/res/values-ne/strings.xml
@@ -44,8 +44,7 @@
     <string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"à€Żà„€ à€Șà„à€°à€Żà„‹à€—à€•à€°à„à€€à€Ÿ à€…à€œà„à€žà€Ÿà€€ à€à€Șà€čà€°à„‚ à€‡à€šà„à€žà„à€Ÿà€Č à€—à€°à„à€š à€žà€•à„à€šà„à€čà„à€šà„à€š"</string>
     <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"à€Żà„‹ à€Șà„à€°à€Żà„‹à€—à€•à€°à„à€€à€Ÿà€Čà€Ÿà€ˆ à€à€Șà€čà€°à„‚ à€‡à€šà„à€žà„à€Ÿà€Č à€—à€°à„à€šà„‡ à€…à€šà„à€źà€€à€ż à€›à„ˆà€š"</string>
     <string name="ok" msgid="7871959885003339302">"à€ à€żà€• à€›"</string>
-    <!-- no translation found for archive (4447791830199354721) -->
-    <skip />
+    <string name="archive" msgid="4447791830199354721">"à€…à€­à€żà€Čà„‡à€–à€źà€Ÿ à€°à€Ÿà€–à„à€šà„à€čà„‹à€žà„"</string>
     <string name="update_anyway" msgid="8792432341346261969">"à€œà„‡ à€­à€ à€Șà€šà€ż à€…à€Șà€Ąà„‡à€Ÿ à€—à€°à„à€šà„à€čà„‹à€žà„"</string>
     <string name="manage_applications" msgid="5400164782453975580">"à€à€Șà€•à„‹ à€Șà„à€°à€Źà€šà„à€§ à€—à€°à„à€šà„…"</string>
     <string name="out_of_space_dlg_title" msgid="4156690013884649502">"à€–à€Ÿà€Čà„€ à€ à€Ÿà€‰à€ à€›à„ˆà€š"</string>
@@ -60,16 +59,11 @@
     <string name="uninstall_update_title" msgid="824411791011583031">"à€…à€Šà„à€Żà€Ÿà€”à€§à€żà€•à€•à„‹ à€žà„à€„à€Ÿà€Șà€šà€Ÿ à€°à€Šà„à€Š à€—à€°à„à€šà„…"</string>
     <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> à€šà€żà€źà„à€š à€à€Șà€•à„‹ à€…à€‚à€¶ à€čà„‹:"</string>
     <string name="uninstall_application_text" msgid="3816830743706143980">"à€€à€Șà€Ÿà€ˆà€‚ à€Żà„‹ à€à€Ș à€…à€šà€‡à€šà„à€žà„à€Ÿà€Č à€—à€°à„à€š à€šà€Ÿà€čà€šà„à€čà„à€šà„à€›?"</string>
-    <!-- no translation found for archive_application_text (8482325710714386348) -->
-    <skip />
-    <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
-    <skip />
-    <!-- no translation found for archive_application_text_user (2586558895535581451) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
-    <skip />
+    <string name="archive_application_text" msgid="8482325710714386348">"à€€à€Șà€Ÿà€ˆà€‚à€•à„‹ à€”à„à€Żà€•à„à€€à€żà€—à€€ à€œà€Ÿà€šà€•à€Ÿà€°à„€ à€žà„‡à€­ à€—à€°à€żà€šà„‡ à€›"</string>
+    <string name="archive_application_text_all_users" msgid="3151229641681672580">"à€Żà„‹ à€à€Ș à€žà€Źà„ˆ à€Șà„à€°à€Żà„‹à€—à€•à€°à„à€€à€Ÿà€čà€°à„‚à€•à€Ÿ à€Čà€Ÿà€—à€ż à€…à€­à€ż‌à€Čà„‡à€–à€źà€Ÿ à€°à€Ÿà€–à„à€šà„‡ à€čà„‹? à€€à€Șà€Ÿà€ˆà€‚à€•à„‹ à€”à„à€Żà€•à„à€€à€żà€—à€€ à€œà€Ÿà€šà€•à€Ÿà€°à„€ à€žà„‡à€­ à€—à€°à€żà€šà„‡ à€›"</string>
+    <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"à€Żà„‹ à€à€Ș à€€à€Șà€Ÿà€ˆà€‚à€•à„‹ à€•à€Ÿà€°à„à€Ż à€Șà„à€°à„‹à€«à€Ÿà€‡à€Čà€Źà€Ÿà€Ÿ à€…à€­à€żà€Čà„‡à€–à€źà€Ÿ à€°à€Ÿà€–à„à€šà„‡ à€čà„‹? à€€à€Șà€Ÿà€ˆà€‚à€•à„‹ à€”à„à€Żà€•à„à€€à€żà€—à€€ à€œà€Ÿà€šà€•à€Ÿà€°à„€ à€žà„‡à€­ à€—à€°à€żà€šà„‡ à€›"</string>
+    <string name="archive_application_text_user" msgid="2586558895535581451">"à€Żà„‹ à€à€Ș <xliff:g id="USERNAME">%1$s</xliff:g> à€•à€Ÿ à€Čà€Ÿà€—à€ż à€…à€­à€żà€Čà„‡à€–à€źà€Ÿ à€°à€Ÿà€–à„à€šà„‡ à€čà„‹? à€€à€Șà€Ÿà€ˆà€‚à€•à„‹ à€”à„à€Żà€•à„à€€à€żà€—à€€ à€œà€Ÿà€šà€•à€Ÿà€°à„€ à€žà„‡à€­ à€—à€°à€żà€šà„‡ à€›"</string>
+    <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"à€€à€Șà€Ÿà€ˆà€‚ à€†à€«à„à€šà„‹ à€šà€żà€œà„€ à€žà„à€Șà„‡à€žà€Źà€Ÿà€Ÿ à€Żà„‹ à€à€Ș à€…à€­à€żà€Čà„‡à€–à€źà€Ÿ à€°à€Ÿà€–à„à€š à€šà€Ÿà€čà€šà„à€čà„à€šà„à€›? à€€à€Șà€Ÿà€ˆà€‚à€•à„‹ à€”à„à€Żà€•à„à€€à€żà€—à€€ à€œà€Ÿà€šà€•à€Ÿà€°à„€ à€žà„‡à€­ à€—à€°à€żà€šà„‡ à€›"</string>
     <string name="uninstall_application_text_all_users" msgid="575491774380227119">"à€€à€Șà€Ÿà€ˆà€‚ "<b>"à€žà€Źà„ˆ"</b>" à€Șà„à€°à€Żà„‹à€—à€•à€°à„à€€à€Ÿà€•à€Ÿ à€Čà€Ÿà€—à€ż à€Żà„‹ à€à€Ș à€…à€šà€‡à€šà„à€žà„à€Ÿà€Č à€—à€°à„à€š à€šà€Ÿà€čà€šà„à€čà„à€šà„à€›? à€Ąà€żà€­à€Ÿà€‡à€žà€•à€Ÿ "<b>"à€žà€Źà„ˆ"</b>" à€Șà„à€°à€Żà„‹à€—à€•à€°à„à€€à€Ÿà€čà€°à„‚à€Źà€Ÿà€Ÿ à€‰à€•à„à€€ à€à€Ș à€° à€Żà€žà€•à„‹ à€Ąà„‡à€Ÿà€Ÿ à€čà€Ÿà€Ÿà€‡à€šà„‡ à€›à„€"</string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"à€€à€Șà€Ÿà€ˆà€‚ à€Șà„à€°à€Żà„‹à€—à€•à€°à„à€€à€Ÿ <xliff:g id="USERNAME">%1$s</xliff:g> à€•à€Ÿ à€Čà€Ÿà€—à€ż à€Żà„‹ à€à€Ș à€…à€šà€‡à€šà„à€žà„à€Ÿà€Č à€—à€°à„à€š à€šà€Ÿà€čà€šà„à€čà„à€šà„à€›?"</string>
     <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"à€€à€Șà€Ÿà€ˆà€‚ à€†à€«à„à€šà„‹ à€•à€Ÿà€°à„à€Ż à€Șà„à€°à„‹à€«à€Ÿà€‡à€Čà€Źà€Ÿà€Ÿ à€Żà„‹ à€à€Ș à€…à€šà€‡à€šà„à€žà„à€Ÿà€Č à€—à€°à„à€š à€šà€Ÿà€čà€šà„à€čà„à€šà„à€›?"</string>
@@ -108,8 +102,7 @@
     <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"à€€à€Șà€Ÿà€ˆà€‚à€•à„‹ à€Ÿà„à€Żà€Ÿà€Źà„à€Čà„‡à€Ÿ à€€à€„à€Ÿ à€”à„à€Żà€•à„à€€à€żà€—à€€ à€Ąà„‡à€Ÿà€Ÿ à€…à€œà„à€žà€Ÿà€€ à€à€Șà€čà€°à„‚à€Źà€Ÿà€Ÿ à€čà„à€šà„‡ à€†à€•à„à€°à€źà€Łà€•à„‹ à€šà€Șà„‡à€Ÿà€Ÿà€źà€Ÿ à€Șà€°à„à€šà„‡ à€Źà€ąà„€ à€œà„‹à€–à€żà€źà€źà€Ÿ à€čà„à€šà„à€›à€šà„à„€ à€Żà„‹ à€à€Ș à€žà„à€„à€Ÿà€Șà€šà€Ÿ à€—à€°à„‡à€° à€€à€Șà€Ÿà€ˆà€‚ à€Żà€žà€•à„‹ à€Șà„à€°à€Żà„‹à€—à€Źà€Ÿà€Ÿ à€€à€Șà€Ÿà€ˆà€‚à€•à„‹ à€Ÿà„à€Żà€Ÿà€Źà„à€Čà„‡à€Ÿà€źà€Ÿ à€čà„à€š à€žà€•à„à€šà„‡ à€•à„à€·à€€à€ż à€”à€Ÿ à€Ąà„‡à€Ÿà€Ÿà€•à„‹ à€šà„‹à€•à„à€žà€Ÿà€šà„€à€•à€Ÿ à€Čà€Ÿà€—à€ż à€žà„à€”à€Żà€‚ à€œà€żà€źà„à€źà„‡à€”à€Ÿà€° à€čà„à€šà„‡ à€•à„à€°à€Ÿà€źà€Ÿ à€žà€čà€źà€€ à€čà„à€šà„à€čà„à€šà„à€›à„€"</string>
     <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"à€€à€Șà€Ÿà€ˆà€‚à€•à„‹ à€Ÿà€żà€­à„€ à€€à€„à€Ÿ à€”à„à€Żà€•à„à€€à€żà€—à€€ à€Ąà„‡à€Ÿà€Ÿ à€…à€œà„à€žà€Ÿà€€ à€à€Șà€čà€°à„‚à€Źà€Ÿà€Ÿ à€čà„à€šà„‡ à€†à€•à„à€°à€źà€Łà€•à„‹ à€šà€Șà„‡à€Ÿà€Ÿà€źà€Ÿ à€Șà€°à„à€šà„‡ à€Źà€ąà„€ à€œà„‹à€–à€żà€źà€źà€Ÿ à€čà„à€šà„à€›à€šà„à„€ à€Żà„‹ à€à€Ș à€žà„à€„à€Ÿà€Șà€šà€Ÿ à€—à€°à„‡à€° à€€à€Șà€Ÿà€ˆà€‚ à€Żà€žà€•à„‹ à€Șà„à€°à€Żà„‹à€—à€Źà€Ÿà€Ÿ à€€à€Șà€Ÿà€ˆà€‚à€•à„‹ à€Ÿà€żà€­à„€ à€źà€Ÿ à€čà„à€š à€žà€•à„à€šà„‡ à€•à„à€·à€€à€ż à€”à€Ÿ à€Ąà„‡à€Ÿà€Ÿà€•à„‹ à€šà„‹à€•à„à€žà€Ÿà€šà„€à€•à€Ÿ à€Čà€Ÿà€—à€ż à€žà„à€”à€Żà€‚ à€œà€żà€źà„à€źà„‡à€”à€Ÿà€° à€čà„à€šà„‡ à€•à„à€°à€Ÿà€źà€Ÿ à€žà€čà€źà€€ à€čà„à€šà„à€čà„à€šà„à€›à„€"</string>
     <string name="cloned_app_label" msgid="7503612829833756160">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> à€•à„à€Čà„‹à€š"</string>
-    <!-- no translation found for archiving_app_label (1127085259724124725) -->
-    <skip />
+    <string name="archiving_app_label" msgid="1127085259724124725">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> à€…à€­à€żà€Čà„‡à€–à€źà€Ÿ à€°à€Ÿà€–à„à€šà„‡ à€čà„‹?"</string>
     <string name="anonymous_source_continue" msgid="4375745439457209366">"à€œà€Ÿà€°à„€ à€°à€Ÿà€–à„à€šà„à€čà„‹à€žà„"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"à€žà„‡à€Ÿà€żà€™à€čà€°à„‚"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"à€”à„‡à€Żà€° à€à€Șà€čà€°à„‚à€•à„‹ à€žà„à€„à€Ÿà€Șà€šà€Ÿ/à€žà„à€„à€Ÿà€Șà€šà€Ÿ à€°à€Šà„à€Š à€—à€°à„à€Šà„ˆ"</string>
diff --git a/packages/PackageInstaller/res/values-nl/strings.xml b/packages/PackageInstaller/res/values-nl/strings.xml
index 4816bab..d5aac78 100644
--- a/packages/PackageInstaller/res/values-nl/strings.xml
+++ b/packages/PackageInstaller/res/values-nl/strings.xml
@@ -44,8 +44,7 @@
     <string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Onbekende apps kunnen niet worden geïnstalleerd door deze gebruiker"</string>
     <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Deze gebruiker mag geen apps installeren"</string>
     <string name="ok" msgid="7871959885003339302">"OK"</string>
-    <!-- no translation found for archive (4447791830199354721) -->
-    <skip />
+    <string name="archive" msgid="4447791830199354721">"Archiveren"</string>
     <string name="update_anyway" msgid="8792432341346261969">"Toch updaten"</string>
     <string name="manage_applications" msgid="5400164782453975580">"Apps beheren"</string>
     <string name="out_of_space_dlg_title" msgid="4156690013884649502">"Geen ruimte beschikbaar"</string>
@@ -60,16 +59,11 @@
     <string name="uninstall_update_title" msgid="824411791011583031">"Update verwijderen"</string>
     <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> maakt deel uit van de volgende app:"</string>
     <string name="uninstall_application_text" msgid="3816830743706143980">"Wil je deze app verwijderen?"</string>
-    <!-- no translation found for archive_application_text (8482325710714386348) -->
-    <skip />
-    <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
-    <skip />
-    <!-- no translation found for archive_application_text_user (2586558895535581451) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
-    <skip />
+    <string name="archive_application_text" msgid="8482325710714386348">"Je persoonsgegevens worden opgeslagen"</string>
+    <string name="archive_application_text_all_users" msgid="3151229641681672580">"Deze app archiveren voor alle gebruikers? Je persoonsgegevens worden opgeslagen."</string>
+    <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Deze app in je werkprofiel archiveren? Je persoonsgegevens worden opgeslagen."</string>
+    <string name="archive_application_text_user" msgid="2586558895535581451">"Deze app voor <xliff:g id="USERNAME">%1$s</xliff:g> archiveren? Je persoonsgegevens worden opgeslagen."</string>
+    <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Wil je deze app in je privéruimte archiveren? Je persoonsgegevens worden opgeslagen."</string>
     <string name="uninstall_application_text_all_users" msgid="575491774380227119">"Wil je deze app verwijderen voor "<b>"alle"</b>" gebruikers? Deze app en de gegevens ervan worden verwijderd voor "<b>"alle"</b>" gebruikers van het apparaat."</string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Wil je deze app verwijderen voor de gebruiker <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
     <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Wil je deze app verwijderen uit je werkprofiel?"</string>
@@ -108,8 +102,7 @@
     <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Je tablet en persoonsgegevens zijn kwetsbaarder voor aanvallen door onbekende apps. Als je deze app installeert, ga je ermee akkoord dat je verantwoordelijk bent voor eventuele schade aan je tablet of gegevensverlies als gevolg van het gebruik van de app."</string>
     <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Je tv en persoonsgegevens zijn kwetsbaarder voor aanvallen door onbekende apps. Als je deze app installeert, ga je ermee akkoord dat je verantwoordelijk bent voor eventuele schade aan je tv of gegevensverlies als gevolg van het gebruik van de app."</string>
     <string name="cloned_app_label" msgid="7503612829833756160">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>-kloon"</string>
-    <!-- no translation found for archiving_app_label (1127085259724124725) -->
-    <skip />
+    <string name="archiving_app_label" msgid="1127085259724124725">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> archiveren?"</string>
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Doorgaan"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Instellingen"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Wear-apps installeren/verwijderen"</string>
diff --git a/packages/PackageInstaller/res/values-or/strings.xml b/packages/PackageInstaller/res/values-or/strings.xml
index 70eab2d..a4060de 100644
--- a/packages/PackageInstaller/res/values-or/strings.xml
+++ b/packages/PackageInstaller/res/values-or/strings.xml
@@ -44,8 +44,7 @@
     <string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"àŹàŹčàŹż à­Ÿà­àŹœàŹ°àŹ™à­àŹ• àŹŠà­à­±àŹŸàŹ°àŹŸ àŹ…àŹœàŹŁàŹŸ àŹ†àŹȘ୍‍ àŹ‡àŹšàŹ·à­àŹŸàŹČ୍‍ àŹ•àŹ°àŹŸàŹŻàŹŸàŹ‡àŹȘàŹŸàŹ°àŹżàŹŹ àŹšàŹŸàŹčàŹżàŹ"</string>
     <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"àŹàŹčàŹż à­Ÿà­àŹœàŹ°à­‌ àŹ†àŹȘ୍‍ àŹ‡àŹšàŹ·à­àŹŸàŹČ୍‍ àŹ•àŹ°àŹżàŹȘàŹŸàŹ°àŹżàŹŹà­‡ àŹšàŹŸàŹčàŹżàŹ"</string>
     <string name="ok" msgid="7871959885003339302">"àŹ àŹżàŹ•à­ àŹ…àŹ›àŹż"</string>
-    <!-- no translation found for archive (4447791830199354721) -->
-    <skip />
+    <string name="archive" msgid="4447791830199354721">"àŹ†àŹ°à­àŹ•àŹŸàŹ‡àŹ­ àŹ•àŹ°àŹšà­àŹ€à­"</string>
     <string name="update_anyway" msgid="8792432341346261969">"àŹŻà­‡ àŹ•à­ŒàŹŁàŹžàŹż àŹźàŹ€à­‡ àŹ…àŹȘàŹĄà­‡àŹŸ àŹ•àŹ°àŹšà­àŹ€à­"</string>
     <string name="manage_applications" msgid="5400164782453975580">"àŹ†àŹȘ୍‌àŹ—à­àŹĄàŹŒàŹżàŹ•àŹ° àŹȘàŹ°àŹżàŹšàŹŸàŹłàŹšàŹŸ àŹ•àŹ°àŹšà­àŹ€à­"</string>
     <string name="out_of_space_dlg_title" msgid="4156690013884649502">"àŹ†àŹ‰ àŹžà­àŹ„àŹŸàŹš àŹšàŹŸàŹčàŹżàŹ"</string>
@@ -60,16 +59,11 @@
     <string name="uninstall_update_title" msgid="824411791011583031">"àŹ…àŹȘàŹĄà­‡àŹŸà­‍ àŹ…àŹšàŹ‡àŹšàŹ·à­àŹŸàŹČ୍‌ àŹ•àŹ°àŹšà­àŹ€à­"</string>
     <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> àŹčà­‡àŹ‰àŹ›àŹż àŹšàŹżàŹźà­àŹš àŹ†àŹȘ୍‍àŹ° àŹàŹ• àŹ…àŹ‚àŹ¶à„€"</string>
     <string name="uninstall_application_text" msgid="3816830743706143980">"àŹ†àŹȘàŹŁ àŹàŹčàŹż àŹ†àŹȘ୍‍ àŹ…àŹšàŹ‡àŹšàŹ·à­àŹŸàŹČ୍‌ àŹ•àŹ°àŹżàŹŹàŹŸàŹ•à­ àŹšàŹŸàŹčàŹŸàŹàŹšà­àŹ€àŹż àŹ•àŹż?"</string>
-    <!-- no translation found for archive_application_text (8482325710714386348) -->
-    <skip />
-    <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
-    <skip />
-    <!-- no translation found for archive_application_text_user (2586558895535581451) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
-    <skip />
+    <string name="archive_application_text" msgid="8482325710714386348">"àŹ†àŹȘàŹŁàŹ™à­àŹ• àŹŹà­à­ŸàŹ•à­àŹ€àŹżàŹ—àŹ€ àŹĄàŹŸàŹŸàŹŸ àŹžà­‡àŹ­ àŹčà­‹àŹ‡àŹŻàŹżàŹŹ"</string>
+    <string name="archive_application_text_all_users" msgid="3151229641681672580">"àŹžàŹźàŹžà­àŹ€ à­Ÿà­àŹœàŹ°àŹ™à­àŹ• àŹȘàŹŸàŹ‡àŹ àŹàŹčàŹż àŹ†àŹȘàŹ•à­ àŹ†àŹ°à­àŹ•àŹŸàŹ‡àŹ­ àŹ•àŹ°àŹżàŹŹà­‡? àŹ†àŹȘàŹŁàŹ™à­àŹ• àŹŹà­à­ŸàŹ•à­àŹ€àŹżàŹ—àŹ€ àŹĄàŹŸàŹŸàŹŸ àŹžà­‡àŹ­ àŹčà­‹àŹ‡àŹŻàŹżàŹŹ"</string>
+    <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"àŹ†àŹȘàŹŁàŹ™à­àŹ• à­±àŹŸàŹ°à­àŹ• àŹȘà­àŹ°à­‹àŹ«àŹŸàŹ‡àŹČàŹ°à­‡ àŹàŹčàŹż àŹ†àŹȘàŹ•à­ àŹ†àŹ°à­àŹ•àŹŸàŹ‡àŹ­ àŹ•àŹ°àŹżàŹŹà­‡? àŹ†àŹȘàŹŁàŹ™à­àŹ• àŹŹà­à­ŸàŹ•à­àŹ€àŹżàŹ—àŹ€ àŹĄàŹŸàŹŸàŹŸ àŹžà­‡àŹ­ àŹčà­‹àŹ‡àŹŻàŹżàŹŹ"</string>
+    <string name="archive_application_text_user" msgid="2586558895535581451">"<xliff:g id="USERNAME">%1$s</xliff:g>àŹ™à­àŹ• àŹȘàŹŸàŹ‡àŹ àŹàŹčàŹż àŹ†àŹȘàŹ•à­ àŹ†àŹ°à­àŹ•àŹŸàŹ‡àŹ­ àŹ•àŹ°àŹżàŹŹà­‡? àŹ†àŹȘàŹŁàŹ™à­àŹ• àŹŹà­à­ŸàŹ•à­àŹ€àŹżàŹ—àŹ€ àŹĄàŹŸàŹŸàŹŸ àŹžà­‡àŹ­ àŹčà­‹àŹ‡àŹŻàŹżàŹŹ"</string>
+    <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"àŹ†àŹȘàŹŁ àŹ†àŹȘàŹŁàŹ™à­àŹ• àŹȘà­àŹ°àŹŸàŹ‡àŹ­à­‡àŹŸ àŹžà­àŹȘà­‡àŹžàŹ°à­ àŹàŹčàŹż àŹ†àŹȘàŹ•à­ àŹ†àŹ°à­àŹ•àŹŸàŹ‡àŹ­ àŹ•àŹ°àŹżàŹŹàŹŸàŹ•à­ àŹšàŹŸàŹčàŹŸàŹàŹšà­àŹ€àŹż? àŹ†àŹȘàŹŁàŹ™à­àŹ• àŹŹà­à­ŸàŹ•à­àŹ€àŹżàŹ—àŹ€ àŹĄàŹŸàŹŸàŹŸ àŹžà­‡àŹ­ àŹčà­‹àŹ‡àŹŻàŹżàŹŹ"</string>
     <string name="uninstall_application_text_all_users" msgid="575491774380227119">"àŹ†àŹȘàŹŁ "<b>"àŹžàŹźàŹžà­àŹ€"</b>" à­Ÿà­àŹœàŹ°à­‌àŹ™à­àŹ• àŹȘàŹŸàŹ‡àŹ àŹàŹčàŹż àŹ†àŹȘ୍‌àŹ•à­ àŹ…àŹšàŹ·à­àŹŸàŹČ୍‍ àŹ•àŹ°àŹżàŹŹàŹŸàŹ•à­ àŹšàŹŸàŹčà­àŹàŹ›àŹšà­àŹ€àŹż àŹ•àŹż? àŹĄàŹżàŹ­àŹŸàŹ‡àŹžà­‌àŹ°à­‡ àŹ„àŹżàŹŹàŹŸ "<b>"àŹžàŹźàŹžà­àŹ€"</b>" à­Ÿà­àŹœàŹ°à­‌ àŹ†àŹȘ୍àŹČàŹżàŹ•à­‡àŹ¶àŹšà­‍ àŹàŹŹàŹ‚ àŹ€àŹŸàŹčàŹŸàŹ° àŹĄàŹŸàŹŸàŹŸ àŹŹàŹŸàŹčàŹŸàŹ° àŹ•àŹ°àŹżàŹŠàŹżàŹ†àŹŻàŹżàŹŹà„€"</string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"à­Ÿà­àŹœàŹ°à­‌ <xliff:g id="USERNAME">%1$s</xliff:g>àŹ™à­àŹ• àŹȘàŹŸàŹ‡àŹ àŹ†àŹȘàŹŁ àŹàŹčàŹż àŹ†àŹȘ୍‍ àŹ‡àŹšàŹ·à­àŹŸàŹČ୍‍ àŹ•àŹ°àŹżàŹŹà­‡ àŹ•àŹż?"</string>
     <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"àŹ†àŹȘàŹŁ àŹ†àŹȘàŹŁàŹ™à­àŹ• à­±àŹŸàŹ°à­àŹ• àŹȘà­àŹ°à­‹àŹ«àŹŸàŹ‡àŹČàŹ°à­ àŹàŹčàŹż àŹ†àŹȘàŹ•à­ àŹ…àŹšàŹ‡àŹšàŹ·à­àŹŸàŹČ àŹ•àŹ°àŹżàŹŹàŹŸàŹ•à­ àŹšàŹŸàŹčàŹŸàŹàŹšà­àŹ€àŹż àŹ•àŹż?"</string>
@@ -108,8 +102,7 @@
     <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"àŹ…àŹœàŹŁàŹŸ àŹ†àŹȘ୍‌ àŹŠà­à­±àŹŸàŹ°àŹŸ àŹ†àŹȘàŹŁàŹ™à­àŹ• àŹŸàŹŸàŹŹàŹČà­‡àŹŸà­‍ àŹàŹŹàŹ‚ àŹŹà­à­ŸàŹ•à­àŹ€àŹżàŹ—àŹ€ àŹĄàŹŸàŹŸàŹŸàŹ•à­ àŹšàŹ·à­àŹŸ àŹ•àŹ°àŹŸàŹŻàŹŸàŹ‡àŹȘàŹŸàŹ°àŹżàŹŹàŹŸàŹ° àŹžàŹźà­àŹ­àŹŸàŹŹàŹšàŹŸ àŹŹàŹčà­àŹ€ àŹ…àŹ§àŹżàŹ•à„€ àŹàŹčàŹż àŹ†àŹȘ୍‌àŹ•à­ àŹ‡àŹšàŹ·à­àŹŸàŹČ୍‌ àŹ•àŹ°àŹżàŹŹàŹŸàŹ° àŹ…àŹ°à­àŹ„ àŹčà­‡àŹ‰àŹ›àŹż àŹ†àŹȘàŹŁàŹ™à­àŹ• àŹŸàŹŸàŹŹà­‌àŹČà­‡àŹŸà­‌àŹ°à­‡ àŹ˜àŹŸàŹżàŹŹàŹŸ àŹ•à­ŒàŹŁàŹžàŹż àŹȘà­àŹ°àŹ•àŹŸàŹ° àŹ•à­àŹ·àŹ€àŹż àŹ•àŹżàŹźà­àŹŹàŹŸ àŹžà­‡àŹ—à­àŹĄàŹŒàŹżàŹ•àŹ° àŹŹà­à­ŸàŹŹàŹčàŹŸàŹ°àŹ°à­ àŹčà­‡àŹŹàŹŸ àŹ•à­ŒàŹŁàŹžàŹż àŹȘà­àŹ°àŹ•àŹŸàŹ° àŹĄàŹŸàŹŸàŹŸàŹ° àŹčàŹŸàŹšà­€ àŹȘàŹŸàŹ‡àŹ àŹ†àŹȘàŹŁ àŹŠàŹŸà­Ÿà­€ àŹ°àŹčàŹżàŹŹàŹŸàŹ•à­ àŹ°àŹŸàŹœàŹż àŹčà­àŹ…àŹšà­àŹ€àŹżà„€"</string>
     <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"àŹ…àŹœàŹŁàŹŸ àŹ†àŹȘ୍‌ àŹŠà­à­±àŹŸàŹ°àŹŸ àŹ†àŹȘàŹŁàŹ™à­àŹ• àŹŸàŹżàŹ­àŹż àŹàŹŹàŹ‚ àŹŹà­à­ŸàŹ•à­àŹ€àŹżàŹ—àŹ€ àŹĄàŹŸàŹŸàŹŸàŹ•à­ àŹšàŹ·à­àŹŸ àŹ•àŹ°àŹŸàŹŻàŹŸàŹ‡àŹȘàŹŸàŹ°àŹżàŹŹàŹŸàŹ° àŹžàŹźà­àŹ­àŹŸàŹŹàŹšàŹŸ àŹŹàŹčà­àŹ€ àŹ…àŹ§àŹżàŹ•à„€ àŹàŹčàŹż àŹ†àŹȘ୍‌àŹ•à­ àŹ‡àŹšàŹ·à­àŹŸàŹČ୍‌ àŹ•àŹ°àŹżàŹŹàŹŸàŹ° àŹ…àŹ°à­àŹ„ àŹčà­‡àŹ‰àŹ›àŹż àŹ†àŹȘàŹŁàŹ™à­àŹ• àŹŸàŹżàŹ­àŹżàŹ°à­‡ àŹ˜àŹŸàŹżàŹŹàŹŸ àŹ•à­ŒàŹŁàŹžàŹż àŹȘà­àŹ°àŹ•àŹŸàŹ° àŹ•à­àŹ·àŹ€àŹż àŹ•àŹżàŹźà­àŹŹàŹŸ àŹžà­‡àŹ—à­àŹĄàŹŒàŹżàŹ•àŹ° àŹŹà­à­ŸàŹŹàŹčàŹŸàŹ°àŹ°à­ àŹčà­‡àŹŹàŹŸ àŹ•à­ŒàŹŁàŹžàŹż àŹȘà­àŹ°àŹ•àŹŸàŹ° àŹĄàŹŸàŹŸàŹŸàŹ° àŹčàŹŸàŹšà­€ àŹȘàŹŸàŹ‡àŹ àŹ†àŹȘàŹŁ àŹŠàŹŸà­Ÿà­€ àŹ°àŹčàŹżàŹŹàŹŸàŹ•à­ àŹ°àŹŸàŹœàŹż àŹčà­àŹ…àŹšà­àŹ€àŹżà„€"</string>
     <string name="cloned_app_label" msgid="7503612829833756160">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> àŹ•à­àŹČà­‹àŹš"</string>
-    <!-- no translation found for archiving_app_label (1127085259724124725) -->
-    <skip />
+    <string name="archiving_app_label" msgid="1127085259724124725">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>àŹ•à­ àŹ†àŹ°à­àŹ•àŹŸàŹ‡àŹ­ àŹ•àŹ°àŹżàŹŹà­‡?"</string>
     <string name="anonymous_source_continue" msgid="4375745439457209366">"àŹœàŹŸàŹ°àŹż àŹ°àŹ–àŹšà­àŹ€à­"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"àŹžà­‡àŹŸàŹżàŹ‚àŹž"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"à­±àŹżàŹ…àŹ°à­‍ àŹ†àŹȘ୍‍ àŹ‡àŹšàŹ·à­àŹŸàŹČ୍‌/àŹ…àŹšàŹ‡àŹšàŹ·à­àŹŸàŹČ୍‍ àŹ•àŹ°àŹŸàŹŻàŹŸàŹ‰àŹ›àŹż"</string>
diff --git a/packages/PackageInstaller/res/values-pa/strings.xml b/packages/PackageInstaller/res/values-pa/strings.xml
index d91d032..c54d93f 100644
--- a/packages/PackageInstaller/res/values-pa/strings.xml
+++ b/packages/PackageInstaller/res/values-pa/strings.xml
@@ -44,8 +44,7 @@
     <string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"àš‡àšč àš”àš°àš€à©‹àš‚àš•àšŸàš° àš…àš—àšżàš†àš€ àšàšȘàšŸàš‚ àššà©‚à©° àšžàš„àšŸàšȘàš€ àššàščà©€àš‚ àš•àš° àšžàš•àšŠàšŸ"</string>
     <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"àš‡àšž àš”àš°àš€à©‹àš‚àš•àšŸàš° àššà©‚à©° àšàšȘàšŸàš‚ àšžàš„àšŸàšȘàš€ àš•àš°àšš àšŠà©€ àš‡àšœàšŸàšœàšŒàš€ àššàščà©€àš‚ àščੈ"</string>
     <string name="ok" msgid="7871959885003339302">"àš à©€àš• àščੈ"</string>
-    <!-- no translation found for archive (4447791830199354721) -->
-    <skip />
+    <string name="archive" msgid="4447791830199354721">"àšȘà©àš°àšŸàšČà©‡àš–àšŹà©±àš§ àš•àš°à©‹"</string>
     <string name="update_anyway" msgid="8792432341346261969">"àš«àšżàš° àš”à©€ àš…à©±àšȘàšĄà©‡àšŸ àš•àš°à©‹"</string>
     <string name="manage_applications" msgid="5400164782453975580">"àšàšȘàšŸàš‚ àšȘà©àš°àšŹà©°àš§àšżàš€ àš•àš°à©‹"</string>
     <string name="out_of_space_dlg_title" msgid="4156690013884649502">"àšœàš—à©àščàšŸ àš–àšŸàšČੀ àššàščà©€àš‚"</string>
@@ -60,16 +59,11 @@
     <string name="uninstall_update_title" msgid="824411791011583031">"àš…à©±àšȘàšĄà©‡àšŸ àš…àšŁàšžàš„àšŸàšȘàš€ àš•àš°à©‹"</string>
     <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> àš…à©±àš—à©‡ àšŠàšżà©±àš€à©€ àšàšȘ àšŠàšŸ àš­àšŸàš— àščੈ:"</string>
     <string name="uninstall_application_text" msgid="3816830743706143980">"àš•à©€ àš€à©àšžà©€àš‚ àš‡àšž àšàšȘ àššà©‚à©° àš…àšŁàšžàš„àšŸàšȘàš€ àš•àš°àššàšŸ àššàšŸàščà©à©°àšŠà©‡ àščੋ?"</string>
-    <!-- no translation found for archive_application_text (8482325710714386348) -->
-    <skip />
-    <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
-    <skip />
-    <!-- no translation found for archive_application_text_user (2586558895535581451) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
-    <skip />
+    <string name="archive_application_text" msgid="8482325710714386348">"àš€à©àščàšŸàšĄàšŸ àššàšżà©±àšœà©€ àšĄàšŸàšŸàšŸ àš°à©±àš–àšżàš…àš€ àš•à©€àš€àšŸ àšœàšŸàš”à©‡àš—àšŸ"</string>
+    <string name="archive_application_text_all_users" msgid="3151229641681672580">"àš•à©€ àš‡àšž àšàšȘ àššà©‚à©° àšžàšŸàš°à©‡ àš”àš°àš€à©‹àš‚àš•àšŸàš°àšŸàš‚ àšČàšˆ àšȘà©àš°àšŸàšČà©‡àš–àšŹà©±àš§ àš•àš°àššàšŸ àščੈ? àš€à©àščàšŸàšĄàšŸ àššàšżà©±àšœà©€ àšĄàšŸàšŸàšŸ àš°à©±àš–àšżàš…àš€ àš•à©€àš€àšŸ àšœàšŸàš”à©‡àš—àšŸ"</string>
+    <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"àš•à©€ àš‡àšž àšàšȘ àššà©‚à©° àš€à©àščàšŸàšĄà©‡ àš•àšŸàš°àšœ àšȘà©àš°à©‹àš«àšŸàšˆàšČ \'àš€à©‡ àšȘà©àš°àšŸàšČà©‡àš–àšŹà©±àš§ àš•àš°àššàšŸ àščੈ? àš€à©àščàšŸàšĄàšŸ àššàšżà©±àšœà©€ àšĄàšŸàšŸàšŸ àš°à©±àš–àšżàš…àš€ àš•à©€àš€àšŸ àšœàšŸàš”à©‡àš—àšŸ"</string>
+    <string name="archive_application_text_user" msgid="2586558895535581451">"àš•à©€ àš‡àšž àšàšȘ àššà©‚à©° <xliff:g id="USERNAME">%1$s</xliff:g> àšČàšˆ àšȘà©àš°àšŸàšČà©‡àš–àšŹà©±àš§ àš•àš°àššàšŸ àščੈ? àš€à©àščàšŸàšĄàšŸ àššàšżà©±àšœà©€ àšĄàšŸàšŸàšŸ àš°à©±àš–àšżàš…àš€ àš•à©€àš€àšŸ àšœàšŸàš”à©‡àš—àšŸ"</string>
+    <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"àš•à©€ àš€à©àšžà©€àš‚ àš‡àšž àšàšȘ àššà©‚à©° àš†àšȘàšŁà©€ àššàšżà©±àšœà©€ àšžàšȘà©‡àšž àš€à©‹àš‚ àšȘà©àš°àšŸàšČà©‡àš–àšŹà©±àš§ àš•àš°àššàšŸ àššàšŸàščà©à©°àšŠà©‡ àščੋ? àš€à©àščàšŸàšĄàšŸ àššàšżà©±àšœà©€ àšĄàšŸàšŸàšŸ àš°à©±àš–àšżàš…àš€ àš•à©€àš€àšŸ àšœàšŸàš”à©‡àš—àšŸ"</string>
     <string name="uninstall_application_text_all_users" msgid="575491774380227119">"àš•à©€ àš€à©àšžà©€àš‚ àš‡àšž àšàšȘ àššà©‚à©° "<b>"àšžàšŸàš°à©‡"</b>" àš”àš°àš€à©‹àš‚àš•àšŸàš°àšŸàš‚ àšČàšˆ àš…àšŁàšžàš„àšŸàšȘàš€ àš•àš°àššàšŸ àššàšŸàščà©à©°àšŠà©‡ àščੋ? àšàšȘàšČà©€àš•à©‡àšžàšŒàšš àš…àš€à©‡ àš‡àšžàšŠàšŸ àšĄàšŸàšŸàšŸ àšĄà©€àš”àšŸàšˆàšž \'àš€à©‡ "<b>"àšžàšŸàš°à©‡"</b>" àš”àš°àš€à©‹àš‚àš•àšŸàš°àšŸàš‚ àš”à©±àšČà©‹àš‚ àščàšŸàšŸ àšŠàšżà©±àš€àšŸ àšœàšŸàš”à©‡àš—àšŸà„€"</string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"àš•à©€ àš€à©àšžà©€àš‚ àš”àš°àš€à©‹àš‚àš•àšŸàš° <xliff:g id="USERNAME">%1$s</xliff:g> àšČàšˆ àš‡àšž àšàšȘ àššà©‚à©° àš…àšŁàšžàš„àšŸàšȘàš€ àš•àš°àššàšŸ àššàšŸàščà©à©°àšŠà©‡ àščੋ?"</string>
     <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"àš•à©€ àš€à©àšžà©€àš‚ àš‡àšž àšàšȘ àššà©‚à©° àš†àšȘàšŁà©‡ àš•àšŸàš°àšœ àšȘà©àš°à©‹àš«àšŸàšˆàšČ àš€à©‹àš‚ àš…àšŁàšžàš„àšŸàšȘàš€ àš•àš°àššàšŸ àššàšŸàščà©à©°àšŠà©‡ àščੋ?"</string>
@@ -108,8 +102,7 @@
     <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"àš€à©àščàšŸàšĄàšŸ àšŸà©ˆàšŹàšČà©ˆà©±àšŸ àš…àš€à©‡ àššàšżà©±àšœà©€ àšĄàšŸàšŸàšŸ àš…àš—àšżàš†àš€ àšàšȘàšŸàš‚ àš€à©‹àš‚ àščàšźàšČੇ àšȘà©àš°àš€à©€ àš”àš§à©‡àš°à©‡ àš”àšżà©°àššàšŁàšžàšŒà©€àšČ àščàššà„€ àš‡àšč àšàšȘ àšžàš„àšŸàšȘàš€ àš•àš°àš•à©‡, àš€à©àšžà©€àš‚ àšžàščàšżàšźàš€à©€ àšŠàšżà©°àšŠà©‡ àščੋ àš•àšż àš†àšȘàšŁà©‡ àšŸà©ˆàšŹàšČà©ˆà©±àšŸ àššà©‚à©° àščà©‹àšŁ àš”àšŸàšČੇ àš•àšżàšžà©‡ àš”à©€ àššà©àš•àšžàšŸàšš àšœàšŸàš‚ àšĄàšŸàšŸà©‡ àšŠà©€ àščàšŸàššà©€ àšČàšˆ àš€à©àšžà©€àš‚ àšœàšŒà©à©°àšźà©‡àš”àšŸàš° àščੋ àšœà©‹ àšžàšŒàšŸàš‡àšŠ àš‡àšž àšàšȘ àššà©‚à©° àš”àš°àš€àšŁ àšŠà©‡ àššàš€à©€àšœà©‡ àš”àšœà©‹àš‚ àščੋ àšžàš•àšŠàšŸ àščà©ˆà„€"</string>
     <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"àš€à©àščàšŸàšĄàšŸ àšŸà©€àš”à©€ àš…àš€à©‡ àššàšżà©±àšœà©€ àšĄàšŸàšŸàšŸ àš…àš—àšżàš†àš€ àšàšȘàšŸàš‚ àš€à©‹àš‚ àščàšźàšČੇ àšȘà©àš°àš€à©€ àš”àš§à©‡àš°à©‡ àš”àšżà©°àššàšŁàšžàšŒà©€àšČ àščàššà„€ àš‡àšč àšàšȘ àšžàš„àšŸàšȘàš€ àš•àš°àš•à©‡, àš€à©àšžà©€àš‚ àšžàščàšżàšźàš€à©€ àšŠàšżà©°àšŠà©‡ àščੋ àš•àšż àš†àšȘàšŁà©‡ àšŸà©€àš”à©€ àššà©‚à©° àščà©‹àšŁ àš”àšŸàšČੇ àš•àšżàšžà©‡ àš”à©€ àššà©àš•àšžàšŸàšš àšœàšŸàš‚ àšĄàšŸàšŸà©‡ àšŠà©€ àščàšŸàššà©€ àšČàšˆ àš€à©àšžà©€àš‚ àšœàšŒà©à©°àšźà©‡àš”àšŸàš° àščੋ àšœà©‹ àšžàšŒàšŸàš‡àšŠ àš‡àšž àšàšȘ àššà©‚à©° àš”àš°àš€àšŁ àšŠà©‡ àššàš€à©€àšœà©‡ àš”àšœà©‹àš‚ àščੋ àšžàš•àšŠàšŸ àščà©ˆà„€"</string>
     <string name="cloned_app_label" msgid="7503612829833756160">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> àšŠàšŸ àš•àšČà©‹àšš"</string>
-    <!-- no translation found for archiving_app_label (1127085259724124725) -->
-    <skip />
+    <string name="archiving_app_label" msgid="1127085259724124725">"àš•à©€ <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> àššà©‚à©° àšȘà©àš°àšŸàšČà©‡àš–àšŹà©±àš§ àš•àš°àššàšŸ àščੈ?"</string>
     <string name="anonymous_source_continue" msgid="4375745439457209366">"àšœàšŸàš°à©€ àš°à©±àš–à©‹"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"àšžà©ˆàšŸàšżà©°àš—àšŸàš‚"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"àš”à©€àš…àš° àšàšȘàšŸàš‚ àšžàš„àšŸàšȘàš€ àšœàšŸàš‚ àš…àšŁàšžàš„àšŸàšȘàš€ àš•àš°àššàšŸ"</string>
diff --git a/packages/PackageInstaller/res/values-pl/strings.xml b/packages/PackageInstaller/res/values-pl/strings.xml
index 41bec16..ec9ac15 100644
--- a/packages/PackageInstaller/res/values-pl/strings.xml
+++ b/packages/PackageInstaller/res/values-pl/strings.xml
@@ -44,8 +44,7 @@
     <string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Ten uĆŒytkownik nie moĆŒe instalować nieznanych aplikacji"</string>
     <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Ten uĆŒytkownik nie moĆŒe instalować aplikacji"</string>
     <string name="ok" msgid="7871959885003339302">"OK"</string>
-    <!-- no translation found for archive (4447791830199354721) -->
-    <skip />
+    <string name="archive" msgid="4447791830199354721">"Archiwizuj"</string>
     <string name="update_anyway" msgid="8792432341346261969">"Zaktualizuj mimo to"</string>
     <string name="manage_applications" msgid="5400164782453975580">"Zarządzaj aplikacjami"</string>
     <string name="out_of_space_dlg_title" msgid="4156690013884649502">"Brak miejsca"</string>
@@ -60,16 +59,11 @@
     <string name="uninstall_update_title" msgid="824411791011583031">"Odinstaluj aktualizację"</string>
     <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> jest częƛcią następującej aplikacji:"</string>
     <string name="uninstall_application_text" msgid="3816830743706143980">"Odinstalować tę aplikację?"</string>
-    <!-- no translation found for archive_application_text (8482325710714386348) -->
-    <skip />
-    <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
-    <skip />
-    <!-- no translation found for archive_application_text_user (2586558895535581451) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
-    <skip />
+    <string name="archive_application_text" msgid="8482325710714386348">"Twoje dane osobiste zostaną zapisane"</string>
+    <string name="archive_application_text_all_users" msgid="3151229641681672580">"Zarchiwizować tę aplikację dla wszystkich uĆŒytkowników? Twoje dane osobiste zostaną zapisane"</string>
+    <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Zarchiwizować tę aplikację w profilu sƂuĆŒbowym? Twoje dane osobiste zostaną zapisane"</string>
+    <string name="archive_application_text_user" msgid="2586558895535581451">"Zarchiwizować tę aplikację dla uĆŒytkownika <xliff:g id="USERNAME">%1$s</xliff:g>? Twoje dane osobiste zostaną zapisane"</string>
+    <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Chcesz zarchiwizować tę aplikację ze swojego obszaru prywatnego? Twoje dane osobiste zostaną zapisane"</string>
     <string name="uninstall_application_text_all_users" msgid="575491774380227119">"Chcesz odinstalować tę aplikację dla "<b>"wszystkich"</b>" uĆŒytkowników? Ta aplikacja i jej dane zostaną usunięte dla "<b>"wszystkich"</b>" uĆŒytkowników na urządzeniu."</string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Chcesz odinstalować tę aplikację dla uĆŒytkownika <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
     <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Czy chcesz odinstalować tę aplikację z profilu sƂuĆŒbowego?"</string>
@@ -108,8 +102,7 @@
     <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Dane na tablecie i prywatne są bardziej naraĆŒone na atak nieznanych aplikacji. Instalując tę aplikację, bierzesz na siebie odpowiedzialnoƛć za ewentualne uszkodzenie tabletu lub utratę danych w wyniku jej uĆŒywania."</string>
     <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Dane na telewizorze i prywatne są bardziej naraĆŒone na atak nieznanych aplikacji. Instalując tę aplikację, bierzesz na siebie odpowiedzialnoƛć za ewentualne uszkodzenie telewizora lub utratę danych w wyniku jej uĆŒywania."</string>
     <string name="cloned_app_label" msgid="7503612829833756160">"Klon aplikacji <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
-    <!-- no translation found for archiving_app_label (1127085259724124725) -->
-    <skip />
+    <string name="archiving_app_label" msgid="1127085259724124725">"Zarchiwizować aplikację <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>?"</string>
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Dalej"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Ustawienia"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Instalacja/usuwanie aplikacji na Wear"</string>
diff --git a/packages/PackageInstaller/res/values-pt-rBR/strings.xml b/packages/PackageInstaller/res/values-pt-rBR/strings.xml
index 9d943a5..9c540e7 100644
--- a/packages/PackageInstaller/res/values-pt-rBR/strings.xml
+++ b/packages/PackageInstaller/res/values-pt-rBR/strings.xml
@@ -44,8 +44,7 @@
     <string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Apps desconhecidos não podem ser instalados por este usuário"</string>
     <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Este usuário não tem permissão para instalar apps"</string>
     <string name="ok" msgid="7871959885003339302">"OK"</string>
-    <!-- no translation found for archive (4447791830199354721) -->
-    <skip />
+    <string name="archive" msgid="4447791830199354721">"Arquivar"</string>
     <string name="update_anyway" msgid="8792432341346261969">"Atualizar mesmo assim"</string>
     <string name="manage_applications" msgid="5400164782453975580">"Gerenciar apps"</string>
     <string name="out_of_space_dlg_title" msgid="4156690013884649502">"Sem espaço"</string>
@@ -60,16 +59,11 @@
     <string name="uninstall_update_title" msgid="824411791011583031">"Desinstalar atualização"</string>
     <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> é parte do seguinte app:"</string>
     <string name="uninstall_application_text" msgid="3816830743706143980">"Você quer desinstalar este app?"</string>
-    <!-- no translation found for archive_application_text (8482325710714386348) -->
-    <skip />
-    <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
-    <skip />
-    <!-- no translation found for archive_application_text_user (2586558895535581451) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
-    <skip />
+    <string name="archive_application_text" msgid="8482325710714386348">"Seus dados pessoais serão salvos"</string>
+    <string name="archive_application_text_all_users" msgid="3151229641681672580">"Arquivar esse app para todos os usuários? Seus dados pessoais serão salvos"</string>
+    <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Arquivar esse app no seu perfil de trabalho? Seus dados pessoais serão salvos"</string>
+    <string name="archive_application_text_user" msgid="2586558895535581451">"Arquivar esse app para <xliff:g id="USERNAME">%1$s</xliff:g>? Seus dados pessoais serão salvos"</string>
+    <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Você quer desinstalar esse app do seu espaço particular? Seus dados pessoais serão salvos"</string>
     <string name="uninstall_application_text_all_users" msgid="575491774380227119">"Quer desinstalar este app para "<b>"todos"</b>" os usuários? O aplicativo e os dados dele serão removidos para "<b>"todos"</b>" os usuários do dispositivo."</string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Quer desinstalar este app para o usuário <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
     <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Você quer desinstalar esse app do seu perfil de trabalho?"</string>
@@ -108,8 +102,7 @@
     <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Seu tablet e seus dados pessoais estão mais vulneráveis a ataques de apps desconhecidos. Ao instalar esse app, você concorda que é responsável por qualquer perda de dados ou dano ao dispositivo causados pelo uso desses apps."</string>
     <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Sua TV e seus dados pessoais estão mais vulneráveis a ataques de apps desconhecidos. Ao instalar esse app, você concorda que é responsável por qualquer perda de dados ou dano ao dispositivo causados pelo uso desses apps."</string>
     <string name="cloned_app_label" msgid="7503612829833756160">"Clone de <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
-    <!-- no translation found for archiving_app_label (1127085259724124725) -->
-    <skip />
+    <string name="archiving_app_label" msgid="1127085259724124725">"Arquivar <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>?"</string>
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Continuar"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Configurações"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Instalando/desinstalando apps do Wear"</string>
diff --git a/packages/PackageInstaller/res/values-pt/strings.xml b/packages/PackageInstaller/res/values-pt/strings.xml
index 9d943a5..9c540e7 100644
--- a/packages/PackageInstaller/res/values-pt/strings.xml
+++ b/packages/PackageInstaller/res/values-pt/strings.xml
@@ -44,8 +44,7 @@
     <string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Apps desconhecidos não podem ser instalados por este usuário"</string>
     <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Este usuário não tem permissão para instalar apps"</string>
     <string name="ok" msgid="7871959885003339302">"OK"</string>
-    <!-- no translation found for archive (4447791830199354721) -->
-    <skip />
+    <string name="archive" msgid="4447791830199354721">"Arquivar"</string>
     <string name="update_anyway" msgid="8792432341346261969">"Atualizar mesmo assim"</string>
     <string name="manage_applications" msgid="5400164782453975580">"Gerenciar apps"</string>
     <string name="out_of_space_dlg_title" msgid="4156690013884649502">"Sem espaço"</string>
@@ -60,16 +59,11 @@
     <string name="uninstall_update_title" msgid="824411791011583031">"Desinstalar atualização"</string>
     <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> é parte do seguinte app:"</string>
     <string name="uninstall_application_text" msgid="3816830743706143980">"Você quer desinstalar este app?"</string>
-    <!-- no translation found for archive_application_text (8482325710714386348) -->
-    <skip />
-    <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
-    <skip />
-    <!-- no translation found for archive_application_text_user (2586558895535581451) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
-    <skip />
+    <string name="archive_application_text" msgid="8482325710714386348">"Seus dados pessoais serão salvos"</string>
+    <string name="archive_application_text_all_users" msgid="3151229641681672580">"Arquivar esse app para todos os usuários? Seus dados pessoais serão salvos"</string>
+    <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Arquivar esse app no seu perfil de trabalho? Seus dados pessoais serão salvos"</string>
+    <string name="archive_application_text_user" msgid="2586558895535581451">"Arquivar esse app para <xliff:g id="USERNAME">%1$s</xliff:g>? Seus dados pessoais serão salvos"</string>
+    <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Você quer desinstalar esse app do seu espaço particular? Seus dados pessoais serão salvos"</string>
     <string name="uninstall_application_text_all_users" msgid="575491774380227119">"Quer desinstalar este app para "<b>"todos"</b>" os usuários? O aplicativo e os dados dele serão removidos para "<b>"todos"</b>" os usuários do dispositivo."</string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Quer desinstalar este app para o usuário <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
     <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Você quer desinstalar esse app do seu perfil de trabalho?"</string>
@@ -108,8 +102,7 @@
     <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Seu tablet e seus dados pessoais estão mais vulneráveis a ataques de apps desconhecidos. Ao instalar esse app, você concorda que é responsável por qualquer perda de dados ou dano ao dispositivo causados pelo uso desses apps."</string>
     <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Sua TV e seus dados pessoais estão mais vulneráveis a ataques de apps desconhecidos. Ao instalar esse app, você concorda que é responsável por qualquer perda de dados ou dano ao dispositivo causados pelo uso desses apps."</string>
     <string name="cloned_app_label" msgid="7503612829833756160">"Clone de <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
-    <!-- no translation found for archiving_app_label (1127085259724124725) -->
-    <skip />
+    <string name="archiving_app_label" msgid="1127085259724124725">"Arquivar <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>?"</string>
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Continuar"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Configurações"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Instalando/desinstalando apps do Wear"</string>
diff --git a/packages/PackageInstaller/res/values-ro/strings.xml b/packages/PackageInstaller/res/values-ro/strings.xml
index cf50304..0641370 100644
--- a/packages/PackageInstaller/res/values-ro/strings.xml
+++ b/packages/PackageInstaller/res/values-ro/strings.xml
@@ -44,8 +44,7 @@
     <string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Aplicațiile necunoscute nu pot fi instalate de acest utilizator"</string>
     <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Acest utilizator nu are permisiunea să instaleze aplicații"</string>
     <string name="ok" msgid="7871959885003339302">"OK"</string>
-    <!-- no translation found for archive (4447791830199354721) -->
-    <skip />
+    <string name="archive" msgid="4447791830199354721">"Arhivează"</string>
     <string name="update_anyway" msgid="8792432341346261969">"Actualizează oricum"</string>
     <string name="manage_applications" msgid="5400164782453975580">"Gestionează"</string>
     <string name="out_of_space_dlg_title" msgid="4156690013884649502">"Spațiu de stocare insuficient"</string>
@@ -60,16 +59,11 @@
     <string name="uninstall_update_title" msgid="824411791011583031">"Dezinstalează actualizarea"</string>
     <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> face parte din următoarea aplicație:"</string>
     <string name="uninstall_application_text" msgid="3816830743706143980">"Dezinstalezi această aplicație?"</string>
-    <!-- no translation found for archive_application_text (8482325710714386348) -->
-    <skip />
-    <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
-    <skip />
-    <!-- no translation found for archive_application_text_user (2586558895535581451) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
-    <skip />
+    <string name="archive_application_text" msgid="8482325710714386348">"Datele tale cu caracter personal vor fi salvate"</string>
+    <string name="archive_application_text_all_users" msgid="3151229641681672580">"Arhivezi aplicația pentru toți utilizatorii? Datele tale cu caracter personal vor fi salvate"</string>
+    <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Arhivezi aplicația din profilul de serviciu? Datele tale cu caracter personal vor fi salvate"</string>
+    <string name="archive_application_text_user" msgid="2586558895535581451">"Arhivezi aplicația pentru <xliff:g id="USERNAME">%1$s</xliff:g>? Datele tale cu caracter personal vor fi salvate"</string>
+    <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Arhivezi aplicația din spațiul privat? Datele tale cu caracter personal vor fi salvate"</string>
     <string name="uninstall_application_text_all_users" msgid="575491774380227119">"Dezinstalezi această aplicație pentru "<b>"toți"</b>" utilizatorii? Aplicația și datele acesteia vor fi eliminate de la "<b>"toți"</b>" utilizatorii de pe acest dispozitiv."</string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Dezinstalezi această aplicație pentru utilizatorul <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
     <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Dezinstalezi această aplicație din profilul de serviciu?"</string>
@@ -108,8 +102,7 @@
     <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Tableta și datele tale personale sunt mai vulnerabile la un atac din partea aplicațiilor necunoscute. Dacă instalezi aplicația, accepți că ești singura persoană responsabilă pentru deteriorarea tabletei sau pentru pierderea datelor, care pot avea loc în urma folosirii acesteia."</string>
     <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Televizorul și datele tale cu caracter personal sunt mai vulnerabile la un atac din partea aplicațiilor necunoscute. Dacă instalezi această aplicație, accepți că ești singura persoană responsabilă pentru deteriorarea televizorului sau pentru pierderea datelor, care pot avea loc în urma folosirii acesteia."</string>
     <string name="cloned_app_label" msgid="7503612829833756160">"Clonează <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
-    <!-- no translation found for archiving_app_label (1127085259724124725) -->
-    <skip />
+    <string name="archiving_app_label" msgid="1127085259724124725">"Arhivezi <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>?"</string>
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Continuă"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Setări"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Se (dez)instalează aplicațiile Wear"</string>
diff --git a/packages/PackageInstaller/res/values-ru/strings.xml b/packages/PackageInstaller/res/values-ru/strings.xml
index 2a85a0c..17222d1 100644
--- a/packages/PackageInstaller/res/values-ru/strings.xml
+++ b/packages/PackageInstaller/res/values-ru/strings.xml
@@ -44,8 +44,7 @@
     <string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Đ­Ń‚ĐŸŃ‚ ĐżĐŸĐ»ŃŒĐ·ĐŸĐČĐ°Ń‚Đ”Đ»ŃŒ ĐœĐ” ĐŒĐŸĐ¶Đ”Ń‚ ŃƒŃŃ‚Đ°ĐœĐ°ĐČлОĐČать ĐœĐ”ĐžĐ·ĐČĐ”ŃŃ‚ĐœŃ‹Đ” ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžŃ."</string>
     <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Đ­Ń‚ĐŸĐŒŃƒ ĐżĐŸĐ»ŃŒĐ·ĐŸĐČатДлю ĐœĐ” Ń€Đ°Đ·Ń€Đ”ŃˆĐ”ĐœĐŸ ŃƒŃŃ‚Đ°ĐœĐ°ĐČлОĐČать ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžŃ."</string>
     <string name="ok" msgid="7871959885003339302">"ОК"</string>
-    <!-- no translation found for archive (4447791830199354721) -->
-    <skip />
+    <string name="archive" msgid="4447791830199354721">"ОтпраĐČоть ĐČ Đ°Ń€Ń…ĐžĐČ"</string>
     <string name="update_anyway" msgid="8792432341346261969">"ВсД раĐČĐœĐŸ ĐŸĐ±ĐœĐŸĐČоть"</string>
     <string name="manage_applications" msgid="5400164782453975580">"УпраĐČĐ»Đ”ĐœĐžĐ” ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžŃĐŒĐž"</string>
     <string name="out_of_space_dlg_title" msgid="4156690013884649502">"ĐĐ”ĐŽĐŸŃŃ‚Đ°Ń‚ĐŸŃ‡ĐœĐŸ ĐŒĐ”ŃŃ‚Đ°"</string>
@@ -60,16 +59,11 @@
     <string name="uninstall_update_title" msgid="824411791011583031">"ĐŁĐŽĐ°Đ»ĐžŃ‚ŃŒ ĐŸĐ±ĐœĐŸĐČĐ»Đ”ĐœĐžĐ”"</string>
     <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> – часть ŃĐ»Đ”ĐŽŃƒŃŽŃ‰Đ”ĐłĐŸ ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžŃ:"</string>
     <string name="uninstall_application_text" msgid="3816830743706143980">"ĐŁĐŽĐ°Đ»ĐžŃ‚ŃŒ ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžĐ”?"</string>
-    <!-- no translation found for archive_application_text (8482325710714386348) -->
-    <skip />
-    <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
-    <skip />
-    <!-- no translation found for archive_application_text_user (2586558895535581451) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
-    <skip />
+    <string name="archive_application_text" msgid="8482325710714386348">"ĐŸĐ”Ń€ŃĐŸĐœĐ°Đ»ŃŒĐœŃ‹Đ” ĐŽĐ°ĐœĐœŃ‹Đ” Đ±ŃƒĐŽŃƒŃ‚ ŃĐŸŃ…Ń€Đ°ĐœĐ”ĐœŃ‹."</string>
+    <string name="archive_application_text_all_users" msgid="3151229641681672580">"ОтпраĐČоть ŃŃ‚ĐŸ ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžĐ” ĐČ Đ°Ń€Ń…ĐžĐČ ĐŽĐ»Ń ĐČсДх ĐżĐŸĐ»ŃŒĐ·ĐŸĐČатДлДĐč? ĐŸĐ”Ń€ŃĐŸĐœĐ°Đ»ŃŒĐœŃ‹Đ” ĐŽĐ°ĐœĐœŃ‹Đ” Đ±ŃƒĐŽŃƒŃ‚ ŃĐŸŃ…Ń€Đ°ĐœĐ”ĐœŃ‹."</string>
+    <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"ОтпраĐČоть ĐČ Đ°Ń€Ń…ĐžĐČ ŃŃ‚ĐŸ ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžĐ” Оз Ń€Đ°Đ±ĐŸŃ‡Đ”ĐłĐŸ ĐżŃ€ĐŸŃ„ĐžĐ»Ń? ĐŸĐ”Ń€ŃĐŸĐœĐ°Đ»ŃŒĐœŃ‹Đ” ĐŽĐ°ĐœĐœŃ‹Đ” Đ±ŃƒĐŽŃƒŃ‚ ŃĐŸŃ…Ń€Đ°ĐœĐ”ĐœŃ‹."</string>
+    <string name="archive_application_text_user" msgid="2586558895535581451">"ОтпраĐČоть ŃŃ‚ĐŸ ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžĐ” ĐČ Đ°Ń€Ń…ĐžĐČ ĐŽĐ»Ń ĐżĐŸĐ»ŃŒĐ·ĐŸĐČĐ°Ń‚Đ”Đ»Ń <xliff:g id="USERNAME">%1$s</xliff:g>? ĐŸĐ”Ń€ŃĐŸĐœĐ°Đ»ŃŒĐœŃ‹Đ” ĐŽĐ°ĐœĐœŃ‹Đ” Đ±ŃƒĐŽŃƒŃ‚ ŃĐŸŃ…Ń€Đ°ĐœĐ”ĐœŃ‹."</string>
+    <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"ОтпраĐČоть ĐČ Đ°Ń€Ń…ĐžĐČ ŃŃ‚ĐŸ ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžĐ” Оз Đ»ĐžŃ‡ĐœĐŸĐłĐŸ ĐżŃ€ĐŸŃŃ‚Ń€Đ°ĐœŃŃ‚ĐČа? ĐŸĐ”Ń€ŃĐŸĐœĐ°Đ»ŃŒĐœŃ‹Đ” ĐŽĐ°ĐœĐœŃ‹Đ” Đ±ŃƒĐŽŃƒŃ‚ ŃĐŸŃ…Ń€Đ°ĐœĐ”ĐœŃ‹."</string>
     <string name="uninstall_application_text_all_users" msgid="575491774380227119">"ĐŁĐŽĐ°Đ»ĐžŃ‚ŃŒ ŃŃ‚ĐŸ ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžĐ” ĐŽĐ»Ń "<b>"ĐČсДх"</b>" ĐżĐŸĐ»ŃŒĐ·ĐŸĐČатДлДĐč ŃƒŃŃ‚Ń€ĐŸĐčстĐČа? ĐžĐœĐž ĐżĐŸŃ‚Đ”Ń€ŃŃŽŃ‚ ĐŽĐŸŃŃ‚ŃƒĐż ĐșаĐș Đș ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžŃŽ, таĐș Đž Đș сĐČŃĐ·Đ°ĐœĐœŃ‹ĐŒ с ĐœĐžĐŒ ĐŽĐ°ĐœĐœŃ‹ĐŒ."<b></b></string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"ĐŁĐŽĐ°Đ»ĐžŃ‚ŃŒ ŃŃ‚ĐŸ ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžĐ” Оз ĐżŃ€ĐŸŃ„ĐžĐ»Ń <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
     <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"ĐŁĐŽĐ°Đ»ĐžŃ‚ŃŒ ŃŃ‚ĐŸ ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžĐ” Оз Ń€Đ°Đ±ĐŸŃ‡Đ”ĐłĐŸ ĐżŃ€ĐŸŃ„ĐžĐ»Ń?"</string>
@@ -108,8 +102,7 @@
     <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Вашо ĐżĐ”Ń€ŃĐŸĐœĐ°Đ»ŃŒĐœŃ‹Đ” ĐŽĐ°ĐœĐœŃ‹Đ” Đž ĐŽĐ°ĐœĐœŃ‹Đ” ĐżĐ»Đ°ĐœŃˆĐ”Ń‚Đ° Đ±ĐŸĐ»Đ”Đ” ŃƒŃĐ·ĐČĐžĐŒŃ‹ ĐŽĐ»Ń атаĐș ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžĐč Оз ĐœĐ”ĐžĐ·ĐČĐ”ŃŃ‚ĐœŃ‹Ń… ĐžŃŃ‚ĐŸŃ‡ĐœĐžĐșĐŸĐČ. ĐŁŃŃ‚Đ°ĐœĐ°ĐČлОĐČая ŃŃ‚ĐŸ ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžĐ”, ĐČы бДрДтД ĐœĐ° ŃĐ”Đ±Ń ĐČсю ĐŸŃ‚ĐČДтстĐČĐ”ĐœĐœĐŸŃŃ‚ŃŒ за ĐżĐŸŃĐ»Đ”ĐŽŃŃ‚ĐČоя, сĐČŃĐ·Đ°ĐœĐœŃ‹Đ” с Đ”ĐłĐŸ ĐžŃĐżĐŸĐ»ŃŒĐ·ĐŸĐČĐ°ĐœĐžĐ”ĐŒ, Ń‚ĐŸ Đ”ŃŃ‚ŃŒ за Đ»ŃŽĐ±ĐŸĐč ŃƒŃ‰Đ”Ń€Đ±, ĐœĐ°ĐœĐ”ŃĐ”ĐœĐœŃ‹Đč ĐżĐ»Đ°ĐœŃˆĐ”Ń‚Ńƒ, Đž ĐČĐŸĐ·ĐŒĐŸĐ¶ĐœŃƒŃŽ ĐżĐŸŃ‚Đ”Ń€ŃŽ ĐŽĐ°ĐœĐœŃ‹Ń…."</string>
     <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Вашо ĐżĐ”Ń€ŃĐŸĐœĐ°Đ»ŃŒĐœŃ‹Đ” ĐŽĐ°ĐœĐœŃ‹Đ” Đž ĐŽĐ°ĐœĐœŃ‹Đ” тДлДĐČĐžĐ·ĐŸŃ€Đ° Đ±ĐŸĐ»Đ”Đ” ŃƒŃĐ·ĐČĐžĐŒŃ‹ ĐŽĐ»Ń атаĐș ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžĐč Оз ĐœĐ”ĐžĐ·ĐČĐ”ŃŃ‚ĐœŃ‹Ń… ĐžŃŃ‚ĐŸŃ‡ĐœĐžĐșĐŸĐČ. ĐŁŃŃ‚Đ°ĐœĐ°ĐČлОĐČая ŃŃ‚ĐŸ ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžĐ”, ĐČы бДрДтД ĐœĐ° ŃĐ”Đ±Ń ĐČсю ĐŸŃ‚ĐČДтстĐČĐ”ĐœĐœĐŸŃŃ‚ŃŒ за ĐżĐŸŃĐ»Đ”ĐŽŃŃ‚ĐČоя, сĐČŃĐ·Đ°ĐœĐœŃ‹Đ” с Đ”ĐłĐŸ ĐžŃĐżĐŸĐ»ŃŒĐ·ĐŸĐČĐ°ĐœĐžĐ”ĐŒ, Ń‚ĐŸ Đ”ŃŃ‚ŃŒ за Đ»ŃŽĐ±ĐŸĐč ŃƒŃ‰Đ”Ń€Đ±, ĐœĐ°ĐœĐ”ŃĐ”ĐœĐœŃ‹Đč тДлДĐČĐžĐ·ĐŸŃ€Ńƒ, Đž ĐČĐŸĐ·ĐŒĐŸĐ¶ĐœŃƒŃŽ ĐżĐŸŃ‚Đ”Ń€ŃŽ ĐŽĐ°ĐœĐœŃ‹Ń…."</string>
     <string name="cloned_app_label" msgid="7503612829833756160">"ĐšĐ»ĐŸĐœ ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžŃ <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
-    <!-- no translation found for archiving_app_label (1127085259724124725) -->
-    <skip />
+    <string name="archiving_app_label" msgid="1127085259724124725">"ОтпраĐČоть ĐČ Đ°Ń€Ń…ĐžĐČ ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžĐ” \"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>\"?"</string>
     <string name="anonymous_source_continue" msgid="4375745439457209366">"ĐŸŃ€ĐŸĐŽĐŸĐ»Đ¶ĐžŃ‚ŃŒ"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"ĐĐ°ŃŃ‚Ń€ĐŸĐčĐșĐž"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"ĐŁŃŃ‚Đ°ĐœĐŸĐČĐșа/ŃƒĐŽĐ°Đ»Đ”ĐœĐžĐ” ĐżŃ€ĐžĐ»ĐŸĐ¶. ĐŽĐ»Ń Wear OS"</string>
diff --git a/packages/PackageInstaller/res/values-si/strings.xml b/packages/PackageInstaller/res/values-si/strings.xml
index ae94d48..8ba36b8 100644
--- a/packages/PackageInstaller/res/values-si/strings.xml
+++ b/packages/PackageInstaller/res/values-si/strings.xml
@@ -44,8 +44,7 @@
     <string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"ඞෙඞ ඎරිශීගකà¶șා ඞඟින් à¶±à·œà¶Żà¶±à·Šà¶±à· à¶șà·™à¶Żà·”à¶žà·Š à·ƒà·Šà¶źà·à¶Žà¶±à¶ș කළ නොහැක"</string>
     <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"ඞෙඞ ඎරිශීගකà¶șාට à¶șà·™à¶Żà·”à¶žà·Š à·ƒà·Šà¶źà·à¶Žà¶±à¶ș කිරීඞට අවසර නැත"</string>
     <string name="ok" msgid="7871959885003339302">"හරි"</string>
-    <!-- no translation found for archive (4447791830199354721) -->
-    <skip />
+    <string name="archive" msgid="4447791830199354721">"ගේඛනාරක්ෂණà¶ș"</string>
     <string name="update_anyway" msgid="8792432341346261969">"කෙසේ වෙතත් à¶șාවත්කාගීන කරන්න"</string>
     <string name="manage_applications" msgid="5400164782453975580">"à¶șà·™à¶Żà·”à¶žà·Š කළඞනාකරණà¶ș කරන්න"</string>
     <string name="out_of_space_dlg_title" msgid="4156690013884649502">"ඉඩ නොඞැත"</string>
@@ -60,16 +59,11 @@
     <string name="uninstall_update_title" msgid="824411791011583031">"à¶șාවත්කාගිනà¶ș à¶…à·ƒà·Šà¶źà·à¶Žà¶±à¶ș කරන්න"</string>
     <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> à¶Žà·„à¶­ à¶șà·™à¶Żà·”à¶žà·š කොටසකි:"</string>
     <string name="uninstall_application_text" msgid="3816830743706143980">"ඔබට ඞෙඞ à¶șà·™à¶Żà·”à¶ž à¶…à·ƒà·Šà¶źà·à¶Žà¶±à¶ș කිරීඞට අවශ්‍à¶șà¶Ż?"</string>
-    <!-- no translation found for archive_application_text (8482325710714386348) -->
-    <skip />
-    <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
-    <skip />
-    <!-- no translation found for archive_application_text_user (2586558895535581451) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
-    <skip />
+    <string name="archive_application_text" msgid="8482325710714386348">"ඔබේ à¶Žà·”à¶Żà·Šà¶œà¶œà·’à¶š à¶Żà¶­à·Šà¶­ සුරැකෙනු ඇත"</string>
+    <string name="archive_application_text_all_users" msgid="3151229641681672580">"සිà¶șගු ඎරිශීගකà¶șින් à·ƒà¶łà·„à· ඞෙඞ à¶șà·™à¶Żà·”à¶ž ගේඛනාරක්ෂණà¶șකරන්න à¶Ż? ඔබේ à¶Žà·”à¶Żà·Šà¶œà¶œà·’à¶š à¶Żà¶­à·Šà¶­ සුරැකෙනු ඇත"</string>
+    <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"ඞෙඞ à¶șà·™à¶Żà·”à¶ž ඔබේ කාර්à¶șාග ඎැතිකඩෙහි ගේඛනාරක්ෂණà¶ș කරන්න à¶Ż? ඔබේ à¶Žà·”à¶Żà·Šà¶œà¶œà·’à¶š à¶Żà¶­à·Šà¶­ සුරැකෙනු ඇත"</string>
+    <string name="archive_application_text_user" msgid="2586558895535581451">"<xliff:g id="USERNAME">%1$s</xliff:g> à·ƒà¶łà·„à· ඞෙඞ à¶șà·™à¶Żà·”à¶ž ගේඛනාරක්ෂණà¶ș කරන්න à¶Ż? ඔබේ à¶Žà·”à¶Żà·Šà¶œà¶œà·’à¶š à¶Żà¶­à·Šà¶­ සුරැකෙනු ඇත"</string>
+    <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"ඔබට ඔබේ à¶Žà·žà¶Żà·Šà¶œà¶œà·’à¶š අවකාශà¶șෙන් ඞෙඞ à¶șà·™à¶Żà·”à¶ž ගේඛනාරක්ෂණà¶ș කිරීඞට අවශ්‍à¶ș à¶Ż? ඔබේ à¶Žà·”à¶Żà·Šà¶œà¶œà·’à¶š à¶Żà¶­à·Šà¶­ සුරැකෙනු ඇත"</string>
     <string name="uninstall_application_text_all_users" msgid="575491774380227119"><b>"සිà¶șගු"</b>" ඎරිශීගකà¶șන් à·ƒà¶łà·„à· ඞෙඞ à¶șà·™à¶Żà·”à¶ž à¶…à·ƒà·Šà¶źà·à¶Žà¶±à¶ș කිරීඞට ඔබට අවශ්‍à¶șà¶Ż? උඎාංගà¶șෙහි "<b>"සිà¶șගු"</b>" ඎරිශීගකà¶șන් à·ƒà¶łà·„à· à¶șà·™à¶Żà·”à¶ž සහ එහි à¶Żà¶­à·Šà¶­ ඉවත්වනු ඇත."</string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"<xliff:g id="USERNAME">%1$s</xliff:g> ඎරිශීගකà¶șා à·ƒà¶łà·„à· ඞෙඞ à¶șà·™à¶Żà·”à¶ž à¶…à·ƒà·Šà¶źà·à¶Žà¶±à¶ș කිරීඞට ඔබට අවශ්‍à¶șà¶șà¶Ż?"</string>
     <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"ඔබට කාර්à¶șාග ඎැතිකඩ වෙතින් ඞෙඞ à¶șà·™à¶Żà·”à¶ž à¶…à·ƒà·Šà¶źà·à¶Žà¶±à¶ș කිරීඞට අවශ්‍à¶șà¶Ż?"</string>
@@ -108,8 +102,7 @@
     <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"ඔබගේ ටැබ්ගට් ඎරිගණකà¶ș සහ à¶Žà·”à¶Żà·Šà¶œà¶œà·’à¶š à¶Żà¶­à·Šà¶­à·€à¶œà¶§ à¶±à·œà¶Żà¶±à·Šà¶±à· à¶șà·™à¶Żà·”à¶žà·Š ඞඟින් à¶­à¶»à·Šà¶ąà¶± එග්ග කිරීඞේ හැකිà¶șාව වැඩිà¶ș. ඞෙඞ à¶șà·™à¶Żà·”à¶žà·Š à·ƒà·Šà¶źà·à¶Žà¶±à¶ș කිරීඞෙන් සහ භාවිත කිරීඞෙන් ඔබ ඔබේ ටැබ්ගට් ඎරිගණකà¶ș à·ƒà¶łà·„à· à·ƒà·’à¶Żà·” වන à¶șà¶žà·Š හානි හෝ එà¶ș භාවිත කිරීඞේ à¶Žà·Š‍රතිඔගà¶șක් ගෙස à·ƒà·’à¶Żà·” වන à¶Żà¶­à·Šà¶­ හානි à·ƒà¶łà·„à· ඔබ වගකිව à¶șුතු බවට එකඟ වේ."</string>
     <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"ඔබගේ TV සහ à¶Žà·”à¶Żà·Šà¶œà¶œà·’à¶š à¶Żà¶­à·Šà¶­à·€à¶œà¶§ à¶±à·œà¶Żà¶±à·Šà¶±à· à¶șà·™à¶Żà·”à¶žà·Š ඞඟින් à¶­à¶»à·Šà¶ąà¶± එග්ග කිරීඞේ හැකිà¶șාව වැඩිà¶ș. ඞෙඞ à¶șà·™à¶Żà·”à¶žà·Š à·ƒà·Šà¶źà·à¶Žà¶±à¶ș කිරීඞෙන් සහ භාවිත කිරීඞෙන් ඔබ ඔබේ TV à·ƒà¶łà·„à· à·ƒà·’à¶Żà·” වන à¶șà¶žà·Š හානි හෝ එà¶ș භාවිත කිරීඞේ à¶Žà·Š‍රතිඔගà¶șක් ගෙස à·ƒà·’à¶Żà·” වන à¶Żà¶­à·Šà¶­ හානි à·ƒà¶łà·„à· ඔබ වගකිව à¶șුතු බවට එකඟ වේ."</string>
     <string name="cloned_app_label" msgid="7503612829833756160">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ක්ගෝනà¶ș"</string>
-    <!-- no translation found for archiving_app_label (1127085259724124725) -->
-    <skip />
+    <string name="archiving_app_label" msgid="1127085259724124725">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ගේඛනාරක්ෂණà¶ș කරන්න à¶Ż?"</string>
     <string name="anonymous_source_continue" msgid="4375745439457209366">"à¶‰à¶Żà·’à¶»à·’à¶șà¶§ à¶șන්න"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"සැකසීඞ්"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Wear à¶șà·™à¶Żà·”à¶žà·Š à·ƒà·Šà¶źà·à¶Žà¶±à¶ș/à¶…à·ƒà·Šà¶źà·à¶Žà¶±à¶ș කරඞින්"</string>
diff --git a/packages/PackageInstaller/res/values-sk/strings.xml b/packages/PackageInstaller/res/values-sk/strings.xml
index 53d4a84..e2428cf 100644
--- a/packages/PackageInstaller/res/values-sk/strings.xml
+++ b/packages/PackageInstaller/res/values-sk/strings.xml
@@ -44,8 +44,7 @@
     <string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Tento pouĆŸívateÄŸ nemôĆŸe inštalovaĆ„ neznáme aplikácie"</string>
     <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Tento pouĆŸívateÄŸ nemá povolené inštalovaĆ„ aplikácie"</string>
     <string name="ok" msgid="7871959885003339302">"OK"</string>
-    <!-- no translation found for archive (4447791830199354721) -->
-    <skip />
+    <string name="archive" msgid="4447791830199354721">"ArchivovaƄ"</string>
     <string name="update_anyway" msgid="8792432341346261969">"Napriek tomu aktualizovaƄ"</string>
     <string name="manage_applications" msgid="5400164782453975580">"SpravovaĆ„ aplikácie"</string>
     <string name="out_of_space_dlg_title" msgid="4156690013884649502">"Nedostatok miesta"</string>
@@ -60,16 +59,11 @@
     <string name="uninstall_update_title" msgid="824411791011583031">"OdinštalovaĆ„ aktualizáciu"</string>
     <string name="uninstall_activity_text" msgid="1928194674397770771">"Aktivita <xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> je súčasĆ„ou nasledujúcej aplikácie:"</string>
     <string name="uninstall_application_text" msgid="3816830743706143980">"Chcete túto aplikáciu odinštalovaĆ„?"</string>
-    <!-- no translation found for archive_application_text (8482325710714386348) -->
-    <skip />
-    <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
-    <skip />
-    <!-- no translation found for archive_application_text_user (2586558895535581451) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
-    <skip />
+    <string name="archive_application_text" msgid="8482325710714386348">"Vaše osobné údaje budú uloĆŸené"</string>
+    <string name="archive_application_text_all_users" msgid="3151229641681672580">"Chcete túto aplikáciu archivovaĆ„ pre všetkých pouĆŸívateÄŸov? Vaše osobné údaje budú uloĆŸené."</string>
+    <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Chcete túto aplikáciu archivovaĆ„ vo svojom pracovnom profile? Vaše osobné údaje budú uloĆŸené."</string>
+    <string name="archive_application_text_user" msgid="2586558895535581451">"Chcete túto aplikáciu archivovaĆ„ pre pouĆŸíateÄŸa <xliff:g id="USERNAME">%1$s</xliff:g>? Vaše osobné údaje budú uloĆŸené."</string>
+    <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Chcete túto aplikáciu archivovaĆ„ zo svojho súkromného priestoru? Vaše osobné údaje budú uloĆŸené."</string>
     <string name="uninstall_application_text_all_users" msgid="575491774380227119">"Chcete odinštalovaĆ„ túto aplikáciu pre "<b>"všetkých"</b>" pouĆŸívateÄŸov? Aplikácia a jej údaje sa odstránia z tohto zariadenia pre "<b>"všetkých"</b>" pouĆŸívateÄŸov."</string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Chcete túto aplikáciu odinštalovaĆ„ pre pouĆŸívateÄŸa <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
     <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Chcete túto aplikáciu odinštalovaĆ„ zo svojho pracovného profilu?"</string>
@@ -108,8 +102,7 @@
     <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Váš tablet a osobné dáta sú náchylnejšie na útok z neznámych aplikácií. Inštaláciou tejto aplikácie vyjadrujete súhlas s tým, ĆŸe nesiete zodpovednosĆ„ za akékoÄŸvek poškodenie tabletu alebo stratu dát, ktoré by mohli nastaĆ„ pri jej pouĆŸívaní."</string>
     <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Váš televízor a osobné údaje sú náchylnejšie na útok z neznámych aplikácií. Inštaláciou tejto aplikácie vyjadrujete súhlas s tým, ĆŸe nesiete zodpovednosĆ„ za akékoÄŸvek poškodenie televízora alebo stratu údajov, ktoré by mohli nastaĆ„ pri jej pouĆŸívaní."</string>
     <string name="cloned_app_label" msgid="7503612829833756160">"Klon aplikácie <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
-    <!-- no translation found for archiving_app_label (1127085259724124725) -->
-    <skip />
+    <string name="archiving_app_label" msgid="1127085259724124725">"Chcete archivovaƄ <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>?"</string>
     <string name="anonymous_source_continue" msgid="4375745439457209366">"PokračovaĆ„"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Nastavenia"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Inštalácia/odinštalovanie aplikácií Wear"</string>
diff --git a/packages/PackageInstaller/res/values-sl/strings.xml b/packages/PackageInstaller/res/values-sl/strings.xml
index 2f6b697..a172dab 100644
--- a/packages/PackageInstaller/res/values-sl/strings.xml
+++ b/packages/PackageInstaller/res/values-sl/strings.xml
@@ -44,8 +44,7 @@
     <string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Ta uporabnik nima dovoljenja za namešÄanje neznanih aplikacij"</string>
     <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Ta uporabnik nima dovoljenja za namešÄanje aplikacij"</string>
     <string name="ok" msgid="7871959885003339302">"V redu"</string>
-    <!-- no translation found for archive (4447791830199354721) -->
-    <skip />
+    <string name="archive" msgid="4447791830199354721">"Arhiviranje"</string>
     <string name="update_anyway" msgid="8792432341346261969">"Vseeno posodobi"</string>
     <string name="manage_applications" msgid="5400164782453975580">"Upravlj. aplik."</string>
     <string name="out_of_space_dlg_title" msgid="4156690013884649502">"Zmanjkalo je prostora"</string>
@@ -60,16 +59,11 @@
     <string name="uninstall_update_title" msgid="824411791011583031">"Odstrani posodobitev"</string>
     <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> je del te aplikacije:"</string>
     <string name="uninstall_application_text" msgid="3816830743706143980">"Ali ĆŸelite odmestiti to aplikacijo?"</string>
-    <!-- no translation found for archive_application_text (8482325710714386348) -->
-    <skip />
-    <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
-    <skip />
-    <!-- no translation found for archive_application_text_user (2586558895535581451) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
-    <skip />
+    <string name="archive_application_text" msgid="8482325710714386348">"Vaši osebni podatki bodo shranjeni."</string>
+    <string name="archive_application_text_all_users" msgid="3151229641681672580">"Ali ĆŸelite to aplikacijo arhivirati za vse uporabnike? Vaši osebni podatki bodo shranjeni."</string>
+    <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Ali ĆŸelite to aplikacijo arhivirati v delovnem profilu? Vaši osebni podatki bodo shranjeni."</string>
+    <string name="archive_application_text_user" msgid="2586558895535581451">"Ali ĆŸelite arhivirati to aplikacijo za uporabnika <xliff:g id="USERNAME">%1$s</xliff:g>? Vaši osebni podatki bodo shranjeni."</string>
+    <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Ali ĆŸelite arhivirati to aplikacijo iz zasebnega prostora? Vaši osebni podatki bodo shranjeni."</string>
     <string name="uninstall_application_text_all_users" msgid="575491774380227119">"Ali ĆŸelite odstraniti aplikacijo za "<b>"vse"</b>" uporabnike? Aplikacija in njeni podatki bodo odstranjeni iz "<b>"vseh"</b>" uporabnikov v napravi."</string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Ali ĆŸelite to aplikacijo odstraniti za uporabnika <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
     <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Ali ĆŸelite to aplikacijo odmestiti iz delovnega profila?"</string>
@@ -108,8 +102,7 @@
     <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Neznane aplikacije lahko resno ogrozijo varnost tabličnega računalnika in osebnih podatkov. Z namestitvijo te aplikacije se strinjate, da ste sami odgovorni za morebitno škodo, nastalo v tabličnem računalniku, ali izgubo podatkov, do katerih lahko pride zaradi uporabe te aplikacije."</string>
     <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Neznane aplikacije lahko resno ogrozijo varnost televizorja in osebnih podatkov. Z namestitvijo te aplikacije se strinjate, da ste sami odgovorni za morebitno škodo, nastalo v televizorju, ali izgubo podatkov, do katerih lahko pride zaradi uporabe te aplikacije."</string>
     <string name="cloned_app_label" msgid="7503612829833756160">"Klonirana aplikacija <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
-    <!-- no translation found for archiving_app_label (1127085259724124725) -->
-    <skip />
+    <string name="archiving_app_label" msgid="1127085259724124725">"Ali ĆŸelite arhivirati paket <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>?"</string>
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Naprej"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Nastavitve"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"NamešÄanje/odstranjev. aplikacij za Wear"</string>
diff --git a/packages/PackageInstaller/res/values-sq/strings.xml b/packages/PackageInstaller/res/values-sq/strings.xml
index 1c4b354..f0bb589 100644
--- a/packages/PackageInstaller/res/values-sq/strings.xml
+++ b/packages/PackageInstaller/res/values-sq/strings.xml
@@ -44,8 +44,7 @@
     <string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Aplikacionet e panjohura nuk mund të instalohen nga ky përdorues"</string>
     <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Ky përdorues nuk lejohet të instalojë aplikacione"</string>
     <string name="ok" msgid="7871959885003339302">"Në rregull"</string>
-    <!-- no translation found for archive (4447791830199354721) -->
-    <skip />
+    <string name="archive" msgid="4447791830199354721">"Arkivo"</string>
     <string name="update_anyway" msgid="8792432341346261969">"Përditësoje gjithsesi"</string>
     <string name="manage_applications" msgid="5400164782453975580">"Menaxho aplikacionet"</string>
     <string name="out_of_space_dlg_title" msgid="4156690013884649502">"Nuk ka hapësirë"</string>
@@ -60,16 +59,11 @@
     <string name="uninstall_update_title" msgid="824411791011583031">"Çinstalo përditësimin"</string>
     <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> është pjesë e aplikacionit të mëposhtëm:"</string>
     <string name="uninstall_application_text" msgid="3816830743706143980">"Dëshiron ta çinstalosh këtë aplikacion?"</string>
-    <!-- no translation found for archive_application_text (8482325710714386348) -->
-    <skip />
-    <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
-    <skip />
-    <!-- no translation found for archive_application_text_user (2586558895535581451) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
-    <skip />
+    <string name="archive_application_text" msgid="8482325710714386348">"Të dhënat e tua personale do të ruhen"</string>
+    <string name="archive_application_text_all_users" msgid="3151229641681672580">"Të arkivohet ky aplikacion për të gjithë përdoruesit? Të dhënat e tua personale do të ruhen"</string>
+    <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Të arkivohet ky aplikacion në profilin tënd të punës? Të dhënat e tua personale do të ruhen"</string>
+    <string name="archive_application_text_user" msgid="2586558895535581451">"Të arkivohet ky aplikacion për <xliff:g id="USERNAME">%1$s</xliff:g>? Të dhënat e tua personale do të ruhen"</string>
+    <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Dëshiron ta arkivosh këtë aplikacion nga hapësira jote private? Të dhënat e tua personale do të ruhen"</string>
     <string name="uninstall_application_text_all_users" msgid="575491774380227119">"Dëshiron ta çinstalosh këtë aplikacion për "<b>"të gjithë"</b>" përdoruesit? Aplikacioni dhe të dhënat e tij do të hiqen nga "<b>"të gjithë"</b>" përdoruesit e pajisjes."</string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Dëshiron ta çinstalosh këtë aplikacion për përdoruesin <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
     <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Dëshiron ta çinstalosh këtë aplikacion nga profili yt i punës?"</string>
@@ -108,8 +102,7 @@
     <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Tableti dhe të dhënat e tua personale janë më të cenueshme nga sulmet nga aplikacione të panjohura. Duke instaluar këtë aplikacion, ti pranon se je përgjegjës për çdo dëm ndaj tabletit tënd ose çdo humbje të të dhënave që mund të rezultojë nga përdorimi i tij."</string>
     <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Televizori dhe të dhënat e tua personale janë më të cenueshme nga sulmet nga aplikacione të panjohura. Duke instaluar këtë aplikacion, ti pranon se je përgjegjës për çdo dëm ndaj televizorit tënd ose çdo humbje të të dhënave që mund të rezultojë nga përdorimi i tij."</string>
     <string name="cloned_app_label" msgid="7503612829833756160">"Klon i <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
-    <!-- no translation found for archiving_app_label (1127085259724124725) -->
-    <skip />
+    <string name="archiving_app_label" msgid="1127085259724124725">"Të arkivohet <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>?"</string>
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Vazhdo"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Cilësimet"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Instalimi/çinstalimi i aplikacioneve të Wear"</string>
diff --git a/packages/PackageInstaller/res/values-sr/strings.xml b/packages/PackageInstaller/res/values-sr/strings.xml
index 4402d7f..57dce51 100644
--- a/packages/PackageInstaller/res/values-sr/strings.xml
+++ b/packages/PackageInstaller/res/values-sr/strings.xml
@@ -44,8 +44,7 @@
     <string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"ОĐČај ĐșĐŸŃ€ĐžŃĐœĐžĐș ĐœĐ” ĐŒĐŸĐ¶Đ” Ўа ĐžĐœŃŃ‚Đ°Đ»ĐžŃ€Đ° ĐœĐ”ĐżĐŸĐ·ĐœĐ°Ń‚Đ” аплОĐșĐ°Ń†ĐžŃ˜Đ”"</string>
     <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"ОĐČĐŸĐŒ ĐșĐŸŃ€ĐžŃĐœĐžĐșу ĐœĐžŃ˜Đ” ĐŽĐŸĐ·ĐČĐŸŃ™Đ”ĐœĐŸ Ўа ĐžĐœŃŃ‚Đ°Đ»ĐžŃ€Đ° аплОĐșĐ°Ń†ĐžŃ˜Đ”"</string>
     <string name="ok" msgid="7871959885003339302">"ĐŸĐŸŃ‚ĐČрЮо"</string>
-    <!-- no translation found for archive (4447791830199354721) -->
-    <skip />
+    <string name="archive" msgid="4447791830199354721">"АрхоĐČорај"</string>
     <string name="update_anyway" msgid="8792432341346261969">"ИпаĐș Đ°Đ¶ŃƒŃ€ĐžŃ€Đ°Ń˜"</string>
     <string name="manage_applications" msgid="5400164782453975580">"УпраĐČŃ™Đ°Ń˜Ń‚Đ” апл."</string>
     <string name="out_of_space_dlg_title" msgid="4156690013884649502">"ĐĐ”ĐŒĐ° ĐČОшД ĐżŃ€ĐŸŃŃ‚ĐŸŃ€Đ°"</string>
@@ -60,16 +59,11 @@
     <string name="uninstall_update_title" msgid="824411791011583031">"Đ”Đ”ĐžĐœŃŃ‚Đ°Đ»ĐžŃ€Đ°Ń˜ Đ°Đ¶ŃƒŃ€ĐžŃ€Đ°ŃšĐ”"</string>
     <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> јД ĐŽĐ”ĐŸ слДЎДћД аплОĐșĐ°Ń†ĐžŃ˜Đ”:"</string>
     <string name="uninstall_application_text" msgid="3816830743706143980">"ЖДлОтД Ўа ĐŽĐ”ĐžĐœŃŃ‚Đ°Đ»ĐžŃ€Đ°Ń‚Đ” ĐŸĐČу аплОĐșацоју?"</string>
-    <!-- no translation found for archive_application_text (8482325710714386348) -->
-    <skip />
-    <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
-    <skip />
-    <!-- no translation found for archive_application_text_user (2586558895535581451) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
-    <skip />
+    <string name="archive_application_text" msgid="8482325710714386348">"Đ›ĐžŃ‡ĐœĐž ĐżĐŸĐŽĐ°Ń†Đž ћД бОтО сачуĐČĐ°ĐœĐž"</string>
+    <string name="archive_application_text_all_users" msgid="3151229641681672580">"ЖДлОтД лО Ўа архоĐČОратД ĐŸĐČу аплОĐșацоју за сĐČĐ” ĐșĐŸŃ€ĐžŃĐœĐžĐșĐ”? Đ›ĐžŃ‡ĐœĐž ĐżĐŸĐŽĐ°Ń†Đž ћД бОтО сачуĐČĐ°ĐœĐž"</string>
+    <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"ЖДлОтД лО Ўа архоĐČОратД ĐŸĐČу аплОĐșацоју са ĐżĐŸŃĐ»ĐŸĐČĐœĐŸĐł ĐżŃ€ĐŸŃ„ĐžĐ»Đ°? Đ›ĐžŃ‡ĐœĐž ĐżĐŸĐŽĐ°Ń†Đž ћД бОтО сачуĐČĐ°ĐœĐž"</string>
+    <string name="archive_application_text_user" msgid="2586558895535581451">"ЖДлОтД лО Ўа архоĐČОратД ĐŸĐČу аплОĐșацоју за ĐșĐŸŃ€ĐžŃĐœĐžĐșа <xliff:g id="USERNAME">%1$s</xliff:g>? Đ›ĐžŃ‡ĐœĐž ĐżĐŸĐŽĐ°Ń†Đž ћД бОтО сачуĐČĐ°ĐœĐž"</string>
+    <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"ЖДлОтД лО Ўа архоĐČОратД ĐŸĐČу аплОĐșацоју Оз проĐČĐ°Ń‚ĐœĐŸĐł ĐżŃ€ĐŸŃŃ‚ĐŸŃ€Đ°? Đ›ĐžŃ‡ĐœĐž ĐżĐŸĐŽĐ°Ń†Đž ћД бОтО сачуĐČĐ°ĐœĐž"</string>
     <string name="uninstall_application_text_all_users" msgid="575491774380227119">"Да лО жДлОтД Ўа ĐŽĐ”ĐžĐœŃŃ‚Đ°Đ»ĐžŃ€Đ°Ń‚Đ” ĐŸĐČу аплОĐșацоју за "<b>"сĐČĐ”"</b>" ĐșĐŸŃ€ĐžŃĐœĐžĐșĐ”? АплОĐșацоја Đž ĐżĐŸĐŽĐ°Ń†Đž уз њД бОћД уĐșĐ»ĐŸŃšĐ”ĐœĐž за "<b>"сĐČĐ”"</b>" ĐșĐŸŃ€ĐžŃĐœĐžĐșĐ” ĐŸĐČĐŸĐł ŃƒŃ€Đ”Ń’Đ°Ń˜Đ°."</string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"ЖДлОтД лО Ўа ĐŽĐ”ĐžĐœŃŃ‚Đ°Đ»ĐžŃ€Đ°Ń‚Đ” ĐŸĐČу аплОĐșацоју за ĐșĐŸŃ€ĐžŃĐœĐžĐșа <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
     <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Да лО жДлОтД Ўа ĐŽĐ”ĐžĐœŃŃ‚Đ°Đ»ĐžŃ€Đ°Ń‚Đ” ĐŸĐČу аплОĐșацоју са ĐżĐŸŃĐ»ĐŸĐČĐœĐŸĐł ĐżŃ€ĐŸŃ„ĐžĐ»Đ°?"</string>
@@ -108,8 +102,7 @@
     <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"йаблДт Đž Đ»ĐžŃ‡ĐœĐž ĐżĐŸĐŽĐ°Ń†Đž су ĐżĐŸĐŽĐ»ĐŸĐ¶ĐœĐžŃ˜Đž ĐœĐ°ĐżĐ°ĐŽŃƒ ĐœĐ”ĐżĐŸĐ·ĐœĐ°Ń‚ĐžŃ… аплОĐșацоја. АĐșĐŸ ĐžĐœŃŃ‚Đ°Đ»ĐžŃ€Đ°Ń‚Đ” ĐŸĐČу аплОĐșацоју, прохĐČататД Ўа стД ĐŸĐŽĐłĐŸĐČĐŸŃ€ĐœĐž за Đ”ĐČĐ”ĐœŃ‚ŃƒĐ°Đ»ĐœĐ° ĐŸŃˆŃ‚Đ”Ń›Đ”ŃšĐ° таблДта ОлО ĐłŃƒĐ±ĐžŃ‚Đ°Đș ĐżĐŸĐŽĐ°Ń‚Đ°Đșа ĐŽĐŸ ĐșĐŸŃ˜ĐžŃ… ĐŒĐŸĐ¶Đ” Ўа ĐŽĐŸŃ’Đ” Đ·Đ±ĐŸĐł ŃšĐ”ĐœĐŸĐł ĐșĐŸŃ€ĐžŃˆŃ›Đ”ŃšĐ°."</string>
     <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"бВ Đž Đ»ĐžŃ‡ĐœĐž ĐżĐŸĐŽĐ°Ń†Đž су ĐżĐŸĐŽĐ»ĐŸĐ¶ĐœĐžŃ˜Đž ĐœĐ°ĐżĐ°ĐŽŃƒ ĐœĐ”ĐżĐŸĐ·ĐœĐ°Ń‚ĐžŃ… аплОĐșацоја. АĐșĐŸ ĐžĐœŃŃ‚Đ°Đ»ĐžŃ€Đ°Ń‚Đ” ĐŸĐČу аплОĐșацоју, прохĐČататД Ўа стД ĐŸĐŽĐłĐŸĐČĐŸŃ€ĐœĐž за Đ”ĐČĐ”ĐœŃ‚ŃƒĐ°Đ»ĐœĐ° ĐŸŃˆŃ‚Đ”Ń›Đ”ŃšĐ° бВ-а ОлО ĐłŃƒĐ±ĐžŃ‚Đ°Đș ĐżĐŸĐŽĐ°Ń‚Đ°Đșа ĐŽĐŸ ĐșĐŸŃ˜ĐžŃ… ĐŒĐŸĐ¶Đ” Ўа ĐŽĐŸŃ’Đ” Đ·Đ±ĐŸĐł ŃšĐ”ĐœĐŸĐł ĐșĐŸŃ€ĐžŃˆŃ›Đ”ŃšĐ°."</string>
     <string name="cloned_app_label" msgid="7503612829833756160">"ĐšĐ»ĐŸĐœ аплОĐșĐ°Ń†ĐžŃ˜Đ” <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
-    <!-- no translation found for archiving_app_label (1127085259724124725) -->
-    <skip />
+    <string name="archiving_app_label" msgid="1127085259724124725">"ЖДлОтД лО Ўа архоĐČОратД <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>?"</string>
     <string name="anonymous_source_continue" msgid="4375745439457209366">"НастаĐČĐž"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"ĐŸĐŸĐŽĐ”ŃˆĐ°ĐČања"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Đ˜ĐœŃŃ‚Đ°Đ»ĐžŃ€Đ°ŃšĐ”/ĐŽĐ”ĐžĐœŃŃ‚Đ°Đ»ĐžŃ€Đ°ŃšĐ” Wear аплОĐșац."</string>
diff --git a/packages/PackageInstaller/res/values-sv/strings.xml b/packages/PackageInstaller/res/values-sv/strings.xml
index ed016a8..d06f040 100644
--- a/packages/PackageInstaller/res/values-sv/strings.xml
+++ b/packages/PackageInstaller/res/values-sv/strings.xml
@@ -44,8 +44,7 @@
     <string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Denna användare får inte installera okända appar"</string>
     <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Användaren har inte behörighet att installera appar"</string>
     <string name="ok" msgid="7871959885003339302">"OK"</string>
-    <!-- no translation found for archive (4447791830199354721) -->
-    <skip />
+    <string name="archive" msgid="4447791830199354721">"Arkivera"</string>
     <string name="update_anyway" msgid="8792432341346261969">"Uppdatera ändå"</string>
     <string name="manage_applications" msgid="5400164782453975580">"Hantera appar"</string>
     <string name="out_of_space_dlg_title" msgid="4156690013884649502">"Slut på utrymme"</string>
@@ -60,16 +59,11 @@
     <string name="uninstall_update_title" msgid="824411791011583031">"Avinstallera uppdatering"</string>
     <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> är en del av följande app:"</string>
     <string name="uninstall_application_text" msgid="3816830743706143980">"Vill du avinstallera appen?"</string>
-    <!-- no translation found for archive_application_text (8482325710714386348) -->
-    <skip />
-    <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
-    <skip />
-    <!-- no translation found for archive_application_text_user (2586558895535581451) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
-    <skip />
+    <string name="archive_application_text" msgid="8482325710714386348">"Din privata data sparas"</string>
+    <string name="archive_application_text_all_users" msgid="3151229641681672580">"Vill du arkivera appen för alla användare? Din privata data sparas"</string>
+    <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Vill du arkivera appen i din jobbprofil? Din privata data sparas"</string>
+    <string name="archive_application_text_user" msgid="2586558895535581451">"Vill du arkivera appen för <xliff:g id="USERNAME">%1$s</xliff:g>? Din privata data sparas"</string>
+    <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Vill du arkivera appen från ditt privata rum? Din privata data sparas"</string>
     <string name="uninstall_application_text_all_users" msgid="575491774380227119">"Vill du avinstallera den här appen för "<b>"alla"</b>" användare? Appen och alla data i den tas bort från "<b>"alla"</b>" användare på enheten."</string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Vill du avinstallera appen för användaren <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
     <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Vill du avinstallera appen från jobbprofilen?"</string>
@@ -108,8 +102,7 @@
     <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Din surfplatta och personliga data är mer sårbara för attacker från okända appar. Genom att installera denna app bekräftar du att du är ansvarig för eventuella skador på surfplattan och för dataförlust som kan uppstå vid användning av denna app."</string>
     <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Din tv och personliga data är mer sårbar för attacker från okända appar. Genom att installera denna app bekräftar du att du är ansvarig för eventuella skador på tv:n och för dataförlust som kan uppstå vid användning av denna app."</string>
     <string name="cloned_app_label" msgid="7503612829833756160">"Klon av <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
-    <!-- no translation found for archiving_app_label (1127085259724124725) -->
-    <skip />
+    <string name="archiving_app_label" msgid="1127085259724124725">"Vill du arkivera <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>?"</string>
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Fortsätt"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Inställningar"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Wear-appar installeras/avinstalleras"</string>
diff --git a/packages/PackageInstaller/res/values-sw/strings.xml b/packages/PackageInstaller/res/values-sw/strings.xml
index 7f03d91..49af8fc 100644
--- a/packages/PackageInstaller/res/values-sw/strings.xml
+++ b/packages/PackageInstaller/res/values-sw/strings.xml
@@ -44,8 +44,7 @@
     <string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Mtumiaji huyu hana idhini ya kusakinisha programu ambazo hazijulikani"</string>
     <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Mtumiaji huyu haruhusiwi kusakinisha programu"</string>
     <string name="ok" msgid="7871959885003339302">"Sawa"</string>
-    <!-- no translation found for archive (4447791830199354721) -->
-    <skip />
+    <string name="archive" msgid="4447791830199354721">"Hifadhi kwenye kumbukumbu"</string>
     <string name="update_anyway" msgid="8792432341346261969">"Sasisha tu"</string>
     <string name="manage_applications" msgid="5400164782453975580">"Dhibiti programu"</string>
     <string name="out_of_space_dlg_title" msgid="4156690013884649502">"Nafasi imejaa"</string>
@@ -60,16 +59,11 @@
     <string name="uninstall_update_title" msgid="824411791011583031">"Ondoa sasisho"</string>
     <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> ni sehemu ya programu ifuatayo:"</string>
     <string name="uninstall_application_text" msgid="3816830743706143980">"Ungependa kuondoa programu hii?"</string>
-    <!-- no translation found for archive_application_text (8482325710714386348) -->
-    <skip />
-    <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
-    <skip />
-    <!-- no translation found for archive_application_text_user (2586558895535581451) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
-    <skip />
+    <string name="archive_application_text" msgid="8482325710714386348">"Data yako binafsi itahifadhiwa"</string>
+    <string name="archive_application_text_all_users" msgid="3151229641681672580">"Ungependa kuweka programu hii kwenye kumbukumbu kwa watumiaji wote? Data yako binafsi itahifadhiwa"</string>
+    <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Ungependa kuweka programu hii kwenye kumbukumbu katika wasifu wako wa kazini? Data yako binafsi itahifadhiwa"</string>
+    <string name="archive_application_text_user" msgid="2586558895535581451">"Ungependa kuweka programu hii ya <xliff:g id="USERNAME">%1$s</xliff:g> kwenye kumbukumbu? Data yako binafsi itahifadhiwa"</string>
+    <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Je, ungependa kuweka programu hii kwenye kumbukumbu kutoka kwenye sehemu yako ya faragha? Data yako binafsi itahifadhiwa"</string>
     <string name="uninstall_application_text_all_users" msgid="575491774380227119">"Je, ungependa kuondoa programu hii kwa watumiaji "<b>"wote"</b>"? Programu na data yake zitaondolewa kutoka kwa watumiaji "<b>"wote"</b>" kwenye kifaa."</string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Je, ungependa kuondoa programu hii kwa mtumiaji <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
     <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Ungependa kuondoa programu hii kwenye wasifu wako wa kazini?"</string>
@@ -108,8 +102,7 @@
     <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Data yako ya binafsi na ya kompyuta yako kibao inaweza kuathiriwa na programu ambazo hazijulikani. Kwa kusakinisha programu hii, unakubali kuwajibikia uharibifu wowote kwenye kompyuta yako kibao au kupotea kwa data kutokana na matumizi yake."</string>
     <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Data yako ya binafsi na ya televisheni yako inaweza kuathiriwa na programu ambazo hazijulikani. Kwa kusakinisha programu hii, unakubali kuwajibikia uharibifu wowote kwenye televisheni yako au kupotea kwa data kutokana na matumizi yake."</string>
     <string name="cloned_app_label" msgid="7503612829833756160">"Nakala ya <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
-    <!-- no translation found for archiving_app_label (1127085259724124725) -->
-    <skip />
+    <string name="archiving_app_label" msgid="1127085259724124725">"Ungependa kuweka <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> kwenye kumbukumbu?"</string>
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Endelea"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Mipangilio"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Inasakinisha/inaondoa programu za Android Wear"</string>
diff --git a/packages/PackageInstaller/res/values-ta/strings.xml b/packages/PackageInstaller/res/values-ta/strings.xml
index 3c98411..865781f 100644
--- a/packages/PackageInstaller/res/values-ta/strings.xml
+++ b/packages/PackageInstaller/res/values-ta/strings.xml
@@ -44,8 +44,7 @@
     <string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"àź…àź±àźżàźŻàźȘàŻàźȘàźŸàźŸàź€ àź†àźȘàŻàźžàŻˆ àź‡àźšàŻàź€àźȘàŻ àźȘàźŻàź©àź°àźŸàźČàŻ àźšàźżàź±àŻàź” àź‡àźŻàźČàźŸàź€àŻ"</string>
     <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"àź†àźȘàŻàźžàŻˆ àźšàźżàź±àŻàź” àź‡àźšàŻàź€àźȘàŻ àźȘàźŻàź©àź°àŻàź•àŻàź•àŻ àź…àź©àŻàźźàź€àźżàźŻàźżàźČàŻàźČàŻˆ"</string>
     <string name="ok" msgid="7871959885003339302">"àźšàź°àźż"</string>
-    <!-- no translation found for archive (4447791830199354721) -->
-    <skip />
+    <string name="archive" msgid="4447791830199354721">"àź•àźŸàźȘàŻàźȘàźżàźŸàŻ"</string>
     <string name="update_anyway" msgid="8792432341346261969">"àźȘàź°àź”àźŸàźŻàźżàźČàŻàźČàŻˆ, àźȘàŻàź€àŻàźȘàŻàźȘàźżàź•àŻàź•àź”àŻàźźàŻ"</string>
     <string name="manage_applications" msgid="5400164782453975580">"àź†àźȘàŻàźžàŻˆ àźšàźżàź°àŻàź”àź•àźż"</string>
     <string name="out_of_space_dlg_title" msgid="4156690013884649502">"àźȘàŻ‹àź€àŻàźźàźŸàź© àźšàŻ‡àźźàźżàźȘàŻàźȘàźżàźŸàźźàŻ àź‡àźČàŻàźČàŻˆ"</string>
@@ -60,16 +59,11 @@
     <string name="uninstall_update_title" msgid="824411791011583031">"àźȘàŻàź€àŻàźȘàŻàźȘàźżàźȘàŻàźȘàŻˆ àźšàźżàź±àŻàź”àźČàŻ àźšàŻ€àź•àŻàź•àŻàź€àźČàŻ"</string>
     <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> àźŽàź©àŻàźȘàź€àŻ àźȘàźżàź©àŻàź”àź°àŻàźźàŻ àź†àźȘàŻàźžàźżàź©àŻ àźȘàź•àŻàź€àźżàźŻàźŸàź•àŻàźźàŻ:"</string>
     <string name="uninstall_application_text" msgid="3816830743706143980">"àź‡àźšàŻàź€ àź†àźȘàŻàźžàŻˆ àźšàźżàź±àŻàź”àźČàŻ àźšàŻ€àź•àŻàź• àź”àźżàź°àŻàźźàŻàźȘàŻàź•àźżàź±àŻ€àź°àŻàź•àźłàźŸ?"</string>
-    <!-- no translation found for archive_application_text (8482325710714386348) -->
-    <skip />
-    <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
-    <skip />
-    <!-- no translation found for archive_application_text_user (2586558895535581451) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
-    <skip />
+    <string name="archive_application_text" msgid="8482325710714386348">"àź‰àź™àŻàź•àźłàŻ àź€àź©àźżàźȘàŻàźȘàźŸàŻàźŸ àź€àź°àź”àŻ àźšàŻ‡àźźàźżàź•àŻàź•àźȘàŻàźȘàźŸàŻàźźàŻ"</string>
+    <string name="archive_application_text_all_users" msgid="3151229641681672580">"àź…àź©àŻˆàź€àŻàź€àŻàźȘàŻ àźȘàźŻàź©àź°àŻàź•àźłàŻàź•àŻàź•àŻàźźàŻ àź‡àźšàŻàź€ àź†àźȘàŻàźžàŻˆàź•àŻ àź•àźŸàźȘàŻàźȘàźżàźŸàź”àźŸ? àź‰àź™àŻàź•àźłàŻ àź€àź©àźżàźȘàŻàźȘàźŸàŻàźŸ àź€àź°àź”àŻ àźšàŻ‡àźźàźżàź•àŻàź•àźȘàŻàźȘàźŸàŻàźźàŻ"</string>
+    <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"àź‰àź™àŻàź•àźłàŻ àźȘàźŁàźżàź•àŻ àź•àźŁàź•àŻàź•àźżàźČàŻ àź‡àźšàŻàź€ àź†àźȘàŻàźžàŻˆàź•àŻ àź•àźŸàźȘàŻàźȘàźżàźŸàź”àźŸ? àź‰àź™àŻàź•àźłàŻ àź€àź©àźżàźȘàŻàźȘàźŸàŻàźŸ àź€àź°àź”àŻ àźšàŻ‡àźźàźżàź•àŻàź•àźȘàŻàźȘàźŸàŻàźźàŻ"</string>
+    <string name="archive_application_text_user" msgid="2586558895535581451">"<xliff:g id="USERNAME">%1$s</xliff:g>àź•àŻàź•àŻ àź‡àźšàŻàź€ àź†àźȘàŻàźžàŻˆàź•àŻ àź•àźŸàźȘàŻàźȘàźżàźŸàź”àźŸ? àź‰àź™àŻàź•àźłàŻ àź€àź©àźżàźȘàŻàźȘàźŸàŻàźŸ àź€àź°àź”àŻ àźšàŻ‡àźźàźżàź•àŻàź•àźȘàŻàźȘàźŸàŻàźźàŻ"</string>
+    <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"àź‰àź™àŻàź•àźłàŻ àź€àź©àźżàźȘàŻàźȘàźŸàŻàźŸ àźšàŻ‡àźźàźżàźȘàŻàźȘàźżàźŸàź€àŻàź€àźżàźČàźżàź°àŻàźšàŻàź€àŻ àź‡àźšàŻàź€ àź†àźȘàŻàźžàŻˆàź•àŻ àź•àźŸàźȘàŻàźȘàźżàźŸ àź”àŻ‡àźŁàŻàźŸàŻàźźàźŸ? àź‰àź™àŻàź•àźłàŻ àź€àź©àźżàźȘàŻàźȘàźŸàŻàźŸ àź€àź°àź”àŻ àźšàŻ‡àźźàźżàź•àŻàź•àźȘàŻàźȘàźŸàŻàźźàŻ"</string>
     <string name="uninstall_application_text_all_users" msgid="575491774380227119">"àź‡àźšàŻàź€ àź†àźȘàŻàźžàŻˆ "<b>"àź…àź©àŻˆàź€àŻàź€àŻàźȘàŻ"</b>" àźȘàźŻàź©àź°àŻàź•àźłàŻàź•àŻàź•àŻàźźàŻ àźšàźżàź±àŻàź”àźČàŻ àźšàŻ€àź•àŻàź• àź”àźżàź°àŻàźźàŻàźȘàŻàź•àźżàź±àŻ€àź°àŻàź•àźłàźŸ? àź†àźȘàŻàźžàŻàźźàŻ àź…àź€àź©àŻ àź€àź°àź”àŻàźźàŻ àźšàźŸàź€àź©àź€àŻàź€àźżàźČàŻàźłàŻàźł "<b>"àź…àź©àŻˆàź€àŻàź€àŻàźȘàŻ"</b>" àźȘàźŻàź©àź°àŻàź•àźłàźżàźŸàźźàźżàź°àŻàźšàŻàź€àŻàźźàŻ àź…àź•àź±àŻàź±àźȘàŻàźȘàźŸàŻàźźàŻ."</string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"<xliff:g id="USERNAME">%1$s</xliff:g> àźŽàź©àŻàź± àźȘàźŻàź©àź°àŻàź•àŻàź•àŻ àź‡àźšàŻàź€ àź†àźȘàŻàźžàŻˆ àźšàźżàź±àŻàź”àźČàŻ àźšàŻ€àź•àŻàź• àź”àźżàź°àŻàźźàŻàźȘàŻàź•àźżàź±àŻ€àź°àŻàź•àźłàźŸ?"</string>
     <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"àź‰àź™àŻàź•àźłàŻ àźȘàźŁàźżàź•àŻ àź•àźŁàź•àŻàź•àźżàźČàźżàź°àŻàźšàŻàź€àŻ àź‡àźšàŻàź€ àź†àźȘàŻàźžàŻˆ àźšàźżàź±àŻàź”àźČàŻ àźšàŻ€àź•àŻàź• àź”àźżàź°àŻàźźàŻàźȘàŻàź•àźżàź±àŻ€àź°àŻàź•àźłàźŸ?"</string>
@@ -108,8 +102,7 @@
     <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"àź…àź±àźżàźŻàźȘàŻàźȘàźŸàźŸàź€ àź†àźȘàŻàźžàźŸàźČàŻ àź‰àź™àŻàź•àźłàŻ àźŸàŻ‡àźȘàŻàźČàŻ†àźŸàŻàźŸàŻàźźàŻ àź€àź©àźżàźȘàŻàźȘàźŸàŻàźŸ àź€àź°àź”àŻàźźàŻ àźźàźżàź• àźŽàźłàźżàź€àźŸàź•àźȘàŻ àźȘàźŸàź€àźżàźȘàŻàźȘàŻàź•àŻàź•àŻàźłàŻàźłàźŸàź•àźČàźŸàźźàŻ. àź‡àźšàŻàź€ àź†àźȘàŻàźžàŻˆ àźšàźżàź±àŻàź”àŻàź”àź€àź©àŻ àźźàŻ‚àźČàźźàŻ, àź‡àź€àŻˆàźȘàŻ àźȘàźŻàź©àŻàźȘàźŸàŻàź€àŻàź€àŻàźźàŻàźȘàŻ‹àź€àŻ àźŸàŻ‡àźȘàŻàźČàŻ†àźŸàŻàźŸàźżàźČàŻ àźàź€àŻ‡àź©àŻàźźàŻ àźšàźżàź•àŻàź•àźČàŻ àźàź±àŻàźȘàźŸàŻàźŸàźŸàźČàŻ‹ àź‰àź™àŻàź•àźłàź€àŻ àź€àź°àź”àŻˆ àź‡àźŽàźšàŻàź€àźŸàźČàŻ‹ àź…àź€àź±àŻàź•àŻ àźšàŻ€àź™àŻàź•àźłàŻ‡ àźȘàŻŠàź±àŻàźȘàŻàźȘàźŸàź”àŻ€àź°àŻàź•àźłàŻ àźŽàź©àŻàźȘàź€àŻˆ àźàź±àŻàź•àźżàź±àŻ€àź°àŻàź•àźłàŻ."</string>
     <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"àź…àź±àźżàźŻàźȘàŻàźȘàźŸàźŸàź€ àź†àźȘàŻàźžàźŸàźČàŻ àź‰àź™àŻàź•àźłàŻ àźŸàźżàź”àźżàźŻàŻàźźàŻ àź€àź©àźżàźȘàŻàźȘàźŸàŻàźŸ àź€àź°àź”àŻàźźàŻ àźźàźżàź• àźŽàźłàźżàź€àźŸàź•àźȘàŻ àźȘàźŸàź€àźżàźȘàŻàźȘàŻàź•àŻàź•àŻàźłàŻàźłàźŸàź•àźČàźŸàźźàŻ. àź‡àźšàŻàź€ àź†àźȘàŻàźžàŻˆ àźšàźżàź±àŻàź”àŻàź”àź€àź©àŻ àźźàŻ‚àźČàźźàŻ, àź‡àź€àŻˆàźȘàŻ àźȘàźŻàź©àŻàźȘàźŸàŻàź€àŻàź€àŻàźźàŻàźȘàŻ‹àź€àŻ àźŸàźżàź”àźżàźŻàźżàźČàŻ àźàź€àŻ‡àź©àŻàźźàŻ àźšàźżàź•àŻàź•àźČàŻ àźàź±àŻàźȘàźŸàŻàźŸàźŸàźČàŻ‹ àź‰àź™àŻàź•àźłàź€àŻ àź€àź°àź”àŻˆ àź‡àźŽàźšàŻàź€àźŸàźČàŻ‹ àź…àź€àź±àŻàź•àŻ àźšàŻ€àź™àŻàź•àźłàŻ‡ àźȘàŻŠàź±àŻàźȘàŻàźȘàźŸàź”àŻ€àź°àŻàź•àźłàŻ àźŽàź©àŻàźȘàź€àŻˆ àźàź±àŻàź•àźżàź±àŻ€àź°àŻàź•àźłàŻ."</string>
     <string name="cloned_app_label" msgid="7503612829833756160">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> àź•àŻàźłàŻ‹àź©àŻ"</string>
-    <!-- no translation found for archiving_app_label (1127085259724124725) -->
-    <skip />
+    <string name="archiving_app_label" msgid="1127085259724124725">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> àźàź•àŻ àź•àźŸàźȘàŻàźȘàźżàźŸàź”àźŸ?"</string>
     <string name="anonymous_source_continue" msgid="4375745439457209366">"àź€àŻŠàźŸàź°àŻàź•"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"àź…àźźàŻˆàźȘàŻàźȘàŻàź•àźłàŻ"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Wear àź†àźȘàŻàźžàŻˆ àźšàźżàź±àŻàź”àŻàź€àźČàŻ/àźšàźżàź±àŻàź”àźČàŻ àźšàŻ€àź•àŻàź•àŻàź€àźČàŻ"</string>
diff --git a/packages/PackageInstaller/res/values-te/strings.xml b/packages/PackageInstaller/res/values-te/strings.xml
index ef00c9f..2deac93 100644
--- a/packages/PackageInstaller/res/values-te/strings.xml
+++ b/packages/PackageInstaller/res/values-te/strings.xml
@@ -44,8 +44,7 @@
     <string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"ఈ à°”à°żà°šà°żà°Żà±‹à°—à°Šà°Ÿà°°à± ఀెà°Čà°żà°Żà°šà°ż à°Żà°Ÿà°Ș్‌à°Čచు ఇచ్‌ఞ్టటà°Č్ à°šà±‡à°Żà°Čేరు"</string>
     <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"à°Żà°Ÿà°Ș్‌à°Čచు ఇచ్‌ఞ్టటà°Č్ à°šà±‡à°Żà°Ąà°Ÿà°šà°żà°•à°ż ఈ à°”à°żà°šà°żà°Żà±‹à°—à°Šà°Ÿà°°à±à°•à± à°…à°šà±à°źà°€à°ż à°Čేఊు"</string>
     <string name="ok" msgid="7871959885003339302">"ఞరే"</string>
-    <!-- no translation found for archive (4447791830199354721) -->
-    <skip />
+    <string name="archive" msgid="4447791830199354721">"ఆర్కైఔ్ à°šà±‡à°Żà°‚à°Ąà°ż"</string>
     <string name="update_anyway" msgid="8792432341346261969">"à°à°Šà±‡à°źà±ˆà°šà°Ÿ అà°Ș్‌à°Ąà±‡à°Ÿà± à°šà±‡à°Żà°‚à°Ąà°ż"</string>
     <string name="manage_applications" msgid="5400164782453975580">"à°Żà°Ÿà°Ș్‌à°Čచు à°šà°żà°°à±à°”à°čà°żà°‚à°šà°‚à°Ąà°ż"</string>
     <string name="out_of_space_dlg_title" msgid="4156690013884649502">"à°–à°Ÿà°łà±€ à°Čేఊు"</string>
@@ -60,16 +59,11 @@
     <string name="uninstall_update_title" msgid="824411791011583031">"అà°Ș్‌à°Ąà±‡à°Ÿà± అచ్‌ఇచ్‌ఞ్టటà°Č్ à°šà±‡à°Żà°ż"</string>
     <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> à°…à°šà±‡à°Šà°ż à°•à°żà°‚à°Šà°ż à°Żà°Ÿà°Ș్‌à°Čో ఒక à°­à°Ÿà°—à°‚:"</string>
     <string name="uninstall_application_text" msgid="3816830743706143980">"à°źà±€à°°à± ఈ à°Żà°Ÿà°Ș్‌చు అచ్‌ఇచ్‌ఞ్టటà°Č్ à°šà±‡à°Żà°Ÿà°Čచుకుంటుచ్చటరట?"</string>
-    <!-- no translation found for archive_application_text (8482325710714386348) -->
-    <skip />
-    <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
-    <skip />
-    <!-- no translation found for archive_application_text_user (2586558895535581451) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
-    <skip />
+    <string name="archive_application_text" msgid="8482325710714386348">"à°źà±€ à°”à±à°Żà°•à±à°€à°żà°—à°€ à°Ąà±‡à°Ÿà°Ÿ ఞేఔ్ à°šà±‡à°Żà°Źà°Ąà±à°€à±à°‚à°Šà°ż"</string>
+    <string name="archive_application_text_all_users" msgid="3151229641681672580">"à°Żà±‚à°œà°°à±‌à°Čà°‚à°Šà°°à°żà°•à±€ ఈ à°Żà°Ÿà°Ș్‌చు ఆర్కైఔ్ à°šà±‡à°Żà°Ÿà°Čà°Ÿ? à°źà±€ à°”à±à°Żà°•à±à°€à°żà°—à°€ à°Ąà±‡à°Ÿà°Ÿ ఞేఔ్ à°šà±‡à°Żà°Źà°Ąà±à°€à±à°‚à°Šà°ż"</string>
+    <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"à°źà±€ ఔర్క్ à°Ș్రొఫైà°Č్‌à°Čో ఈ à°Żà°Ÿà°Ș్‌చు ఆర్కైఔ్ à°šà±‡à°Żà°Ÿà°Čà°Ÿ? à°źà±€ à°”à±à°Żà°•à±à°€à°żà°—à°€ à°Ąà±‡à°Ÿà°Ÿ ఞేఔ్ à°šà±‡à°Żà°Źà°Ąà±à°€à±à°‚à°Šà°ż"</string>
+    <string name="archive_application_text_user" msgid="2586558895535581451">"<xliff:g id="USERNAME">%1$s</xliff:g>‌కు ఈ à°Żà°Ÿà°Ș్‌చు ఆర్కైఔ్ à°šà±‡à°Żà°Ÿà°Čà°Ÿ? à°źà±€ à°”à±à°Żà°•à±à°€à°żà°—à°€ à°Ąà±‡à°Ÿà°Ÿ ఞేఔ్ à°šà±‡à°Żà°Źà°Ąà±à°€à±à°‚à°Šà°ż"</string>
+    <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"à°źà±€à°°à± à°źà±€ à°Ș్రైఔేట్ ఞ్à°Șేఞ్ à°šà±à°‚à°Ąà°ż ఈ à°Żà°Ÿà°Ș్‌చు ఆర్కైఔ్ à°šà±‡à°Żà°Ÿà°Čచుకుంటుచ్చటరట? à°źà±€ à°”à±à°Żà°•à±à°€à°żà°—à°€ à°Ąà±‡à°Ÿà°Ÿ ఞేఔ్ à°šà±‡à°Żà°Źà°Ąà±à°€à±à°‚à°Šà°ż"</string>
     <string name="uninstall_application_text_all_users" msgid="575491774380227119">"à°źà±€à°°à± ఈ à°Żà°Ÿà°Ș్‌చు "<b>"అంఊరు"</b>" à°”à°żà°šà°żà°Żà±‹à°—à°Šà°Ÿà°°à±à°Čకు అచ్‌ఇచ్‌ఞ్టటà°Č్ à°šà±‡à°Żà°Ÿà°Čచుకుంటుచ్చటరట? అà°Ș్à°Čà°żà°•à±‡à°·à°šà±, à°Šà°Ÿà°šà°ż à°Ąà±‡à°Ÿà°Ÿ à°Șà°°à°żà°•à°°à°‚à°Čà±‹à°šà°ż "<b>"అంఊరు"</b>" à°”à°żà°šà°żà°Żà±‹à°—à°Šà°Ÿà°°à±à°Č à°šà±à°‚à°Ąà°ż à°€à±€à°žà°żà°”à±‡à°Żà°Źà°Ąà±à°€à±à°‚à°Šà°ż."</string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"à°źà±€à°°à± à°”à°żà°šà°żà°Żà±‹à°—à°Šà°Ÿà°°à± <xliff:g id="USERNAME">%1$s</xliff:g> కోఞం ఈ à°Żà°Ÿà°Ș్‌చు అచ్‌ఇచ్‌ఞ్టటà°Č్ à°šà±‡à°Żà°Ÿà°Čచుకుంటుచ్చటరట?"</string>
     <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"à°źà±€ ఔర్క్ à°Ș్రొఫైà°Č్ à°šà±à°‚à°Ąà°ż ఈ à°Żà°Ÿà°Ș్‌చు à°źà±€à°°à± అచ్‌ఇచ్‌ఞ్టటà°Č్ à°šà±‡à°Żà°Ÿà°Čచుకుంటుచ్చటరట?"</string>
@@ -108,8 +102,7 @@
     <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"à°źà±€ à°Ÿà°Ÿà°Źà±à°Čెట్ à°źà°°à°żà°Żà± à°”à±à°Żà°•à±à°€à°żà°—à°€ à°Ąà±‡à°Ÿà°Ÿà°Șై ఀెà°Čà°żà°Żà°šà°ż à°Żà°Ÿà°Ș్‌à°Čు à°Šà°Ÿà°Ąà°ż à°šà±‡à°Żà°Ąà°Ÿà°šà°żà°•à°ż ఎక్కుఔ అఔకటశం à°‰à°‚à°Šà°ż. à°źà±€à°°à± ఈ à°Żà°Ÿà°Ș్‌చు ఇచ్‌ఞ్టటà°Č్ à°šà±‡à°Żà°Ąà°‚ ఊ్ఔటరట, à°Šà±€à°šà°żà°šà°ż ఉà°Șà°Żà±‹à°—à°żà°‚à°šà°Ąà°‚ à°”à°Čà°š à°źà±€ à°Ÿà°Ÿà°Źà±à°Čెట్‌కు ఏఊైచట à°čà°Ÿà°šà°ż à°œà°°à°żà°—à°żà°šà°Ÿ à°Čేఊట à°Ąà±‡à°Ÿà°Ÿ కోà°Č్à°Șà±‹à°Żà°żà°šà°Ÿ à°Źà°Ÿà°§à±à°Żà°€ à°źà±€à°Šà±‡à°šà°šà°ż à°…à°‚à°—à±€à°•à°°à°żà°žà±à°€à±à°šà±à°šà°Ÿà°°à±."</string>
     <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"à°źà±€ టీఔీ à°źà°°à°żà°Żà± à°”à±à°Żà°•à±à°€à°żà°—à°€ à°Ąà±‡à°Ÿà°Ÿà°Șై ఀెà°Čà°żà°Żà°šà°ż à°Żà°Ÿà°Ș్‌à°Čు à°Šà°Ÿà°Ąà°ż à°šà±‡à°Żà°Ąà°Ÿà°šà°żà°•à°ż ఎక్కుఔ అఔకటశం à°‰à°‚à°Šà°ż. à°źà±€à°°à± ఈ à°Żà°Ÿà°Ș్‌చు ఇచ్‌ఞ్టటà°Č్ à°šà±‡à°Żà°Ąà°‚ ఊ్ఔటరట, à°Šà±€à°šà°żà°šà°ż ఉà°Șà°Żà±‹à°—à°żà°‚à°šà°Ąà°‚ à°”à°Čà°š à°źà±€ à°Ÿà±€à°”à±€à°•à°ż ఏఊైచట à°čà°Ÿà°šà°ż à°œà°°à°żà°—à°żà°šà°Ÿ à°Čేఊట à°Ąà±‡à°Ÿà°Ÿ కోà°Č్à°Șà±‹à°Żà°żà°šà°Ÿ à°Źà°Ÿà°§à±à°Żà°€ à°źà±€à°Šà±‡à°šà°šà°ż à°…à°‚à°—à±€à°•à°°à°żà°žà±à°€à±à°šà±à°šà°Ÿà°°à±."</string>
     <string name="cloned_app_label" msgid="7503612829833756160">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> క్à°Čోచ్"</string>
-    <!-- no translation found for archiving_app_label (1127085259724124725) -->
-    <skip />
+    <string name="archiving_app_label" msgid="1127085259724124725">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>‌చు ఆర్కైఔ్ à°šà±‡à°Żà°Ÿà°Čà°Ÿ?"</string>
     <string name="anonymous_source_continue" msgid="4375745439457209366">"à°•à±Šà°šà°žà°Ÿà°—à°‚à°Ąà°ż"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"à°žà±†à°Ÿà±à°Ÿà°żà°‚à°—à±‌à°Čు"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Wear à°Żà°Ÿà°Ș్‌à°Čచు ఇచ్‌ఞ్టటà°Č్/అచ్‌ఇచ్‌ఞ్టటà°Č్ à°šà±‡à°žà±à°€à±‹à°‚à°Šà°ż"</string>
diff --git a/packages/PackageInstaller/res/values-th/strings.xml b/packages/PackageInstaller/res/values-th/strings.xml
index e330985..e770a1d 100644
--- a/packages/PackageInstaller/res/values-th/strings.xml
+++ b/packages/PackageInstaller/res/values-th/strings.xml
@@ -44,8 +44,7 @@
     <string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"àžœàžčàč‰àčƒàžŠàč‰àžŁàžČàžąàž™àž”àč‰àč„àžĄàčˆàžȘàžČàžĄàžČàžŁàž–àž•àžŽàž”àž•àž±àč‰àž‡àčàž­àž›àž—àž”àčˆàč„àžĄàčˆàžŁàžčàč‰àžˆàž±àž"</string>
     <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"àžœàžčàč‰àčƒàžŠàč‰àžŁàžČàžąàž™àž”àč‰àč„àžĄàčˆàč„àž”àč‰àžŁàž±àžšàž­àž™àžžàžàžČàž•àčƒàž«àč‰àž•àžŽàž”àž•àž±àč‰àž‡àčàž­àž›"</string>
     <string name="ok" msgid="7871959885003339302">"àž•àžàž„àž‡"</string>
-    <!-- no translation found for archive (4447791830199354721) -->
-    <skip />
+    <string name="archive" msgid="4447791830199354721">"àč€àžàč‡àžš"</string>
     <string name="update_anyway" msgid="8792432341346261969">"àž­àž±àž›àč€àž”àž•àč€àž„àžą"</string>
     <string name="manage_applications" msgid="5400164782453975580">"àžˆàž±àž”àžàžČàžŁàčàž­àž›"</string>
     <string name="out_of_space_dlg_title" msgid="4156690013884649502">"àč„àžĄàčˆàžĄàž”àžžàž·àč‰àž™àž—àž”àčˆ"</string>
@@ -60,16 +59,11 @@
     <string name="uninstall_update_title" msgid="824411791011583031">"àž–àž­àž™àžàžČàžŁàž•àžŽàž”àž•àž±àč‰àž‡àžàžČàžŁàž­àž±àž›àč€àž”àž•"</string>
     <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> àč€àž›àč‡àž™àžȘàčˆàž§àž™àž«àž™àž¶àčˆàž‡àž‚àž­àž‡àčàž­àž›àž•àčˆàž­àč„àž›àž™àž”àč‰"</string>
     <string name="uninstall_application_text" msgid="3816830743706143980">"àž•àč‰àž­àž‡àžàžČàžŁàž–àž­àž™àžàžČàžŁàž•àžŽàž”àž•àž±àč‰àž‡àčàž­àž›àž™àž”àč‰àč„àž«àžĄ"</string>
-    <!-- no translation found for archive_application_text (8482325710714386348) -->
-    <skip />
-    <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
-    <skip />
-    <!-- no translation found for archive_application_text_user (2586558895535581451) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
-    <skip />
+    <string name="archive_application_text" msgid="8482325710714386348">"àžŁàž°àžšàžšàžˆàž°àžšàž±àž™àž—àž¶àžàž‚àč‰àž­àžĄàžčàž„àžȘàčˆàž§àž™àž•àž±àž§àž‚àž­àž‡àž„àžžàž“àč„àž§àč‰"</string>
+    <string name="archive_application_text_all_users" msgid="3151229641681672580">"àč€àžàč‡àžšàčàž­àž›àž™àž”àč‰àžȘàžłàž«àžŁàž±àžšàžœàžčàč‰àčƒàžŠàč‰àž—àžžàžàž„àž™àč„àž«àžĄ àžŁàž°àžšàžšàžˆàž°àžšàž±àž™àž—àž¶àžàž‚àč‰àž­àžĄàžčàž„àžȘàčˆàž§àž™àž•àž±àž§àž‚àž­àž‡àž„àžžàž“àč„àž§àč‰"</string>
+    <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"àč€àžàč‡àžšàčàž­àž›àž™àž”àč‰àčƒàž™àč‚àž›àžŁàč„àžŸàž„àčŒàž‡àžČàž™àč„àž«àžĄ àžŁàž°àžšàžšàžˆàž°àžšàž±àž™àž—àž¶àžàž‚àč‰àž­àžĄàžčàž„àžȘàčˆàž§àž™àž•àž±àž§àž‚àž­àž‡àž„àžžàž“àč„àž§àč‰"</string>
+    <string name="archive_application_text_user" msgid="2586558895535581451">"àč€àžàč‡àžšàčàž­àž›àž™àž”àč‰àč„àž›àžȘàžłàž«àžŁàž±àžš <xliff:g id="USERNAME">%1$s</xliff:g> àč„àž«àžĄ àžŁàž°àžšàžšàžˆàž°àžšàž±àž™àž—àž¶àžàž‚àč‰àž­àžĄàžčàž„àžȘàčˆàž§àž™àž•àž±àž§àž‚àž­àž‡àž„àžžàž“àč„àž§àč‰"</string>
+    <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"àž„àžžàž“àž•àč‰àž­àž‡àžàžČàžŁàč€àžàč‡àžšàčàž­àž›àž™àž”àč‰àč„àž›àžˆàžČàžàžžàž·àč‰àž™àž—àž”àčˆàžȘàčˆàž§àž™àž•àž±àž§àč„àž«àžĄ àžŁàž°àžšàžšàžˆàž°àžšàž±àž™àž—àž¶àžàž‚àč‰àž­àžĄàžčàž„àžȘàčˆàž§àž™àž•àž±àž§àž‚àž­àž‡àž„àžžàž“àč„àž§àč‰"</string>
     <string name="uninstall_application_text_all_users" msgid="575491774380227119">"àž•àč‰àž­àž‡àžàžČàžŁàž–àž­àž™àžàžČàžŁàž•àžŽàž”àž•àž±àč‰àž‡àčàž­àž›àž™àž”àč‰àžȘàžłàž«àžŁàž±àžšàžœàžčàč‰àčƒàžŠàč‰"<b>"àž—àž±àč‰àž‡àž«àžĄàž”"</b>"àč„àž«àžĄ àžŁàž°àžšàžšàžˆàž°àž™àžłàčàž­àž›àčàž„àž°àž‚àč‰àž­àžĄàžčàž„àčƒàž™àčàž­àž›àž­àž­àžàžˆàžČàžàžœàžčàč‰àčƒàžŠàč‰"<b>"àž—àž±àč‰àž‡àž«àžĄàž”"</b>"àž—àž”àčˆàž­àžąàžčàčˆàčƒàž™àž­àžžàž›àžàžŁàž“àčŒ"</string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"àž•àč‰àž­àž‡àžàžČàžŁàž–àž­àž™àžàžČàžŁàž•àžŽàž”àž•àž±àč‰àž‡àčàž­àž›àž™àž”àč‰àžȘàžłàž«àžŁàž±àžšàžœàžčàč‰àčƒàžŠàč‰ <xliff:g id="USERNAME">%1$s</xliff:g> àč„àž«àžĄ"</string>
     <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"àž„àžžàž“àž•àč‰àž­àž‡àžàžČàžŁàž–àž­àž™àžàžČàžŁàž•àžŽàž”àž•àž±àč‰àž‡àčàž­àž›àž™àž”àč‰àžˆàžČàžàč‚àž›àžŁàč„àžŸàž„àčŒàž‡àžČàž™àčƒàžŠàčˆàč„àž«àžĄ"</string>
@@ -108,8 +102,7 @@
     <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"àčàž—àč‡àžšàč€àž„àč‡àž•àčàž„àž°àž‚àč‰àž­àžĄàžčàž„àžȘàčˆàž§àž™àž•àž±àž§àž‚àž­àž‡àž„àžžàž“àžĄàž”àž„àž§àžČàžĄàč€àžȘàž”àčˆàžąàž‡àžĄàžČàžàž‚àž¶àč‰àž™àž—àž”àčˆàžˆàž°àž–àžčàžàč‚àžˆàžĄàž•àž”àžˆàžČàžàčàž­àž›àž—àž”àčˆàč„àžĄàčˆàžŁàžčàč‰àžˆàž±àž àžàžČàžŁàž•àžŽàž”àž•àž±àč‰àž‡àčàž­àž›àž™àž”àč‰àčàžȘàž”àž‡àž§àčˆàžČàž„àžžàž“àžąàž­àžĄàžŁàž±àžšàž§àčˆàžČàžˆàž°àžŁàž±àžšàžœàžŽàž”àžŠàž­àžšàž•àčˆàž­àž„àž§àžČàžĄàč€àžȘàž”àžąàž«àžČàžąàčƒàž”àč† àž—àž”àčˆàžˆàž°àč€àžàžŽàž”àž‚àž¶àč‰àž™àžàž±àžšàčàž—àč‡àžšàč€àž„àč‡àž•àž«àžŁàž·àž­àžàžČàžŁàžȘàžčàžàč€àžȘàž”àžąàž‚àč‰àž­àžĄàžčàž„àž—àž”àčˆàž­àžČàžˆàč€àžàžŽàž”àžˆàžČàžàžàžČàžŁàčƒàžŠàč‰àž‡àžČàž™àčàž­àž›àž”àž±àž‡àžàž„àčˆàžČàž§"</string>
     <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"àž—àž”àž§àž”àčàž„àž°àž‚àč‰àž­àžĄàžčàž„àžȘàčˆàž§àž™àž•àž±àž§àž‚àž­àž‡àž„àžžàž“àžĄàž”àž„àž§àžČàžĄàč€àžȘàž”àčˆàžąàž‡àžĄàžČàžàž‚àž¶àč‰àž™àž—àž”àčˆàžˆàž°àž–àžčàžàč‚àžˆàžĄàž•àž”àžˆàžČàžàčàž­àž›àž—àž”àčˆàč„àžĄàčˆàžŁàžčàč‰àžˆàž±àž àžàžČàžŁàž•àžŽàž”àž•àž±àč‰àž‡àčàž­àž›àž™àž”àč‰àčàžȘàž”àž‡àž§àčˆàžČàž„àžžàž“àžąàž­àžĄàžŁàž±àžšàž§àčˆàžČàžˆàž°àžŁàž±àžšàžœàžŽàž”àžŠàž­àžšàž•àčˆàž­àž„àž§àžČàžĄàč€àžȘàž”àžąàž«àžČàžąàčƒàž”àč† àž—àž”àčˆàžˆàž°àč€àžàžŽàž”àž‚àž¶àč‰àž™àžàž±àžšàž—àž”àž§àž”àž«àžŁàž·àž­àžàžČàžŁàžȘàžčàžàč€àžȘàž”àžąàž‚àč‰àž­àžĄàžčàž„àž—àž”àčˆàž­àžČàžˆàč€àžàžŽàž”àžˆàžČàžàžàžČàžŁàčƒàžŠàč‰àž‡àžČàž™àčàž­àž›àž”àž±àž‡àžàž„àčˆàžČàž§"</string>
     <string name="cloned_app_label" msgid="7503612829833756160">"àč‚àž„àž„àž™àž‚àž­àž‡ <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
-    <!-- no translation found for archiving_app_label (1127085259724124725) -->
-    <skip />
+    <string name="archiving_app_label" msgid="1127085259724124725">"àč€àžàč‡àžš <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> àč„àž«àžĄ"</string>
     <string name="anonymous_source_continue" msgid="4375745439457209366">"àž”àžłàč€àž™àžŽàž™àžàžČàžŁàž•àčˆàž­"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"àžàžČàžŁàž•àž±àč‰àž‡àž„àčˆàžČ"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"àžàžłàž„àž±àž‡àž•àžŽàž”àž•àž±àč‰àž‡/àž–àž­àž™àžàžČàžŁàž•àžŽàž”àž•àž±àč‰àž‡àčàž­àž› Wear"</string>
diff --git a/packages/PackageInstaller/res/values-tl/strings.xml b/packages/PackageInstaller/res/values-tl/strings.xml
index e7ef36b..e654130 100644
--- a/packages/PackageInstaller/res/values-tl/strings.xml
+++ b/packages/PackageInstaller/res/values-tl/strings.xml
@@ -44,8 +44,7 @@
     <string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Hindi maaaring mag-install ang user na ito ng mga hindi kilalang app"</string>
     <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Hindi pinapayagan ang user na ito na mag-install ng mga app"</string>
     <string name="ok" msgid="7871959885003339302">"OK"</string>
-    <!-- no translation found for archive (4447791830199354721) -->
-    <skip />
+    <string name="archive" msgid="4447791830199354721">"I-archive"</string>
     <string name="update_anyway" msgid="8792432341346261969">"I-update pa rin"</string>
     <string name="manage_applications" msgid="5400164782453975580">"Pamahalaan ang app"</string>
     <string name="out_of_space_dlg_title" msgid="4156690013884649502">"Wala nang espasyo"</string>
@@ -60,16 +59,11 @@
     <string name="uninstall_update_title" msgid="824411791011583031">"I-uninstall ang update"</string>
     <string name="uninstall_activity_text" msgid="1928194674397770771">"Bahagi ang <xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> ng sumusunod na app:"</string>
     <string name="uninstall_application_text" msgid="3816830743706143980">"Gusto mo bang i-uninstall ang app na ito?"</string>
-    <!-- no translation found for archive_application_text (8482325710714386348) -->
-    <skip />
-    <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
-    <skip />
-    <!-- no translation found for archive_application_text_user (2586558895535581451) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
-    <skip />
+    <string name="archive_application_text" msgid="8482325710714386348">"Ise-save ang iyong personal na data"</string>
+    <string name="archive_application_text_all_users" msgid="3151229641681672580">"I-archive ang app na ito para sa lahat ng user? Ise-save ang iyong personal na data"</string>
+    <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"I-archive ang app na ito sa iyong profile sa trabaho? Ise-save ang iyong personal na data"</string>
+    <string name="archive_application_text_user" msgid="2586558895535581451">"I-archive ang app na ito para kay <xliff:g id="USERNAME">%1$s</xliff:g>? Ise-save ang iyong personal na data"</string>
+    <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Gusto mo bang i-archive ang app na ito mula sa iyong pribadong space? Ise-save ang iyong personal na data"</string>
     <string name="uninstall_application_text_all_users" msgid="575491774380227119">"Gusto mo bang i-uninstall ang app na ito para sa "<b>"lahat"</b>" ng user? Aalisin ang application at ang data nito sa "<b>"lahat"</b>" ng user sa device."</string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Gusto mo bang i-uninstall ang app na ito para sa user na si <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
     <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Gusto mo bang i-uninstall ang app na ito sa iyong profile sa trabaho?"</string>
@@ -108,8 +102,7 @@
     <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Mas nanganganib ang iyong tablet at personal na data sa mga pag-atake mula sa mga hindi kilalang app. Sa pamamagitan ng pag-install ng app na ito, sumasang-ayon kang ikaw ang responsable sa anumang pinsala sa iyong tablet o pagkawala ng data na maaaring magresulta mula sa paggamit nito."</string>
     <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Mas nanganganib ang iyong TV at personal na data sa mga pag-atake mula sa mga hindi kilalang app. Sa pamamagitan ng pag-install ng app na ito, sumasang-ayon kang ikaw ang responsable sa anumang pinsala sa iyong TV o pagkawala ng data na maaaring magresulta mula sa paggamit nito."</string>
     <string name="cloned_app_label" msgid="7503612829833756160">"Clone ng <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
-    <!-- no translation found for archiving_app_label (1127085259724124725) -->
-    <skip />
+    <string name="archiving_app_label" msgid="1127085259724124725">"I-archive ang <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>?"</string>
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Magpatuloy"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Mga Setting"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Ini-install/ina-uninstall ang wear app"</string>
diff --git a/packages/PackageInstaller/res/values-tr/strings.xml b/packages/PackageInstaller/res/values-tr/strings.xml
index 3a0e053..79d7a94 100644
--- a/packages/PackageInstaller/res/values-tr/strings.xml
+++ b/packages/PackageInstaller/res/values-tr/strings.xml
@@ -44,8 +44,7 @@
     <string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Bilinmeyen uygulamalar bu kullanıcı tarafından yüklenemez"</string>
     <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Bu kullanıcının uygulama yüklemesine izin verilmiyor"</string>
     <string name="ok" msgid="7871959885003339302">"Tamam"</string>
-    <!-- no translation found for archive (4447791830199354721) -->
-    <skip />
+    <string name="archive" msgid="4447791830199354721">"Arßiv"</string>
     <string name="update_anyway" msgid="8792432341346261969">"Yine de güncelle"</string>
     <string name="manage_applications" msgid="5400164782453975580">"Uygulamaları yönet"</string>
     <string name="out_of_space_dlg_title" msgid="4156690013884649502">"Yer kalmadı"</string>
@@ -60,16 +59,11 @@
     <string name="uninstall_update_title" msgid="824411791011583031">"Güncelleme kaldırılsın mı?"</string>
     <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g>, ßu uygulamanın bir parçasıdır:"</string>
     <string name="uninstall_application_text" msgid="3816830743706143980">"Bu uygulamanın yüklemesini kaldırmak istiyor musunuz?"</string>
-    <!-- no translation found for archive_application_text (8482325710714386348) -->
-    <skip />
-    <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
-    <skip />
-    <!-- no translation found for archive_application_text_user (2586558895535581451) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
-    <skip />
+    <string name="archive_application_text" msgid="8482325710714386348">"Kißisel verileriniz kaydedilir"</string>
+    <string name="archive_application_text_all_users" msgid="3151229641681672580">"Bu uygulama tüm kullanıcılar için arßivlensin mi? Kißisel verileriniz kaydedilir"</string>
+    <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Bu uygulama iß profilinizde arßivlensin mi? Kißisel verileriniz kaydedilir"</string>
+    <string name="archive_application_text_user" msgid="2586558895535581451">"Bu uygulama <xliff:g id="USERNAME">%1$s</xliff:g> için arßivlensin mi? Kißisel verileriniz kaydedilir"</string>
+    <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Özel alanınızdaki bu uygulamayı arßivlemek istiyor musunuz? Kißisel verileriniz kaydedilir"</string>
     <string name="uninstall_application_text_all_users" msgid="575491774380227119">"Bu uygulamanın yüklemesini "<b>"tüm"</b>" kullanıcılar için kaldırmak istiyor musunuz? Uygulama ve verileri cihazdan "<b>"tüm"</b>" kullanıcılar için kaldırılacaktır."</string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"<xliff:g id="USERNAME">%1$s</xliff:g> adlı kullanıcı için bu uygulamanın yüklemesini kaldırmak istiyor musunuz?"</string>
     <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Bu uygulamanın iß profilinizdeki yüklemesini kaldırmak istiyor musunuz?"</string>
@@ -108,8 +102,7 @@
     <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Tabletiniz ve kißisel verileriniz, bilinmeyen uygulamaların saldırılarına karßı daha savunmasızdır. Bu uygulamayı yükleyerek, uygulama kullanımından dolayı tabletinizde olußabilecek hasarın veya uğrayabileceğiniz veri kaybının sorumluluğunu kabul etmiß olursunuz."</string>
     <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"TV\'niz ve kißisel verileriniz, bilinmeyen uygulamaların saldırılarına karßı daha savunmasızdır. Bu uygulamayı yükleyerek, uygulama kullanımından dolayı TV\'nizde olußabilecek hasarın veya uğrayabileceğiniz veri kaybının sorumluluğunu kabul etmiß olursunuz."</string>
     <string name="cloned_app_label" msgid="7503612829833756160">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> Klonu"</string>
-    <!-- no translation found for archiving_app_label (1127085259724124725) -->
-    <skip />
+    <string name="archiving_app_label" msgid="1127085259724124725">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> arßivlensin mi?"</string>
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Devam"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Ayarlar"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Wear uygulamalarını yükleme/kaldırma"</string>
diff --git a/packages/PackageInstaller/res/values-uk/strings.xml b/packages/PackageInstaller/res/values-uk/strings.xml
index 4ac9185..fe966f7 100644
--- a/packages/PackageInstaller/res/values-uk/strings.xml
+++ b/packages/PackageInstaller/res/values-uk/strings.xml
@@ -44,8 +44,7 @@
     <string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"ĐŠĐ”Đč ĐșĐŸŃ€ĐžŃŃ‚ŃƒĐČач ĐœĐ” ĐŒĐŸĐ¶Đ” ĐČŃŃ‚Đ°ĐœĐŸĐČлюĐČато ĐœĐ”ĐČŃ–ĐŽĐŸĐŒŃ– ĐŽĐŸĐŽĐ°Ń‚ĐșĐž"</string>
     <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"ĐŠĐ”Đč ĐșĐŸŃ€ĐžŃŃ‚ŃƒĐČач ĐœĐ” ĐŒĐŸĐ¶Đ” ĐČŃŃ‚Đ°ĐœĐŸĐČлюĐČато ĐŽĐŸĐŽĐ°Ń‚ĐșĐž"</string>
     <string name="ok" msgid="7871959885003339302">"OK"</string>
-    <!-- no translation found for archive (4447791830199354721) -->
-    <skip />
+    <string name="archive" msgid="4447791830199354721">"АрхіĐČуĐČато"</string>
     <string name="update_anyway" msgid="8792432341346261969">"УсД ĐŸĐŽĐœĐŸ ĐŸĐœĐŸĐČото"</string>
     <string name="manage_applications" msgid="5400164782453975580">"ĐšĐ”Ń€ŃƒĐČато ĐŽĐŸĐŽĐ°Ń‚ĐșĐ°ĐŒĐž"</string>
     <string name="out_of_space_dlg_title" msgid="4156690013884649502">"ĐĐ”ĐŽĐŸŃŃ‚Đ°Ń‚. ĐŒŃ–ŃŃ†Ń"</string>
@@ -60,16 +59,11 @@
     <string name="uninstall_update_title" msgid="824411791011583031">"ВОЎалОтО ĐŸĐœĐŸĐČĐ»Đ”ĐœĐœŃ"</string>
     <string name="uninstall_activity_text" msgid="1928194674397770771">"Дія <xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> є Ń‡Đ°ŃŃ‚ĐžĐœĐŸŃŽ таĐșĐŸŃ— ĐżŃ€ĐŸĐłŃ€Đ°ĐŒĐž:"</string>
     <string name="uninstall_application_text" msgid="3816830743706143980">"ВОЎалОтО цДĐč ĐŽĐŸĐŽĐ°Ń‚ĐŸĐș?"</string>
-    <!-- no translation found for archive_application_text (8482325710714386348) -->
-    <skip />
-    <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
-    <skip />
-    <!-- no translation found for archive_application_text_user (2586558895535581451) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
-    <skip />
+    <string name="archive_application_text" msgid="8482325710714386348">"Ваші ĐŸŃĐŸĐ±ĐžŃŃ‚Ń– ĐŽĐ°ĐœŃ– буЎД Đ·Đ±Đ”Ń€Đ”Đ¶Đ”ĐœĐŸ"</string>
+    <string name="archive_application_text_all_users" msgid="3151229641681672580">"АрхіĐČуĐČато цДĐč ĐŽĐŸĐŽĐ°Ń‚ĐŸĐș ĐŽĐ»Ń ĐČсіх ĐșĐŸŃ€ĐžŃŃ‚ŃƒĐČачіĐČ? Ваші ĐŸŃĐŸĐ±ĐžŃŃ‚Ń– ĐŽĐ°ĐœŃ– буЎД Đ·Đ±Đ”Ń€Đ”Đ¶Đ”ĐœĐŸ"</string>
+    <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"АрхіĐČуĐČато цДĐč ĐŽĐŸĐŽĐ°Ń‚ĐŸĐș у ĐČĐ°ŃˆĐŸĐŒŃƒ Ń€ĐŸĐ±ĐŸŃ‡ĐŸĐŒŃƒ ĐżŃ€ĐŸŃ„Ń–Đ»Ń–? Ваші ĐŸŃĐŸĐ±ĐžŃŃ‚Ń– ĐŽĐ°ĐœŃ– буЎД Đ·Đ±Đ”Ń€Đ”Đ¶Đ”ĐœĐŸ"</string>
+    <string name="archive_application_text_user" msgid="2586558895535581451">"АрхіĐČуĐČато цДĐč ĐŽĐŸĐŽĐ°Ń‚ĐŸĐș ĐŽĐ»Ń ĐșĐŸŃ€ĐžŃŃ‚ŃƒĐČача <xliff:g id="USERNAME">%1$s</xliff:g>? Ваші ĐŸŃĐŸĐ±ĐžŃŃ‚Ń– ĐŽĐ°ĐœŃ– буЎД Đ·Đ±Đ”Ń€Đ”Đ¶Đ”ĐœĐŸ"</string>
+    <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"АрхіĐČуĐČато цДĐč ĐŽĐŸĐŽĐ°Ń‚ĐŸĐș Ń–Đ· ĐČĐ°ŃˆĐŸĐłĐŸ проĐČĐ°Ń‚ĐœĐŸĐłĐŸ ĐżŃ€ĐŸŃŃ‚ĐŸŃ€Ńƒ? Ваші ĐŸŃĐŸĐ±ĐžŃŃ‚Ń– ĐŽĐ°ĐœŃ– буЎД Đ·Đ±Đ”Ń€Đ”Đ¶Đ”ĐœĐŸ"</string>
     <string name="uninstall_application_text_all_users" msgid="575491774380227119">"Đ„ĐŸŃ‡Đ”Ń‚Đ” ĐČОЎалОтО цю ĐżŃ€ĐŸĐłŃ€Đ°ĐŒŃƒ ĐŽĐ»Ń "<b>"ĐČсіх"</b>" ĐșĐŸŃ€ĐžŃŃ‚ŃƒĐČачіĐČ? ĐŸŃ€ĐŸĐłŃ€Đ°ĐŒŃƒ та її ĐŽĐ°ĐœŃ– буЎД ĐČĐžĐŽĐ°Đ»Đ”ĐœĐŸ ĐŽĐ»Ń "<b>"ĐČсіх"</b>" ĐșĐŸŃ€ĐžŃŃ‚ŃƒĐČачіĐČ Ń†ŃŒĐŸĐłĐŸ ĐżŃ€ĐžŃŃ‚Ń€ĐŸŃŽ."</string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"ВОЎалОтО цДĐč ĐŽĐŸĐŽĐ°Ń‚ĐŸĐș ĐŽĐ»Ń ĐșĐŸŃ€ĐžŃŃ‚ŃƒĐČача <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
     <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"ВОЎалОтО цДĐč ĐŽĐŸĐŽĐ°Ń‚ĐŸĐș Ń–Đ· ĐČĐ°ŃˆĐŸĐłĐŸ Ń€ĐŸĐ±ĐŸŃ‡ĐŸĐłĐŸ ĐżŃ€ĐŸŃ„Ń–Đ»ŃŽ?"</string>
@@ -108,8 +102,7 @@
     <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Ваш ĐżĐ»Đ°ĐœŃˆĐ”Ń‚ і ĐŸŃĐŸĐ±ĐžŃŃ‚Ń– ĐŽĐ°ĐœŃ– Đ±Ń–Đ»ŃŒŃˆ ŃƒŃ€Đ°Đ·Đ»ĐžĐČі ĐŽĐŸ атаĐș ĐœĐ”ĐČŃ–ĐŽĐŸĐŒĐžŃ… ĐŽĐŸĐŽĐ°Ń‚ĐșіĐČ. ĐŁŃŃ‚Đ°ĐœĐŸĐČлюючО цДĐč ĐŽĐŸĐŽĐ°Ń‚ĐŸĐș, ĐČĐž бДрДтД ĐœĐ° сДбД ĐČŃ–ĐŽĐżĐŸĐČŃ–ĐŽĐ°Đ»ŃŒĐœŃ–ŃŃ‚ŃŒ за ĐżĐŸŃˆĐșĐŸĐŽĐ¶Đ”ĐœĐœŃ ĐżĐ»Đ°ĐœŃˆĐ”Ń‚Đ° чо ĐČтрату ĐŽĐ°ĐœĐžŃ… ŃƒĐœĐ°ŃĐ»Ń–ĐŽĐŸĐș ĐČĐžĐșĐŸŃ€ĐžŃŃ‚Đ°ĐœĐœŃ ĐŽĐŸĐŽĐ°Ń‚Đșа."</string>
     <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Ваш тДлДĐČŃ–Đ·ĐŸŃ€ і ĐŸŃĐŸĐ±ĐžŃŃ‚Ń– ĐŽĐ°ĐœŃ– Đ±Ń–Đ»ŃŒŃˆ ŃƒŃ€Đ°Đ·Đ»ĐžĐČі ĐŽĐŸ атаĐș ĐœĐ”ĐČŃ–ĐŽĐŸĐŒĐžŃ… ĐŽĐŸĐŽĐ°Ń‚ĐșіĐČ. ĐŁŃŃ‚Đ°ĐœĐŸĐČлюючО цДĐč ĐŽĐŸĐŽĐ°Ń‚ĐŸĐș, ĐČĐž бДрДтД ĐœĐ° сДбД ĐČŃ–ĐŽĐżĐŸĐČŃ–ĐŽĐ°Đ»ŃŒĐœŃ–ŃŃ‚ŃŒ за ĐżĐŸŃˆĐșĐŸĐŽĐ¶Đ”ĐœĐœŃ тДлДĐČŃ–Đ·ĐŸŃ€Đ° чо ĐČтрату ĐŽĐ°ĐœĐžŃ… ŃƒĐœĐ°ŃĐ»Ń–ĐŽĐŸĐș ĐČĐžĐșĐŸŃ€ĐžŃŃ‚Đ°ĐœĐœŃ ĐŽĐŸĐŽĐ°Ń‚Đșа."</string>
     <string name="cloned_app_label" msgid="7503612829833756160">"ĐšĐŸĐżŃ–Ń ĐŽĐŸĐŽĐ°Ń‚Đșа <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
-    <!-- no translation found for archiving_app_label (1127085259724124725) -->
-    <skip />
+    <string name="archiving_app_label" msgid="1127085259724124725">"АрхіĐČуĐČато ĐŽĐŸĐŽĐ°Ń‚ĐŸĐș <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>?"</string>
     <string name="anonymous_source_continue" msgid="4375745439457209366">"ĐŸŃ€ĐŸĐŽĐŸĐČжОтО"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"ĐĐ°Đ»Đ°ŃˆŃ‚ŃƒĐČĐ°ĐœĐœŃ"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Đ’ŃŃ‚Đ°ĐœĐŸĐČĐ»Đ”ĐœĐœŃ Đ°Đ±ĐŸ ĐČĐžĐŽĐ°Đ»Đ”ĐœĐœŃ ĐŽĐŸĐŽĐ°Ń‚ĐșіĐČ Wear"</string>
diff --git a/packages/PackageInstaller/res/values-ur/strings.xml b/packages/PackageInstaller/res/values-ur/strings.xml
index 6730574..98d6e8f 100644
--- a/packages/PackageInstaller/res/values-ur/strings.xml
+++ b/packages/PackageInstaller/res/values-ur/strings.xml
@@ -44,8 +44,7 @@
     <string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"یہ Ű”Ű§Ű±Ù Ù†Ű§Ù…Űčلوم Ű§ÛŒÙŸŰł کو Ű§Ù†ŰłÙčŰ§Ù„ نہیÚș ک۱ ŰłÚ©ŰȘۧ"</string>
     <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"ۧ۳ Ű”Ű§Ű±Ù کو Ű§ÛŒÙŸŰł Ű§Ù†ŰłÙčŰ§Ù„ Ú©Ű±Ù†Û’ کی ۧۏۧŰČŰȘ نہیÚș ہے"</string>
     <string name="ok" msgid="7871959885003339302">"Ùčڟیک ہے"</string>
-    <!-- no translation found for archive (4447791830199354721) -->
-    <skip />
+    <string name="archive" msgid="4447791830199354721">"ŰąŰ±Ú©Ű§ŰŠÛŒÙˆ Ú©Ű±ÛŒÚș"</string>
     <string name="update_anyway" msgid="8792432341346261969">"ŰšÛŰ± Ű­Ű§Ù„ Ű§ÙŸ ڈیÙč Ú©Ű±ÛŒÚș"</string>
     <string name="manage_applications" msgid="5400164782453975580">"Ű§ÛŒÙŸŰł Ù…Ù†ŰžÙ… Ú©Ű±ÛŒÚș"</string>
     <string name="out_of_space_dlg_title" msgid="4156690013884649502">"ŰŹÚŻÛ نہیÚș ہے"</string>
@@ -60,16 +59,11 @@
     <string name="uninstall_update_title" msgid="824411791011583031">"Ű§ÙŸ ڈیÙč Ű§ÙŽÙ† Ű§Ù†ŰłÙčŰ§Ù„ Ú©Ű±ÛŒÚș"</string>
     <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> ۯ۱ۏ Ű°ÛŒÙ„ Ű§ÛŒÙŸ کۧ Ű­Ű”Û ہے:"</string>
     <string name="uninstall_application_text" msgid="3816830743706143980">"Ú©ÛŒŰ§ ŰąÙŸ ۧ۳ Ű§ÛŒÙŸ کو Ű§ÙŽÙ† Ű§Ù†ŰłÙčŰ§Ù„ Ú©Ű±Ù†Ű§ Ú†Ű§ÛŰȘے ہیÚș۟"</string>
-    <!-- no translation found for archive_application_text (8482325710714386348) -->
-    <skip />
-    <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
-    <skip />
-    <!-- no translation found for archive_application_text_user (2586558895535581451) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
-    <skip />
+    <string name="archive_application_text" msgid="8482325710714386348">"ŰąÙŸ کۧ ۰ۧŰȘی ڈیÙčۧ Ù…Ű­ÙÙˆŰž ہو ŰŹŰ§ŰŠÛ’ گۧ"</string>
+    <string name="archive_application_text_all_users" msgid="3151229641681672580">"ۧ۳ Ű§ÛŒÙŸ کو ŰȘÙ…Ű§Ù… Ű”Ű§Ű±ÙÛŒÙ† کے لیے ŰąŰ±Ú©Ű§ŰŠÛŒÙˆ Ú©Ű±ÛŒÚș۟ ŰąÙŸ کۧ ۰ۧŰȘی ڈیÙčۧ Ù…Ű­ÙÙˆŰž ہو ŰŹŰ§ŰŠÛ’ گۧ"</string>
+    <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"ۧ۳ Ű§ÛŒÙŸ کو Ű§ÙŸÙ†ÛŒ ŰŻÙŰȘŰ±ÛŒ ÙŸŰ±ÙˆÙŰ§ŰŠÙ„ ÙŸŰ± ŰąŰ±Ú©Ű§ŰŠÛŒÙˆ Ú©Ű±ÛŒÚș۟ ŰąÙŸ کۧ ۰ۧŰȘی ڈیÙčۧ Ù…Ű­ÙÙˆŰž ہو ŰŹŰ§ŰŠÛ’ گۧ"</string>
+    <string name="archive_application_text_user" msgid="2586558895535581451">"<xliff:g id="USERNAME">%1$s</xliff:g> کے لیے ۧ۳ Ű§ÛŒÙŸ کو ŰąŰ±Ú©Ű§ŰŠÛŒÙˆ Ú©Ű±ÛŒÚș۟ ŰąÙŸ کۧ ۰ۧŰȘی ڈیÙčۧ Ù…Ű­ÙÙˆŰž ہو ŰŹŰ§ŰŠÛ’ گۧ"</string>
+    <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Ú©ÛŒŰ§ ŰąÙŸ ۧ۳ Ű§ÛŒÙŸ کو Ű§ÙŸÙ†ÛŒ Ù†ŰŹÛŒ ŰŹÚŻÛ ŰłÛ’ ŰąŰ±Ú©Ű§ŰŠÛŒÙˆ Ú©Ű±Ù†Ű§ Ú†Ű§ÛŰȘے ہیÚș۟ ŰąÙŸ کۧ ۰ۧŰȘی ڈیÙčۧ Ù…Ű­ÙÙˆŰž ہو ŰŹŰ§ŰŠÛ’ گۧ"</string>
     <string name="uninstall_application_text_all_users" msgid="575491774380227119">"Ú©ÛŒŰ§ ŰąÙŸ "<b>"ŰłŰšÚŸÛŒ"</b>" Ű”Ű§Ű±ÙÛŒÙ† Ú©ÛŒÙ„ŰŠÛ’ ۧ۳ Ű§ÛŒÙŸ کو Ű§ÙŽÙ† Ű§Ù†ŰłÙčŰ§Ù„ Ú©Ű±Ù†Ű§ Ú†Ű§ÛŰȘے ہیÚș۟ Ű§ÛŒÙŸÙ„ÛŒÚ©ÛŒŰŽÙ† Ű§ÙˆŰ± ۧ۳ کۧ ڈیÙčۧ ŰąÙ„Û ÙŸŰ± Ù…ÙˆŰŹÙˆŰŻ "<b>"ŰłŰšÚŸÛŒ"</b>" Ű”Ű§Ű±ÙÛŒÙ† ŰłÛ’ ہÙčۧ ŰŻÛŒŰ§ ŰŹŰ§ŰŠÛ’ ÚŻŰ§Û”"</string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Ú©ÛŒŰ§ ŰąÙŸ ۧ۳ Ű§ÛŒÙŸ کو Ű”Ű§Ű±Ù <xliff:g id="USERNAME">%1$s</xliff:g> Ú©ÛŒÙ„ŰŠÛ’ Ű§ÙŽÙ† Ű§Ù†ŰłÙčŰ§Ù„ Ú©Ű±Ù†Ű§ Ú†Ű§ÛŰȘے ہیÚș۟"</string>
     <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Ú©ÛŒŰ§ ŰąÙŸ Ű§ÙŸÙ†Û’ ŰŻÙŰȘŰ±ÛŒ ÙŸŰ±ÙˆÙŰ§ŰŠÙ„ ŰłÛ’ یہ Ű§ÛŒÙŸ Ű§ÙŽÙ† Ű§Ù†ŰłÙčŰ§Ù„ Ú©Ű±Ù†Ű§ Ú†Ű§ÛŰȘے ہیÚș۟"</string>
@@ -108,8 +102,7 @@
     <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"ŰąÙŸ کے ÙčÛŒŰšÙ„ÛŒÙč Ű§ÙˆŰ± ۰ۧŰȘی ڈیÙčۧ کو Ù†Ű§Ù…Űčلوم Ű§ÛŒÙŸŰł کی ŰŹŰ§Ù†Űš ŰłÛ’ Ű­Ù…Ù„Û’ کۧ ŰČÛŒŰ§ŰŻÛ ŰźŰ·Ű±Û ہے۔ ۧ۳ Ű§ÛŒÙŸ کو Ű§Ù†ŰłÙčŰ§Ù„ ک۱ Ú©Û’ŰŒ ŰąÙŸ ۧ۳ ۚۧŰȘ ŰłÛ’ ۧŰȘÙŰ§Ù‚ ک۱ŰȘے ہیÚș کہ ŰąÙŸ ۧ۳ ŰłÛ’ Ű§ÙŸÙ†Û’ ÙčÛŒŰšÙ„ÛŒÙč کو ہونے ÙˆŰ§Ù„Û’ Ú©ŰłÛŒ ŰšÚŸÛŒ Ù†Ù‚Ű”Ű§Ù† ÛŒŰ§ ڈیÙčۧ کے Ù†Ù‚Ű”Ű§Ù† کے Ù„ŰŠÛ’ ŰźÙˆŰŻ Ű°Ù…Û ۯۧ۱ ہیÚș۔"</string>
     <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"‏ŰąÙŸ کے TV Ű§ÙˆŰ± ۰ۧŰȘی ڈیÙčۧ کو Ù†Ű§Ù…Űčلوم Ű§ÛŒÙŸŰł کی ŰŹŰ§Ù†Űš ŰłÛ’ Ű­Ù…Ù„Û’ کۧ ŰČÛŒŰ§ŰŻÛ ŰźŰ·Ű±Û ہے۔ ۧ۳ Ű§ÛŒÙŸ کو Ű§Ù†ŰłÙčŰ§Ù„ ک۱ Ú©Û’ŰŒ ŰąÙŸ ۧ۳ ۚۧŰȘ ŰłÛ’ ۧŰȘÙŰ§Ù‚ ک۱ŰȘے ہیÚș کہ ŰąÙŸ ۧ۳ ŰłÛ’ Ű§ÙŸÙ†Û’ TV کو ہونے ÙˆŰ§Ù„Û’ Ú©ŰłÛŒ ŰšÚŸÛŒ Ù†Ù‚Ű”Ű§Ù† ÛŒŰ§ ڈیÙčۧ کے Ù†Ù‚Ű”Ű§Ù† کے Ù„ŰŠÛ’ ŰźÙˆŰŻ Ű°Ù…Û ۯۧ۱ ہیÚș۔"</string>
     <string name="cloned_app_label" msgid="7503612829833756160">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> کلون"</string>
-    <!-- no translation found for archiving_app_label (1127085259724124725) -->
-    <skip />
+    <string name="archiving_app_label" msgid="1127085259724124725">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> کو ŰąŰ±Ú©Ű§ŰŠÛŒÙˆ Ú©Ű±ÛŒÚș۟"</string>
     <string name="anonymous_source_continue" msgid="4375745439457209366">"ŰŹŰ§Ű±ÛŒ Ű±Ú©ÚŸÛŒÚș"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"ŰȘ۱ŰȘÛŒŰšŰ§ŰȘ"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"‏wear Ű§ÛŒÙŸŰł کو Ű§Ù†ŰłÙčŰ§Ù„/Ű§ÙŽÙ† Ű§Ù†ŰłÙčŰ§Ù„ Ú©Ű±Ù†Ű§"</string>
diff --git a/packages/PackageInstaller/res/values-uz/strings.xml b/packages/PackageInstaller/res/values-uz/strings.xml
index cc5f6bdd..843efe9 100644
--- a/packages/PackageInstaller/res/values-uz/strings.xml
+++ b/packages/PackageInstaller/res/values-uz/strings.xml
@@ -44,8 +44,7 @@
     <string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Notanish ilovalarni bu foydalanuvchi tomonidan o‘rnatib bo‘lmaydi"</string>
     <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Bu foydalanuvchiga ilovalarni o‘rnatish uchun ruxsat berilmagan"</string>
     <string name="ok" msgid="7871959885003339302">"OK"</string>
-    <!-- no translation found for archive (4447791830199354721) -->
-    <skip />
+    <string name="archive" msgid="4447791830199354721">"Arxivlash"</string>
     <string name="update_anyway" msgid="8792432341346261969">"Baribir yangilansin"</string>
     <string name="manage_applications" msgid="5400164782453975580">"Ilovalarni boshqarish"</string>
     <string name="out_of_space_dlg_title" msgid="4156690013884649502">"Joy qolmadi"</string>
@@ -60,16 +59,11 @@
     <string name="uninstall_update_title" msgid="824411791011583031">"Yangilanishni o‘chirish"</string>
     <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> quyidagi ilovaning bir qismidir:"</string>
     <string name="uninstall_application_text" msgid="3816830743706143980">"Bu ilovani o‘chirib tashlamoqchimisiz?"</string>
-    <!-- no translation found for archive_application_text (8482325710714386348) -->
-    <skip />
-    <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
-    <skip />
-    <!-- no translation found for archive_application_text_user (2586558895535581451) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
-    <skip />
+    <string name="archive_application_text" msgid="8482325710714386348">"Shaxsiy maÊŒlumotlar saqlanadi"</string>
+    <string name="archive_application_text_all_users" msgid="3151229641681672580">"Bu ilova barcha foydalanuvchilar uchun arxivlansinmi? Shaxsiy maÊŒlumotlar saqlanadi"</string>
+    <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Ish profilidagi mazkur ilova arxivlansinmi? Shaxsiy maÊŒlumotlar saqlanadi"</string>
+    <string name="archive_application_text_user" msgid="2586558895535581451">"Bu ilova <xliff:g id="USERNAME">%1$s</xliff:g> uchun arxivlansinmi? Shaxsiy maÊŒlumotlar saqlanadi"</string>
+    <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Maxfiy joydagi mazkur ilova arxivlansinmi? Shaxsiy maÊŒlumotlar saqlanadi"</string>
     <string name="uninstall_application_text_all_users" msgid="575491774380227119">"Ushbu ilova "<b>"barcha"</b>" foydalanuvchilar uchun o‘chirilsinmi? Ilova va uning axborotlari qurilmadagi "<b>"barcha"</b>" foydalanuvchilardan o‘chib ketadi."</string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Haqiqatdan ham <xliff:g id="USERNAME">%1$s</xliff:g> foydalanuvchi uchun ushbu ilovani olib tashlamoqchimisiz?"</string>
     <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Bu ilova ish profilidan olib tashlansinmi?"</string>
@@ -108,8 +102,7 @@
     <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Planshetingiz va shaxsiy axborotlaringiz notanish ilovalar hujumiga zaif bo‘ladi. Bu ilovani o‘rnatish bilan planshetingizga yetkaziladigan shikast va axborotlaringizni o‘chirib yuborilishiga javobgarlikni o‘z zimmangizga olasiz."</string>
     <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"TV va shaxsiy axborotlaringiz notanish ilovalar hujumiga zaif bo‘ladi. Bu ilovani o‘rnatish bilan televizoringizga yetkaziladigan shikast va axborotlaringizni o‘chirib yuborilishiga javobgarlikni o‘z zimmangizga olasiz."</string>
     <string name="cloned_app_label" msgid="7503612829833756160">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> nusxasi"</string>
-    <!-- no translation found for archiving_app_label (1127085259724124725) -->
-    <skip />
+    <string name="archiving_app_label" msgid="1127085259724124725">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> arxivlansinmi?"</string>
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Davom etish"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Sozlamalar"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Wear ilovalarini o‘rnatish/o‘chirish"</string>
diff --git a/packages/PackageInstaller/res/values-vi/strings.xml b/packages/PackageInstaller/res/values-vi/strings.xml
index 6af9b82..40e6dd5 100644
--- a/packages/PackageInstaller/res/values-vi/strings.xml
+++ b/packages/PackageInstaller/res/values-vi/strings.xml
@@ -44,8 +44,7 @@
     <string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Người dùng này không thể cài đáș·t ứng dỄng không xác định"</string>
     <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Người dùng này không Ä‘Æ°á»Łc phép cài đáș·t ứng dỄng"</string>
     <string name="ok" msgid="7871959885003339302">"OK"</string>
-    <!-- no translation found for archive (4447791830199354721) -->
-    <skip />
+    <string name="archive" msgid="4447791830199354721">"Lưu trữ"</string>
     <string name="update_anyway" msgid="8792432341346261969">"Váș«n cáș­p nháș­t"</string>
     <string name="manage_applications" msgid="5400164782453975580">"QuáșŁn lý ứng dỄng"</string>
     <string name="out_of_space_dlg_title" msgid="4156690013884649502">"Háșżt dung lÆ°á»Łng"</string>
@@ -60,16 +59,11 @@
     <string name="uninstall_update_title" msgid="824411791011583031">"Gụ cài đáș·t báșŁn cáș­p nháș­t"</string>
     <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> là một pháș§n cá»§a ứng dỄng sau:"</string>
     <string name="uninstall_application_text" msgid="3816830743706143980">"BáșĄn có muốn gụ cài đáș·t ứng dỄng này không?"</string>
-    <!-- no translation found for archive_application_text (8482325710714386348) -->
-    <skip />
-    <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
-    <skip />
-    <!-- no translation found for archive_application_text_user (2586558895535581451) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
-    <skip />
+    <string name="archive_application_text" msgid="8482325710714386348">"Dữ liệu cá nhân cá»§a báșĄn sáșœ Ä‘Æ°á»Łc lưu"</string>
+    <string name="archive_application_text_all_users" msgid="3151229641681672580">"Lưu trữ ứng dỄng này cho táș„t cáșŁ người dùng. Dữ liệu cá nhân cá»§a báșĄn sáșœ Ä‘Æ°á»Łc lưu"</string>
+    <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Lưu trữ ứng dỄng này trên hồ sÆĄ công việc cá»§a báșĄn? Dữ liệu cá nhân cá»§a báșĄn sáșœ Ä‘Æ°á»Łc lưu"</string>
+    <string name="archive_application_text_user" msgid="2586558895535581451">"Lưu trữ ứng dỄng này cho <xliff:g id="USERNAME">%1$s</xliff:g>? Dữ liệu cá nhân cá»§a báșĄn sáșœ Ä‘Æ°á»Łc lưu"</string>
+    <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"BáșĄn có muốn lưu trữ ứng dỄng này ra khỏi không gian riêng tư cá»§a mình không? Dữ liệu cá nhân cá»§a báșĄn sáșœ Ä‘Æ°á»Łc lưu"</string>
     <string name="uninstall_application_text_all_users" msgid="575491774380227119">"BáșĄn có muốn gụ cài đáș·t ứng dỄng này cho "<b>"táș„t cáșŁ"</b>" người dùng không? Ớng dỄng và dữ liệu cá»§a ứng dỄng sáșœ bị xóa khỏi "<b>"táș„t cáșŁ"</b>" người dùng trên thiáșżt bị."</string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"BáșĄn có muốn gụ cài đáș·t ứng dỄng này cho người dùng <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
     <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"BáșĄn có muốn gụ cài đáș·t ứng dỄng này khỏi hồ sÆĄ công việc cá»§a mình không?"</string>
@@ -108,8 +102,7 @@
     <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Máy tính báșŁng và dữ liệu cá nhân cá»§a báșĄn dễ bị các ứng dỄng không xác định táș„n công hÆĄn. Báș±ng cách cài đáș·t ứng dỄng này, báșĄn đồng ý tá»± chịu trách nhiệm cho mọi hỏng hóc đối với máy tính báșŁng cá»§a mình hoáș·c máș„t mát dữ liệu có thể phát sinh do sá»­ dỄng ứng dỄng này."</string>
     <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"TV và dữ liệu cá nhân cá»§a báșĄn dễ bị các ứng dỄng không xác định táș„n công hÆĄn. Báș±ng cách cài đáș·t ứng dỄng này, báșĄn đồng ý tá»± chịu trách nhiệm cho mọi hỏng hóc đối với TV cá»§a mình hoáș·c máș„t mát dữ liệu có thể phát sinh do sá»­ dỄng ứng dỄng này."</string>
     <string name="cloned_app_label" msgid="7503612829833756160">"BáșŁn sao cá»§a <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
-    <!-- no translation found for archiving_app_label (1127085259724124725) -->
-    <skip />
+    <string name="archiving_app_label" msgid="1127085259724124725">"Lưu trữ <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>?"</string>
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Tiáșżp tỄc"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Cài đáș·t"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Cài đáș·t/gụ cài đáș·t ứng dỄng Wear"</string>
diff --git a/packages/PackageInstaller/res/values-zh-rCN/strings.xml b/packages/PackageInstaller/res/values-zh-rCN/strings.xml
index 3e77d7f..605726a 100644
--- a/packages/PackageInstaller/res/values-zh-rCN/strings.xml
+++ b/packages/PackageInstaller/res/values-zh-rCN/strings.xml
@@ -44,8 +44,7 @@
     <string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"èŻ„ç”šæˆ·æ— æł•ćź‰èŁ…æœȘ矄ćș”甚"</string>
     <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"æ­€ç”šæˆ·æ— æƒćź‰èŁ…ćș”甚"</string>
     <string name="ok" msgid="7871959885003339302">"硼漚"</string>
-    <!-- no translation found for archive (4447791830199354721) -->
-    <skip />
+    <string name="archive" msgid="4447791830199354721">"ćœ’æĄŁ"</string>
     <string name="update_anyway" msgid="8792432341346261969">"仍然曎新"</string>
     <string name="manage_applications" msgid="5400164782453975580">"缡理ćș”甚"</string>
     <string name="out_of_space_dlg_title" msgid="4156690013884649502">"ç©șé—Žäžè¶ł"</string>
@@ -60,16 +59,11 @@
     <string name="uninstall_update_title" msgid="824411791011583031">"ćžèœœæ›Žæ–°"</string>
     <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g>汞äșŽä»„例ćș”甚"</string>
     <string name="uninstall_application_text" msgid="3816830743706143980">"èŠćžèœœæ­€ćș”ç”šć—ïŒŸ"</string>
-    <!-- no translation found for archive_application_text (8482325710714386348) -->
-    <skip />
-    <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
-    <skip />
-    <!-- no translation found for archive_application_text_user (2586558895535581451) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
-    <skip />
+    <string name="archive_application_text" msgid="8482325710714386348">"çł»ç»Ÿć°†äżć­˜æ‚šçš„äžȘäșșæ•°æź"</string>
+    <string name="archive_application_text_all_users" msgid="3151229641681672580">"芁针ćŻčæ‰€æœ‰ç”šæˆ·ćœ’æĄŁæ­€ćș”ç”šć—ïŒŸçł»ç»Ÿć°†äżć­˜æ‚šçš„äžȘäșșæ•°æź"</string>
+    <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"èŠćœ’æĄŁć·„äœœè”„æ–™äž­çš„æ­€ćș”ç”šć—ïŒŸçł»ç»Ÿć°†äżć­˜æ‚šçš„äžȘäșșæ•°æź"</string>
+    <string name="archive_application_text_user" msgid="2586558895535581451">"芁针ćŻč<xliff:g id="USERNAME">%1$s</xliff:g>ćœ’æĄŁæ­€ćș”ç”šć—ïŒŸçł»ç»Ÿć°†äżć­˜æ‚šçš„äžȘäșșæ•°æź"</string>
+    <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"èŠćœ’æĄŁç§ćŻ†ç©ș闎䞭的歀ćș”ç”šć—ïŒŸçł»ç»Ÿć°†äżć­˜æ‚šçš„äžȘäșșæ•°æź"</string>
     <string name="uninstall_application_text_all_users" msgid="575491774380227119">"芁äžș"<b>"所有"</b>"ç”šæˆ·ćžèœœæ­€ćș”ç”šć—ïŒŸçł»ç»Ÿć°†äžșèźŸć€‡äžŠçš„"<b>"所有"</b>"甚户移陀歀ćș”ç”šćŠć…¶æ•°æźă€‚"</string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"芁äžș甚户<xliff:g id="USERNAME">%1$s</xliff:g>ćžèœœæ­€ćș”ç”šć—ïŒŸ"</string>
     <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"æ‚šæƒłä»Žæ‚šçš„ć·„äœœè”„æ–™äž­ćžèœœæ­€ćș”ç”šć—ïŒŸ"</string>
@@ -108,8 +102,7 @@
     <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"æ„ćŽ†äžæ˜Žçš„ćș”ç”šćŸˆćŻèƒœäŒšæŸćźłæ‚šçš„ćčłæżç””è„‘ć’ŒäžȘäșșæ•°æźă€‚ćź‰èŁ…èŻ„ćș”ç”šćłèĄšç€șïŒŒæ‚šćŒæ„ćŻčäșŽć› äœżç”šèŻ„ćș”ç”šćŻèƒœćŻŒè‡Žçš„ä»»äœ•ćčłæżç””è„‘æŸćæˆ–æ•°æźäžąć€±æƒ…ć†”ïŒŒæ‚šèŽŸæœ‰ć…šéƒšèŽŁä»»ă€‚"</string>
     <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"æ„ćŽ†äžæ˜Žçš„ćș”ç”šćŸˆćŻèƒœäŒšæŸćźłæ‚šçš„ç””è§†ć’ŒäžȘäșșæ•°æźă€‚ćź‰èŁ…èŻ„ćș”ç”šćłèĄšç€șïŒŒæ‚šćŒæ„ćŻčäșŽć› äœżç”šèŻ„ćș”ç”šćŻèƒœćŻŒè‡Žçš„ä»»äœ•ç””è§†æŸćæˆ–æ•°æźäžąć€±æƒ…ć†”ïŒŒæ‚šèŽŸæœ‰ć…šéƒšèŽŁä»»ă€‚"</string>
     <string name="cloned_app_label" msgid="7503612829833756160">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> 態隆ćș”甚"</string>
-    <!-- no translation found for archiving_app_label (1127085259724124725) -->
-    <skip />
+    <string name="archiving_app_label" msgid="1127085259724124725">"èŠćœ’æĄŁ<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>ć—ïŒŸ"</string>
     <string name="anonymous_source_continue" msgid="4375745439457209366">"ç»§ç»­"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"èźŸçœź"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"æ­Łćœšćź‰èŁ…/ćžèœœ Wear ćș”甚"</string>
diff --git a/packages/PackageInstaller/res/values-zh-rHK/strings.xml b/packages/PackageInstaller/res/values-zh-rHK/strings.xml
index 4d2edea..ab7b7ff 100644
--- a/packages/PackageInstaller/res/values-zh-rHK/strings.xml
+++ b/packages/PackageInstaller/res/values-zh-rHK/strings.xml
@@ -44,8 +44,7 @@
     <string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"æ­€äœżç”šè€…ç„Ąæł•ćź‰èŁäŸ†æșäžæ˜Žçš„æ‡‰ç”šçš‹ćŒ"</string>
     <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"æ­€äœżç”šè€…ç„Ąæł•ćź‰èŁæ‡‰ç”šçš‹ćŒ"</string>
     <string name="ok" msgid="7871959885003339302">"çąș漚"</string>
-    <!-- no translation found for archive (4447791830199354721) -->
-    <skip />
+    <string name="archive" msgid="4447791830199354721">"氁歘"</string>
     <string name="update_anyway" msgid="8792432341346261969">"仍芁曎新"</string>
     <string name="manage_applications" msgid="5400164782453975580">"çźĄç†æ‡‰ç”šçš‹ćŒ"</string>
     <string name="out_of_space_dlg_title" msgid="4156690013884649502">"ć„Č歘ç©șé–“äžè¶ł"</string>
@@ -60,16 +59,11 @@
     <string name="uninstall_update_title" msgid="824411791011583031">"è§Łé™€ćź‰èŁæ›Žæ–°"</string>
     <string name="uninstall_activity_text" msgid="1928194674397770771">"「<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g>ă€ć±Źæ–Œä»„äž‹æ‡‰ç”šçš‹ćŒïŒš"</string>
     <string name="uninstall_application_text" msgid="3816830743706143980">"äœ èŠè§Łé™€ćź‰èŁæ­€æ‡‰ç”šçš‹ćŒć—ŽïŒŸ"</string>
-    <!-- no translation found for archive_application_text (8482325710714386348) -->
-    <skip />
-    <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
-    <skip />
-    <!-- no translation found for archive_application_text_user (2586558895535581451) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
-    <skip />
+    <string name="archive_application_text" msgid="8482325710714386348">"çł»ç”±æœƒć„Čć­˜äœ çš„ć€‹äșșèł‡æ–™"</string>
+    <string name="archive_application_text_all_users" msgid="3151229641681672580">"芁ç‚șæ‰€æœ‰äœżç”šè€…ć°ć­˜æ­€æ‡‰ç”šçš‹ćŒć—ŽïŒŸçł»ç”±æœƒć„Čć­˜äœ çš„ć€‹äșșèł‡æ–™"</string>
+    <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"èŠć°ć­˜ć·„äœœèš­ćźšæȘ”äž­çš„é€™ć€‹æ‡‰ç”šçš‹ćŒć—ŽïŒŸçł»ç”±æœƒć„Čć­˜äœ çš„ć€‹äșșèł‡æ–™"</string>
+    <string name="archive_application_text_user" msgid="2586558895535581451">"芁ç‚ș<xliff:g id="USERNAME">%1$s</xliff:g>ć°ć­˜æ­€æ‡‰ç”šçš‹ćŒć—ŽïŒŸçł»ç”±æœƒć„Čć­˜äœ çš„ć€‹äșșèł‡æ–™"</string>
+    <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"èŠć°ć­˜ç§äșșç©șé–“äž­çš„é€™ć€‹æ‡‰ç”šçš‹ćŒć—ŽïŒŸçł»ç”±æœƒć„Čć­˜äœ çš„ć€‹äșșèł‡æ–™"</string>
     <string name="uninstall_application_text_all_users" msgid="575491774380227119">"䜠芁ç‚ș"<b>"所有"</b>"äœżç”šè€…è§Łé™€ćź‰èŁé€™ć€‹æ‡‰ç”šçš‹ćŒć—ŽïŒŸæ‡‰ç”šçš‹ćŒćŠć…¶èł‡æ–™æœƒćŸžèŁçœźäžŠçš„"<b>"所有"</b>"äœżç”šè€…èš­ćźšæȘ”䞭移陀。"</string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"䜠芁ç‚șäœżç”šè€…<xliff:g id="USERNAME">%1$s</xliff:g>è§Łé™€ćź‰èŁæ­€æ‡‰ç”šçš‹ćŒć—ŽïŒŸ"</string>
     <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"èŠćŸžć·„äœœèš­ćźšæȘ”è§Łé™€ćź‰èŁæ­€æ‡‰ç”šçš‹ćŒć—ŽïŒŸ"</string>
@@ -108,8 +102,7 @@
     <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"䟆æșäžæ˜Žçš„æ‡‰ç”šçš‹ćŒćŻèƒœæœƒäŸ”ćźłäœ çš„ćčłæżé›»è…Šć’Œć€‹äșșèł‡æ–™ă€‚ćź‰èŁæ­€æ‡‰ç”šçš‹ćŒïŒŒćłèĄšç€șäœ ćŒæ„æ‰żæ“”ć› äœżç”šé€™ć€‹æ‡‰ç”šçš‹ćŒè€Œć°Žè‡Žćčłæżé›»è…ŠæćŁžæˆ–èł‡æ–™éș怱的èȏ任。"</string>
     <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"䟆æșäžæ˜Žçš„æ‡‰ç”šçš‹ćŒćŻèƒœæœƒäŸ”ćźłäœ çš„é›»èŠ–ć’Œć€‹äșșèł‡æ–™ă€‚ćź‰èŁæ­€æ‡‰ç”šçš‹ćŒïŒŒćłèĄšç€șäœ ćŒæ„æ‰żæ“”ć› äœżç”šé€™ć€‹æ‡‰ç”šçš‹ćŒè€Œć°Žè‡Žé›»èŠ–æćŁžæˆ–èł‡æ–™éș怱的èȏ任。"</string>
     <string name="cloned_app_label" msgid="7503612829833756160">"「<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>ă€è€‡èŁœæœŹ"</string>
-    <!-- no translation found for archiving_app_label (1127085259724124725) -->
-    <skip />
+    <string name="archiving_app_label" msgid="1127085259724124725">"èŠć°ć­˜<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>ć—ŽïŒŸ"</string>
     <string name="anonymous_source_continue" msgid="4375745439457209366">"çčŒçșŒ"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"èš­ćźš"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"æ­Łćœšćź‰èŁ/è§Łé™€ćź‰èŁ Wear æ‡‰ç”šçš‹ćŒ"</string>
diff --git a/packages/PackageInstaller/res/values-zh-rTW/strings.xml b/packages/PackageInstaller/res/values-zh-rTW/strings.xml
index 30e66a0..16d9a30 100644
--- a/packages/PackageInstaller/res/values-zh-rTW/strings.xml
+++ b/packages/PackageInstaller/res/values-zh-rTW/strings.xml
@@ -44,8 +44,7 @@
     <string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"é€™äœäœżç”šè€…ç„Ąæł•ćź‰èŁäžæ˜Žçš„æ‡‰ç”šçš‹ćŒ"</string>
     <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"é€™äœäœżç”šè€…ç„Ąæł•ćź‰èŁæ‡‰ç”šçš‹ćŒ"</string>
     <string name="ok" msgid="7871959885003339302">"çąș漚"</string>
-    <!-- no translation found for archive (4447791830199354721) -->
-    <skip />
+    <string name="archive" msgid="4447791830199354721">"氁歘"</string>
     <string name="update_anyway" msgid="8792432341346261969">"仍芁曎新"</string>
     <string name="manage_applications" msgid="5400164782453975580">"çźĄç†æ‡‰ç”šçš‹ćŒ"</string>
     <string name="out_of_space_dlg_title" msgid="4156690013884649502">"ç©șé–“äžè¶ł"</string>
@@ -60,16 +59,11 @@
     <string name="uninstall_update_title" msgid="824411791011583031">"è§Łé™€ćź‰èŁæ›Žæ–°"</string>
     <string name="uninstall_activity_text" msgid="1928194674397770771">"「<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g>ă€ć±Źæ–Œäž‹ćˆ—æ‡‰ç”šçš‹ćŒïŒš"</string>
     <string name="uninstall_application_text" msgid="3816830743706143980">"èŠè§Łé™€ćź‰èŁé€™ć€‹æ‡‰ç”šçš‹ćŒć—ŽïŒŸ"</string>
-    <!-- no translation found for archive_application_text (8482325710714386348) -->
-    <skip />
-    <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
-    <skip />
-    <!-- no translation found for archive_application_text_user (2586558895535581451) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
-    <skip />
+    <string name="archive_application_text" msgid="8482325710714386348">"çł»ç”±æœƒć„Čć­˜äœ çš„ć€‹äșșèł‡æ–™"</string>
+    <string name="archive_application_text_all_users" msgid="3151229641681672580">"芁ç‚șæ‰€æœ‰äœżç”šè€…ć°ć­˜é€™ć€‹æ‡‰ç”šçš‹ćŒć—ŽïŒŸçł»ç”±æœƒć„Čć­˜äœ çš„ć€‹äșșèł‡æ–™"</string>
+    <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"èŠćœšć·„äœœèł‡æ–™ć€Ÿć°ć­˜é€™ć€‹æ‡‰ç”šçš‹ćŒć—ŽïŒŸçł»ç”±æœƒć„Čć­˜äœ çš„ć€‹äșșèł‡æ–™"</string>
+    <string name="archive_application_text_user" msgid="2586558895535581451">"芁ç‚ș<xliff:g id="USERNAME">%1$s</xliff:g>ć°ć­˜é€™ć€‹æ‡‰ç”šçš‹ćŒć—ŽïŒŸçł»ç”±æœƒć„Čć­˜äœ çš„ć€‹äșșèł‡æ–™"</string>
+    <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"èŠćŸžç§äșșç©șé–“ć°ć­˜é€™ć€‹æ‡‰ç”šçš‹ćŒć—ŽïŒŸçł»ç”±æœƒć„Čć­˜äœ çš„ć€‹äșșèł‡æ–™"</string>
     <string name="uninstall_application_text_all_users" msgid="575491774380227119">"芁ç‚ș"<b>"所有"</b>"äœżç”šè€…è§Łé™€ćź‰èŁé€™ć€‹æ‡‰ç”šçš‹ćŒć—ŽïŒŸè©Čæ‡‰ç”šçš‹ćŒćŠć…¶èł‡æ–™æœƒćŸžèŁçœźäžŠçš„"<b>"所有"</b>"äœżç”šè€…èš­ćźšæȘ”移陀。"</string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"芁ç‚șäœżç”šè€… <xliff:g id="USERNAME">%1$s</xliff:g> è§Łé™€ćź‰èŁé€™ć€‹æ‡‰ç”šçš‹ćŒć—ŽïŒŸ"</string>
     <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"èŠćŸžć·„äœœèł‡æ–™ć€Ÿè§Łé™€ćź‰èŁé€™ć€‹æ‡‰ç”šçš‹ćŒć—ŽïŒŸ"</string>
@@ -108,8 +102,7 @@
     <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"äŸ†æ­·äžæ˜Žçš„æ‡‰ç”šçš‹ćŒćŻèƒœæœƒæćźłäœ çš„ćčłæżé›»è…Šć’Œć€‹äșșèł‡æ–™ă€‚ćŠ‚ć› ćź‰èŁćŠäœżç”šé€™ć€‹æ‡‰ç”šçš‹ćŒïŒŒć°Žè‡Žäœ çš„ćčłæżé›»è…Šć—ææˆ–èł‡æ–™éșć€±ïŒŒè«‹è‡ȘèĄŒèČ èȬ。"</string>
     <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"äŸ†æ­·äžæ˜Žçš„æ‡‰ç”šçš‹ćŒćŻèƒœæœƒæćźłäœ çš„é›»èŠ–ć’Œć€‹äșșèł‡æ–™ă€‚ćŠ‚ć› ćź‰èŁćŠäœżç”šé€™ć€‹æ‡‰ç”šçš‹ćŒïŒŒć°Žè‡Žäœ çš„é›»èŠ–ć—ææˆ–èł‡æ–™éșć€±ïŒŒè«‹è‡ȘèĄŒèČ èȬ。"</string>
     <string name="cloned_app_label" msgid="7503612829833756160">"「<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>ă€ć‰ŻæœŹ"</string>
-    <!-- no translation found for archiving_app_label (1127085259724124725) -->
-    <skip />
+    <string name="archiving_app_label" msgid="1127085259724124725">"èŠć°ć­˜ă€Œ<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>ă€ć—ŽïŒŸ"</string>
     <string name="anonymous_source_continue" msgid="4375745439457209366">"çčŒçșŒ"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"èš­ćźš"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"ćź‰èŁ/è§Łé™€ćź‰èŁäž­çš„ Wear æ‡‰ç”šçš‹ćŒ"</string>
diff --git a/packages/PackageInstaller/res/values-zu/strings.xml b/packages/PackageInstaller/res/values-zu/strings.xml
index 0bfc9b7..624ed0f 100644
--- a/packages/PackageInstaller/res/values-zu/strings.xml
+++ b/packages/PackageInstaller/res/values-zu/strings.xml
@@ -44,8 +44,7 @@
     <string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Izinhlelo zokusebenza ezingaziwa azikwazi ukufakwa ilo msebenzisi"</string>
     <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Lo msebenzisi akavunyelwe ukufaka izinhlelo zokusebenza"</string>
     <string name="ok" msgid="7871959885003339302">"KULUNGILE"</string>
-    <!-- no translation found for archive (4447791830199354721) -->
-    <skip />
+    <string name="archive" msgid="4447791830199354721">"Ingobo yomlando"</string>
     <string name="update_anyway" msgid="8792432341346261969">"Buyekeza noma kunjalo"</string>
     <string name="manage_applications" msgid="5400164782453975580">"Phatha izinhlelo zokusebenza"</string>
     <string name="out_of_space_dlg_title" msgid="4156690013884649502">"Iphelelwe yisikhala"</string>
@@ -60,16 +59,11 @@
     <string name="uninstall_update_title" msgid="824411791011583031">"Khipha isibuyekezo"</string>
     <string name="uninstall_activity_text" msgid="1928194674397770771">"I-<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> ingxenye yohlelo lokusebenza olulandelayo:"</string>
     <string name="uninstall_application_text" msgid="3816830743706143980">"Ufuna ukukhipha le app?"</string>
-    <!-- no translation found for archive_application_text (8482325710714386348) -->
-    <skip />
-    <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
-    <skip />
-    <!-- no translation found for archive_application_text_user (2586558895535581451) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
-    <skip />
+    <string name="archive_application_text" msgid="8482325710714386348">"Idatha yakho yomuntu siqu izolondolozwa"</string>
+    <string name="archive_application_text_all_users" msgid="3151229641681672580">"Faka le app kungobo yomlando yabo bonke abasebenzisi? Idatha yakho yomuntu siqu izolondolozwa"</string>
+    <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Faka kungobo yomlando le app ekuphrofayela yakho yomsebenzi? Idatha yakho yomuntu siqu izolondolozwa"</string>
+    <string name="archive_application_text_user" msgid="2586558895535581451">"Faka le app kungobo yamlando uyifakele u-<xliff:g id="USERNAME">%1$s</xliff:g>? Idatha yakho yomuntu siqu izolondolozwa"</string>
+    <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Ingabe ufuna ukuthi le app esendaweni yakho yangasese ifakwe kungobo yomlando? Idatha yakho yomuntu siqu izolondolozwa"</string>
     <string name="uninstall_application_text_all_users" msgid="575491774380227119">"Ingabe ufuna ukukhipha lolu hlelo lokusebenza kubo "<b>"bonke"</b>" abasebenzisi? Uhlelo lokusebenza nedatha yalo kuzosuswa kubo "<b>"bonke"</b>" abasebenzisi kudivayisi."</string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Ingabe ufuna ukukhiphela lolu hlelo lokusebenza kumsebenzisi ongu-<xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
     <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Ingabe ufuna ukukhipha le app kusukela kuphrofayela yakho yokusebenza?"</string>
@@ -108,8 +102,7 @@
     <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Ithebulethi yakho nedatha yomuntu siqu zisengcupheni kakhulu ekuhlaselweni izinhlelo zokusebenza ezingaziwa. Ngokufaka lolu hlelo lokusebenza, uyavuma ukuthi unesibopho sanoma ikuphi ukonakala kuthebulethi yakho noma ukulahleka kwedatha okungabangelwa ukusetshenziswa kwayo."</string>
     <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Idatha yakho ye-TV neyomuntu siqu isengcupheni kakhulu ekuhlaselweni izinhlelo zokusebenza ezingaziwa. Ngokufaka lolu hlelo lokusebenza, uyavuma ukuthi unesibopho sanoma ikuphi ukonakala ku-TV yakho noma ukulahlekelwa kwedatha okungabangelwa ukusetshenziswa kwayo."</string>
     <string name="cloned_app_label" msgid="7503612829833756160">"I-Clone ye-<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
-    <!-- no translation found for archiving_app_label (1127085259724124725) -->
-    <skip />
+    <string name="archiving_app_label" msgid="1127085259724124725">"Faka i-<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> kungobo yomlando?"</string>
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Qhubeka"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Amasethingi"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Ifaka/ikhipha izinhlelo zokusebenza ze-wear"</string>
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/UnarchiveActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/UnarchiveActivity.java
index 754437e..b5af845 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/UnarchiveActivity.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/UnarchiveActivity.java
@@ -76,7 +76,7 @@
         boolean hasRequestInstallPermission = Arrays.asList(getRequestedPermissions(callingPackage))
                 .contains(permission.REQUEST_INSTALL_PACKAGES);
         boolean hasInstallPermission = getBaseContext().checkPermission(permission.INSTALL_PACKAGES,
-                0 /* random value for pid */, callingUid) != PackageManager.PERMISSION_GRANTED;
+                0 /* random value for pid */, callingUid) == PackageManager.PERMISSION_GRANTED;
         if (!hasRequestInstallPermission && !hasInstallPermission) {
             Log.e(TAG, "Uid " + callingUid + " does not have "
                     + permission.REQUEST_INSTALL_PACKAGES + " or "
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/UnarchiveFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/UnarchiveFragment.java
index 6ccbc4c..42dd382 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/UnarchiveFragment.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/UnarchiveFragment.java
@@ -28,12 +28,13 @@
     @Override
     public Dialog onCreateDialog(Bundle savedInstanceState) {
         String appTitle = getArguments().getString(UnarchiveActivity.APP_TITLE);
+        String installerTitle = getArguments().getString(UnarchiveActivity.INSTALLER_TITLE);
 
         AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(getActivity());
 
         dialogBuilder.setTitle(
                 String.format(getContext().getString(R.string.unarchive_application_title),
-                        appTitle));
+                        appTitle, installerTitle));
         dialogBuilder.setMessage(R.string.unarchive_body_text);
 
         dialogBuilder.setPositiveButton(R.string.restore, this);
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java
index 170cb45..9ad3e3c 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java
@@ -91,7 +91,8 @@
         // be stale, if e.g. the app was uninstalled while the activity was destroyed.
         super.onCreate(null);
 
-        if (usePiaV2() && !isTv()) {
+        // TODO(b/318521110) Enable PIA v2 for archive dialog.
+        if (usePiaV2() && !isTv() && !isArchiveDialog(getIntent())) {
             Log.i(TAG, "Using Pia V2");
 
             boolean returnResult = getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false);
@@ -224,6 +225,11 @@
         showConfirmationDialog();
     }
 
+    private boolean isArchiveDialog(Intent intent) {
+        return (intent.getIntExtra(PackageInstaller.EXTRA_DELETE_FLAGS, 0)
+                & PackageManager.DELETE_ARCHIVE) != 0;
+    }
+
     /**
      * Parses specific {@link android.content.pm.PackageManager.DeleteFlags} from {@link Intent}
      * to archive an app if requested.
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/InstallRepository.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/InstallRepository.java
deleted file mode 100644
index c8175ad..0000000
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/InstallRepository.java
+++ /dev/null
@@ -1,912 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.packageinstaller.v2.model;
-
-import static com.android.packageinstaller.v2.model.PackageUtil.canPackageQuery;
-import static com.android.packageinstaller.v2.model.PackageUtil.generateStubPackageInfo;
-import static com.android.packageinstaller.v2.model.PackageUtil.getAppSnippet;
-import static com.android.packageinstaller.v2.model.PackageUtil.getPackageInfo;
-import static com.android.packageinstaller.v2.model.PackageUtil.getPackageNameForUid;
-import static com.android.packageinstaller.v2.model.PackageUtil.isCallerSessionOwner;
-import static com.android.packageinstaller.v2.model.PackageUtil.isInstallPermissionGrantedOrRequested;
-import static com.android.packageinstaller.v2.model.PackageUtil.isPermissionGranted;
-import static com.android.packageinstaller.v2.model.installstagedata.InstallAborted.ABORT_REASON_DONE;
-import static com.android.packageinstaller.v2.model.installstagedata.InstallAborted.ABORT_REASON_INTERNAL_ERROR;
-import static com.android.packageinstaller.v2.model.installstagedata.InstallAborted.ABORT_REASON_POLICY;
-import static com.android.packageinstaller.v2.model.installstagedata.InstallAborted.DLG_PACKAGE_ERROR;
-import static com.android.packageinstaller.v2.model.installstagedata.InstallUserActionRequired.USER_ACTION_REASON_ANONYMOUS_SOURCE;
-import static com.android.packageinstaller.v2.model.installstagedata.InstallUserActionRequired.USER_ACTION_REASON_INSTALL_CONFIRMATION;
-import static com.android.packageinstaller.v2.model.installstagedata.InstallUserActionRequired.USER_ACTION_REASON_UNKNOWN_SOURCE;
-
-import android.Manifest;
-import android.app.Activity;
-import android.app.AppOpsManager;
-import android.app.PendingIntent;
-import android.app.admin.DevicePolicyManager;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.InstallSourceInfo;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageInstaller;
-import android.content.pm.PackageInstaller.SessionInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.ApplicationInfoFlags;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.res.AssetFileDescriptor;
-import android.net.Uri;
-import android.os.ParcelFileDescriptor;
-import android.os.Process;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.text.TextUtils;
-import android.util.EventLog;
-import android.util.Log;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.lifecycle.MutableLiveData;
-import com.android.packageinstaller.R;
-import com.android.packageinstaller.common.EventResultPersister;
-import com.android.packageinstaller.common.InstallEventReceiver;
-import com.android.packageinstaller.v2.model.PackageUtil.AppSnippet;
-import com.android.packageinstaller.v2.model.installstagedata.InstallAborted;
-import com.android.packageinstaller.v2.model.installstagedata.InstallFailed;
-import com.android.packageinstaller.v2.model.installstagedata.InstallInstalling;
-import com.android.packageinstaller.v2.model.installstagedata.InstallReady;
-import com.android.packageinstaller.v2.model.installstagedata.InstallStage;
-import com.android.packageinstaller.v2.model.installstagedata.InstallStaging;
-import com.android.packageinstaller.v2.model.installstagedata.InstallSuccess;
-import com.android.packageinstaller.v2.model.installstagedata.InstallUserActionRequired;
-import java.io.File;
-import java.io.IOException;
-
-public class InstallRepository {
-
-    public static final String EXTRA_STAGED_SESSION_ID =
-        "com.android.packageinstaller.extra.STAGED_SESSION_ID";
-    private static final String SCHEME_PACKAGE = "package";
-    private static final String BROADCAST_ACTION =
-        "com.android.packageinstaller.ACTION_INSTALL_COMMIT";
-    private static final String TAG = InstallRepository.class.getSimpleName();
-    private final Context mContext;
-    private final PackageManager mPackageManager;
-    private final PackageInstaller mPackageInstaller;
-    private final UserManager mUserManager;
-    private final DevicePolicyManager mDevicePolicyManager;
-    private final AppOpsManager mAppOpsManager;
-    private final MutableLiveData<InstallStage> mStagingResult = new MutableLiveData<>();
-    private final MutableLiveData<InstallStage> mInstallResult = new MutableLiveData<>();
-    private final boolean mLocalLOGV = false;
-    private Intent mIntent;
-    private boolean mIsSessionInstall;
-    private boolean mIsTrustedSource;
-    /**
-     * Session ID for a session created when caller uses PackageInstaller APIs
-     */
-    private int mSessionId;
-    /**
-     * Session ID for a session created by this app
-     */
-    private int mStagedSessionId = SessionInfo.INVALID_ID;
-    private int mCallingUid;
-    private String mCallingPackage;
-    private SessionStager mSessionStager;
-    private AppOpRequestInfo mAppOpRequestInfo;
-    private AppSnippet mAppSnippet;
-    /**
-     * PackageInfo of the app being installed on device.
-     */
-    private PackageInfo mNewPackageInfo;
-
-    public InstallRepository(Context context) {
-        mContext = context;
-        mPackageManager = context.getPackageManager();
-        mPackageInstaller = mPackageManager.getPackageInstaller();
-        mDevicePolicyManager = context.getSystemService(DevicePolicyManager.class);
-        mUserManager = context.getSystemService(UserManager.class);
-        mAppOpsManager = context.getSystemService(AppOpsManager.class);
-    }
-
-    /**
-     * Extracts information from the incoming install intent, checks caller's permission to install
-     * packages, verifies that the caller is the install session owner (in case of a session based
-     * install) and checks if the current user has restrictions set that prevent app installation,
-     *
-     * @param intent the incoming {@link Intent} object for installing a package
-     * @param callerInfo {@link CallerInfo} that holds the callingUid and callingPackageName
-     * @return <p>{@link InstallAborted} if there are errors while performing the checks</p>
-     *     <p>{@link InstallStaging} after successfully performing the checks</p>
-     */
-    public InstallStage performPreInstallChecks(Intent intent, CallerInfo callerInfo) {
-        mIntent = intent;
-
-        String callingAttributionTag = null;
-
-        mIsSessionInstall =
-            PackageInstaller.ACTION_CONFIRM_PRE_APPROVAL.equals(intent.getAction())
-                || PackageInstaller.ACTION_CONFIRM_INSTALL.equals(intent.getAction());
-
-        mSessionId = mIsSessionInstall
-            ? intent.getIntExtra(PackageInstaller.EXTRA_SESSION_ID, SessionInfo.INVALID_ID)
-            : SessionInfo.INVALID_ID;
-
-        mStagedSessionId = mIntent.getIntExtra(EXTRA_STAGED_SESSION_ID, SessionInfo.INVALID_ID);
-
-        mCallingPackage = callerInfo.getPackageName();
-
-        if (mCallingPackage == null && mSessionId != SessionInfo.INVALID_ID) {
-            PackageInstaller.SessionInfo sessionInfo = mPackageInstaller.getSessionInfo(mSessionId);
-            mCallingPackage = (sessionInfo != null) ? sessionInfo.getInstallerPackageName() : null;
-            callingAttributionTag =
-                (sessionInfo != null) ? sessionInfo.getInstallerAttributionTag() : null;
-        }
-
-        // Uid of the source package, coming from ActivityManager
-        mCallingUid = callerInfo.getUid();
-        if (mCallingUid == Process.INVALID_UID) {
-            Log.e(TAG, "Could not determine the launching uid.");
-        }
-        final ApplicationInfo sourceInfo = getSourceInfo(mCallingPackage);
-        // Uid of the source package, with a preference to uid from ApplicationInfo
-        final int originatingUid = sourceInfo != null ? sourceInfo.uid : mCallingUid;
-        mAppOpRequestInfo = new AppOpRequestInfo(
-            getPackageNameForUid(mContext, originatingUid, mCallingPackage),
-            originatingUid, callingAttributionTag);
-
-        if (mCallingUid == Process.INVALID_UID && sourceInfo == null) {
-            // Caller's identity could not be determined. Abort the install
-            return new InstallAborted.Builder(ABORT_REASON_INTERNAL_ERROR).build();
-        }
-
-        if ((mSessionId != SessionInfo.INVALID_ID
-            && !isCallerSessionOwner(mPackageInstaller, originatingUid, mSessionId))
-            || (mStagedSessionId != SessionInfo.INVALID_ID
-            && !isCallerSessionOwner(mPackageInstaller, Process.myUid(), mStagedSessionId))) {
-            return new InstallAborted.Builder(ABORT_REASON_INTERNAL_ERROR).build();
-        }
-
-        mIsTrustedSource = isInstallRequestFromTrustedSource(sourceInfo, mIntent, originatingUid);
-
-        if (!isInstallPermissionGrantedOrRequested(mContext, mCallingUid, originatingUid,
-            mIsTrustedSource)) {
-            return new InstallAborted.Builder(ABORT_REASON_INTERNAL_ERROR).build();
-        }
-
-        String restriction = getDevicePolicyRestrictions();
-        if (restriction != null) {
-            InstallAborted.Builder abortedBuilder =
-                new InstallAborted.Builder(ABORT_REASON_POLICY).setMessage(restriction);
-            final Intent adminSupportDetailsIntent =
-                mDevicePolicyManager.createAdminSupportIntent(restriction);
-            if (adminSupportDetailsIntent != null) {
-                abortedBuilder.setResultIntent(adminSupportDetailsIntent);
-            }
-            return abortedBuilder.build();
-        }
-
-        maybeRemoveInvalidInstallerPackageName(callerInfo);
-
-        return new InstallStaging();
-    }
-
-    /**
-     * @return the ApplicationInfo for the installation source (the calling package), if available
-     */
-    @Nullable
-    private ApplicationInfo getSourceInfo(@Nullable String callingPackage) {
-        if (callingPackage == null) {
-            return null;
-        }
-        try {
-            return mPackageManager.getApplicationInfo(callingPackage, 0);
-        } catch (PackageManager.NameNotFoundException ignored) {
-            return null;
-        }
-    }
-
-    private boolean isInstallRequestFromTrustedSource(ApplicationInfo sourceInfo, Intent intent,
-        int originatingUid) {
-        boolean isNotUnknownSource = intent.getBooleanExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, false);
-        return sourceInfo != null && sourceInfo.isPrivilegedApp()
-            && (isNotUnknownSource
-            || isPermissionGranted(mContext, Manifest.permission.INSTALL_PACKAGES, originatingUid));
-    }
-
-    private String getDevicePolicyRestrictions() {
-        final String[] restrictions = new String[]{
-            UserManager.DISALLOW_INSTALL_APPS,
-            UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES,
-            UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY
-        };
-
-        for (String restriction : restrictions) {
-            if (!mUserManager.hasUserRestrictionForUser(restriction, Process.myUserHandle())) {
-                continue;
-            }
-            return restriction;
-        }
-        return null;
-    }
-
-    private void maybeRemoveInvalidInstallerPackageName(CallerInfo callerInfo) {
-        final String installerPackageNameFromIntent =
-            mIntent.getStringExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME);
-        if (installerPackageNameFromIntent == null) {
-            return;
-        }
-        if (!TextUtils.equals(installerPackageNameFromIntent, callerInfo.getPackageName())
-            && !isPermissionGranted(mPackageManager, Manifest.permission.INSTALL_PACKAGES,
-            callerInfo.getPackageName())) {
-            Log.e(TAG, "The given installer package name " + installerPackageNameFromIntent
-                + " is invalid. Remove it.");
-            EventLog.writeEvent(0x534e4554, "236687884", callerInfo.getUid(),
-                "Invalid EXTRA_INSTALLER_PACKAGE_NAME");
-            mIntent.removeExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME);
-        }
-    }
-
-    public void stageForInstall() {
-        Uri uri = mIntent.getData();
-        if (mStagedSessionId != SessionInfo.INVALID_ID
-            || mIsSessionInstall
-            || (uri != null && SCHEME_PACKAGE.equals(uri.getScheme()))) {
-            // For a session based install or installing with a package:// URI, there is no file
-            // for us to stage.
-            mStagingResult.setValue(new InstallReady());
-            return;
-        }
-        if (uri != null
-            && ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())
-            && canPackageQuery(mContext, mCallingUid, uri)) {
-
-            if (mStagedSessionId > 0) {
-                final PackageInstaller.SessionInfo info =
-                    mPackageInstaller.getSessionInfo(mStagedSessionId);
-                if (info == null || !info.isActive() || info.getResolvedBaseApkPath() == null) {
-                    Log.w(TAG, "Session " + mStagedSessionId + " in funky state; ignoring");
-                    if (info != null) {
-                        cleanupStagingSession();
-                    }
-                    mStagedSessionId = 0;
-                }
-            }
-
-            // Session does not exist, or became invalid.
-            if (mStagedSessionId <= 0) {
-                // Create session here to be able to show error.
-                try (final AssetFileDescriptor afd =
-                    mContext.getContentResolver().openAssetFileDescriptor(uri, "r")) {
-                    ParcelFileDescriptor pfd = afd != null ? afd.getParcelFileDescriptor() : null;
-                    PackageInstaller.SessionParams params =
-                        createSessionParams(mIntent, pfd, uri.toString());
-                    mStagedSessionId = mPackageInstaller.createSession(params);
-                } catch (IOException e) {
-                    Log.w(TAG, "Failed to create a staging session", e);
-                    mStagingResult.setValue(
-                        new InstallAborted.Builder(ABORT_REASON_INTERNAL_ERROR)
-                            .setResultIntent(new Intent().putExtra(Intent.EXTRA_INSTALL_RESULT,
-                                PackageManager.INSTALL_FAILED_INVALID_APK))
-                            .setActivityResultCode(Activity.RESULT_FIRST_USER)
-                            .build());
-                    return;
-                }
-            }
-
-            SessionStageListener listener = new SessionStageListener() {
-                @Override
-                public void onStagingSuccess(SessionInfo info) {
-                    //TODO: Verify if the returned sessionInfo should be used anywhere
-                    mStagingResult.setValue(new InstallReady());
-                }
-
-                @Override
-                public void onStagingFailure() {
-                    cleanupStagingSession();
-                    mStagingResult.setValue(
-                        new InstallAborted.Builder(ABORT_REASON_INTERNAL_ERROR)
-                            .setResultIntent(new Intent().putExtra(Intent.EXTRA_INSTALL_RESULT,
-                                PackageManager.INSTALL_FAILED_INVALID_APK))
-                            .setActivityResultCode(Activity.RESULT_FIRST_USER)
-                            .build());
-                }
-            };
-            if (mSessionStager != null) {
-                mSessionStager.cancel(true);
-            }
-            mSessionStager = new SessionStager(mContext, uri, mStagedSessionId, listener);
-            mSessionStager.execute();
-        }
-    }
-
-    public int getStagedSessionId() {
-        return mStagedSessionId;
-    }
-
-    private void cleanupStagingSession() {
-        if (mStagedSessionId > 0) {
-            try {
-                mPackageInstaller.abandonSession(mStagedSessionId);
-            } catch (SecurityException ignored) {
-            }
-            mStagedSessionId = 0;
-        }
-    }
-
-    private PackageInstaller.SessionParams createSessionParams(@NonNull Intent intent,
-        @Nullable ParcelFileDescriptor pfd, @NonNull String debugPathName) {
-        PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
-            PackageInstaller.SessionParams.MODE_FULL_INSTALL);
-        final Uri referrerUri = intent.getParcelableExtra(Intent.EXTRA_REFERRER, Uri.class);
-        params.setPackageSource(
-            referrerUri != null ? PackageInstaller.PACKAGE_SOURCE_DOWNLOADED_FILE
-                : PackageInstaller.PACKAGE_SOURCE_LOCAL_FILE);
-        params.setInstallAsInstantApp(false);
-        params.setReferrerUri(referrerUri);
-        params.setOriginatingUri(
-            intent.getParcelableExtra(Intent.EXTRA_ORIGINATING_URI, Uri.class));
-        params.setOriginatingUid(intent.getIntExtra(Intent.EXTRA_ORIGINATING_UID,
-            Process.INVALID_UID));
-        params.setInstallerPackageName(intent.getStringExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME));
-        params.setInstallReason(PackageManager.INSTALL_REASON_USER);
-        // Disable full screen intent usage by for sideloads.
-        params.setPermissionState(Manifest.permission.USE_FULL_SCREEN_INTENT,
-            PackageInstaller.SessionParams.PERMISSION_STATE_DENIED);
-
-        if (pfd != null) {
-            try {
-                final PackageInstaller.InstallInfo result = mPackageInstaller.readInstallInfo(pfd,
-                    debugPathName, 0);
-                params.setAppPackageName(result.getPackageName());
-                params.setInstallLocation(result.getInstallLocation());
-                params.setSize(result.calculateInstalledSize(params, pfd));
-            } catch (PackageInstaller.PackageParsingException e) {
-                Log.e(TAG, "Cannot parse package " + debugPathName + ". Assuming defaults.", e);
-                params.setSize(pfd.getStatSize());
-            } catch (IOException e) {
-                Log.e(TAG,
-                    "Cannot calculate installed size " + debugPathName
-                        + ". Try only apk size.", e);
-            }
-        } else {
-            Log.e(TAG, "Cannot parse package " + debugPathName + ". Assuming defaults.");
-        }
-        return params;
-    }
-
-    /**
-     * Processes Install session, file:// or package:// URI to generate data pertaining to user
-     * confirmation for an install. This method also checks if the source app has the AppOp granted
-     * to install unknown apps. If an AppOp is to be requested, cache the user action prompt data to
-     * be reused once appOp has been granted
-     *
-     * @return <ul>
-     *     <li>InstallAborted </li>
-     *         <ul>
-     *             <li> If install session is invalid (not sealed or resolvedBaseApk path
-     *             is invalid) </li>
-     *             <li> Source app doesn't have visibility to target app </li>
-     *             <li> The APK is invalid </li>
-     *             <li> URI is invalid </li>
-     *             <li> Can't get ApplicationInfo for source app, to request AppOp </li>
-     *         </ul>
-     *    <li> InstallUserActionRequired</li>
-     *         <ul>
-     *             <li> If AppOP is granted and user action is required to proceed
-     *             with install </li>
-     *             <li> If AppOp grant is to be requested from the user</li>
-     *         </ul>
-     *  </ul>
-     */
-    public InstallStage requestUserConfirmation() {
-        if (mIsTrustedSource) {
-            if (mLocalLOGV) {
-                Log.i(TAG, "install allowed");
-            }
-            // Returns InstallUserActionRequired stage if install details could be successfully
-            // computed, else it returns InstallAborted.
-            return generateConfirmationSnippet();
-        } else {
-            InstallStage unknownSourceStage = handleUnknownSources(mAppOpRequestInfo);
-            if (unknownSourceStage.getStageCode() == InstallStage.STAGE_READY) {
-                // Source app already has appOp granted.
-                return generateConfirmationSnippet();
-            } else {
-                return unknownSourceStage;
-            }
-        }
-    }
-
-
-    private InstallStage generateConfirmationSnippet() {
-        final Object packageSource;
-        int pendingUserActionReason = -1;
-        if (PackageInstaller.ACTION_CONFIRM_INSTALL.equals(mIntent.getAction())) {
-            final SessionInfo info = mPackageInstaller.getSessionInfo(mSessionId);
-            String resolvedPath = info != null ? info.getResolvedBaseApkPath() : null;
-
-            if (info == null || !info.isSealed() || resolvedPath == null) {
-                Log.w(TAG, "Session " + mSessionId + " in funky state; ignoring");
-                return new InstallAborted.Builder(ABORT_REASON_INTERNAL_ERROR).build();
-            }
-            packageSource = Uri.fromFile(new File(resolvedPath));
-            // TODO: Not sure where is this used yet. PIA.java passes it to
-            //  InstallInstalling if not null
-            // mOriginatingURI = null;
-            // mReferrerURI = null;
-            pendingUserActionReason = info.getPendingUserActionReason();
-        } else if (PackageInstaller.ACTION_CONFIRM_PRE_APPROVAL.equals(mIntent.getAction())) {
-            final SessionInfo info = mPackageInstaller.getSessionInfo(mSessionId);
-
-            if (info == null || !info.isPreApprovalRequested()) {
-                Log.w(TAG, "Session " + mSessionId + " in funky state; ignoring");
-                return new InstallAborted.Builder(ABORT_REASON_INTERNAL_ERROR).build();
-            }
-            packageSource = info;
-            // mOriginatingURI = null;
-            // mReferrerURI = null;
-            pendingUserActionReason = info.getPendingUserActionReason();
-        } else {
-            // Two possible origins:
-            // 1. Installation with SCHEME_PACKAGE.
-            // 2. Installation with "file://" for session created by this app
-            if (mIntent.getData() != null && mIntent.getData().getScheme().equals(SCHEME_PACKAGE)) {
-                packageSource = mIntent.getData();
-            } else {
-                SessionInfo stagedSessionInfo = mPackageInstaller.getSessionInfo(mStagedSessionId);
-                packageSource = Uri.fromFile(new File(stagedSessionInfo.getResolvedBaseApkPath()));
-            }
-            // mOriginatingURI = mIntent.getParcelableExtra(Intent.EXTRA_ORIGINATING_URI);
-            // mReferrerURI = mIntent.getParcelableExtra(Intent.EXTRA_REFERRER);
-            pendingUserActionReason = PackageInstaller.REASON_CONFIRM_PACKAGE_CHANGE;
-        }
-
-        // if there's nothing to do, quietly slip into the ether
-        if (packageSource == null) {
-            Log.w(TAG, "Unspecified source");
-            return new InstallAborted.Builder(ABORT_REASON_INTERNAL_ERROR)
-                .setResultIntent(new Intent().putExtra(Intent.EXTRA_INSTALL_RESULT,
-                    PackageManager.INSTALL_FAILED_INVALID_URI))
-                .setActivityResultCode(Activity.RESULT_FIRST_USER)
-                .build();
-        }
-
-        return processAppSnippet(packageSource, pendingUserActionReason);
-    }
-
-    /**
-     * Parse the Uri (post-commit install session) or use the SessionInfo (pre-commit install
-     * session) to set up the installer for this install.
-     *
-     * @param source The source of package URI or SessionInfo
-     * @return {@code true} iff the installer could be set up
-     */
-    private InstallStage processAppSnippet(Object source, int userActionReason) {
-        if (source instanceof Uri) {
-            return processPackageUri((Uri) source, userActionReason);
-        } else if (source instanceof SessionInfo) {
-            return processSessionInfo((SessionInfo) source, userActionReason);
-        }
-        return new InstallAborted.Builder(ABORT_REASON_INTERNAL_ERROR).build();
-    }
-
-    /**
-     * Parse the Uri and set up the installer for this package.
-     *
-     * @param packageUri The URI to parse
-     * @return {@code true} iff the installer could be set up
-     */
-    private InstallStage processPackageUri(final Uri packageUri, int userActionReason) {
-        final String scheme = packageUri.getScheme();
-        final String packageName = packageUri.getSchemeSpecificPart();
-
-        if (scheme == null) {
-            return new InstallAborted.Builder(ABORT_REASON_INTERNAL_ERROR).build();
-        }
-
-        if (mLocalLOGV) {
-            Log.i(TAG, "processPackageUri(): uri = " + packageUri + ", scheme = " + scheme);
-        }
-
-        switch (scheme) {
-            case SCHEME_PACKAGE -> {
-                for (UserHandle handle : mUserManager.getUserHandles(true)) {
-                    PackageManager pmForUser = mContext.createContextAsUser(handle, 0)
-                        .getPackageManager();
-                    try {
-                        if (pmForUser.canPackageQuery(mCallingPackage, packageName)) {
-                            mNewPackageInfo = pmForUser.getPackageInfo(packageName,
-                                PackageManager.GET_PERMISSIONS
-                                    | PackageManager.MATCH_UNINSTALLED_PACKAGES);
-                        }
-                    } catch (NameNotFoundException ignored) {
-                    }
-                }
-                if (mNewPackageInfo == null) {
-                    Log.w(TAG, "Requested package " + packageUri.getSchemeSpecificPart()
-                        + " not available. Discontinuing installation");
-                    return new InstallAborted.Builder(ABORT_REASON_INTERNAL_ERROR)
-                        .setErrorDialogType(DLG_PACKAGE_ERROR)
-                        .setResultIntent(new Intent().putExtra(Intent.EXTRA_INSTALL_RESULT,
-                            PackageManager.INSTALL_FAILED_INVALID_APK))
-                        .setActivityResultCode(Activity.RESULT_FIRST_USER)
-                        .build();
-                }
-                mAppSnippet = getAppSnippet(mContext, mNewPackageInfo);
-                if (mLocalLOGV) {
-                    Log.i(TAG, "Created snippet for " + mAppSnippet.getLabel());
-                }
-            }
-            case ContentResolver.SCHEME_FILE -> {
-                File sourceFile = new File(packageUri.getPath());
-                mNewPackageInfo = getPackageInfo(mContext, sourceFile,
-                    PackageManager.GET_PERMISSIONS);
-
-                // Check for parse errors
-                if (mNewPackageInfo == null) {
-                    Log.w(TAG, "Parse error when parsing manifest. Discontinuing installation");
-                    return new InstallAborted.Builder(ABORT_REASON_INTERNAL_ERROR)
-                        .setErrorDialogType(DLG_PACKAGE_ERROR)
-                        .setResultIntent(new Intent().putExtra(Intent.EXTRA_INSTALL_RESULT,
-                            PackageManager.INSTALL_FAILED_INVALID_APK))
-                        .setActivityResultCode(Activity.RESULT_FIRST_USER)
-                        .build();
-                }
-                if (mLocalLOGV) {
-                    Log.i(TAG, "Creating snippet for local file " + sourceFile);
-                }
-                mAppSnippet = getAppSnippet(mContext, mNewPackageInfo.applicationInfo, sourceFile);
-            }
-            default -> {
-                Log.e(TAG, "Unexpected URI scheme " + packageUri);
-                return new InstallAborted.Builder(ABORT_REASON_INTERNAL_ERROR).build();
-            }
-        }
-
-        return new InstallUserActionRequired.Builder(
-            USER_ACTION_REASON_INSTALL_CONFIRMATION, mAppSnippet)
-            .setDialogMessage(getUpdateMessage(mNewPackageInfo, userActionReason))
-            .setAppUpdating(isAppUpdating(mNewPackageInfo))
-            .build();
-    }
-
-    /**
-     * Use the SessionInfo and set up the installer for pre-commit install session.
-     *
-     * @param sessionInfo The SessionInfo to compose
-     * @return {@code true} iff the installer could be set up
-     */
-    private InstallStage processSessionInfo(@NonNull SessionInfo sessionInfo,
-        int userActionReason) {
-        mNewPackageInfo = generateStubPackageInfo(sessionInfo.getAppPackageName());
-
-        mAppSnippet = getAppSnippet(mContext, sessionInfo);
-        return new InstallUserActionRequired.Builder(
-            USER_ACTION_REASON_INSTALL_CONFIRMATION, mAppSnippet)
-            .setAppUpdating(isAppUpdating(mNewPackageInfo))
-            .setDialogMessage(getUpdateMessage(mNewPackageInfo, userActionReason))
-            .build();
-    }
-
-    private String getUpdateMessage(PackageInfo pkgInfo, int userActionReason) {
-        if (isAppUpdating(pkgInfo)) {
-            final CharSequence existingUpdateOwnerLabel = getExistingUpdateOwnerLabel(pkgInfo);
-            final CharSequence requestedUpdateOwnerLabel = getApplicationLabel(mCallingPackage);
-
-            if (!TextUtils.isEmpty(existingUpdateOwnerLabel)
-                && userActionReason == PackageInstaller.REASON_REMIND_OWNERSHIP) {
-                return mContext.getString(R.string.install_confirm_question_update_owner_reminder,
-                    requestedUpdateOwnerLabel, existingUpdateOwnerLabel);
-            }
-        }
-        return null;
-    }
-
-    private CharSequence getExistingUpdateOwnerLabel(PackageInfo pkgInfo) {
-        try {
-            final String packageName = pkgInfo.packageName;
-            final InstallSourceInfo sourceInfo = mPackageManager.getInstallSourceInfo(packageName);
-            final String existingUpdateOwner = sourceInfo.getUpdateOwnerPackageName();
-            return getApplicationLabel(existingUpdateOwner);
-        } catch (NameNotFoundException e) {
-            return null;
-        }
-    }
-
-    private CharSequence getApplicationLabel(String packageName) {
-        try {
-            final ApplicationInfo appInfo = mPackageManager.getApplicationInfo(packageName,
-                ApplicationInfoFlags.of(0));
-            return mPackageManager.getApplicationLabel(appInfo);
-        } catch (NameNotFoundException e) {
-            return null;
-        }
-    }
-
-    private boolean isAppUpdating(PackageInfo newPkgInfo) {
-        String pkgName = newPkgInfo.packageName;
-        // Check if there is already a package on the device with this name
-        // but it has been renamed to something else.
-        String[] oldName = mPackageManager.canonicalToCurrentPackageNames(new String[]{pkgName});
-        if (oldName != null && oldName.length > 0 && oldName[0] != null) {
-            pkgName = oldName[0];
-            newPkgInfo.packageName = pkgName;
-            newPkgInfo.applicationInfo.packageName = pkgName;
-        }
-        // Check if package is already installed. display confirmation dialog if replacing pkg
-        try {
-            // This is a little convoluted because we want to get all uninstalled
-            // apps, but this may include apps with just data, and if it is just
-            // data we still want to count it as "installed".
-            ApplicationInfo appInfo = mPackageManager.getApplicationInfo(pkgName,
-                PackageManager.MATCH_UNINSTALLED_PACKAGES);
-            if ((appInfo.flags & ApplicationInfo.FLAG_INSTALLED) == 0) {
-                return false;
-            }
-        } catch (NameNotFoundException e) {
-            return false;
-        }
-        return true;
-    }
-
-    /**
-     * Once the user returns from Settings related to installing from unknown sources, reattempt
-     * the installation if the source app is granted permission to install other apps. Abort the
-     * installation if the source app is still not granted installing permission.
-     * @return {@link InstallUserActionRequired} containing data required to ask user confirmation
-     * to proceed with the install.
-     * {@link InstallAborted} if there was an error while recomputing, or the source still
-     * doesn't have install permission.
-     */
-    public InstallStage reattemptInstall() {
-        InstallStage unknownSourceStage = handleUnknownSources(mAppOpRequestInfo);
-        if (unknownSourceStage.getStageCode() == InstallStage.STAGE_READY) {
-            // Source app now has appOp granted.
-            return generateConfirmationSnippet();
-        } else if (unknownSourceStage.getStageCode() == InstallStage.STAGE_ABORTED) {
-            // There was some error in determining the AppOp code for the source app.
-            // Abort installation
-            return unknownSourceStage;
-        } else {
-            // AppOpsManager again returned a MODE_ERRORED or MODE_DEFAULT op code. This was
-            // unexpected while reattempting the install. Let's abort it.
-            Log.e(TAG, "AppOp still not granted.");
-            return new InstallAborted.Builder(ABORT_REASON_INTERNAL_ERROR).build();
-        }
-    }
-
-    private InstallStage handleUnknownSources(AppOpRequestInfo requestInfo) {
-        if (requestInfo.getCallingPackage() == null) {
-            Log.i(TAG, "No source found for package " + mNewPackageInfo.packageName);
-            return new InstallUserActionRequired.Builder(
-                USER_ACTION_REASON_ANONYMOUS_SOURCE, null)
-                .build();
-        }
-        // Shouldn't use static constant directly, see b/65534401.
-        final String appOpStr =
-            AppOpsManager.permissionToOp(Manifest.permission.REQUEST_INSTALL_PACKAGES);
-        final int appOpMode = mAppOpsManager.noteOpNoThrow(appOpStr,
-            requestInfo.getOriginatingUid(),
-            requestInfo.getCallingPackage(), requestInfo.getAttributionTag(),
-            "Started package installation activity");
-
-        if (mLocalLOGV) {
-            Log.i(TAG, "handleUnknownSources(): appMode=" + appOpMode);
-        }
-        switch (appOpMode) {
-            case AppOpsManager.MODE_DEFAULT:
-                mAppOpsManager.setMode(appOpStr, requestInfo.getOriginatingUid(),
-                    requestInfo.getCallingPackage(), AppOpsManager.MODE_ERRORED);
-                // fall through
-            case AppOpsManager.MODE_ERRORED:
-                try {
-                    ApplicationInfo sourceInfo =
-                        mPackageManager.getApplicationInfo(requestInfo.getCallingPackage(), 0);
-                    AppSnippet sourceAppSnippet = getAppSnippet(mContext, sourceInfo);
-                    return new InstallUserActionRequired.Builder(
-                        USER_ACTION_REASON_UNKNOWN_SOURCE, sourceAppSnippet)
-                        .setDialogMessage(requestInfo.getCallingPackage())
-                        .build();
-                } catch (NameNotFoundException e) {
-                    Log.e(TAG, "Did not find appInfo for " + requestInfo.getCallingPackage());
-                    return new InstallAborted.Builder(ABORT_REASON_INTERNAL_ERROR).build();
-                }
-            case AppOpsManager.MODE_ALLOWED:
-                return new InstallReady();
-            default:
-                Log.e(TAG, "Invalid app op mode " + appOpMode
-                    + " for OP_REQUEST_INSTALL_PACKAGES found for uid "
-                    + requestInfo.getOriginatingUid());
-                return new InstallAborted.Builder(ABORT_REASON_INTERNAL_ERROR).build();
-        }
-    }
-
-
-    /**
-     * Kick off the installation. Register a broadcast listener to get the result of the
-     * installation and commit the staged session here. If the installation was session based,
-     * signal the PackageInstaller that the user has granted permission to proceed with the install
-     */
-    public void initiateInstall() {
-        if (mSessionId > 0) {
-            mPackageInstaller.setPermissionsResult(mSessionId, true);
-            mInstallResult.setValue(new InstallAborted.Builder(ABORT_REASON_DONE)
-                .setActivityResultCode(Activity.RESULT_OK).build());
-            return;
-        }
-
-        Uri uri = mIntent.getData();
-        if (uri != null && SCHEME_PACKAGE.equals(uri.getScheme())) {
-            try {
-                mPackageManager.installExistingPackage(mNewPackageInfo.packageName);
-                setStageBasedOnResult(PackageInstaller.STATUS_SUCCESS, -1, null, -1);
-            } catch (PackageManager.NameNotFoundException e) {
-                setStageBasedOnResult(PackageInstaller.STATUS_FAILURE,
-                    PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null, -1);
-            }
-            return;
-        }
-
-        if (mStagedSessionId <= 0) {
-            // How did we even land here?
-            Log.e(TAG, "Invalid local session and caller initiated session");
-            mInstallResult.setValue(new InstallAborted.Builder(ABORT_REASON_INTERNAL_ERROR)
-                .build());
-            return;
-        }
-
-        int installId;
-        try {
-            mInstallResult.setValue(new InstallInstalling(mAppSnippet));
-            installId = InstallEventReceiver.addObserver(mContext,
-                EventResultPersister.GENERATE_NEW_ID, this::setStageBasedOnResult);
-        } catch (EventResultPersister.OutOfIdsException e) {
-            setStageBasedOnResult(PackageInstaller.STATUS_FAILURE,
-                PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null, -1);
-            return;
-        }
-
-        Intent broadcastIntent = new Intent(BROADCAST_ACTION);
-        broadcastIntent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
-        broadcastIntent.setPackage(mContext.getPackageName());
-        broadcastIntent.putExtra(EventResultPersister.EXTRA_ID, installId);
-
-        PendingIntent pendingIntent = PendingIntent.getBroadcast(
-            mContext, installId, broadcastIntent,
-            PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);
-
-        try {
-            PackageInstaller.Session session = mPackageInstaller.openSession(mStagedSessionId);
-            session.commit(pendingIntent.getIntentSender());
-        } catch (Exception e) {
-            Log.e(TAG, "Session " + mStagedSessionId + " could not be opened.", e);
-            mPackageInstaller.abandonSession(mStagedSessionId);
-            setStageBasedOnResult(PackageInstaller.STATUS_FAILURE,
-                PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null, -1);
-        }
-    }
-
-    private void setStageBasedOnResult(int statusCode, int legacyStatus, String message,
-        int serviceId) {
-        if (statusCode == PackageInstaller.STATUS_SUCCESS) {
-            boolean shouldReturnResult = mIntent.getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false);
-
-            InstallSuccess.Builder successBuilder = new InstallSuccess.Builder(mAppSnippet)
-                .setShouldReturnResult(shouldReturnResult);
-            Intent resultIntent;
-            if (shouldReturnResult) {
-                resultIntent = new Intent()
-                    .putExtra(Intent.EXTRA_INSTALL_RESULT, PackageManager.INSTALL_SUCCEEDED);
-            } else {
-                resultIntent = mPackageManager
-                    .getLaunchIntentForPackage(mNewPackageInfo.packageName);
-            }
-            successBuilder.setResultIntent(resultIntent);
-
-            mInstallResult.setValue(successBuilder.build());
-        } else {
-            mInstallResult.setValue(
-                new InstallFailed(mAppSnippet, statusCode, legacyStatus, message));
-        }
-    }
-
-    public MutableLiveData<InstallStage> getInstallResult() {
-        return mInstallResult;
-    }
-
-    /**
-     * Cleanup the staged session. Also signal the packageinstaller that an install session is to
-     * be aborted
-     */
-    public void cleanupInstall() {
-        if (mSessionId > 0) {
-            mPackageInstaller.setPermissionsResult(mSessionId, false);
-        } else if (mStagedSessionId > 0) {
-            cleanupStagingSession();
-        }
-    }
-
-    /**
-     * When the identity of the install source could not be determined, user can skip checking the
-     * source and directly proceed with the install.
-     */
-    public InstallStage forcedSkipSourceCheck() {
-        return generateConfirmationSnippet();
-    }
-
-    public MutableLiveData<Integer> getStagingProgress() {
-        if (mSessionStager != null) {
-            return mSessionStager.getProgress();
-        }
-        return new MutableLiveData<>(0);
-    }
-
-    public MutableLiveData<InstallStage> getStagingResult() {
-        return mStagingResult;
-    }
-
-    public interface SessionStageListener {
-
-        void onStagingSuccess(SessionInfo info);
-
-        void onStagingFailure();
-    }
-
-    public static class CallerInfo {
-
-        private final String mPackageName;
-        private final int mUid;
-
-        public CallerInfo(String packageName, int uid) {
-            mPackageName = packageName;
-            mUid = uid;
-        }
-
-        public String getPackageName() {
-            return mPackageName;
-        }
-
-        public int getUid() {
-            return mUid;
-        }
-    }
-
-    public static class AppOpRequestInfo {
-
-        private String mCallingPackage;
-        private String mAttributionTag;
-        private int mOrginatingUid;
-
-        public AppOpRequestInfo(String callingPackage, int orginatingUid, String attributionTag) {
-            mCallingPackage = callingPackage;
-            mOrginatingUid = orginatingUid;
-            mAttributionTag = attributionTag;
-        }
-
-        public String getCallingPackage() {
-            return mCallingPackage;
-        }
-
-        public String getAttributionTag() {
-            return mAttributionTag;
-        }
-
-        public int getOriginatingUid() {
-            return mOrginatingUid;
-        }
-    }
-}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/InstallRepository.kt b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/InstallRepository.kt
new file mode 100644
index 0000000..aeabbd5
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/InstallRepository.kt
@@ -0,0 +1,877 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.packageinstaller.v2.model
+
+import android.Manifest
+import android.app.Activity
+import android.app.AppOpsManager
+import android.app.PendingIntent
+import android.app.admin.DevicePolicyManager
+import android.content.ContentResolver
+import android.content.Context
+import android.content.Intent
+import android.content.pm.ApplicationInfo
+import android.content.pm.PackageInfo
+import android.content.pm.PackageInstaller
+import android.content.pm.PackageInstaller.SessionInfo
+import android.content.pm.PackageInstaller.SessionParams
+import android.content.pm.PackageManager
+import android.net.Uri
+import android.os.ParcelFileDescriptor
+import android.os.Process
+import android.os.UserManager
+import android.text.TextUtils
+import android.util.EventLog
+import android.util.Log
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import com.android.packageinstaller.R
+import com.android.packageinstaller.common.EventResultPersister
+import com.android.packageinstaller.common.EventResultPersister.OutOfIdsException
+import com.android.packageinstaller.common.InstallEventReceiver
+import com.android.packageinstaller.v2.model.InstallAborted.Companion.ABORT_REASON_DONE
+import com.android.packageinstaller.v2.model.InstallAborted.Companion.ABORT_REASON_INTERNAL_ERROR
+import com.android.packageinstaller.v2.model.InstallAborted.Companion.ABORT_REASON_POLICY
+import com.android.packageinstaller.v2.model.InstallAborted.Companion.DLG_NONE
+import com.android.packageinstaller.v2.model.InstallAborted.Companion.DLG_PACKAGE_ERROR
+import com.android.packageinstaller.v2.model.InstallUserActionRequired.Companion.USER_ACTION_REASON_ANONYMOUS_SOURCE
+import com.android.packageinstaller.v2.model.InstallUserActionRequired.Companion.USER_ACTION_REASON_INSTALL_CONFIRMATION
+import com.android.packageinstaller.v2.model.InstallUserActionRequired.Companion.USER_ACTION_REASON_UNKNOWN_SOURCE
+import com.android.packageinstaller.v2.model.PackageUtil.canPackageQuery
+import com.android.packageinstaller.v2.model.PackageUtil.generateStubPackageInfo
+import com.android.packageinstaller.v2.model.PackageUtil.getAppSnippet
+import com.android.packageinstaller.v2.model.PackageUtil.getPackageInfo
+import com.android.packageinstaller.v2.model.PackageUtil.getPackageNameForUid
+import com.android.packageinstaller.v2.model.PackageUtil.isCallerSessionOwner
+import com.android.packageinstaller.v2.model.PackageUtil.isInstallPermissionGrantedOrRequested
+import com.android.packageinstaller.v2.model.PackageUtil.isPermissionGranted
+import java.io.File
+import java.io.IOException
+import kotlinx.coroutines.DelicateCoroutinesApi
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.GlobalScope
+import kotlinx.coroutines.launch
+
+class InstallRepository(private val context: Context) {
+
+    private val packageManager: PackageManager = context.packageManager
+    private val packageInstaller: PackageInstaller = packageManager.packageInstaller
+    private val userManager: UserManager? = context.getSystemService(UserManager::class.java)
+    private val devicePolicyManager: DevicePolicyManager? =
+        context.getSystemService(DevicePolicyManager::class.java)
+    private val appOpsManager: AppOpsManager? = context.getSystemService(AppOpsManager::class.java)
+    private val localLOGV = false
+    private var isSessionInstall = false
+    private var isTrustedSource = false
+    private val _stagingResult = MutableLiveData<InstallStage>()
+    val stagingResult: LiveData<InstallStage>
+        get() = _stagingResult
+    private val _installResult = MutableLiveData<InstallStage>()
+    val installResult: LiveData<InstallStage>
+        get() = _installResult
+
+    /**
+     * Session ID for a session created when caller uses PackageInstaller APIs
+     */
+    private var sessionId = SessionInfo.INVALID_ID
+
+    /**
+     * Session ID for a session created by this app
+     */
+    var stagedSessionId = SessionInfo.INVALID_ID
+        private set
+    private var callingUid = Process.INVALID_UID
+    private var callingPackage: String? = null
+    private var sessionStager: SessionStager? = null
+    private lateinit var intent: Intent
+    private lateinit var appOpRequestInfo: AppOpRequestInfo
+    private lateinit var appSnippet: PackageUtil.AppSnippet
+
+    /**
+     * PackageInfo of the app being installed on device.
+     */
+    private var newPackageInfo: PackageInfo? = null
+
+    /**
+     * Extracts information from the incoming install intent, checks caller's permission to install
+     * packages, verifies that the caller is the install session owner (in case of a session based
+     * install) and checks if the current user has restrictions set that prevent app installation,
+     *
+     * @param intent the incoming [Intent] object for installing a package
+     * @param callerInfo [CallerInfo] that holds the callingUid and callingPackageName
+     * @return
+     *  * [InstallAborted] if there are errors while performing the checks
+     *  * [InstallStaging] after successfully performing the checks
+     */
+    fun performPreInstallChecks(intent: Intent, callerInfo: CallerInfo): InstallStage {
+        this.intent = intent
+
+        var callingAttributionTag: String? = null
+
+        isSessionInstall =
+            PackageInstaller.ACTION_CONFIRM_PRE_APPROVAL == intent.action
+                || PackageInstaller.ACTION_CONFIRM_INSTALL == intent.action
+
+        sessionId = if (isSessionInstall)
+            intent.getIntExtra(PackageInstaller.EXTRA_SESSION_ID, SessionInfo.INVALID_ID)
+        else SessionInfo.INVALID_ID
+
+        stagedSessionId = intent.getIntExtra(EXTRA_STAGED_SESSION_ID, SessionInfo.INVALID_ID)
+
+        callingPackage = callerInfo.packageName
+
+        if (callingPackage == null && sessionId != SessionInfo.INVALID_ID) {
+            val sessionInfo: SessionInfo? = packageInstaller.getSessionInfo(sessionId)
+            callingPackage = sessionInfo?.getInstallerPackageName()
+            callingAttributionTag = sessionInfo?.getInstallerAttributionTag()
+        }
+
+        // Uid of the source package, coming from ActivityManager
+        callingUid = callerInfo.uid
+        if (callingUid == Process.INVALID_UID) {
+            Log.e(LOG_TAG, "Could not determine the launching uid.")
+        }
+        val sourceInfo: ApplicationInfo? = getSourceInfo(callingPackage)
+        // Uid of the source package, with a preference to uid from ApplicationInfo
+        val originatingUid = sourceInfo?.uid ?: callingUid
+        appOpRequestInfo = AppOpRequestInfo(
+            getPackageNameForUid(context, originatingUid, callingPackage),
+            originatingUid, callingAttributionTag
+        )
+
+        if (callingUid == Process.INVALID_UID && sourceInfo == null) {
+            // Caller's identity could not be determined. Abort the install
+            return InstallAborted(ABORT_REASON_INTERNAL_ERROR)
+        }
+
+        if ((sessionId != SessionInfo.INVALID_ID
+                && !isCallerSessionOwner(packageInstaller, originatingUid, sessionId))
+            || (stagedSessionId != SessionInfo.INVALID_ID
+                && !isCallerSessionOwner(packageInstaller, Process.myUid(), stagedSessionId))
+        ) {
+            return InstallAborted(ABORT_REASON_INTERNAL_ERROR)
+        }
+
+        isTrustedSource = isInstallRequestFromTrustedSource(sourceInfo, this.intent, originatingUid)
+        if (!isInstallPermissionGrantedOrRequested(
+                context, callingUid, originatingUid, isTrustedSource
+            )
+        ) {
+            return InstallAborted(ABORT_REASON_INTERNAL_ERROR)
+        }
+
+        val restriction = getDevicePolicyRestrictions()
+        if (restriction != null) {
+            val adminSupportDetailsIntent =
+                devicePolicyManager!!.createAdminSupportIntent(restriction)
+            return InstallAborted(
+                ABORT_REASON_POLICY, message = restriction, resultIntent = adminSupportDetailsIntent
+            )
+        }
+
+        maybeRemoveInvalidInstallerPackageName(callerInfo)
+
+        return InstallStaging()
+    }
+
+    /**
+     * @return the ApplicationInfo for the installation source (the calling package), if available
+     */
+    private fun getSourceInfo(callingPackage: String?): ApplicationInfo? {
+        return try {
+            callingPackage?.let { packageManager.getApplicationInfo(it, 0) }
+        } catch (ignored: PackageManager.NameNotFoundException) {
+            null
+        }
+    }
+
+    private fun isInstallRequestFromTrustedSource(
+        sourceInfo: ApplicationInfo?,
+        intent: Intent,
+        originatingUid: Int,
+    ): Boolean {
+        val isNotUnknownSource = intent.getBooleanExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, false)
+        return (sourceInfo != null && sourceInfo.isPrivilegedApp
+            && (isNotUnknownSource
+            || isPermissionGranted(context, Manifest.permission.INSTALL_PACKAGES, originatingUid)))
+    }
+
+    private fun getDevicePolicyRestrictions(): String? {
+        val restrictions = arrayOf(
+            UserManager.DISALLOW_INSTALL_APPS,
+            UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES,
+            UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY
+        )
+        for (restriction in restrictions) {
+            if (!userManager!!.hasUserRestrictionForUser(restriction, Process.myUserHandle())) {
+                continue
+            }
+            return restriction
+        }
+        return null
+    }
+
+    private fun maybeRemoveInvalidInstallerPackageName(callerInfo: CallerInfo) {
+        val installerPackageNameFromIntent =
+            intent.getStringExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME) ?: return
+
+        if (!TextUtils.equals(installerPackageNameFromIntent, callerInfo.packageName)
+            && callerInfo.packageName != null
+            && isPermissionGranted(
+                packageManager, Manifest.permission.INSTALL_PACKAGES, callerInfo.packageName
+            )
+        ) {
+            Log.e(
+                LOG_TAG, "The given installer package name $installerPackageNameFromIntent"
+                    + " is invalid. Remove it."
+            )
+            EventLog.writeEvent(
+                0x534e4554, "236687884", callerInfo.uid,
+                "Invalid EXTRA_INSTALLER_PACKAGE_NAME"
+            )
+            intent.removeExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME)
+        }
+    }
+
+    @OptIn(DelicateCoroutinesApi::class)
+    fun stageForInstall() {
+        val uri = intent.data
+        if (stagedSessionId != SessionInfo.INVALID_ID
+            || isSessionInstall
+            || (uri != null && SCHEME_PACKAGE == uri.scheme)
+        ) {
+            // For a session based install or installing with a package:// URI, there is no file
+            // for us to stage.
+            _stagingResult.value = InstallReady()
+            return
+        }
+        if (uri != null
+            && ContentResolver.SCHEME_CONTENT == uri.scheme
+            && canPackageQuery(context, callingUid, uri)
+        ) {
+            if (stagedSessionId > 0) {
+                val info: SessionInfo? = packageInstaller.getSessionInfo(stagedSessionId)
+                if (info == null || !info.isActive || info.resolvedBaseApkPath == null) {
+                    Log.w(LOG_TAG, "Session $stagedSessionId in funky state; ignoring")
+                    if (info != null) {
+                        cleanupStagingSession()
+                    }
+                    stagedSessionId = 0
+                }
+            }
+
+            // Session does not exist, or became invalid.
+            if (stagedSessionId <= 0) {
+                // Create session here to be able to show error.
+                try {
+                    context.contentResolver.openAssetFileDescriptor(uri, "r").use { afd ->
+                        val pfd: ParcelFileDescriptor? = afd?.parcelFileDescriptor
+                        val params: SessionParams =
+                            createSessionParams(intent, pfd, uri.toString())
+                        stagedSessionId = packageInstaller.createSession(params)
+                    }
+                } catch (e: Exception) {
+                    Log.w(LOG_TAG, "Failed to create a staging session", e)
+                    _stagingResult.value = InstallAborted(
+                        ABORT_REASON_INTERNAL_ERROR,
+                        resultIntent = Intent().putExtra(
+                            Intent.EXTRA_INSTALL_RESULT, PackageManager.INSTALL_FAILED_INVALID_APK
+                        ),
+                        activityResultCode = Activity.RESULT_FIRST_USER,
+                        errorDialogType =  if (e is IOException) DLG_PACKAGE_ERROR else DLG_NONE
+                    )
+                    return
+                }
+            }
+
+            sessionStager = SessionStager(context, uri, stagedSessionId)
+            GlobalScope.launch(Dispatchers.Main) {
+                val wasFileStaged = sessionStager!!.execute()
+
+                if (wasFileStaged) {
+                    _stagingResult.value = InstallReady()
+                } else {
+                    cleanupStagingSession()
+                    _stagingResult.value = InstallAborted(
+                        ABORT_REASON_INTERNAL_ERROR,
+                        resultIntent = Intent().putExtra(
+                            Intent.EXTRA_INSTALL_RESULT, PackageManager.INSTALL_FAILED_INVALID_APK
+                        ),
+                        activityResultCode = Activity.RESULT_FIRST_USER
+                    )
+                }
+            }
+        } else {
+            _stagingResult.value = InstallAborted(
+                ABORT_REASON_INTERNAL_ERROR,
+                resultIntent = Intent().putExtra(
+                    Intent.EXTRA_INSTALL_RESULT, PackageManager.INSTALL_FAILED_INVALID_URI
+                ),
+                activityResultCode = Activity.RESULT_FIRST_USER
+            )
+        }
+    }
+
+    private fun cleanupStagingSession() {
+        if (stagedSessionId > 0) {
+            try {
+                packageInstaller.abandonSession(stagedSessionId)
+            } catch (ignored: SecurityException) {
+            }
+            stagedSessionId = 0
+        }
+    }
+
+    private fun createSessionParams(
+        intent: Intent,
+        pfd: ParcelFileDescriptor?,
+        debugPathName: String,
+    ): SessionParams {
+        val params = SessionParams(SessionParams.MODE_FULL_INSTALL)
+        val referrerUri = intent.getParcelableExtra(Intent.EXTRA_REFERRER, Uri::class.java)
+        params.setPackageSource(
+            if (referrerUri != null)
+                PackageInstaller.PACKAGE_SOURCE_DOWNLOADED_FILE
+            else PackageInstaller.PACKAGE_SOURCE_LOCAL_FILE
+        )
+        params.setInstallAsInstantApp(false)
+        params.setReferrerUri(referrerUri)
+        params.setOriginatingUri(
+            intent.getParcelableExtra(Intent.EXTRA_ORIGINATING_URI, Uri::class.java)
+        )
+        params.setOriginatingUid(
+            intent.getIntExtra(Intent.EXTRA_ORIGINATING_UID, Process.INVALID_UID)
+        )
+        params.setInstallerPackageName(intent.getStringExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME))
+        params.setInstallReason(PackageManager.INSTALL_REASON_USER)
+        // Disable full screen intent usage by for sideloads.
+        params.setPermissionState(
+            Manifest.permission.USE_FULL_SCREEN_INTENT, SessionParams.PERMISSION_STATE_DENIED
+        )
+        if (pfd != null) {
+            try {
+                val installInfo = packageInstaller.readInstallInfo(pfd, debugPathName, 0)
+                params.setAppPackageName(installInfo.packageName)
+                params.setInstallLocation(installInfo.installLocation)
+                params.setSize(installInfo.calculateInstalledSize(params, pfd))
+            } catch (e: PackageInstaller.PackageParsingException) {
+                Log.e(LOG_TAG, "Cannot parse package $debugPathName. Assuming defaults.", e)
+                params.setSize(pfd.statSize)
+            } catch (e: IOException) {
+                Log.e(LOG_TAG, "Cannot calculate installed size $debugPathName. " +
+                    "Try only apk size.", e
+                )
+            }
+        } else {
+            Log.e(LOG_TAG, "Cannot parse package $debugPathName. Assuming defaults.")
+        }
+        return params
+    }
+
+    /**
+     * Processes Install session, file:// or package:// URI to generate data pertaining to user
+     * confirmation for an install. This method also checks if the source app has the AppOp granted
+     * to install unknown apps. If an AppOp is to be requested, cache the user action prompt data to
+     * be reused once appOp has been granted
+     *
+     * @return
+     *  * [InstallAborted]
+     *      *  If install session is invalid (not sealed or resolvedBaseApk path is invalid)
+     *      *  Source app doesn't have visibility to target app
+     *      *  The APK is invalid
+     *      *  URI is invalid
+     *      *  Can't get ApplicationInfo for source app, to request AppOp
+     *
+     *  *  [InstallUserActionRequired]
+     *      * If AppOP is granted and user action is required to proceed with install
+     *      * If AppOp grant is to be requested from the user
+     */
+    fun requestUserConfirmation(): InstallStage {
+        return if (isTrustedSource) {
+            if (localLOGV) {
+                Log.i(LOG_TAG, "install allowed")
+            }
+            // Returns InstallUserActionRequired stage if install details could be successfully
+            // computed, else it returns InstallAborted.
+            generateConfirmationSnippet()
+        } else {
+            val unknownSourceStage = handleUnknownSources(appOpRequestInfo)
+            if (unknownSourceStage.stageCode == InstallStage.STAGE_READY) {
+                // Source app already has appOp granted.
+                generateConfirmationSnippet()
+            } else {
+                unknownSourceStage
+            }
+        }
+    }
+
+    private fun generateConfirmationSnippet(): InstallStage {
+        val packageSource: Any?
+        val pendingUserActionReason: Int
+
+        if (PackageInstaller.ACTION_CONFIRM_INSTALL == intent.action) {
+            val info = packageInstaller.getSessionInfo(sessionId)
+            val resolvedPath = info?.resolvedBaseApkPath
+            if (info == null || !info.isSealed || resolvedPath == null) {
+                Log.w(LOG_TAG, "Session $sessionId in funky state; ignoring")
+                return InstallAborted(ABORT_REASON_INTERNAL_ERROR)
+            }
+            packageSource = Uri.fromFile(File(resolvedPath))
+            // TODO: Not sure where is this used yet. PIA.java passes it to
+            //  InstallInstalling if not null
+            // mOriginatingURI = null;
+            // mReferrerURI = null;
+            pendingUserActionReason = info.getPendingUserActionReason()
+        } else if (PackageInstaller.ACTION_CONFIRM_PRE_APPROVAL == intent.action) {
+            val info = packageInstaller.getSessionInfo(sessionId)
+            if (info == null || !info.isPreApprovalRequested) {
+                Log.w(LOG_TAG, "Session $sessionId in funky state; ignoring")
+                return InstallAborted(ABORT_REASON_INTERNAL_ERROR)
+            }
+            packageSource = info
+            // mOriginatingURI = null;
+            // mReferrerURI = null;
+            pendingUserActionReason = info.getPendingUserActionReason()
+        } else {
+            // Two possible origins:
+            // 1. Installation with SCHEME_PACKAGE.
+            // 2. Installation with "file://" for session created by this app
+            packageSource =
+                if (intent.data?.scheme == SCHEME_PACKAGE) {
+                    intent.data
+                } else {
+                    val stagedSessionInfo = packageInstaller.getSessionInfo(stagedSessionId)
+                    Uri.fromFile(File(stagedSessionInfo?.resolvedBaseApkPath!!))
+                }
+            // mOriginatingURI = mIntent.getParcelableExtra(Intent.EXTRA_ORIGINATING_URI);
+            // mReferrerURI = mIntent.getParcelableExtra(Intent.EXTRA_REFERRER);
+            pendingUserActionReason = PackageInstaller.REASON_CONFIRM_PACKAGE_CHANGE
+        }
+
+        // if there's nothing to do, quietly slip into the ether
+        if (packageSource == null) {
+            Log.w(LOG_TAG, "Unspecified source")
+            return InstallAborted(
+                ABORT_REASON_INTERNAL_ERROR,
+                resultIntent = Intent().putExtra(
+                    Intent.EXTRA_INSTALL_RESULT,
+                    PackageManager.INSTALL_FAILED_INVALID_URI
+                ),
+                activityResultCode = Activity.RESULT_FIRST_USER
+            )
+        }
+        return processAppSnippet(packageSource, pendingUserActionReason)
+    }
+
+    /**
+     * Parse the Uri (post-commit install session) or use the SessionInfo (pre-commit install
+     * session) to set up the installer for this install.
+     *
+     * @param source The source of package URI or SessionInfo
+     * @return
+     *  * [InstallUserActionRequired] if source could be processed
+     *  * [InstallAborted] if source is invalid or there was an error is processing a source
+     */
+    private fun processAppSnippet(source: Any, userActionReason: Int): InstallStage {
+        return when (source) {
+            is Uri -> processPackageUri(source, userActionReason)
+            is SessionInfo -> processSessionInfo(source, userActionReason)
+            else -> InstallAborted(ABORT_REASON_INTERNAL_ERROR)
+        }
+    }
+
+    /**
+     * Parse the Uri and set up the installer for this package.
+     *
+     * @param packageUri The URI to parse
+     * @return
+     *  * [InstallUserActionRequired] if source could be processed
+     *  * [InstallAborted] if source is invalid or there was an error is processing a source
+     */
+    private fun processPackageUri(packageUri: Uri, userActionReason: Int): InstallStage {
+        val scheme = packageUri.scheme
+        val packageName = packageUri.schemeSpecificPart
+        if (scheme == null) {
+            return InstallAborted(ABORT_REASON_INTERNAL_ERROR)
+        }
+        if (localLOGV) {
+            Log.i(LOG_TAG, "processPackageUri(): uri = $packageUri, scheme = $scheme")
+        }
+        when (scheme) {
+            SCHEME_PACKAGE -> {
+                for (handle in userManager!!.getUserHandles(true)) {
+                    val pmForUser = context.createContextAsUser(handle, 0).packageManager
+                    try {
+                        if (pmForUser.canPackageQuery(callingPackage!!, packageName)) {
+                            newPackageInfo = pmForUser.getPackageInfo(
+                                packageName,
+                                PackageManager.GET_PERMISSIONS
+                                    or PackageManager.MATCH_UNINSTALLED_PACKAGES
+                            )
+                        }
+                    } catch (ignored: PackageManager.NameNotFoundException) {
+                    }
+                }
+                if (newPackageInfo == null) {
+                    Log.w(
+                        LOG_TAG, "Requested package " + packageUri.schemeSpecificPart
+                            + " not available. Discontinuing installation"
+                    )
+                    return InstallAborted(
+                        ABORT_REASON_INTERNAL_ERROR,
+                        errorDialogType = DLG_PACKAGE_ERROR,
+                        resultIntent = Intent().putExtra(
+                            Intent.EXTRA_INSTALL_RESULT, PackageManager.INSTALL_FAILED_INVALID_APK
+                        ),
+                        activityResultCode = Activity.RESULT_FIRST_USER
+                    )
+                }
+                appSnippet = getAppSnippet(context, newPackageInfo!!)
+                if (localLOGV) {
+                    Log.i(LOG_TAG, "Created snippet for " + appSnippet.label)
+                }
+            }
+
+            ContentResolver.SCHEME_FILE -> {
+                val sourceFile = packageUri.path?.let { File(it) }
+                newPackageInfo = sourceFile?.let {
+                    getPackageInfo(context, it, PackageManager.GET_PERMISSIONS)
+                }
+
+                // Check for parse errors
+                if (newPackageInfo == null) {
+                    Log.w(
+                        LOG_TAG, "Parse error when parsing manifest. " +
+                            "Discontinuing installation"
+                    )
+                    return InstallAborted(
+                        ABORT_REASON_INTERNAL_ERROR,
+                        errorDialogType = DLG_PACKAGE_ERROR,
+                        resultIntent = Intent().putExtra(
+                            Intent.EXTRA_INSTALL_RESULT,
+                            PackageManager.INSTALL_FAILED_INVALID_APK
+                        ),
+                        activityResultCode = Activity.RESULT_FIRST_USER
+                    )
+                }
+                if (localLOGV) {
+                    Log.i(LOG_TAG, "Creating snippet for local file $sourceFile")
+                }
+                appSnippet = getAppSnippet(context, newPackageInfo!!, sourceFile!!)
+            }
+
+            else -> {
+                Log.e(LOG_TAG, "Unexpected URI scheme $packageUri")
+                return InstallAborted(ABORT_REASON_INTERNAL_ERROR)
+            }
+        }
+        return InstallUserActionRequired(
+            USER_ACTION_REASON_INSTALL_CONFIRMATION, appSnippet, isAppUpdating(newPackageInfo!!),
+            getUpdateMessage(newPackageInfo!!, userActionReason)
+        )
+    }
+
+    /**
+     * Use the SessionInfo and set up the installer for pre-commit install session.
+     *
+     * @param sessionInfo The SessionInfo to compose
+     * @return
+     *  * [InstallUserActionRequired] if source could be processed
+     *  * [InstallAborted] if source is invalid or there was an error is processing a source
+     */
+    private fun processSessionInfo(sessionInfo: SessionInfo, userActionReason: Int): InstallStage {
+        newPackageInfo = generateStubPackageInfo(sessionInfo.getAppPackageName())
+        appSnippet = getAppSnippet(context, sessionInfo)
+
+        return InstallUserActionRequired(
+            USER_ACTION_REASON_INSTALL_CONFIRMATION, appSnippet, isAppUpdating(newPackageInfo!!),
+            getUpdateMessage(newPackageInfo!!, userActionReason)
+
+        )
+    }
+
+    private fun getUpdateMessage(pkgInfo: PackageInfo, userActionReason: Int): String? {
+        if (isAppUpdating(pkgInfo)) {
+            val existingUpdateOwnerLabel = getExistingUpdateOwnerLabel(pkgInfo)
+            val requestedUpdateOwnerLabel = getApplicationLabel(callingPackage)
+            if (!TextUtils.isEmpty(existingUpdateOwnerLabel)
+                && userActionReason == PackageInstaller.REASON_REMIND_OWNERSHIP
+            ) {
+                return context.getString(
+                    R.string.install_confirm_question_update_owner_reminder,
+                    requestedUpdateOwnerLabel, existingUpdateOwnerLabel
+                )
+            }
+        }
+        return null
+    }
+
+    private fun getExistingUpdateOwnerLabel(pkgInfo: PackageInfo): CharSequence? {
+        return try {
+            val packageName = pkgInfo.packageName
+            val sourceInfo = packageManager.getInstallSourceInfo(packageName)
+            val existingUpdateOwner = sourceInfo.updateOwnerPackageName
+            getApplicationLabel(existingUpdateOwner)
+        } catch (e: PackageManager.NameNotFoundException) {
+            null
+        }
+    }
+
+    private fun getApplicationLabel(packageName: String?): CharSequence? {
+        return try {
+            val appInfo = packageName?.let {
+                packageManager.getApplicationInfo(
+                    it, PackageManager.ApplicationInfoFlags.of(0)
+                )
+            }
+            appInfo?.let { packageManager.getApplicationLabel(it) }
+        } catch (e: PackageManager.NameNotFoundException) {
+            null
+        }
+    }
+
+    private fun isAppUpdating(newPkgInfo: PackageInfo): Boolean {
+        var pkgName = newPkgInfo.packageName
+        // Check if there is already a package on the device with this name
+        // but it has been renamed to something else.
+        val oldName = packageManager.canonicalToCurrentPackageNames(arrayOf(pkgName))
+        if (oldName != null && oldName.isNotEmpty() && oldName[0] != null) {
+            pkgName = oldName[0]
+            newPkgInfo.packageName = pkgName
+            newPkgInfo.applicationInfo?.packageName = pkgName
+        }
+
+        // Check if package is already installed. display confirmation dialog if replacing pkg
+        try {
+            // This is a little convoluted because we want to get all uninstalled
+            // apps, but this may include apps with just data, and if it is just
+            // data we still want to count it as "installed".
+            val appInfo = packageManager.getApplicationInfo(
+                pkgName, PackageManager.MATCH_UNINSTALLED_PACKAGES
+            )
+            if (appInfo.flags and ApplicationInfo.FLAG_INSTALLED == 0) {
+                return false
+            }
+        } catch (e: PackageManager.NameNotFoundException) {
+            return false
+        }
+        return true
+    }
+
+    /**
+     * Once the user returns from Settings related to installing from unknown sources, reattempt
+     * the installation if the source app is granted permission to install other apps. Abort the
+     * installation if the source app is still not granted installing permission.
+     *
+     * @return
+     * * [InstallUserActionRequired] containing data required to ask user confirmation
+     * to proceed with the install.
+     * * [InstallAborted] if there was an error while recomputing, or the source still
+     * doesn't have install permission.
+     */
+    fun reattemptInstall(): InstallStage {
+        val unknownSourceStage = handleUnknownSources(appOpRequestInfo)
+        return when (unknownSourceStage.stageCode) {
+            InstallStage.STAGE_READY -> {
+                // Source app now has appOp granted.
+                generateConfirmationSnippet()
+            }
+
+            InstallStage.STAGE_ABORTED -> {
+                // There was some error in determining the AppOp code for the source app.
+                // Abort installation
+                unknownSourceStage
+            }
+
+            else -> {
+                // AppOpsManager again returned a MODE_ERRORED or MODE_DEFAULT op code. This was
+                // unexpected while reattempting the install. Let's abort it.
+                Log.e(LOG_TAG, "AppOp still not granted.")
+                InstallAborted(ABORT_REASON_INTERNAL_ERROR)
+            }
+        }
+    }
+
+    private fun handleUnknownSources(requestInfo: AppOpRequestInfo): InstallStage {
+        if (requestInfo.callingPackage == null) {
+            Log.i(LOG_TAG, "No source found for package " + newPackageInfo?.packageName)
+            return InstallUserActionRequired(USER_ACTION_REASON_ANONYMOUS_SOURCE)
+        }
+        // Shouldn't use static constant directly, see b/65534401.
+        val appOpStr = AppOpsManager.permissionToOp(Manifest.permission.REQUEST_INSTALL_PACKAGES)
+        val appOpMode = appOpsManager!!.noteOpNoThrow(
+            appOpStr!!, requestInfo.originatingUid, requestInfo.callingPackage,
+            requestInfo.attributionTag, "Started package installation activity"
+        )
+        if (localLOGV) {
+            Log.i(LOG_TAG, "handleUnknownSources(): appMode=$appOpMode")
+        }
+
+        return when (appOpMode) {
+            AppOpsManager.MODE_DEFAULT, AppOpsManager.MODE_ERRORED -> {
+                if (appOpMode == AppOpsManager.MODE_DEFAULT) {
+                    appOpsManager.setMode(
+                        appOpStr, requestInfo.originatingUid, requestInfo.callingPackage,
+                        AppOpsManager.MODE_ERRORED
+                    )
+                }
+                try {
+                    val sourceInfo =
+                        packageManager.getApplicationInfo(requestInfo.callingPackage, 0)
+                    val sourceAppSnippet = getAppSnippet(context, sourceInfo)
+                    InstallUserActionRequired(
+                        USER_ACTION_REASON_UNKNOWN_SOURCE, appSnippet = sourceAppSnippet,
+                        dialogMessage = requestInfo.callingPackage
+                    )
+                } catch (e: PackageManager.NameNotFoundException) {
+                    Log.e(LOG_TAG, "Did not find appInfo for " + requestInfo.callingPackage)
+                    InstallAborted(ABORT_REASON_INTERNAL_ERROR)
+                }
+            }
+
+            AppOpsManager.MODE_ALLOWED -> InstallReady()
+
+            else -> {
+                Log.e(
+                    LOG_TAG, "Invalid app op mode $appOpMode for " +
+                        "OP_REQUEST_INSTALL_PACKAGES found for uid $requestInfo.originatingUid"
+                )
+                InstallAborted(ABORT_REASON_INTERNAL_ERROR)
+            }
+        }
+    }
+
+    /**
+     * Kick off the installation. Register a broadcast listener to get the result of the
+     * installation and commit the staged session here. If the installation was session based,
+     * signal the PackageInstaller that the user has granted permission to proceed with the install
+     */
+    fun initiateInstall() {
+        if (sessionId > 0) {
+            packageInstaller.setPermissionsResult(sessionId, true)
+            _installResult.value = InstallAborted(
+                ABORT_REASON_DONE, activityResultCode = Activity.RESULT_OK
+            )
+            return
+        }
+        val uri = intent.data
+        if (SCHEME_PACKAGE == uri?.scheme) {
+            try {
+                packageManager.installExistingPackage(
+                    newPackageInfo!!.packageName, PackageManager.INSTALL_REASON_USER
+                )
+                setStageBasedOnResult(PackageInstaller.STATUS_SUCCESS, -1, null)
+            } catch (e: PackageManager.NameNotFoundException) {
+                setStageBasedOnResult(
+                    PackageInstaller.STATUS_FAILURE, PackageManager.INSTALL_FAILED_INTERNAL_ERROR,
+                    null)
+            }
+            return
+        }
+        if (stagedSessionId <= 0) {
+            // How did we even land here?
+            Log.e(LOG_TAG, "Invalid local session and caller initiated session")
+            _installResult.value = InstallAborted(ABORT_REASON_INTERNAL_ERROR)
+            return
+        }
+        val installId: Int
+        try {
+            _installResult.value = InstallInstalling(appSnippet)
+            installId = InstallEventReceiver.addObserver(
+                context, EventResultPersister.GENERATE_NEW_ID
+            ) { statusCode: Int, legacyStatus: Int, message: String?, serviceId: Int ->
+                setStageBasedOnResult(statusCode, legacyStatus, message)
+            }
+        } catch (e: OutOfIdsException) {
+            setStageBasedOnResult(
+                PackageInstaller.STATUS_FAILURE, PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null)
+            return
+        }
+        val broadcastIntent = Intent(BROADCAST_ACTION)
+        broadcastIntent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND)
+        broadcastIntent.setPackage(context.packageName)
+        broadcastIntent.putExtra(EventResultPersister.EXTRA_ID, installId)
+        val pendingIntent = PendingIntent.getBroadcast(
+            context, installId, broadcastIntent,
+            PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE
+        )
+        try {
+            val session = packageInstaller.openSession(stagedSessionId)
+            session.commit(pendingIntent.intentSender)
+        } catch (e: Exception) {
+            Log.e(LOG_TAG, "Session $stagedSessionId could not be opened.", e)
+            packageInstaller.abandonSession(stagedSessionId)
+            setStageBasedOnResult(
+                PackageInstaller.STATUS_FAILURE, PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null)
+        }
+    }
+
+    private fun setStageBasedOnResult(
+        statusCode: Int,
+        legacyStatus: Int,
+        message: String?
+    ) {
+        if (statusCode == PackageInstaller.STATUS_SUCCESS) {
+            val shouldReturnResult = intent.getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)
+            val resultIntent = if (shouldReturnResult) {
+                Intent().putExtra(Intent.EXTRA_INSTALL_RESULT, PackageManager.INSTALL_SUCCEEDED)
+            } else {
+                packageManager.getLaunchIntentForPackage(newPackageInfo!!.packageName)
+            }
+            _installResult.setValue(InstallSuccess(appSnippet, shouldReturnResult, resultIntent))
+        } else {
+            _installResult.setValue(InstallFailed(appSnippet, statusCode, legacyStatus, message))
+        }
+    }
+
+    /**
+     * Cleanup the staged session. Also signal the packageinstaller that an install session is to
+     * be aborted
+     */
+    fun cleanupInstall() {
+        if (sessionId > 0) {
+            packageInstaller.setPermissionsResult(sessionId, false)
+        } else if (stagedSessionId > 0) {
+            cleanupStagingSession()
+        }
+    }
+
+    /**
+     * When the identity of the install source could not be determined, user can skip checking the
+     * source and directly proceed with the install.
+     */
+    fun forcedSkipSourceCheck(): InstallStage {
+        return generateConfirmationSnippet()
+    }
+
+    val stagingProgress: LiveData<Int>
+        get() = sessionStager?.progress ?: MutableLiveData(0)
+
+    companion object {
+        const val EXTRA_STAGED_SESSION_ID = "com.android.packageinstaller.extra.STAGED_SESSION_ID"
+        const val SCHEME_PACKAGE = "package"
+        const val BROADCAST_ACTION = "com.android.packageinstaller.ACTION_INSTALL_COMMIT"
+        private val LOG_TAG = InstallRepository::class.java.simpleName
+    }
+
+    data class CallerInfo(val packageName: String?, val uid: Int)
+    data class AppOpRequestInfo(
+        val callingPackage: String?,
+        val originatingUid: Int,
+        val attributionTag: String?,
+    )
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/InstallStages.kt b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/InstallStages.kt
new file mode 100644
index 0000000..bbb9bca
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/InstallStages.kt
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.packageinstaller.v2.model
+
+import android.app.Activity
+import android.content.Intent
+import android.content.pm.PackageManager
+import android.graphics.drawable.Drawable
+
+sealed class InstallStage(val stageCode: Int) {
+
+    companion object {
+        const val STAGE_DEFAULT = -1
+        const val STAGE_ABORTED = 0
+        const val STAGE_STAGING = 1
+        const val STAGE_READY = 2
+        const val STAGE_USER_ACTION_REQUIRED = 3
+        const val STAGE_INSTALLING = 4
+        const val STAGE_SUCCESS = 5
+        const val STAGE_FAILED = 6
+    }
+}
+
+class InstallStaging : InstallStage(STAGE_STAGING)
+
+class InstallReady : InstallStage(STAGE_READY)
+
+data class InstallUserActionRequired(
+    val actionReason: Int,
+    private val appSnippet: PackageUtil.AppSnippet? = null,
+    val isAppUpdating: Boolean = false,
+    val dialogMessage: String? = null,
+) : InstallStage(STAGE_USER_ACTION_REQUIRED) {
+
+    val appIcon: Drawable?
+        get() = appSnippet?.icon
+
+    val appLabel: String?
+        get() = appSnippet?.let { appSnippet.label as String? }
+
+    companion object {
+        const val USER_ACTION_REASON_UNKNOWN_SOURCE = 0
+        const val USER_ACTION_REASON_ANONYMOUS_SOURCE = 1
+        const val USER_ACTION_REASON_INSTALL_CONFIRMATION = 2
+    }
+}
+
+data class InstallInstalling(private val appSnippet: PackageUtil.AppSnippet) :
+    InstallStage(STAGE_INSTALLING) {
+
+    val appIcon: Drawable?
+        get() = appSnippet.icon
+
+    val appLabel: String?
+        get() = appSnippet.label as String?
+}
+
+data class InstallSuccess(
+    private val appSnippet: PackageUtil.AppSnippet,
+    val shouldReturnResult: Boolean = false,
+    /**
+     *
+     * * If the caller is requesting a result back, this will hold the Intent with
+     * [Intent.EXTRA_INSTALL_RESULT] set to [PackageManager.INSTALL_SUCCEEDED] which is sent
+     * back to the caller.
+     *
+     * * If the caller doesn't want the result back, this will hold the Intent that launches
+     * the newly installed / updated app if a launchable activity exists.
+     */
+    val resultIntent: Intent? = null,
+) : InstallStage(STAGE_SUCCESS) {
+
+    val appIcon: Drawable?
+        get() = appSnippet.icon
+
+    val appLabel: String?
+        get() = appSnippet.label as String?
+}
+
+data class InstallFailed(
+    private val appSnippet: PackageUtil.AppSnippet,
+    val legacyCode: Int,
+    val statusCode: Int,
+    val message: String?,
+) : InstallStage(STAGE_FAILED) {
+
+    val appIcon: Drawable?
+        get() = appSnippet.icon
+
+    val appLabel: String?
+        get() = appSnippet.label as String?
+}
+
+data class InstallAborted(
+    val abortReason: Int,
+    /**
+     * It will hold the restriction name, when the restriction was enforced by the system, and not
+     * a device admin.
+     */
+    val message: String? = null,
+    /**
+     * * If abort reason is [ABORT_REASON_POLICY], then this will hold the Intent
+     * to display a support dialog when a feature was disabled by an admin. It will be
+     * `null` if the feature is disabled by the system. In this case, the restriction name
+     * will be set in [message]
+     * * If the abort reason is [ABORT_REASON_INTERNAL_ERROR], it **may** hold an
+     * intent to be sent as a result to the calling activity.
+     */
+    val resultIntent: Intent? = null,
+    val activityResultCode: Int = Activity.RESULT_CANCELED,
+    val errorDialogType: Int? = DLG_NONE,
+) : InstallStage(STAGE_ABORTED) {
+
+    companion object {
+        const val ABORT_REASON_INTERNAL_ERROR = 0
+        const val ABORT_REASON_POLICY = 1
+        const val ABORT_REASON_DONE = 2
+        const val DLG_NONE = 0
+        const val DLG_PACKAGE_ERROR = 1
+    }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/PackageUtil.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/PackageUtil.java
deleted file mode 100644
index fe05237..0000000
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/PackageUtil.java
+++ /dev/null
@@ -1,462 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.packageinstaller.v2.model;
-
-import android.Manifest;
-import android.content.Context;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageInstaller;
-import android.content.pm.PackageInstaller.SessionInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.ProviderInfo;
-import android.content.res.Resources;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.net.Uri;
-import android.os.Build;
-import android.os.Process;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.util.Log;
-import androidx.annotation.NonNull;
-import java.io.File;
-import java.util.Arrays;
-import java.util.Objects;
-
-public class PackageUtil {
-
-    private static final String TAG = InstallRepository.class.getSimpleName();
-    private static final String DOWNLOADS_AUTHORITY = "downloads";
-    private static final String SPLIT_BASE_APK_END_WITH = "base.apk";
-
-    /**
-     * Determines if the UID belongs to the system downloads provider and returns the
-     * {@link ApplicationInfo} of the provider
-     *
-     * @param uid UID of the caller
-     * @return {@link ApplicationInfo} of the provider if a downloads provider exists, it is a
-     *     system app, and its UID matches with the passed UID, null otherwise.
-     */
-    public static ApplicationInfo getSystemDownloadsProviderInfo(PackageManager pm, int uid) {
-        final ProviderInfo providerInfo = pm.resolveContentProvider(
-            DOWNLOADS_AUTHORITY, 0);
-        if (providerInfo == null) {
-            // There seems to be no currently enabled downloads provider on the system.
-            return null;
-        }
-        ApplicationInfo appInfo = providerInfo.applicationInfo;
-        if ((appInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0 && uid == appInfo.uid) {
-            return appInfo;
-        }
-        return null;
-    }
-
-    /**
-     * Get the maximum target sdk for a UID.
-     *
-     * @param context The context to use
-     * @param uid The UID requesting the install/uninstall
-     * @return The maximum target SDK or -1 if the uid does not match any packages.
-     */
-    public static int getMaxTargetSdkVersionForUid(@NonNull Context context, int uid) {
-        PackageManager pm = context.getPackageManager();
-        final String[] packages = pm.getPackagesForUid(uid);
-        int targetSdkVersion = -1;
-        if (packages != null) {
-            for (String packageName : packages) {
-                try {
-                    ApplicationInfo info = pm.getApplicationInfo(packageName, 0);
-                    targetSdkVersion = Math.max(targetSdkVersion, info.targetSdkVersion);
-                } catch (PackageManager.NameNotFoundException e) {
-                    // Ignore and try the next package
-                }
-            }
-        }
-        return targetSdkVersion;
-    }
-
-    public static boolean canPackageQuery(Context context, int callingUid, Uri packageUri) {
-        PackageManager pm = context.getPackageManager();
-        ProviderInfo info = pm.resolveContentProvider(packageUri.getAuthority(),
-            PackageManager.ComponentInfoFlags.of(0));
-        if (info == null) {
-            return false;
-        }
-        String targetPackage = info.packageName;
-
-        String[] callingPackages = pm.getPackagesForUid(callingUid);
-        if (callingPackages == null) {
-            return false;
-        }
-        for (String callingPackage : callingPackages) {
-            try {
-                if (pm.canPackageQuery(callingPackage, targetPackage)) {
-                    return true;
-                }
-            } catch (PackageManager.NameNotFoundException e) {
-                // no-op
-            }
-        }
-        return false;
-    }
-
-    /**
-     * @param context the {@link Context} object
-     * @param permission the permission name to check
-     * @param callingUid the UID of the caller who's permission is being checked
-     * @return {@code true} if the callingUid is granted the said permission
-     */
-    public static boolean isPermissionGranted(Context context, String permission, int callingUid) {
-        return context.checkPermission(permission, -1, callingUid)
-            == PackageManager.PERMISSION_GRANTED;
-    }
-
-    /**
-     * @param pm the {@link PackageManager} object
-     * @param permission the permission name to check
-     * @param packageName the name of the package who's permission is being checked
-     * @return {@code true} if the package is granted the said permission
-     */
-    public static boolean isPermissionGranted(PackageManager pm, String permission,
-        String packageName) {
-        return pm.checkPermission(permission, packageName) == PackageManager.PERMISSION_GRANTED;
-    }
-
-    /**
-     * @param context the {@link Context} object
-     * @param callingUid the UID of the caller who's permission is being checked
-     * @param originatingUid the UID from where install is being originated. This could be same as
-     * callingUid or it will be the UID of the package performing a session based install
-     * @param isTrustedSource whether install request is coming from a privileged app or an app that
-     * has {@link Manifest.permission.INSTALL_PACKAGES} permission granted
-     * @return {@code true} if the package is granted the said permission
-     */
-    public static boolean isInstallPermissionGrantedOrRequested(Context context, int callingUid,
-        int originatingUid, boolean isTrustedSource) {
-        boolean isDocumentsManager =
-            isPermissionGranted(context, Manifest.permission.MANAGE_DOCUMENTS, callingUid);
-        boolean isSystemDownloadsProvider =
-            getSystemDownloadsProviderInfo(context.getPackageManager(), callingUid) != null;
-
-        if (!isTrustedSource && !isSystemDownloadsProvider && !isDocumentsManager) {
-
-            final int targetSdkVersion = getMaxTargetSdkVersionForUid(context, originatingUid);
-            if (targetSdkVersion < 0) {
-                // Invalid originating uid supplied. Abort install.
-                Log.w(TAG, "Cannot get target sdk version for uid " + originatingUid);
-                return false;
-            } else if (targetSdkVersion >= Build.VERSION_CODES.O
-                && !isUidRequestingPermission(context.getPackageManager(), originatingUid,
-                Manifest.permission.REQUEST_INSTALL_PACKAGES)) {
-                Log.e(TAG, "Requesting uid " + originatingUid + " needs to declare permission "
-                    + Manifest.permission.REQUEST_INSTALL_PACKAGES);
-                return false;
-            }
-        }
-        return true;
-    }
-
-    /**
-     * @param pm the {@link PackageManager} object
-     * @param uid the UID of the caller who's permission is being checked
-     * @param permission the permission name to check
-     * @return {@code true} if the caller is requesting the said permission in its Manifest
-     */
-    public static boolean isUidRequestingPermission(PackageManager pm, int uid, String permission) {
-        final String[] packageNames = pm.getPackagesForUid(uid);
-        if (packageNames == null) {
-            return false;
-        }
-        for (final String packageName : packageNames) {
-            final PackageInfo packageInfo;
-            try {
-                packageInfo = pm.getPackageInfo(packageName,
-                    PackageManager.GET_PERMISSIONS);
-            } catch (PackageManager.NameNotFoundException e) {
-                // Ignore and try the next package
-                continue;
-            }
-            if (packageInfo.requestedPermissions != null
-                && Arrays.asList(packageInfo.requestedPermissions).contains(permission)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * @param pi the {@link PackageInstaller} object to use
-     * @param originatingUid the UID of the package performing a session based install
-     * @param sessionId ID of the install session
-     * @return {@code true} if the caller is the session owner
-     */
-    public static boolean isCallerSessionOwner(PackageInstaller pi, int originatingUid,
-        int sessionId) {
-        if (originatingUid == Process.ROOT_UID) {
-            return true;
-        }
-        PackageInstaller.SessionInfo sessionInfo = pi.getSessionInfo(sessionId);
-        if (sessionInfo == null) {
-            return false;
-        }
-        int installerUid = sessionInfo.getInstallerUid();
-        return originatingUid == installerUid;
-    }
-
-    /**
-     * Generates a stub {@link PackageInfo} object for the given packageName
-     */
-    public static PackageInfo generateStubPackageInfo(String packageName) {
-        final PackageInfo info = new PackageInfo();
-        final ApplicationInfo aInfo = new ApplicationInfo();
-        info.applicationInfo = aInfo;
-        info.packageName = info.applicationInfo.packageName = packageName;
-        return info;
-    }
-
-    /**
-     * Generates an {@link AppSnippet} containing an appIcon and appLabel from the
-     * {@link SessionInfo} object
-     */
-    public static AppSnippet getAppSnippet(Context context, SessionInfo info) {
-        PackageManager pm = context.getPackageManager();
-        CharSequence label = info.getAppLabel();
-        Drawable icon = info.getAppIcon() != null ?
-            new BitmapDrawable(context.getResources(), info.getAppIcon())
-            : pm.getDefaultActivityIcon();
-        return new AppSnippet(label, icon);
-    }
-
-    /**
-     * Generates an {@link AppSnippet} containing an appIcon and appLabel from the
-     * {@link PackageInfo} object
-     */
-    public static AppSnippet getAppSnippet(Context context, PackageInfo pkgInfo) {
-        return getAppSnippet(context, pkgInfo.applicationInfo);
-    }
-
-    /**
-     * Generates an {@link AppSnippet} containing an appIcon and appLabel from the
-     * {@link ApplicationInfo} object
-     */
-    public static AppSnippet getAppSnippet(Context context, ApplicationInfo appInfo) {
-        PackageManager pm = context.getPackageManager();
-        CharSequence label = pm.getApplicationLabel(appInfo);
-        Drawable icon = pm.getApplicationIcon(appInfo);
-        return new AppSnippet(label, icon);
-    }
-
-    /**
-     * Generates an {@link AppSnippet} containing an appIcon and appLabel from the
-     * supplied APK file
-     */
-    public static AppSnippet getAppSnippet(Context context, ApplicationInfo appInfo,
-        File sourceFile) {
-        ApplicationInfo appInfoFromFile = processAppInfoForFile(appInfo, sourceFile);
-        CharSequence label = getAppLabelFromFile(context, appInfoFromFile);
-        Drawable icon = getAppIconFromFile(context, appInfoFromFile);
-        return new AppSnippet(label, icon);
-    }
-
-    /**
-     * Utility method to load application label
-     *
-     * @param context context of package that can load the resources
-     * @param appInfo ApplicationInfo object of package whose resources are to be loaded
-     */
-    public static CharSequence getAppLabelFromFile(Context context, ApplicationInfo appInfo) {
-        PackageManager pm = context.getPackageManager();
-        CharSequence label = null;
-        // Try to load the label from the package's resources. If an app has not explicitly
-        // specified any label, just use the package name.
-        if (appInfo.labelRes != 0) {
-            try {
-                label = appInfo.loadLabel(pm);
-            } catch (Resources.NotFoundException e) {
-            }
-        }
-        if (label == null) {
-            label = (appInfo.nonLocalizedLabel != null) ?
-                appInfo.nonLocalizedLabel : appInfo.packageName;
-        }
-        return label;
-    }
-
-    /**
-     * Utility method to load application icon
-     *
-     * @param context context of package that can load the resources
-     * @param appInfo ApplicationInfo object of package whose resources are to be loaded
-     */
-    public static Drawable getAppIconFromFile(Context context, ApplicationInfo appInfo) {
-        PackageManager pm = context.getPackageManager();
-        Drawable icon = null;
-        // Try to load the icon from the package's resources. If an app has not explicitly
-        // specified any resource, just use the default icon for now.
-        try {
-            if (appInfo.icon != 0) {
-                try {
-                    icon = appInfo.loadIcon(pm);
-                } catch (Resources.NotFoundException e) {
-                }
-            }
-            if (icon == null) {
-                icon = context.getPackageManager().getDefaultActivityIcon();
-            }
-        } catch (OutOfMemoryError e) {
-            Log.i(TAG, "Could not load app icon", e);
-        }
-        return icon;
-    }
-
-    private static ApplicationInfo processAppInfoForFile(ApplicationInfo appInfo, File sourceFile) {
-        final String archiveFilePath = sourceFile.getAbsolutePath();
-        appInfo.publicSourceDir = archiveFilePath;
-
-        if (appInfo.splitNames != null && appInfo.splitSourceDirs == null) {
-            final File[] files = sourceFile.getParentFile().listFiles();
-            final String[] splits = Arrays.stream(appInfo.splitNames)
-                .map(i -> findFilePath(files, i + ".apk"))
-                .filter(Objects::nonNull)
-                .toArray(String[]::new);
-
-            appInfo.splitSourceDirs = splits;
-            appInfo.splitPublicSourceDirs = splits;
-        }
-        return appInfo;
-    }
-
-    private static String findFilePath(File[] files, String postfix) {
-        for (File file : files) {
-            final String path = file.getAbsolutePath();
-            if (path.endsWith(postfix)) {
-                return path;
-            }
-        }
-        return null;
-    }
-
-    /**
-     * @return the packageName corresponding to a UID.
-     */
-    public static String getPackageNameForUid(Context context, int sourceUid,
-        String callingPackage) {
-        if (sourceUid == Process.INVALID_UID) {
-            return null;
-        }
-        // If the sourceUid belongs to the system downloads provider, we explicitly return the
-        // name of the Download Manager package. This is because its UID is shared with multiple
-        // packages, resulting in uncertainty about which package will end up first in the list
-        // of packages associated with this UID
-        PackageManager pm = context.getPackageManager();
-        ApplicationInfo systemDownloadProviderInfo = getSystemDownloadsProviderInfo(
-            pm, sourceUid);
-        if (systemDownloadProviderInfo != null) {
-            return systemDownloadProviderInfo.packageName;
-        }
-        String[] packagesForUid = pm.getPackagesForUid(sourceUid);
-        if (packagesForUid == null) {
-            return null;
-        }
-        if (packagesForUid.length > 1) {
-            if (callingPackage != null) {
-                for (String packageName : packagesForUid) {
-                    if (packageName.equals(callingPackage)) {
-                        return packageName;
-                    }
-                }
-            }
-            Log.i(TAG, "Multiple packages found for source uid " + sourceUid);
-        }
-        return packagesForUid[0];
-    }
-
-    /**
-     * Utility method to get package information for a given {@link File}
-     */
-    public static PackageInfo getPackageInfo(Context context, File sourceFile, int flags) {
-        String filePath = sourceFile.getAbsolutePath();
-        if (filePath.endsWith(SPLIT_BASE_APK_END_WITH)) {
-            File dir = sourceFile.getParentFile();
-            if (dir.listFiles().length > 1) {
-                // split apks, use file directory to get archive info
-                filePath = dir.getPath();
-            }
-        }
-        try {
-            return context.getPackageManager().getPackageArchiveInfo(filePath, flags);
-        } catch (Exception ignored) {
-            return null;
-        }
-    }
-
-    /**
-     * Is a profile part of a user?
-     *
-     * @param userManager The user manager
-     * @param userHandle The handle of the user
-     * @param profileHandle The handle of the profile
-     *
-     * @return If the profile is part of the user or the profile parent of the user
-     */
-    public static boolean isProfileOfOrSame(UserManager userManager, UserHandle userHandle,
-        UserHandle profileHandle) {
-        if (userHandle.equals(profileHandle)) {
-            return true;
-        }
-        return userManager.getProfileParent(profileHandle) != null
-            && userManager.getProfileParent(profileHandle).equals(userHandle);
-    }
-
-    /**
-     * The class to hold an incoming package's icon and label.
-     * See {@link #getAppSnippet(Context, SessionInfo)},
-     * {@link #getAppSnippet(Context, PackageInfo)},
-     * {@link #getAppSnippet(Context, ApplicationInfo)},
-     * {@link #getAppSnippet(Context, ApplicationInfo, File)}
-     */
-    public static class AppSnippet {
-
-        private CharSequence mLabel;
-        private Drawable mIcon;
-
-        public AppSnippet(CharSequence label, Drawable icon) {
-            mLabel = label;
-            mIcon = icon;
-        }
-
-        public AppSnippet() {
-        }
-
-        public CharSequence getLabel() {
-            return mLabel;
-        }
-
-        public void setLabel(CharSequence mLabel) {
-            this.mLabel = mLabel;
-        }
-
-        public Drawable getIcon() {
-            return mIcon;
-        }
-
-        public void setIcon(Drawable mIcon) {
-            this.mIcon = mIcon;
-        }
-    }
-}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/PackageUtil.kt b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/PackageUtil.kt
new file mode 100644
index 0000000..8d8c2f1
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/PackageUtil.kt
@@ -0,0 +1,440 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.packageinstaller.v2.model
+
+import android.Manifest
+import android.content.Context
+import android.content.pm.ApplicationInfo
+import android.content.pm.PackageInfo
+import android.content.pm.PackageInstaller
+import android.content.pm.PackageManager
+import android.content.res.Resources
+import android.graphics.drawable.BitmapDrawable
+import android.graphics.drawable.Drawable
+import android.net.Uri
+import android.os.Build
+import android.os.Process
+import android.os.UserHandle
+import android.os.UserManager
+import android.util.Log
+import java.io.File
+
+object PackageUtil {
+    private val LOG_TAG = InstallRepository::class.java.simpleName
+    private const val DOWNLOADS_AUTHORITY = "downloads"
+    private const val SPLIT_BASE_APK_END_WITH = "base.apk"
+
+    /**
+     * Determines if the UID belongs to the system downloads provider and returns the
+     * [ApplicationInfo] of the provider
+     *
+     * @param uid UID of the caller
+     * @return [ApplicationInfo] of the provider if a downloads provider exists, it is a
+     * system app, and its UID matches with the passed UID, null otherwise.
+     */
+    private fun getSystemDownloadsProviderInfo(pm: PackageManager, uid: Int): ApplicationInfo? {
+        // Check if there are currently enabled downloads provider on the system.
+        val providerInfo = pm.resolveContentProvider(DOWNLOADS_AUTHORITY, 0)
+            ?: return null
+        val appInfo = providerInfo.applicationInfo
+        return if ((appInfo.flags and ApplicationInfo.FLAG_SYSTEM != 0) && uid == appInfo.uid) {
+            appInfo
+        } else null
+    }
+
+    /**
+     * Get the maximum target sdk for a UID.
+     *
+     * @param context The context to use
+     * @param uid The UID requesting the install/uninstall
+     * @return The maximum target SDK or -1 if the uid does not match any packages.
+     */
+    @JvmStatic
+    fun getMaxTargetSdkVersionForUid(context: Context, uid: Int): Int {
+        val pm = context.packageManager
+        val packages = pm.getPackagesForUid(uid)
+        var targetSdkVersion = -1
+        if (packages != null) {
+            for (packageName in packages) {
+                try {
+                    val info = pm.getApplicationInfo(packageName!!, 0)
+                    targetSdkVersion = maxOf(targetSdkVersion, info.targetSdkVersion)
+                } catch (e: PackageManager.NameNotFoundException) {
+                    // Ignore and try the next package
+                }
+            }
+        }
+        return targetSdkVersion
+    }
+
+    @JvmStatic
+    fun canPackageQuery(context: Context, callingUid: Int, packageUri: Uri): Boolean {
+        val pm = context.packageManager
+        val info = pm.resolveContentProvider(
+            packageUri.authority!!,
+            PackageManager.ComponentInfoFlags.of(0)
+        ) ?: return false
+        val targetPackage = info.packageName
+        val callingPackages = pm.getPackagesForUid(callingUid) ?: return false
+        for (callingPackage in callingPackages) {
+            try {
+                if (pm.canPackageQuery(callingPackage!!, targetPackage)) {
+                    return true
+                }
+            } catch (e: PackageManager.NameNotFoundException) {
+                // no-op
+            }
+        }
+        return false
+    }
+
+    /**
+     * @param context the [Context] object
+     * @param permission the permission name to check
+     * @param callingUid the UID of the caller who's permission is being checked
+     * @return `true` if the callingUid is granted the said permission
+     */
+    @JvmStatic
+    fun isPermissionGranted(context: Context, permission: String, callingUid: Int): Boolean {
+        return (context.checkPermission(permission, -1, callingUid)
+            == PackageManager.PERMISSION_GRANTED)
+    }
+
+    /**
+     * @param pm the [PackageManager] object
+     * @param permission the permission name to check
+     * @param packageName the name of the package who's permission is being checked
+     * @return `true` if the package is granted the said permission
+     */
+    @JvmStatic
+    fun isPermissionGranted(pm: PackageManager, permission: String, packageName: String): Boolean {
+        return pm.checkPermission(permission, packageName) == PackageManager.PERMISSION_GRANTED
+    }
+
+    /**
+     * @param context the [Context] object
+     * @param callingUid the UID of the caller who's permission is being checked
+     * @param originatingUid the UID from where install is being originated. This could be same as
+     * callingUid or it will be the UID of the package performing a session based install
+     * @param isTrustedSource whether install request is coming from a privileged app or an app that
+     * has [Manifest.permission.INSTALL_PACKAGES] permission granted
+     * @return `true` if the package is granted the said permission
+     */
+    @JvmStatic
+    fun isInstallPermissionGrantedOrRequested(
+        context: Context,
+        callingUid: Int,
+        originatingUid: Int,
+        isTrustedSource: Boolean,
+    ): Boolean {
+        val isDocumentsManager =
+            isPermissionGranted(context, Manifest.permission.MANAGE_DOCUMENTS, callingUid)
+        val isSystemDownloadsProvider =
+            getSystemDownloadsProviderInfo(context.packageManager, callingUid) != null
+
+        if (!isTrustedSource && !isSystemDownloadsProvider && !isDocumentsManager) {
+            val targetSdkVersion = getMaxTargetSdkVersionForUid(context, originatingUid)
+            if (targetSdkVersion < 0) {
+                // Invalid originating uid supplied. Abort install.
+                Log.w(LOG_TAG, "Cannot get target sdk version for uid $originatingUid")
+                return false
+            } else if (targetSdkVersion >= Build.VERSION_CODES.O
+                && !isUidRequestingPermission(
+                    context.packageManager, originatingUid,
+                    Manifest.permission.REQUEST_INSTALL_PACKAGES
+                )
+            ) {
+                Log.e(
+                    LOG_TAG, "Requesting uid " + originatingUid + " needs to declare permission "
+                        + Manifest.permission.REQUEST_INSTALL_PACKAGES
+                )
+                return false
+            }
+        }
+        return true
+    }
+
+    /**
+     * @param pm the [PackageManager] object
+     * @param uid the UID of the caller who's permission is being checked
+     * @param permission the permission name to check
+     * @return `true` if the caller is requesting the said permission in its Manifest
+     */
+    private fun isUidRequestingPermission(
+        pm: PackageManager,
+        uid: Int,
+        permission: String,
+    ): Boolean {
+        val packageNames = pm.getPackagesForUid(uid) ?: return false
+        for (packageName in packageNames) {
+            val packageInfo: PackageInfo = try {
+                pm.getPackageInfo(packageName!!, PackageManager.GET_PERMISSIONS)
+            } catch (e: PackageManager.NameNotFoundException) {
+                // Ignore and try the next package
+                continue
+            }
+            if (packageInfo.requestedPermissions != null
+                && listOf(*packageInfo.requestedPermissions!!).contains(permission)
+            ) {
+                return true
+            }
+        }
+        return false
+    }
+
+    /**
+     * @param pi the [PackageInstaller] object to use
+     * @param originatingUid the UID of the package performing a session based install
+     * @param sessionId ID of the install session
+     * @return `true` if the caller is the session owner
+     */
+    @JvmStatic
+    fun isCallerSessionOwner(pi: PackageInstaller, originatingUid: Int, sessionId: Int): Boolean {
+        if (originatingUid == Process.ROOT_UID) {
+            return true
+        }
+        val sessionInfo = pi.getSessionInfo(sessionId) ?: return false
+        val installerUid = sessionInfo.getInstallerUid()
+        return originatingUid == installerUid
+    }
+
+    /**
+     * Generates a stub [PackageInfo] object for the given packageName
+     */
+    @JvmStatic
+    fun generateStubPackageInfo(packageName: String?): PackageInfo {
+        val info = PackageInfo()
+        val aInfo = ApplicationInfo()
+        info.applicationInfo = aInfo
+        info.applicationInfo!!.packageName = packageName
+        info.packageName = info.applicationInfo!!.packageName
+        return info
+    }
+
+    /**
+     * Generates an [AppSnippet] containing an appIcon and appLabel from the
+     * [PackageInstaller.SessionInfo] object
+     */
+    @JvmStatic
+    fun getAppSnippet(context: Context, info: PackageInstaller.SessionInfo): AppSnippet {
+        val pm = context.packageManager
+        val label = info.getAppLabel()
+        val icon = if (info.getAppIcon() != null) BitmapDrawable(
+            context.resources,
+            info.getAppIcon()
+        ) else pm.defaultActivityIcon
+        return AppSnippet(label, icon)
+    }
+
+    /**
+     * Generates an [AppSnippet] containing an appIcon and appLabel from the
+     * [PackageInfo] object
+     */
+    @JvmStatic
+    fun getAppSnippet(context: Context, pkgInfo: PackageInfo): AppSnippet {
+        return pkgInfo.applicationInfo?.let { getAppSnippet(context, it) } ?: run {
+            AppSnippet(pkgInfo.packageName, context.packageManager.defaultActivityIcon)
+        }
+    }
+
+    /**
+     * Generates an [AppSnippet] containing an appIcon and appLabel from the
+     * [ApplicationInfo] object
+     */
+    @JvmStatic
+    fun getAppSnippet(context: Context, appInfo: ApplicationInfo): AppSnippet {
+        val pm = context.packageManager
+        val label = pm.getApplicationLabel(appInfo)
+        val icon = pm.getApplicationIcon(appInfo)
+        return AppSnippet(label, icon)
+    }
+
+    /**
+     * Generates an [AppSnippet] containing an appIcon and appLabel from the
+     * supplied APK file
+     */
+    @JvmStatic
+    fun getAppSnippet(context: Context, pkgInfo: PackageInfo, sourceFile: File): AppSnippet {
+        pkgInfo.applicationInfo?.let {
+            val appInfoFromFile = processAppInfoForFile(it, sourceFile)
+            val label = getAppLabelFromFile(context, appInfoFromFile)
+            val icon = getAppIconFromFile(context, appInfoFromFile)
+            return AppSnippet(label, icon)
+        } ?: run {
+            return AppSnippet(pkgInfo.packageName, context.packageManager.defaultActivityIcon)
+        }
+    }
+
+    /**
+     * Utility method to load application label
+     *
+     * @param context context of package that can load the resources
+     * @param appInfo ApplicationInfo object of package whose resources are to be loaded
+     */
+    private fun getAppLabelFromFile(context: Context, appInfo: ApplicationInfo): CharSequence? {
+        val pm = context.packageManager
+        var label: CharSequence? = null
+        // Try to load the label from the package's resources. If an app has not explicitly
+        // specified any label, just use the package name.
+        if (appInfo.labelRes != 0) {
+            try {
+                label = appInfo.loadLabel(pm)
+            } catch (e: Resources.NotFoundException) {
+            }
+        }
+        if (label == null) {
+            label = if (appInfo.nonLocalizedLabel != null) appInfo.nonLocalizedLabel
+            else appInfo.packageName
+        }
+        return label
+    }
+
+    /**
+     * Utility method to load application icon
+     *
+     * @param context context of package that can load the resources
+     * @param appInfo ApplicationInfo object of package whose resources are to be loaded
+     */
+    private fun getAppIconFromFile(context: Context, appInfo: ApplicationInfo): Drawable? {
+        val pm = context.packageManager
+        var icon: Drawable? = null
+        // Try to load the icon from the package's resources. If an app has not explicitly
+        // specified any resource, just use the default icon for now.
+        try {
+            if (appInfo.icon != 0) {
+                try {
+                    icon = appInfo.loadIcon(pm)
+                } catch (e: Resources.NotFoundException) {
+                }
+            }
+            if (icon == null) {
+                icon = context.packageManager.defaultActivityIcon
+            }
+        } catch (e: OutOfMemoryError) {
+            Log.i(LOG_TAG, "Could not load app icon", e)
+        }
+        return icon
+    }
+
+    private fun processAppInfoForFile(appInfo: ApplicationInfo, sourceFile: File): ApplicationInfo {
+        val archiveFilePath = sourceFile.absolutePath
+        appInfo.publicSourceDir = archiveFilePath
+        if (appInfo.splitNames != null && appInfo.splitSourceDirs == null) {
+            val files = sourceFile.parentFile?.listFiles()
+            val splits = appInfo.splitNames!!
+                .mapNotNull { findFilePath(files, "$it.apk") }
+                .toTypedArray()
+
+            appInfo.splitSourceDirs = splits
+            appInfo.splitPublicSourceDirs = splits
+        }
+        return appInfo
+    }
+
+    private fun findFilePath(files: Array<File>?, postfix: String): String? {
+        files?.let {
+            for (file in it) {
+                val path = file.absolutePath
+                if (path.endsWith(postfix)) {
+                    return path
+                }
+            }
+        }
+        return null
+    }
+
+    /**
+     * @return the packageName corresponding to a UID.
+     */
+    @JvmStatic
+    fun getPackageNameForUid(context: Context, sourceUid: Int, callingPackage: String?): String? {
+        if (sourceUid == Process.INVALID_UID) {
+            return null
+        }
+        // If the sourceUid belongs to the system downloads provider, we explicitly return the
+        // name of the Download Manager package. This is because its UID is shared with multiple
+        // packages, resulting in uncertainty about which package will end up first in the list
+        // of packages associated with this UID
+        val pm = context.packageManager
+        val systemDownloadProviderInfo = getSystemDownloadsProviderInfo(pm, sourceUid)
+        if (systemDownloadProviderInfo != null) {
+            return systemDownloadProviderInfo.packageName
+        }
+        val packagesForUid = pm.getPackagesForUid(sourceUid) ?: return null
+        if (packagesForUid.size > 1) {
+            if (callingPackage != null) {
+                for (packageName in packagesForUid) {
+                    if (packageName == callingPackage) {
+                        return packageName
+                    }
+                }
+            }
+            Log.i(LOG_TAG, "Multiple packages found for source uid $sourceUid")
+        }
+        return packagesForUid[0]
+    }
+
+    /**
+     * Utility method to get package information for a given [File]
+     */
+    @JvmStatic
+    fun getPackageInfo(context: Context, sourceFile: File, flags: Int): PackageInfo? {
+        var filePath = sourceFile.absolutePath
+        if (filePath.endsWith(SPLIT_BASE_APK_END_WITH)) {
+            val dir = sourceFile.parentFile
+            if ((dir?.listFiles()?.size ?: 0) > 1) {
+                // split apks, use file directory to get archive info
+                filePath = dir.path
+            }
+        }
+        return try {
+            context.packageManager.getPackageArchiveInfo(filePath, flags)
+        } catch (ignored: Exception) {
+            null
+        }
+    }
+
+    /**
+     * Is a profile part of a user?
+     *
+     * @param userManager The user manager
+     * @param userHandle The handle of the user
+     * @param profileHandle The handle of the profile
+     *
+     * @return If the profile is part of the user or the profile parent of the user
+     */
+    @JvmStatic
+    fun isProfileOfOrSame(
+        userManager: UserManager,
+        userHandle: UserHandle,
+        profileHandle: UserHandle?,
+    ): Boolean {
+        if (profileHandle == null) {
+            return false
+        }
+        return if (userHandle == profileHandle) {
+            true
+        } else userManager.getProfileParent(profileHandle) != null
+            && userManager.getProfileParent(profileHandle) == userHandle
+    }
+
+    /**
+     * The class to hold an incoming package's icon and label.
+     * See [getAppSnippet]
+     */
+    data class AppSnippet(var label: CharSequence?, var icon: Drawable?)
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/SessionStager.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/SessionStager.java
deleted file mode 100644
index a2c81f1..0000000
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/SessionStager.java
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.packageinstaller.v2.model;
-
-import static android.content.res.AssetFileDescriptor.UNKNOWN_LENGTH;
-
-import android.content.Context;
-import android.content.pm.PackageInstaller;
-import android.content.pm.PackageInstaller.SessionInfo;
-import android.content.res.AssetFileDescriptor;
-import android.net.Uri;
-import android.os.AsyncTask;
-import android.util.Log;
-import androidx.lifecycle.MutableLiveData;
-import com.android.packageinstaller.v2.model.InstallRepository.SessionStageListener;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-
-public class SessionStager extends AsyncTask<Void, Integer, SessionInfo> {
-
-    private static final String TAG = SessionStager.class.getSimpleName();
-    private final Context mContext;
-    private final Uri mUri;
-    private final int mStagedSessionId;
-    private final MutableLiveData<Integer> mProgressLiveData = new MutableLiveData<>(0);
-    private final SessionStageListener mListener;
-
-    SessionStager(Context context, Uri uri, int stagedSessionId, SessionStageListener listener) {
-        mContext = context;
-        mUri = uri;
-        mStagedSessionId = stagedSessionId;
-        mListener = listener;
-    }
-
-    @Override
-    protected PackageInstaller.SessionInfo doInBackground(Void... params) {
-        PackageInstaller pi = mContext.getPackageManager().getPackageInstaller();
-        try (PackageInstaller.Session session = pi.openSession(mStagedSessionId);
-            InputStream in = mContext.getContentResolver().openInputStream(mUri)) {
-            session.setStagingProgress(0);
-
-            if (in == null) {
-                return null;
-            }
-            final long sizeBytes = getContentSizeBytes();
-            mProgressLiveData.postValue(sizeBytes > 0 ? 0 : -1);
-
-            long totalRead = 0;
-            try (OutputStream out = session.openWrite("PackageInstaller", 0, sizeBytes)) {
-                byte[] buffer = new byte[1024 * 1024];
-                while (true) {
-                    int numRead = in.read(buffer);
-
-                    if (numRead == -1) {
-                        session.fsync(out);
-                        break;
-                    }
-
-                    if (isCancelled()) {
-                        break;
-                    }
-
-                    out.write(buffer, 0, numRead);
-                    if (sizeBytes > 0) {
-                        totalRead += numRead;
-                        float fraction = ((float) totalRead / (float) sizeBytes);
-                        session.setStagingProgress(fraction);
-                        publishProgress((int) (fraction * 100.0));
-                    }
-                }
-            }
-            return pi.getSessionInfo(mStagedSessionId);
-        } catch (IOException | SecurityException | IllegalStateException
-                 | IllegalArgumentException e) {
-            Log.w(TAG, "Error staging apk from content URI", e);
-            return null;
-        }
-    }
-
-    private long getContentSizeBytes() {
-        try (AssetFileDescriptor afd = mContext.getContentResolver()
-            .openAssetFileDescriptor(mUri, "r")) {
-            return afd != null ? afd.getLength() : UNKNOWN_LENGTH;
-        } catch (IOException e) {
-            Log.w(TAG, "Failed to open asset file descriptor", e);
-            return UNKNOWN_LENGTH;
-        }
-    }
-
-    public MutableLiveData<Integer> getProgress() {
-        return mProgressLiveData;
-    }
-
-    @Override
-    protected void onProgressUpdate(Integer... progress) {
-        if (progress != null && progress.length > 0) {
-            mProgressLiveData.setValue(progress[0]);
-        }
-    }
-
-    @Override
-    protected void onPostExecute(SessionInfo sessionInfo) {
-        if (sessionInfo == null || !sessionInfo.isActive()
-            || sessionInfo.getResolvedBaseApkPath() == null) {
-            Log.w(TAG, "Session info is invalid: " + sessionInfo);
-            mListener.onStagingFailure();
-            return;
-        }
-        mListener.onStagingSuccess(sessionInfo);
-    }
-}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/SessionStager.kt b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/SessionStager.kt
new file mode 100644
index 0000000..c9bfa17
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/SessionStager.kt
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.packageinstaller.v2.model
+
+import android.content.Context
+import android.content.pm.PackageInstaller
+import android.content.res.AssetFileDescriptor
+import android.net.Uri
+import android.util.Log
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import java.io.IOException
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.withContext
+
+class SessionStager internal constructor(
+    private val context: Context,
+    private val uri: Uri,
+    private val stagedSessionId: Int
+) {
+
+    companion object {
+        private val LOG_TAG = SessionStager::class.java.simpleName
+    }
+
+    private val _progress = MutableLiveData(0)
+    val progress: LiveData<Int>
+        get() = _progress
+
+    suspend fun execute(): Boolean = withContext(Dispatchers.IO) {
+        val pi: PackageInstaller = context.packageManager.packageInstaller
+        var sessionInfo: PackageInstaller.SessionInfo?
+        try {
+            val session = pi.openSession(stagedSessionId)
+            context.contentResolver.openInputStream(uri).use { instream ->
+                session.setStagingProgress(0f)
+
+                if (instream == null) {
+                    return@withContext false
+                }
+
+                val sizeBytes = getContentSizeBytes()
+                publishProgress(if (sizeBytes > 0) 0 else -1)
+
+                var totalRead: Long = 0
+                session.openWrite("PackageInstaller", 0, sizeBytes).use { out ->
+                    val buffer = ByteArray(1024 * 1024)
+                    while (true) {
+                        val numRead = instream.read(buffer)
+                        if (numRead == -1) {
+                            session.fsync(out)
+                            break
+                        }
+                        out.write(buffer, 0, numRead)
+
+                        if (sizeBytes > 0) {
+                            totalRead += numRead.toLong()
+                            val fraction = totalRead.toFloat() / sizeBytes.toFloat()
+                            session.setStagingProgress(fraction)
+                            publishProgress((fraction * 100.0).toInt())
+                        }
+                    }
+                }
+                sessionInfo = pi.getSessionInfo(stagedSessionId)
+            }
+        } catch (e: Exception) {
+            Log.w(LOG_TAG, "Error staging apk from content URI", e)
+            sessionInfo = null
+        }
+
+        return@withContext if (sessionInfo == null
+            || !sessionInfo?.isActive!!
+            || sessionInfo?.resolvedBaseApkPath == null
+        ) {
+            Log.w(LOG_TAG, "Session info is invalid: $sessionInfo")
+            false
+        } else {
+            true
+        }
+    }
+
+    private fun getContentSizeBytes(): Long {
+        return try {
+            context.contentResolver
+                .openAssetFileDescriptor(uri, "r")
+                .use { afd -> afd?.length ?: AssetFileDescriptor.UNKNOWN_LENGTH }
+        } catch (e: IOException) {
+            Log.w(LOG_TAG, "Failed to open asset file descriptor", e)
+            AssetFileDescriptor.UNKNOWN_LENGTH
+        }
+    }
+
+    private suspend fun publishProgress(progressValue: Int) = withContext(Dispatchers.Main) {
+        _progress.value = progressValue
+    }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/UninstallRepository.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/UninstallRepository.java
deleted file mode 100644
index a07c532..0000000
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/UninstallRepository.java
+++ /dev/null
@@ -1,716 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.packageinstaller.v2.model;
-
-import static android.app.AppOpsManager.MODE_ALLOWED;
-import static android.os.UserManager.USER_TYPE_PROFILE_CLONE;
-import static android.os.UserManager.USER_TYPE_PROFILE_MANAGED;
-import static com.android.packageinstaller.v2.model.PackageUtil.getMaxTargetSdkVersionForUid;
-import static com.android.packageinstaller.v2.model.PackageUtil.getPackageNameForUid;
-import static com.android.packageinstaller.v2.model.PackageUtil.isPermissionGranted;
-import static com.android.packageinstaller.v2.model.PackageUtil.isProfileOfOrSame;
-import static com.android.packageinstaller.v2.model.uninstallstagedata.UninstallAborted.ABORT_REASON_APP_UNAVAILABLE;
-import static com.android.packageinstaller.v2.model.uninstallstagedata.UninstallAborted.ABORT_REASON_GENERIC_ERROR;
-import static com.android.packageinstaller.v2.model.uninstallstagedata.UninstallAborted.ABORT_REASON_USER_NOT_ALLOWED;
-
-import android.Manifest;
-import android.app.Activity;
-import android.app.AppOpsManager;
-import android.app.Notification;
-import android.app.NotificationChannel;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.app.admin.DevicePolicyManager;
-import android.app.usage.StorageStats;
-import android.app.usage.StorageStatsManager;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageInstaller;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.UninstallCompleteCallback;
-import android.content.pm.VersionedPackage;
-import android.graphics.drawable.Icon;
-import android.net.Uri;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.Process;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.provider.Settings;
-import android.util.Log;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.lifecycle.MutableLiveData;
-import com.android.packageinstaller.R;
-import com.android.packageinstaller.common.EventResultPersister;
-import com.android.packageinstaller.common.UninstallEventReceiver;
-import com.android.packageinstaller.v2.model.uninstallstagedata.UninstallAborted;
-import com.android.packageinstaller.v2.model.uninstallstagedata.UninstallFailed;
-import com.android.packageinstaller.v2.model.uninstallstagedata.UninstallReady;
-import com.android.packageinstaller.v2.model.uninstallstagedata.UninstallStage;
-import com.android.packageinstaller.v2.model.uninstallstagedata.UninstallSuccess;
-import com.android.packageinstaller.v2.model.uninstallstagedata.UninstallUninstalling;
-import com.android.packageinstaller.v2.model.uninstallstagedata.UninstallUserActionRequired;
-import java.io.IOException;
-import java.util.List;
-
-public class UninstallRepository {
-
-    private static final String TAG = UninstallRepository.class.getSimpleName();
-    private static final String UNINSTALL_FAILURE_CHANNEL = "uninstall_failure";
-    private static final String BROADCAST_ACTION =
-        "com.android.packageinstaller.ACTION_UNINSTALL_COMMIT";
-
-    private static final String EXTRA_UNINSTALL_ID =
-        "com.android.packageinstaller.extra.UNINSTALL_ID";
-    private static final String EXTRA_APP_LABEL =
-        "com.android.packageinstaller.extra.APP_LABEL";
-    private static final String EXTRA_IS_CLONE_APP =
-        "com.android.packageinstaller.extra.IS_CLONE_APP";
-    private static final String EXTRA_PACKAGE_NAME =
-        "com.android.packageinstaller.extra.EXTRA_PACKAGE_NAME";
-
-    private final Context mContext;
-    private final AppOpsManager mAppOpsManager;
-    private final PackageManager mPackageManager;
-    private final UserManager mUserManager;
-    private final NotificationManager mNotificationManager;
-    private final MutableLiveData<UninstallStage> mUninstallResult = new MutableLiveData<>();
-    public UserHandle mUninstalledUser;
-    public UninstallCompleteCallback mCallback;
-    private ApplicationInfo mTargetAppInfo;
-    private ActivityInfo mTargetActivityInfo;
-    private Intent mIntent;
-    private CharSequence mTargetAppLabel;
-    private String mTargetPackageName;
-    private String mCallingActivity;
-    private boolean mUninstallFromAllUsers;
-    private boolean mIsClonedApp;
-    private int mUninstallId;
-
-    public UninstallRepository(Context context) {
-        mContext = context;
-        mAppOpsManager = context.getSystemService(AppOpsManager.class);
-        mPackageManager = context.getPackageManager();
-        mUserManager = context.getSystemService(UserManager.class);
-        mNotificationManager = context.getSystemService(NotificationManager.class);
-    }
-
-    public UninstallStage performPreUninstallChecks(Intent intent, CallerInfo callerInfo) {
-        mIntent = intent;
-
-        int callingUid = callerInfo.getUid();
-        mCallingActivity = callerInfo.getActivityName();
-
-        if (callingUid == Process.INVALID_UID) {
-            Log.e(TAG, "Could not determine the launching uid.");
-            return new UninstallAborted(ABORT_REASON_GENERIC_ERROR);
-            // TODO: should we give any indication to the user?
-        }
-
-        String callingPackage = getPackageNameForUid(mContext, callingUid, null);
-        if (callingPackage == null) {
-            Log.e(TAG, "Package not found for originating uid " + callingUid);
-            return new UninstallAborted(ABORT_REASON_GENERIC_ERROR);
-        } else {
-            if (mAppOpsManager.noteOpNoThrow(
-                AppOpsManager.OPSTR_REQUEST_DELETE_PACKAGES, callingUid, callingPackage)
-                != MODE_ALLOWED) {
-                Log.e(TAG, "Install from uid " + callingUid + " disallowed by AppOps");
-                return new UninstallAborted(ABORT_REASON_GENERIC_ERROR);
-            }
-        }
-
-        if (getMaxTargetSdkVersionForUid(mContext, callingUid) >= Build.VERSION_CODES.P
-            && !isPermissionGranted(mContext, Manifest.permission.REQUEST_DELETE_PACKAGES,
-            callingUid)
-            && !isPermissionGranted(mContext, Manifest.permission.DELETE_PACKAGES, callingUid)) {
-            Log.e(TAG, "Uid " + callingUid + " does not have "
-                + Manifest.permission.REQUEST_DELETE_PACKAGES + " or "
-                + Manifest.permission.DELETE_PACKAGES);
-
-            return new UninstallAborted(ABORT_REASON_GENERIC_ERROR);
-        }
-
-        // Get intent information.
-        // We expect an intent with URI of the form package:<packageName>#<className>
-        // className is optional; if specified, it is the activity the user chose to uninstall
-        final Uri packageUri = intent.getData();
-        if (packageUri == null) {
-            Log.e(TAG, "No package URI in intent");
-            return new UninstallAborted(ABORT_REASON_APP_UNAVAILABLE);
-        }
-        mTargetPackageName = packageUri.getEncodedSchemeSpecificPart();
-        if (mTargetPackageName == null) {
-            Log.e(TAG, "Invalid package name in URI: " + packageUri);
-            return new UninstallAborted(ABORT_REASON_APP_UNAVAILABLE);
-        }
-
-        mUninstallFromAllUsers = intent.getBooleanExtra(Intent.EXTRA_UNINSTALL_ALL_USERS,
-            false);
-        if (mUninstallFromAllUsers && !mUserManager.isAdminUser()) {
-            Log.e(TAG, "Only admin user can request uninstall for all users");
-            return new UninstallAborted(ABORT_REASON_USER_NOT_ALLOWED);
-        }
-
-        mUninstalledUser = intent.getParcelableExtra(Intent.EXTRA_USER, UserHandle.class);
-        if (mUninstalledUser == null) {
-            mUninstalledUser = Process.myUserHandle();
-        } else {
-            List<UserHandle> profiles = mUserManager.getUserProfiles();
-            if (!profiles.contains(mUninstalledUser)) {
-                Log.e(TAG, "User " + Process.myUserHandle() + " can't request uninstall "
-                    + "for user " + mUninstalledUser);
-                return new UninstallAborted(ABORT_REASON_USER_NOT_ALLOWED);
-            }
-        }
-
-        mCallback = intent.getParcelableExtra(PackageInstaller.EXTRA_CALLBACK,
-            PackageManager.UninstallCompleteCallback.class);
-
-        try {
-            mTargetAppInfo = mPackageManager.getApplicationInfo(mTargetPackageName,
-                PackageManager.ApplicationInfoFlags.of(PackageManager.MATCH_ANY_USER));
-        } catch (PackageManager.NameNotFoundException e) {
-            Log.e(TAG, "Unable to get packageName");
-        }
-
-        if (mTargetAppInfo == null) {
-            Log.e(TAG, "Invalid packageName: " + mTargetPackageName);
-            return new UninstallAborted(ABORT_REASON_APP_UNAVAILABLE);
-        }
-
-        // The class name may have been specified (e.g. when deleting an app from all apps)
-        final String className = packageUri.getFragment();
-        if (className != null) {
-            try {
-                mTargetActivityInfo = mPackageManager.getActivityInfo(
-                    new ComponentName(mTargetPackageName, className),
-                    PackageManager.ComponentInfoFlags.of(0));
-            } catch (PackageManager.NameNotFoundException e) {
-                Log.e(TAG, "Unable to get className");
-                // Continue as the ActivityInfo isn't critical.
-            }
-        }
-
-        return new UninstallReady();
-    }
-
-    public UninstallStage generateUninstallDetails() {
-        UninstallUserActionRequired.Builder uarBuilder = new UninstallUserActionRequired.Builder();
-        StringBuilder messageBuilder = new StringBuilder();
-
-        mTargetAppLabel = mTargetAppInfo.loadSafeLabel(mPackageManager);
-
-        // If the Activity label differs from the App label, then make sure the user
-        // knows the Activity belongs to the App being uninstalled.
-        if (mTargetActivityInfo != null) {
-            final CharSequence activityLabel = mTargetActivityInfo.loadSafeLabel(mPackageManager);
-            if (CharSequence.compare(activityLabel, mTargetAppLabel) != 0) {
-                messageBuilder.append(
-                    mContext.getString(R.string.uninstall_activity_text, activityLabel));
-                messageBuilder.append(" ").append(mTargetAppLabel).append(".\n\n");
-            }
-        }
-
-        final boolean isUpdate =
-            (mTargetAppInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0;
-        final UserHandle myUserHandle = Process.myUserHandle();
-        boolean isSingleUser = isSingleUser();
-
-        if (isUpdate) {
-            messageBuilder.append(mContext.getString(
-                isSingleUser ? R.string.uninstall_update_text :
-                    R.string.uninstall_update_text_multiuser));
-        } else if (mUninstallFromAllUsers && !isSingleUser) {
-            messageBuilder.append(mContext.getString(
-                R.string.uninstall_application_text_all_users));
-        } else if (!mUninstalledUser.equals(myUserHandle)) {
-            // Uninstalling user is issuing uninstall for another user
-            UserManager customUserManager = mContext.createContextAsUser(mUninstalledUser, 0)
-                .getSystemService(UserManager.class);
-            String userName = customUserManager.getUserName();
-
-            String uninstalledUserType = getUninstalledUserType(myUserHandle, mUninstalledUser);
-            String messageString;
-            if (USER_TYPE_PROFILE_MANAGED.equals(uninstalledUserType)) {
-                messageString = mContext.getString(
-                    R.string.uninstall_application_text_current_user_work_profile, userName);
-            } else if (USER_TYPE_PROFILE_CLONE.equals(uninstalledUserType)) {
-                mIsClonedApp = true;
-                messageString = mContext.getString(
-                    R.string.uninstall_application_text_current_user_clone_profile);
-            } else {
-                messageString = mContext.getString(
-                    R.string.uninstall_application_text_user, userName);
-            }
-            messageBuilder.append(messageString);
-        } else if (isCloneProfile(mUninstalledUser)) {
-            mIsClonedApp = true;
-            messageBuilder.append(mContext.getString(
-                R.string.uninstall_application_text_current_user_clone_profile));
-        } else if (myUserHandle.equals(UserHandle.SYSTEM)
-            && hasClonedInstance(mTargetAppInfo.packageName)) {
-            messageBuilder.append(mContext.getString(
-                R.string.uninstall_application_text_with_clone_instance, mTargetAppLabel));
-        } else {
-            messageBuilder.append(mContext.getString(R.string.uninstall_application_text));
-        }
-
-        uarBuilder.setMessage(messageBuilder.toString());
-
-        if (mIsClonedApp) {
-            uarBuilder.setTitle(mContext.getString(R.string.cloned_app_label, mTargetAppLabel));
-        } else {
-            uarBuilder.setTitle(mTargetAppLabel.toString());
-        }
-
-        boolean suggestToKeepAppData = false;
-        try {
-            PackageInfo pkgInfo = mPackageManager.getPackageInfo(mTargetPackageName, 0);
-            suggestToKeepAppData =
-                pkgInfo.applicationInfo != null && pkgInfo.applicationInfo.hasFragileUserData();
-        } catch (PackageManager.NameNotFoundException e) {
-            Log.e(TAG, "Cannot check hasFragileUserData for " + mTargetPackageName, e);
-        }
-
-        long appDataSize = 0;
-        if (suggestToKeepAppData) {
-            appDataSize = getAppDataSize(mTargetPackageName,
-                mUninstallFromAllUsers ? null : mUninstalledUser);
-        }
-        uarBuilder.setAppDataSize(appDataSize);
-
-        return uarBuilder.build();
-    }
-
-    /**
-     * Returns whether there is only one "full" user on this device.
-     *
-     * <p><b>Note:</b> on devices that use {@link android.os.UserManager#isHeadlessSystemUserMode()
-     * headless system user mode}, the system user is not "full", so it's not be considered in the
-     * calculation.</p>
-     */
-    private boolean isSingleUser() {
-        final int userCount = mUserManager.getUserCount();
-        return userCount == 1 || (UserManager.isHeadlessSystemUserMode() && userCount == 2);
-    }
-
-    /**
-     * Returns the type of the user from where an app is being uninstalled. We are concerned with
-     * only USER_TYPE_PROFILE_MANAGED and USER_TYPE_PROFILE_CLONE and whether the user and profile
-     * belong to the same profile group.
-     */
-    @Nullable
-    private String getUninstalledUserType(UserHandle myUserHandle,
-        UserHandle uninstalledUserHandle) {
-        if (!mUserManager.isSameProfileGroup(myUserHandle, uninstalledUserHandle)) {
-            return null;
-        }
-
-        UserManager customUserManager = mContext.createContextAsUser(uninstalledUserHandle, 0)
-            .getSystemService(UserManager.class);
-        String[] userTypes = {USER_TYPE_PROFILE_MANAGED, USER_TYPE_PROFILE_CLONE};
-        for (String userType : userTypes) {
-            if (customUserManager.isUserOfType(userType)) {
-                return userType;
-            }
-        }
-        return null;
-    }
-
-    private boolean hasClonedInstance(String packageName) {
-        // Check if clone user is present on the device.
-        UserHandle cloneUser = null;
-        List<UserHandle> profiles = mUserManager.getUserProfiles();
-        for (UserHandle userHandle : profiles) {
-            if (!userHandle.equals(UserHandle.SYSTEM) && isCloneProfile(userHandle)) {
-                cloneUser = userHandle;
-                break;
-            }
-        }
-        // Check if another instance of given package exists in clone user profile.
-        try {
-            return cloneUser != null
-                && mPackageManager.getPackageUidAsUser(packageName,
-                PackageManager.PackageInfoFlags.of(0), cloneUser.getIdentifier()) > 0;
-        } catch (PackageManager.NameNotFoundException e) {
-            return false;
-        }
-    }
-
-    private boolean isCloneProfile(UserHandle userHandle) {
-        UserManager customUserManager = mContext.createContextAsUser(userHandle, 0)
-            .getSystemService(UserManager.class);
-        return customUserManager.isUserOfType(UserManager.USER_TYPE_PROFILE_CLONE);
-    }
-
-    /**
-     * Get number of bytes of the app data of the package.
-     *
-     * @param pkg The package that might have app data.
-     * @param user The user the package belongs to or {@code null} if files of all users should
-     *     be counted.
-     * @return The number of bytes.
-     */
-    private long getAppDataSize(@NonNull String pkg, @Nullable UserHandle user) {
-        if (user != null) {
-            return getAppDataSizeForUser(pkg, user);
-        }
-        // We are uninstalling from all users. Get cumulative app data size for all users.
-        List<UserHandle> userHandles = mUserManager.getUserHandles(true);
-        long totalAppDataSize = 0;
-        int numUsers = userHandles.size();
-        for (int i = 0; i < numUsers; i++) {
-            totalAppDataSize += getAppDataSizeForUser(pkg, userHandles.get(i));
-        }
-        return totalAppDataSize;
-    }
-
-    /**
-     * Get number of bytes of the app data of the package.
-     *
-     * @param pkg The package that might have app data.
-     * @param user The user the package belongs to
-     * @return The number of bytes.
-     */
-    private long getAppDataSizeForUser(@NonNull String pkg, @NonNull UserHandle user) {
-        StorageStatsManager storageStatsManager =
-            mContext.getSystemService(StorageStatsManager.class);
-        try {
-            StorageStats stats = storageStatsManager.queryStatsForPackage(
-                mPackageManager.getApplicationInfo(pkg, 0).storageUuid, pkg, user);
-            return stats.getDataBytes();
-        } catch (PackageManager.NameNotFoundException | IOException | SecurityException e) {
-            Log.e(TAG, "Cannot determine amount of app data for " + pkg, e);
-        }
-        return 0;
-    }
-
-    public void initiateUninstall(boolean keepData) {
-        // Get an uninstallId to track results and show a notification on non-TV devices.
-        try {
-            mUninstallId = UninstallEventReceiver.addObserver(mContext,
-                EventResultPersister.GENERATE_NEW_ID, this::handleUninstallResult);
-        } catch (EventResultPersister.OutOfIdsException e) {
-            Log.e(TAG, "Failed to start uninstall", e);
-            handleUninstallResult(PackageInstaller.STATUS_FAILURE,
-                PackageManager.DELETE_FAILED_INTERNAL_ERROR, null, 0);
-            return;
-        }
-
-        // TODO: Check with UX whether to show UninstallUninstalling dialog / notification?
-        mUninstallResult.setValue(new UninstallUninstalling(mTargetAppLabel, mIsClonedApp));
-
-        Bundle uninstallData = new Bundle();
-        uninstallData.putInt(EXTRA_UNINSTALL_ID, mUninstallId);
-        uninstallData.putString(EXTRA_PACKAGE_NAME, mTargetPackageName);
-        uninstallData.putBoolean(Intent.EXTRA_UNINSTALL_ALL_USERS, mUninstallFromAllUsers);
-        uninstallData.putCharSequence(EXTRA_APP_LABEL, mTargetAppLabel);
-        uninstallData.putBoolean(EXTRA_IS_CLONE_APP, mIsClonedApp);
-        Log.i(TAG, "Uninstalling extras = " + uninstallData);
-
-        // Get a PendingIntent for result broadcast and issue an uninstall request
-        Intent broadcastIntent = new Intent(BROADCAST_ACTION);
-        broadcastIntent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
-        broadcastIntent.putExtra(EventResultPersister.EXTRA_ID, mUninstallId);
-        broadcastIntent.setPackage(mContext.getPackageName());
-
-        PendingIntent pendingIntent =
-            PendingIntent.getBroadcast(mContext, mUninstallId, broadcastIntent,
-                PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);
-
-        if (!startUninstall(mTargetPackageName, mUninstalledUser, pendingIntent,
-            mUninstallFromAllUsers, keepData)) {
-            handleUninstallResult(PackageInstaller.STATUS_FAILURE,
-                PackageManager.DELETE_FAILED_INTERNAL_ERROR, null, 0);
-        }
-    }
-
-    private void handleUninstallResult(int status, int legacyStatus, @Nullable String message,
-        int serviceId) {
-        if (mCallback != null) {
-            // The caller will be informed about the result via a callback
-            mCallback.onUninstallComplete(mTargetPackageName, legacyStatus, message);
-
-            // Since the caller already received the results, just finish the app at this point
-            mUninstallResult.setValue(null);
-            return;
-        }
-
-        boolean returnResult = mIntent.getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false);
-        if (returnResult || mCallingActivity != null) {
-            Intent intent = new Intent();
-            intent.putExtra(Intent.EXTRA_INSTALL_RESULT, legacyStatus);
-
-            if (status == PackageInstaller.STATUS_SUCCESS) {
-                UninstallSuccess.Builder successBuilder = new UninstallSuccess.Builder()
-                    .setResultIntent(intent)
-                    .setActivityResultCode(Activity.RESULT_OK);
-                mUninstallResult.setValue(successBuilder.build());
-            } else {
-                UninstallFailed.Builder failedBuilder = new UninstallFailed.Builder(true)
-                    .setResultIntent(intent)
-                    .setActivityResultCode(Activity.RESULT_FIRST_USER);
-                mUninstallResult.setValue(failedBuilder.build());
-            }
-            return;
-        }
-
-        // Caller did not want the result back. So, we either show a Toast, or a Notification.
-        if (status == PackageInstaller.STATUS_SUCCESS) {
-            UninstallSuccess.Builder successBuilder = new UninstallSuccess.Builder()
-                .setActivityResultCode(legacyStatus)
-                .setMessage(mIsClonedApp
-                    ? mContext.getString(R.string.uninstall_done_clone_app, mTargetAppLabel)
-                    : mContext.getString(R.string.uninstall_done_app, mTargetAppLabel));
-            mUninstallResult.setValue(successBuilder.build());
-        } else {
-            UninstallFailed.Builder failedBuilder = new UninstallFailed.Builder(false);
-            Notification.Builder uninstallFailedNotification = null;
-
-            NotificationChannel uninstallFailureChannel = new NotificationChannel(
-                UNINSTALL_FAILURE_CHANNEL,
-                mContext.getString(R.string.uninstall_failure_notification_channel),
-                NotificationManager.IMPORTANCE_DEFAULT);
-            mNotificationManager.createNotificationChannel(uninstallFailureChannel);
-
-            uninstallFailedNotification = new Notification.Builder(mContext,
-                UNINSTALL_FAILURE_CHANNEL);
-
-            UserHandle myUserHandle = Process.myUserHandle();
-            switch (legacyStatus) {
-                case PackageManager.DELETE_FAILED_DEVICE_POLICY_MANAGER -> {
-                    // Find out if the package is an active admin for some non-current user.
-                    UserHandle otherBlockingUserHandle =
-                        findUserOfDeviceAdmin(myUserHandle, mTargetPackageName);
-
-                    if (otherBlockingUserHandle == null) {
-                        Log.d(TAG, "Uninstall failed because " + mTargetPackageName
-                            + " is a device admin");
-
-                        addDeviceManagerButton(mContext, uninstallFailedNotification);
-                        setBigText(uninstallFailedNotification, mContext.getString(
-                            R.string.uninstall_failed_device_policy_manager));
-                    } else {
-                        Log.d(TAG, "Uninstall failed because " + mTargetPackageName
-                            + " is a device admin of user " + otherBlockingUserHandle);
-
-                        String userName =
-                            mContext.createContextAsUser(otherBlockingUserHandle, 0)
-                                .getSystemService(UserManager.class).getUserName();
-                        setBigText(uninstallFailedNotification, String.format(
-                            mContext.getString(
-                                R.string.uninstall_failed_device_policy_manager_of_user),
-                            userName));
-                    }
-                }
-                case PackageManager.DELETE_FAILED_OWNER_BLOCKED -> {
-                    UserHandle otherBlockingUserHandle = findBlockingUser(mTargetPackageName);
-                    boolean isProfileOfOrSame = isProfileOfOrSame(mUserManager, myUserHandle,
-                        otherBlockingUserHandle);
-
-                    if (isProfileOfOrSame) {
-                        addDeviceManagerButton(mContext, uninstallFailedNotification);
-                    } else {
-                        addManageUsersButton(mContext, uninstallFailedNotification);
-                    }
-
-                    String bigText = null;
-                    if (otherBlockingUserHandle == null) {
-                        Log.d(TAG, "Uninstall failed for " + mTargetPackageName +
-                            " with code " + status + " no blocking user");
-                    } else if (otherBlockingUserHandle == UserHandle.SYSTEM) {
-                        bigText = mContext.getString(
-                            R.string.uninstall_blocked_device_owner);
-                    } else {
-                        bigText = mContext.getString(mUninstallFromAllUsers ?
-                            R.string.uninstall_all_blocked_profile_owner
-                            : R.string.uninstall_blocked_profile_owner);
-                    }
-                    if (bigText != null) {
-                        setBigText(uninstallFailedNotification, bigText);
-                    }
-                }
-                default -> {
-                    Log.d(TAG, "Uninstall blocked for " + mTargetPackageName
-                        + " with legacy code " + legacyStatus);
-                }
-            }
-
-            uninstallFailedNotification.setContentTitle(
-                mContext.getString(R.string.uninstall_failed_app, mTargetAppLabel));
-            uninstallFailedNotification.setOngoing(false);
-            uninstallFailedNotification.setSmallIcon(R.drawable.ic_error);
-            failedBuilder.setUninstallNotification(mUninstallId,
-                uninstallFailedNotification.build());
-
-            mUninstallResult.setValue(failedBuilder.build());
-        }
-    }
-
-    /**
-     * @param myUserHandle {@link UserHandle} of the current user.
-     * @param packageName Name of the package being uninstalled.
-     * @return the {@link UserHandle} of the user in which a package is a device admin.
-     */
-    @Nullable
-    private UserHandle findUserOfDeviceAdmin(UserHandle myUserHandle, String packageName) {
-        for (UserHandle otherUserHandle : mUserManager.getUserHandles(true)) {
-            // We only catch the case when the user in question is neither the
-            // current user nor its profile.
-            if (isProfileOfOrSame(mUserManager, myUserHandle, otherUserHandle)) {
-                continue;
-            }
-            DevicePolicyManager dpm = mContext.createContextAsUser(otherUserHandle, 0)
-                    .getSystemService(DevicePolicyManager.class);
-            if (dpm.packageHasActiveAdmins(packageName)) {
-                return otherUserHandle;
-            }
-        }
-        return null;
-    }
-
-    /**
-     *
-     * @param packageName Name of the package being uninstalled.
-     * @return {@link UserHandle} of the user in which a package is blocked from being uninstalled.
-     */
-    @Nullable
-    private UserHandle findBlockingUser(String packageName) {
-        for (UserHandle otherUserHandle : mUserManager.getUserHandles(true)) {
-            // TODO (b/307399586): Add a negation when the logic of the method
-            //  is fixed
-            if (mPackageManager.canUserUninstall(packageName, otherUserHandle)) {
-                return otherUserHandle;
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Set big text for the notification.
-     *
-     * @param builder The builder of the notification
-     * @param text The text to set.
-     */
-    private void setBigText(@NonNull Notification.Builder builder,
-        @NonNull CharSequence text) {
-        builder.setStyle(new Notification.BigTextStyle().bigText(text));
-    }
-
-    /**
-     * Add a button to the notification that links to the user management.
-     *
-     * @param context The context the notification is created in
-     * @param builder The builder of the notification
-     */
-    private void addManageUsersButton(@NonNull Context context,
-        @NonNull Notification.Builder builder) {
-        builder.addAction((new Notification.Action.Builder(
-            Icon.createWithResource(context, R.drawable.ic_settings_multiuser),
-            context.getString(R.string.manage_users),
-            PendingIntent.getActivity(context, 0, getUserSettingsIntent(),
-                PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE))).build());
-    }
-
-    private Intent getUserSettingsIntent() {
-        Intent intent = new Intent(Settings.ACTION_USER_SETTINGS);
-        intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY | Intent.FLAG_ACTIVITY_NEW_TASK);
-        return intent;
-    }
-
-    /**
-     * Add a button to the notification that links to the device policy management.
-     *
-     * @param context The context the notification is created in
-     * @param builder The builder of the notification
-     */
-    private void addDeviceManagerButton(@NonNull Context context,
-        @NonNull Notification.Builder builder) {
-        builder.addAction((new Notification.Action.Builder(
-            Icon.createWithResource(context, R.drawable.ic_lock),
-            context.getString(R.string.manage_device_administrators),
-            PendingIntent.getActivity(context, 0, getDeviceManagerIntent(),
-                PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE))).build());
-    }
-
-    private Intent getDeviceManagerIntent() {
-        Intent intent = new Intent();
-        intent.setClassName("com.android.settings",
-            "com.android.settings.Settings$DeviceAdminSettingsActivity");
-        intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY | Intent.FLAG_ACTIVITY_NEW_TASK);
-        return intent;
-    }
-
-    /**
-     * Starts an uninstall for the given package.
-     *
-     * @return {@code true} if there was no exception while uninstalling. This does not represent
-     *     the result of the uninstall. Result will be made available in
-     *     {@link #handleUninstallResult(int, int, String, int)}
-     */
-    private boolean startUninstall(String packageName, UserHandle targetUser,
-        PendingIntent pendingIntent, boolean uninstallFromAllUsers, boolean keepData) {
-        int flags = uninstallFromAllUsers ? PackageManager.DELETE_ALL_USERS : 0;
-        flags |= keepData ? PackageManager.DELETE_KEEP_DATA : 0;
-        try {
-            mContext.createContextAsUser(targetUser, 0)
-                .getPackageManager().getPackageInstaller().uninstall(
-                    new VersionedPackage(packageName, PackageManager.VERSION_CODE_HIGHEST),
-                    flags, pendingIntent.getIntentSender());
-            return true;
-        } catch (IllegalArgumentException e) {
-            Log.e(TAG, "Failed to uninstall", e);
-            return false;
-        }
-    }
-
-    public void cancelInstall() {
-        if (mCallback != null) {
-            mCallback.onUninstallComplete(mTargetPackageName,
-                PackageManager.DELETE_FAILED_ABORTED, "Cancelled by user");
-        }
-    }
-
-    public MutableLiveData<UninstallStage> getUninstallResult() {
-        return mUninstallResult;
-    }
-
-    public static class CallerInfo {
-
-        private final String mActivityName;
-        private final int mUid;
-
-        public CallerInfo(String activityName, int uid) {
-            mActivityName = activityName;
-            mUid = uid;
-        }
-
-        public String getActivityName() {
-            return mActivityName;
-        }
-
-        public int getUid() {
-            return mUid;
-        }
-    }
-}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/UninstallRepository.kt b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/UninstallRepository.kt
new file mode 100644
index 0000000..7cc95c5
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/UninstallRepository.kt
@@ -0,0 +1,739 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.packageinstaller.v2.model
+
+import android.Manifest
+import android.app.Activity
+import android.app.AppOpsManager
+import android.app.Notification
+import android.app.NotificationChannel
+import android.app.NotificationManager
+import android.app.PendingIntent
+import android.app.admin.DevicePolicyManager
+import android.app.usage.StorageStatsManager
+import android.content.ComponentName
+import android.content.Context
+import android.content.Intent
+import android.content.pm.ActivityInfo
+import android.content.pm.ApplicationInfo
+import android.content.pm.PackageInstaller
+import android.content.pm.PackageManager
+import android.content.pm.VersionedPackage
+import android.graphics.drawable.Icon
+import android.os.Build
+import android.os.Bundle
+import android.os.Process
+import android.os.UserHandle
+import android.os.UserManager
+import android.provider.Settings
+import android.util.Log
+import androidx.lifecycle.MutableLiveData
+import com.android.packageinstaller.R
+import com.android.packageinstaller.common.EventResultPersister
+import com.android.packageinstaller.common.EventResultPersister.OutOfIdsException
+import com.android.packageinstaller.common.UninstallEventReceiver
+import com.android.packageinstaller.v2.model.PackageUtil.getMaxTargetSdkVersionForUid
+import com.android.packageinstaller.v2.model.PackageUtil.getPackageNameForUid
+import com.android.packageinstaller.v2.model.PackageUtil.isPermissionGranted
+import com.android.packageinstaller.v2.model.PackageUtil.isProfileOfOrSame
+
+class UninstallRepository(private val context: Context) {
+
+    private val appOpsManager: AppOpsManager? = context.getSystemService(AppOpsManager::class.java)
+    private val packageManager: PackageManager = context.packageManager
+    private val userManager: UserManager? = context.getSystemService(UserManager::class.java)
+    private val notificationManager: NotificationManager? =
+        context.getSystemService(NotificationManager::class.java)
+    val uninstallResult = MutableLiveData<UninstallStage?>()
+    private var uninstalledUser: UserHandle? = null
+    private var callback: PackageManager.UninstallCompleteCallback? = null
+    private var targetAppInfo: ApplicationInfo? = null
+    private var targetActivityInfo: ActivityInfo? = null
+    private lateinit var intent: Intent
+    private lateinit var targetAppLabel: CharSequence
+    private var targetPackageName: String? = null
+    private var callingActivity: String? = null
+    private var uninstallFromAllUsers = false
+    private var isClonedApp = false
+    private var uninstallId = 0
+
+    fun performPreUninstallChecks(intent: Intent, callerInfo: CallerInfo): UninstallStage {
+        this.intent = intent
+
+        val callingUid = callerInfo.uid
+        callingActivity = callerInfo.activityName
+
+        if (callingUid == Process.INVALID_UID) {
+            Log.e(LOG_TAG, "Could not determine the launching uid.")
+            return UninstallAborted(UninstallAborted.ABORT_REASON_GENERIC_ERROR)
+            // TODO: should we give any indication to the user?
+        }
+
+        val callingPackage = getPackageNameForUid(context, callingUid, null)
+        if (callingPackage == null) {
+            Log.e(LOG_TAG, "Package not found for originating uid $callingUid")
+            return UninstallAborted(UninstallAborted.ABORT_REASON_GENERIC_ERROR)
+        } else {
+            if (appOpsManager!!.noteOpNoThrow(
+                    AppOpsManager.OPSTR_REQUEST_DELETE_PACKAGES, callingUid, callingPackage
+                ) != AppOpsManager.MODE_ALLOWED
+            ) {
+                Log.e(LOG_TAG, "Install from uid $callingUid disallowed by AppOps")
+                return UninstallAborted(UninstallAborted.ABORT_REASON_GENERIC_ERROR)
+            }
+        }
+
+        if (getMaxTargetSdkVersionForUid(context, callingUid) >= Build.VERSION_CODES.P
+            && !isPermissionGranted(
+                context, Manifest.permission.REQUEST_DELETE_PACKAGES, callingUid
+            )
+            && !isPermissionGranted(context, Manifest.permission.DELETE_PACKAGES, callingUid)
+        ) {
+            Log.e(
+                LOG_TAG, "Uid " + callingUid + " does not have "
+                    + Manifest.permission.REQUEST_DELETE_PACKAGES + " or "
+                    + Manifest.permission.DELETE_PACKAGES
+            )
+            return UninstallAborted(UninstallAborted.ABORT_REASON_GENERIC_ERROR)
+        }
+
+        // Get intent information.
+        // We expect an intent with URI of the form package:<packageName>#<className>
+        // className is optional; if specified, it is the activity the user chose to uninstall
+        val packageUri = intent.data
+        if (packageUri == null) {
+            Log.e(LOG_TAG, "No package URI in intent")
+            return UninstallAborted(UninstallAborted.ABORT_REASON_APP_UNAVAILABLE)
+        }
+        targetPackageName = packageUri.encodedSchemeSpecificPart
+        if (targetPackageName == null) {
+            Log.e(LOG_TAG, "Invalid package name in URI: $packageUri")
+            return UninstallAborted(UninstallAborted.ABORT_REASON_APP_UNAVAILABLE)
+        }
+
+        uninstallFromAllUsers = intent.getBooleanExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, false)
+        if (uninstallFromAllUsers && !userManager!!.isAdminUser) {
+            Log.e(LOG_TAG, "Only admin user can request uninstall for all users")
+            return UninstallAborted(UninstallAborted.ABORT_REASON_USER_NOT_ALLOWED)
+        }
+
+        uninstalledUser = intent.getParcelableExtra(Intent.EXTRA_USER, UserHandle::class.java)
+        if (uninstalledUser == null) {
+            uninstalledUser = Process.myUserHandle()
+        } else {
+            val profiles = userManager!!.userProfiles
+            if (!profiles.contains(uninstalledUser)) {
+                Log.e(
+                    LOG_TAG, "User " + Process.myUserHandle() + " can't request uninstall "
+                        + "for user " + uninstalledUser
+                )
+                return UninstallAborted(UninstallAborted.ABORT_REASON_USER_NOT_ALLOWED)
+            }
+        }
+
+        callback = intent.getParcelableExtra(
+            PackageInstaller.EXTRA_CALLBACK, PackageManager.UninstallCompleteCallback::class.java
+        )
+
+        try {
+            targetAppInfo = packageManager.getApplicationInfo(
+                targetPackageName!!,
+                PackageManager.ApplicationInfoFlags.of(PackageManager.MATCH_ANY_USER.toLong())
+            )
+        } catch (e: PackageManager.NameNotFoundException) {
+            Log.e(LOG_TAG, "Unable to get packageName")
+        }
+
+        if (targetAppInfo == null) {
+            Log.e(LOG_TAG, "Invalid packageName: $targetPackageName")
+            return UninstallAborted(UninstallAborted.ABORT_REASON_APP_UNAVAILABLE)
+        }
+
+        // The class name may have been specified (e.g. when deleting an app from all apps)
+        val className = packageUri.fragment
+        if (className != null) {
+            try {
+                targetActivityInfo = packageManager.getActivityInfo(
+                    ComponentName(targetPackageName!!, className),
+                    PackageManager.ComponentInfoFlags.of(0)
+                )
+            } catch (e: PackageManager.NameNotFoundException) {
+                Log.e(LOG_TAG, "Unable to get className")
+                // Continue as the ActivityInfo isn't critical.
+            }
+        }
+
+        return UninstallReady()
+    }
+
+    fun generateUninstallDetails(): UninstallStage {
+        val messageBuilder = StringBuilder()
+
+        targetAppLabel = targetAppInfo!!.loadSafeLabel(packageManager)
+
+        // If the Activity label differs from the App label, then make sure the user
+        // knows the Activity belongs to the App being uninstalled.
+        if (targetActivityInfo != null) {
+            val activityLabel = targetActivityInfo!!.loadSafeLabel(packageManager)
+            if (!activityLabel.contentEquals(targetAppLabel)) {
+                messageBuilder.append(
+                    context.getString(R.string.uninstall_activity_text, activityLabel)
+                )
+                messageBuilder.append(" ").append(targetAppLabel).append(".\n\n")
+            }
+        }
+
+        val isUpdate = (targetAppInfo!!.flags and ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0
+        val myUserHandle = Process.myUserHandle()
+        val isSingleUser = isSingleUser()
+
+        if (isUpdate) {
+            messageBuilder.append(context.getString(
+                    if (isSingleUser) R.string.uninstall_update_text
+                    else R.string.uninstall_update_text_multiuser
+                )
+            )
+        } else if (uninstallFromAllUsers && !isSingleUser) {
+            messageBuilder.append(context.getString(R.string.uninstall_application_text_all_users))
+        } else if (uninstalledUser != myUserHandle) {
+            // Uninstalling user is issuing uninstall for another user
+            val customUserManager = context.createContextAsUser(uninstalledUser!!, 0)
+                .getSystemService(UserManager::class.java)
+            val userName = customUserManager!!.userName
+
+            val uninstalledUserType = getUninstalledUserType(myUserHandle, uninstalledUser!!)
+            val messageString: String
+            when (uninstalledUserType) {
+                UserManager.USER_TYPE_PROFILE_MANAGED -> {
+                    messageString = context.getString(
+                        R.string.uninstall_application_text_current_user_work_profile, userName
+                    )
+                }
+
+                UserManager.USER_TYPE_PROFILE_CLONE -> {
+                    isClonedApp = true
+                    messageString = context.getString(
+                        R.string.uninstall_application_text_current_user_clone_profile
+                    )
+                }
+
+                else -> {
+                    messageString = context.getString(
+                        R.string.uninstall_application_text_user, userName
+                    )
+                }
+
+            }
+            messageBuilder.append(messageString)
+        } else if (isCloneProfile(uninstalledUser!!)) {
+            isClonedApp = true
+            messageBuilder.append(context.getString(
+                    R.string.uninstall_application_text_current_user_clone_profile
+                )
+            )
+        } else if (myUserHandle == UserHandle.SYSTEM
+            && hasClonedInstance(targetAppInfo!!.packageName)
+        ) {
+            messageBuilder.append(context.getString(
+                    R.string.uninstall_application_text_with_clone_instance, targetAppLabel
+                )
+            )
+        } else {
+            messageBuilder.append(context.getString(R.string.uninstall_application_text))
+        }
+
+        val message = messageBuilder.toString()
+
+        val title = if (isClonedApp) {
+            context.getString(R.string.cloned_app_label, targetAppLabel)
+        } else {
+            targetAppLabel.toString()
+        }
+
+        var suggestToKeepAppData = false
+        try {
+            val pkgInfo = packageManager.getPackageInfo(targetPackageName!!, 0)
+            suggestToKeepAppData =
+                pkgInfo.applicationInfo != null && pkgInfo.applicationInfo!!.hasFragileUserData()
+        } catch (e: PackageManager.NameNotFoundException) {
+            Log.e(LOG_TAG, "Cannot check hasFragileUserData for $targetPackageName", e)
+        }
+
+        var appDataSize: Long = 0
+        if (suggestToKeepAppData) {
+            appDataSize = getAppDataSize(
+                targetPackageName!!,
+                if (uninstallFromAllUsers) null else uninstalledUser
+            )
+        }
+
+        return UninstallUserActionRequired(title, message, appDataSize)
+    }
+
+    /**
+     * Returns whether there is only one "full" user on this device.
+     *
+     * **Note:** On devices that use [headless system user mode]
+     * [android.os.UserManager.isHeadlessSystemUserMode], the system user is not "full",
+     * so it's not be considered in the calculation.
+     */
+    private fun isSingleUser(): Boolean {
+        val userCount = userManager!!.userCount
+        return userCount == 1 || (UserManager.isHeadlessSystemUserMode() && userCount == 2)
+    }
+
+    /**
+     * Returns the type of the user from where an app is being uninstalled. We are concerned with
+     * only USER_TYPE_PROFILE_MANAGED and USER_TYPE_PROFILE_CLONE and whether the user and profile
+     * belong to the same profile group.
+     */
+    private fun getUninstalledUserType(
+        myUserHandle: UserHandle,
+        uninstalledUserHandle: UserHandle
+    ): String? {
+        if (!userManager!!.isSameProfileGroup(myUserHandle, uninstalledUserHandle)) {
+            return null
+        }
+        val customUserManager = context.createContextAsUser(uninstalledUserHandle, 0)
+            .getSystemService(UserManager::class.java)
+        val userTypes =
+            arrayOf(UserManager.USER_TYPE_PROFILE_MANAGED, UserManager.USER_TYPE_PROFILE_CLONE)
+
+        for (userType in userTypes) {
+            if (customUserManager!!.isUserOfType(userType)) {
+                return userType
+            }
+        }
+        return null
+    }
+
+    private fun hasClonedInstance(packageName: String): Boolean {
+        // Check if clone user is present on the device.
+        var cloneUser: UserHandle? = null
+        val profiles = userManager!!.userProfiles
+
+        for (userHandle in profiles) {
+            if (userHandle != UserHandle.SYSTEM && isCloneProfile(userHandle)) {
+                cloneUser = userHandle
+                break
+            }
+        }
+        // Check if another instance of given package exists in clone user profile.
+        return try {
+            cloneUser != null
+                && packageManager.getPackageUidAsUser(
+                packageName, PackageManager.PackageInfoFlags.of(0), cloneUser.identifier
+                ) > 0
+        } catch (e: PackageManager.NameNotFoundException) {
+            false
+        }
+    }
+
+    private fun isCloneProfile(userHandle: UserHandle): Boolean {
+        val customUserManager = context.createContextAsUser(userHandle, 0)
+            .getSystemService(UserManager::class.java)
+        return customUserManager!!.isUserOfType(UserManager.USER_TYPE_PROFILE_CLONE)
+    }
+
+    /**
+     * Get number of bytes of the app data of the package.
+     *
+     * @param pkg The package that might have app data.
+     * @param user The user the package belongs to or `null` if files of all users should
+     * be counted.
+     * @return The number of bytes.
+     */
+    private fun getAppDataSize(pkg: String, user: UserHandle?): Long {
+        if (user != null) {
+            return getAppDataSizeForUser(pkg, user)
+        }
+        // We are uninstalling from all users. Get cumulative app data size for all users.
+        val userHandles = userManager!!.getUserHandles(true)
+        var totalAppDataSize: Long = 0
+        val numUsers = userHandles.size
+        for (i in 0 until numUsers) {
+            totalAppDataSize += getAppDataSizeForUser(pkg, userHandles[i])
+        }
+        return totalAppDataSize
+    }
+
+    /**
+     * Get number of bytes of the app data of the package.
+     *
+     * @param pkg The package that might have app data.
+     * @param user The user the package belongs to
+     * @return The number of bytes.
+     */
+    private fun getAppDataSizeForUser(pkg: String, user: UserHandle): Long {
+        val storageStatsManager = context.getSystemService(StorageStatsManager::class.java)
+        try {
+            val stats = storageStatsManager!!.queryStatsForPackage(
+                packageManager.getApplicationInfo(pkg, 0).storageUuid, pkg, user
+            )
+            return stats.getDataBytes()
+        } catch (e: Exception) {
+            Log.e(LOG_TAG, "Cannot determine amount of app data for $pkg", e)
+        }
+        return 0
+    }
+
+    fun initiateUninstall(keepData: Boolean) {
+        // Get an uninstallId to track results and show a notification on non-TV devices.
+        uninstallId = try {
+            UninstallEventReceiver.addObserver(
+                context, EventResultPersister.GENERATE_NEW_ID, this::handleUninstallResult
+            )
+        } catch (e: OutOfIdsException) {
+            Log.e(LOG_TAG, "Failed to start uninstall", e)
+            handleUninstallResult(
+                PackageInstaller.STATUS_FAILURE,
+                PackageManager.DELETE_FAILED_INTERNAL_ERROR, null, 0
+            )
+            return
+        }
+
+        // TODO: Check with UX whether to show UninstallUninstalling dialog / notification?
+        uninstallResult.value = UninstallUninstalling(targetAppLabel, isClonedApp)
+
+        val uninstallData = Bundle()
+        uninstallData.putInt(EXTRA_UNINSTALL_ID, uninstallId)
+        uninstallData.putString(EXTRA_PACKAGE_NAME, targetPackageName)
+        uninstallData.putBoolean(Intent.EXTRA_UNINSTALL_ALL_USERS, uninstallFromAllUsers)
+        uninstallData.putCharSequence(EXTRA_APP_LABEL, targetAppLabel)
+        uninstallData.putBoolean(EXTRA_IS_CLONE_APP, isClonedApp)
+        Log.i(LOG_TAG, "Uninstalling extras = $uninstallData")
+
+        // Get a PendingIntent for result broadcast and issue an uninstall request
+        val broadcastIntent = Intent(BROADCAST_ACTION)
+        broadcastIntent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND)
+        broadcastIntent.putExtra(EventResultPersister.EXTRA_ID, uninstallId)
+        broadcastIntent.setPackage(context.packageName)
+        val pendingIntent = PendingIntent.getBroadcast(
+            context, uninstallId, broadcastIntent,
+            PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE
+        )
+        if (!startUninstall(
+                targetPackageName!!, uninstalledUser!!, pendingIntent, uninstallFromAllUsers,
+                keepData
+            )
+        ) {
+            handleUninstallResult(
+                PackageInstaller.STATUS_FAILURE,
+                PackageManager.DELETE_FAILED_INTERNAL_ERROR, null, 0
+            )
+        }
+    }
+
+    private fun handleUninstallResult(
+        status: Int,
+        legacyStatus: Int,
+        message: String?,
+        serviceId: Int
+    ) {
+        if (callback != null) {
+            // The caller will be informed about the result via a callback
+            callback!!.onUninstallComplete(targetPackageName!!, legacyStatus, message)
+
+            // Since the caller already received the results, just finish the app at this point
+            uninstallResult.value = null
+            return
+        }
+        val returnResult = intent.getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)
+        if (returnResult || callingActivity != null) {
+            val intent = Intent()
+            intent.putExtra(Intent.EXTRA_INSTALL_RESULT, legacyStatus)
+            if (status == PackageInstaller.STATUS_SUCCESS) {
+                uninstallResult.setValue(
+                    UninstallSuccess(resultIntent = intent, activityResultCode = Activity.RESULT_OK)
+                )
+            } else {
+                uninstallResult.setValue(
+                    UninstallFailed(
+                        returnResult = true,
+                        resultIntent = intent,
+                        activityResultCode = Activity.RESULT_FIRST_USER
+                    )
+                )
+            }
+            return
+        }
+
+        // Caller did not want the result back. So, we either show a Toast, or a Notification.
+        if (status == PackageInstaller.STATUS_SUCCESS) {
+            val statusMessage = if (isClonedApp) context.getString(
+                R.string.uninstall_done_clone_app, targetAppLabel
+            ) else context.getString(R.string.uninstall_done_app, targetAppLabel)
+            uninstallResult.setValue(
+                UninstallSuccess(activityResultCode = legacyStatus, message = statusMessage)
+            )
+        } else {
+            val uninstallFailureChannel = NotificationChannel(
+                UNINSTALL_FAILURE_CHANNEL,
+                context.getString(R.string.uninstall_failure_notification_channel),
+                NotificationManager.IMPORTANCE_DEFAULT
+            )
+            notificationManager!!.createNotificationChannel(uninstallFailureChannel)
+
+            val uninstallFailedNotification: Notification.Builder =
+                Notification.Builder(context, UNINSTALL_FAILURE_CHANNEL)
+
+            val myUserHandle = Process.myUserHandle()
+            when (legacyStatus) {
+                PackageManager.DELETE_FAILED_DEVICE_POLICY_MANAGER -> {
+                    // Find out if the package is an active admin for some non-current user.
+                    val otherBlockingUserHandle =
+                        findUserOfDeviceAdmin(myUserHandle, targetPackageName!!)
+                    if (otherBlockingUserHandle == null) {
+                        Log.d(
+                            LOG_TAG, "Uninstall failed because $targetPackageName"
+                                + " is a device admin"
+                        )
+                        addDeviceManagerButton(context, uninstallFailedNotification)
+                        setBigText(
+                            uninstallFailedNotification, context.getString(
+                                R.string.uninstall_failed_device_policy_manager
+                            )
+                        )
+                    } else {
+                        Log.d(
+                            LOG_TAG, "Uninstall failed because $targetPackageName"
+                                + " is a device admin of user $otherBlockingUserHandle"
+                        )
+                        val userName = context.createContextAsUser(otherBlockingUserHandle, 0)
+                            .getSystemService(UserManager::class.java)!!.userName
+                        setBigText(
+                            uninstallFailedNotification, String.format(
+                                context.getString(
+                                    R.string.uninstall_failed_device_policy_manager_of_user
+                                ), userName
+                            )
+                        )
+                    }
+                }
+
+                PackageManager.DELETE_FAILED_OWNER_BLOCKED -> {
+                    val otherBlockingUserHandle = findBlockingUser(targetPackageName!!)
+                    val isProfileOfOrSame = isProfileOfOrSame(
+                        userManager!!, myUserHandle, otherBlockingUserHandle
+                    )
+                    if (isProfileOfOrSame) {
+                        addDeviceManagerButton(context, uninstallFailedNotification)
+                    } else {
+                        addManageUsersButton(context, uninstallFailedNotification)
+                    }
+                    var bigText: String? = null
+                    if (otherBlockingUserHandle == null) {
+                        Log.d(
+                            LOG_TAG, "Uninstall failed for $targetPackageName " +
+                                "with code $status no blocking user"
+                        )
+                    } else if (otherBlockingUserHandle === UserHandle.SYSTEM) {
+                        bigText = context.getString(R.string.uninstall_blocked_device_owner)
+                    } else {
+                        bigText = context.getString(
+                            if (uninstallFromAllUsers) R.string.uninstall_all_blocked_profile_owner
+                            else R.string.uninstall_blocked_profile_owner
+                        )
+                    }
+                    bigText?.let { setBigText(uninstallFailedNotification, it) }
+                }
+
+                else -> {
+                    Log.d(
+                        LOG_TAG, "Uninstall blocked for $targetPackageName"
+                            + " with legacy code $legacyStatus"
+                    )
+                }
+            }
+            uninstallFailedNotification.setContentTitle(
+                context.getString(R.string.uninstall_failed_app, targetAppLabel)
+            )
+            uninstallFailedNotification.setOngoing(false)
+            uninstallFailedNotification.setSmallIcon(R.drawable.ic_error)
+
+            uninstallResult.setValue(
+                UninstallFailed(
+                    returnResult = false,
+                    uninstallNotificationId = uninstallId,
+                    uninstallNotification = uninstallFailedNotification.build()
+                )
+            )
+        }
+    }
+
+    /**
+     * @param myUserHandle [UserHandle] of the current user.
+     * @param packageName Name of the package being uninstalled.
+     * @return the [UserHandle] of the user in which a package is a device admin.
+     */
+    private fun findUserOfDeviceAdmin(myUserHandle: UserHandle, packageName: String): UserHandle? {
+        for (otherUserHandle in userManager!!.getUserHandles(true)) {
+            // We only catch the case when the user in question is neither the
+            // current user nor its profile.
+            if (isProfileOfOrSame(userManager, myUserHandle, otherUserHandle)) {
+                continue
+            }
+            val dpm = context.createContextAsUser(otherUserHandle, 0)
+                .getSystemService(DevicePolicyManager::class.java)
+            if (dpm!!.packageHasActiveAdmins(packageName)) {
+                return otherUserHandle
+            }
+        }
+        return null
+    }
+
+    /**
+     *
+     * @param packageName Name of the package being uninstalled.
+     * @return [UserHandle] of the user in which a package is blocked from being uninstalled.
+     */
+    private fun findBlockingUser(packageName: String): UserHandle? {
+        for (otherUserHandle in userManager!!.getUserHandles(true)) {
+            // TODO (b/307399586): Add a negation when the logic of the method is fixed
+            if (packageManager.canUserUninstall(packageName, otherUserHandle)) {
+                return otherUserHandle
+            }
+        }
+        return null
+    }
+
+    /**
+     * Set big text for the notification.
+     *
+     * @param builder The builder of the notification
+     * @param text The text to set.
+     */
+    private fun setBigText(
+        builder: Notification.Builder,
+        text: CharSequence
+    ) {
+        builder.setStyle(Notification.BigTextStyle().bigText(text))
+    }
+
+    /**
+     * Add a button to the notification that links to the user management.
+     *
+     * @param context The context the notification is created in
+     * @param builder The builder of the notification
+     */
+    private fun addManageUsersButton(
+        context: Context,
+        builder: Notification.Builder
+    ) {
+        builder.addAction(
+            Notification.Action.Builder(
+                Icon.createWithResource(context, R.drawable.ic_settings_multiuser),
+                context.getString(R.string.manage_users),
+                PendingIntent.getActivity(
+                    context, 0, getUserSettingsIntent(),
+                    PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
+                )
+            )
+                .build()
+        )
+    }
+
+    private fun getUserSettingsIntent(): Intent {
+        val intent = Intent(Settings.ACTION_USER_SETTINGS)
+        intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY or Intent.FLAG_ACTIVITY_NEW_TASK)
+        return intent
+    }
+
+    /**
+     * Add a button to the notification that links to the device policy management.
+     *
+     * @param context The context the notification is created in
+     * @param builder The builder of the notification
+     */
+    private fun addDeviceManagerButton(
+        context: Context,
+        builder: Notification.Builder
+    ) {
+        builder.addAction(
+            Notification.Action.Builder(
+                Icon.createWithResource(context, R.drawable.ic_lock),
+                context.getString(R.string.manage_device_administrators),
+                PendingIntent.getActivity(
+                    context, 0, getDeviceManagerIntent(),
+                    PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
+                )
+            )
+                .build()
+        )
+    }
+
+    private fun getDeviceManagerIntent(): Intent {
+        val intent = Intent()
+        intent.setClassName(
+            "com.android.settings",
+            "com.android.settings.Settings\$DeviceAdminSettingsActivity"
+        )
+        intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY or Intent.FLAG_ACTIVITY_NEW_TASK)
+        return intent
+    }
+
+    /**
+     * Starts an uninstall for the given package.
+     *
+     * @return `true` if there was no exception while uninstalling. This does not represent
+     * the result of the uninstall. Result will be made available in [handleUninstallResult]
+     */
+    private fun startUninstall(
+        packageName: String,
+        targetUser: UserHandle,
+        pendingIntent: PendingIntent,
+        uninstallFromAllUsers: Boolean,
+        keepData: Boolean
+    ): Boolean {
+        var flags = if (uninstallFromAllUsers) PackageManager.DELETE_ALL_USERS else 0
+        flags = flags or if (keepData) PackageManager.DELETE_KEEP_DATA else 0
+
+        return try {
+            context.createContextAsUser(targetUser, 0)
+                .packageManager.packageInstaller.uninstall(
+                    VersionedPackage(packageName, PackageManager.VERSION_CODE_HIGHEST),
+                    flags, pendingIntent.intentSender
+                )
+            true
+        } catch (e: IllegalArgumentException) {
+            Log.e(LOG_TAG, "Failed to uninstall", e)
+            false
+        }
+    }
+
+    fun cancelInstall() {
+        if (callback != null) {
+            callback!!.onUninstallComplete(
+                targetPackageName!!,
+                PackageManager.DELETE_FAILED_ABORTED, "Cancelled by user"
+            )
+        }
+    }
+
+    companion object {
+        private val LOG_TAG = UninstallRepository::class.java.simpleName
+        private const val UNINSTALL_FAILURE_CHANNEL = "uninstall_failure"
+        private const val BROADCAST_ACTION = "com.android.packageinstaller.ACTION_UNINSTALL_COMMIT"
+        private const val EXTRA_UNINSTALL_ID = "com.android.packageinstaller.extra.UNINSTALL_ID"
+        private const val EXTRA_APP_LABEL = "com.android.packageinstaller.extra.APP_LABEL"
+        private const val EXTRA_IS_CLONE_APP = "com.android.packageinstaller.extra.IS_CLONE_APP"
+        private const val EXTRA_PACKAGE_NAME =
+            "com.android.packageinstaller.extra.EXTRA_PACKAGE_NAME"
+    }
+
+    class CallerInfo(val activityName: String?, val uid: Int)
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/UninstallStages.kt b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/UninstallStages.kt
new file mode 100644
index 0000000..f086209
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/UninstallStages.kt
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.packageinstaller.v2.model
+
+import android.app.Activity
+import android.app.Notification
+import android.content.Intent
+import com.android.packageinstaller.R
+
+sealed class UninstallStage(val stageCode: Int) {
+
+    companion object {
+        const val STAGE_DEFAULT = -1
+        const val STAGE_ABORTED = 0
+        const val STAGE_READY = 1
+        const val STAGE_USER_ACTION_REQUIRED = 2
+        const val STAGE_UNINSTALLING = 3
+        const val STAGE_SUCCESS = 4
+        const val STAGE_FAILED = 5
+    }
+}
+
+class UninstallReady : UninstallStage(STAGE_READY)
+
+data class UninstallUserActionRequired(
+    val title: String? = null,
+    val message: String? = null,
+    val appDataSize: Long = 0
+) : UninstallStage(STAGE_USER_ACTION_REQUIRED)
+
+data class UninstallUninstalling(val appLabel: CharSequence, val isCloneUser: Boolean) :
+    UninstallStage(STAGE_UNINSTALLING)
+
+data class UninstallSuccess(
+    val resultIntent: Intent? = null,
+    val activityResultCode: Int = 0,
+    val message: String? = null,
+) : UninstallStage(STAGE_SUCCESS)
+
+data class UninstallFailed(
+    val returnResult: Boolean,
+    /**
+     * If the caller wants the result back, the intent will hold the uninstall failure status code
+     * and legacy code.
+     */
+    val resultIntent: Intent? = null,
+    val activityResultCode: Int = Activity.RESULT_CANCELED,
+    /**
+     * ID used to show [uninstallNotification]
+     */
+    val uninstallNotificationId: Int? = null,
+    /**
+     * When the user does not request a result back, this notification will be shown indicating the
+     * reason for uninstall failure.
+     */
+    val uninstallNotification: Notification? = null,
+) : UninstallStage(STAGE_FAILED) {
+
+    init {
+        if (uninstallNotification != null && uninstallNotificationId == null) {
+            throw IllegalArgumentException(
+                "uninstallNotification cannot be set without uninstallNotificationId"
+            )
+        }
+    }
+}
+
+data class UninstallAborted(val abortReason: Int) : UninstallStage(STAGE_ABORTED) {
+
+    var dialogTitleResource = 0
+    var dialogTextResource = 0
+    val activityResultCode = Activity.RESULT_FIRST_USER
+
+    init {
+        when (abortReason) {
+            ABORT_REASON_APP_UNAVAILABLE -> {
+                dialogTitleResource = R.string.app_not_found_dlg_title
+                dialogTextResource = R.string.app_not_found_dlg_text
+            }
+
+            ABORT_REASON_USER_NOT_ALLOWED -> {
+                dialogTitleResource = 0
+                dialogTextResource = R.string.user_is_not_allowed_dlg_text
+            }
+
+            else -> {
+                dialogTitleResource = 0
+                dialogTextResource = R.string.generic_error_dlg_text
+            }
+        }
+    }
+
+    companion object {
+        const val ABORT_REASON_GENERIC_ERROR = 0
+        const val ABORT_REASON_APP_UNAVAILABLE = 1
+        const val ABORT_REASON_USER_NOT_ALLOWED = 2
+    }
+}
+
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/installstagedata/InstallAborted.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/installstagedata/InstallAborted.java
deleted file mode 100644
index 520b6c5..0000000
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/installstagedata/InstallAborted.java
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.packageinstaller.v2.model.installstagedata;
-
-
-import android.app.Activity;
-import android.content.Intent;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-public class InstallAborted extends InstallStage {
-
-    public static final int ABORT_REASON_INTERNAL_ERROR = 0;
-    public static final int ABORT_REASON_POLICY = 1;
-    public static final int ABORT_REASON_DONE = 2;
-    public static final int DLG_PACKAGE_ERROR = 1;
-    private final int mStage = InstallStage.STAGE_ABORTED;
-    private final int mAbortReason;
-
-    /**
-     * It will hold the restriction name, when the restriction was enforced by the system, and not
-     * a device admin.
-     */
-    @NonNull
-    private final String mMessage;
-    /**
-     * <p>If abort reason is ABORT_REASON_POLICY, then this will hold the Intent
-     * to display a support dialog when a feature was disabled by an admin. It will be
-     * {@code null} if the feature is disabled by the system. In this case, the restriction name
-     * will be set in {@link #mMessage} </p>
-     *
-     * <p>If the abort reason is ABORT_REASON_INTERNAL_ERROR, it <b>may</b> hold an
-     * intent to be sent as a result to the calling activity.</p>
-     */
-    @Nullable
-    private final Intent mIntent;
-    private final int mErrorDialogType;
-    private final int mActivityResultCode;
-
-    private InstallAborted(int reason, @NonNull String message, @Nullable Intent intent,
-        int activityResultCode, int errorDialogType) {
-        mAbortReason = reason;
-        mMessage = message;
-        mIntent = intent;
-        mErrorDialogType = errorDialogType;
-        mActivityResultCode = activityResultCode;
-    }
-
-    public int getAbortReason() {
-        return mAbortReason;
-    }
-
-    @NonNull
-    public String getMessage() {
-        return mMessage;
-    }
-
-    @Nullable
-    public Intent getResultIntent() {
-        return mIntent;
-    }
-
-    public int getErrorDialogType() {
-        return mErrorDialogType;
-    }
-
-    public int getActivityResultCode() {
-        return mActivityResultCode;
-    }
-
-    @Override
-    public int getStageCode() {
-        return mStage;
-    }
-
-    public static class Builder {
-
-        private final int mAbortReason;
-        private String mMessage = "";
-        private Intent mIntent = null;
-        private int mActivityResultCode = Activity.RESULT_CANCELED;
-        private int mErrorDialogType;
-
-        public Builder(int reason) {
-            mAbortReason = reason;
-        }
-
-        public Builder setMessage(@NonNull String message) {
-            mMessage = message;
-            return this;
-        }
-
-        public Builder setResultIntent(@NonNull Intent intent) {
-            mIntent = intent;
-            return this;
-        }
-
-        public Builder setErrorDialogType(int dialogType) {
-            mErrorDialogType = dialogType;
-            return this;
-        }
-
-        public Builder setActivityResultCode(int resultCode) {
-            mActivityResultCode = resultCode;
-            return this;
-        }
-
-        public InstallAborted build() {
-            return new InstallAborted(mAbortReason, mMessage, mIntent, mActivityResultCode,
-                mErrorDialogType);
-        }
-    }
-}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/installstagedata/InstallFailed.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/installstagedata/InstallFailed.java
deleted file mode 100644
index 67e1690..0000000
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/installstagedata/InstallFailed.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.packageinstaller.v2.model.installstagedata;
-
-import android.graphics.drawable.Drawable;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import com.android.packageinstaller.v2.model.PackageUtil.AppSnippet;
-
-public class InstallFailed extends InstallStage {
-
-    private final int mStage = InstallStage.STAGE_FAILED;
-    @NonNull
-    private final AppSnippet mAppSnippet;
-    private final int mStatusCode;
-    private final int mLegacyCode;
-    @Nullable
-    private final String mMessage;
-
-    public InstallFailed(@NonNull AppSnippet appSnippet, int statusCode, int legacyCode,
-        @Nullable String message) {
-        mAppSnippet = appSnippet;
-        mLegacyCode = statusCode;
-        mStatusCode = legacyCode;
-        mMessage = message;
-    }
-
-    @Override
-    public int getStageCode() {
-        return mStage;
-    }
-
-    @NonNull
-    public Drawable getAppIcon() {
-        return mAppSnippet.getIcon();
-    }
-
-    @NonNull
-    public String getAppLabel() {
-        return (String) mAppSnippet.getLabel();
-    }
-
-    public int getStatusCode() {
-        return mStatusCode;
-    }
-
-    public int getLegacyCode() {
-        return mLegacyCode;
-    }
-
-    @Nullable
-    public String getMessage() {
-        return mMessage;
-    }
-}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/installstagedata/InstallInstalling.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/installstagedata/InstallInstalling.java
deleted file mode 100644
index efd4947..0000000
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/installstagedata/InstallInstalling.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.packageinstaller.v2.model.installstagedata;
-
-import android.graphics.drawable.Drawable;
-import androidx.annotation.NonNull;
-import com.android.packageinstaller.v2.model.PackageUtil.AppSnippet;
-
-public class InstallInstalling extends InstallStage {
-
-    private final int mStage = InstallStage.STAGE_INSTALLING;
-    @NonNull
-    private final AppSnippet mAppSnippet;
-
-    public InstallInstalling(@NonNull AppSnippet appSnippet) {
-        mAppSnippet = appSnippet;
-    }
-
-    @Override
-    public int getStageCode() {
-        return mStage;
-    }
-
-    @NonNull
-    public Drawable getAppIcon() {
-        return mAppSnippet.getIcon();
-    }
-
-    @NonNull
-    public String getAppLabel() {
-        return (String) mAppSnippet.getLabel();
-    }
-}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/installstagedata/InstallReady.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/installstagedata/InstallReady.java
deleted file mode 100644
index 548f2c5..0000000
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/installstagedata/InstallReady.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.packageinstaller.v2.model.installstagedata;
-
-public class InstallReady extends InstallStage{
-
-    private final int mStage = InstallStage.STAGE_READY;
-
-    @Override
-    public int getStageCode() {
-        return mStage;
-    }
-}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/installstagedata/InstallStage.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/installstagedata/InstallStage.java
deleted file mode 100644
index f91e64b..0000000
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/installstagedata/InstallStage.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.packageinstaller.v2.model.installstagedata;
-
-public abstract class InstallStage {
-
-    public static final int STAGE_DEFAULT = -1;
-    public static final int STAGE_ABORTED = 0;
-    public static final int STAGE_STAGING = 1;
-    public static final int STAGE_READY = 2;
-    public static final int STAGE_USER_ACTION_REQUIRED = 3;
-    public static final int STAGE_INSTALLING = 4;
-    public static final int STAGE_SUCCESS = 5;
-    public static final int STAGE_FAILED = 6;
-
-    /**
-     * @return the integer value representing current install stage.
-     */
-    public abstract int getStageCode();
-}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/installstagedata/InstallSuccess.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/installstagedata/InstallSuccess.java
deleted file mode 100644
index da48256..0000000
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/installstagedata/InstallSuccess.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.packageinstaller.v2.model.installstagedata;
-
-import android.content.Intent;
-import android.graphics.drawable.Drawable;
-import androidx.annotation.NonNull;
-import com.android.packageinstaller.v2.model.PackageUtil.AppSnippet;
-
-public class InstallSuccess extends InstallStage {
-
-    private final int mStage = InstallStage.STAGE_SUCCESS;
-
-    @NonNull
-    private final AppSnippet mAppSnippet;
-    private final boolean mShouldReturnResult;
-    /**
-     * <p>If the caller is requesting a result back, this will hold the Intent with
-     * EXTRA_INSTALL_RESULT set to INSTALL_SUCCEEDED which is sent back to the caller.</p>
-     * <p>If the caller doesn't want the result back, this will hold the Intent that launches
-     * the newly installed / updated app.</p>
-     */
-    @NonNull
-    private final Intent mResultIntent;
-
-    public InstallSuccess(@NonNull AppSnippet appSnippet, boolean shouldReturnResult,
-        @NonNull Intent launcherIntent) {
-        mAppSnippet = appSnippet;
-        mShouldReturnResult = shouldReturnResult;
-        mResultIntent = launcherIntent;
-    }
-
-    @Override
-    public int getStageCode() {
-        return mStage;
-    }
-
-    @NonNull
-    public Drawable getAppIcon() {
-        return mAppSnippet.getIcon();
-    }
-
-    @NonNull
-    public String getAppLabel() {
-        return (String) mAppSnippet.getLabel();
-    }
-
-    public boolean shouldReturnResult() {
-        return mShouldReturnResult;
-    }
-
-    @NonNull
-    public Intent getResultIntent() {
-        return mResultIntent;
-    }
-
-    public static class Builder {
-
-        private final AppSnippet mAppSnippet;
-        private boolean mShouldReturnResult;
-        private Intent mLauncherIntent;
-
-        public Builder(@NonNull AppSnippet appSnippet) {
-            mAppSnippet = appSnippet;
-        }
-
-        public Builder setShouldReturnResult(boolean returnResult) {
-            mShouldReturnResult = returnResult;
-            return this;
-        }
-
-        public Builder setResultIntent(@NonNull Intent intent) {
-            mLauncherIntent = intent;
-            return this;
-        }
-
-        public InstallSuccess build() {
-            return new InstallSuccess(mAppSnippet, mShouldReturnResult, mLauncherIntent);
-        }
-    }
-}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/installstagedata/InstallUserActionRequired.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/installstagedata/InstallUserActionRequired.java
deleted file mode 100644
index 08a7487..0000000
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/installstagedata/InstallUserActionRequired.java
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.packageinstaller.v2.model.installstagedata;
-
-import android.graphics.drawable.Drawable;
-import androidx.annotation.Nullable;
-import com.android.packageinstaller.v2.model.PackageUtil.AppSnippet;
-
-public class InstallUserActionRequired extends InstallStage {
-
-    public static final int USER_ACTION_REASON_UNKNOWN_SOURCE = 0;
-    public static final int USER_ACTION_REASON_ANONYMOUS_SOURCE = 1;
-    public static final int USER_ACTION_REASON_INSTALL_CONFIRMATION = 2;
-    private final int mStage = InstallStage.STAGE_USER_ACTION_REQUIRED;
-    private final int mActionReason;
-    @Nullable
-    private final AppSnippet mAppSnippet;
-    private final boolean mIsAppUpdating;
-    @Nullable
-    private final String mDialogMessage;
-
-    public InstallUserActionRequired(int actionReason, @Nullable AppSnippet appSnippet,
-        boolean isUpdating, @Nullable String dialogMessage) {
-        mActionReason = actionReason;
-        mAppSnippet = appSnippet;
-        mIsAppUpdating = isUpdating;
-        mDialogMessage = dialogMessage;
-    }
-
-    @Override
-    public int getStageCode() {
-        return mStage;
-    }
-
-    @Nullable
-    public Drawable getAppIcon() {
-        return mAppSnippet != null ? mAppSnippet.getIcon() : null;
-    }
-
-    @Nullable
-    public String getAppLabel() {
-        return mAppSnippet != null ? (String) mAppSnippet.getLabel() : null;
-    }
-
-    public boolean isAppUpdating() {
-        return mIsAppUpdating;
-    }
-
-    @Nullable
-    public String getDialogMessage() {
-        return mDialogMessage;
-    }
-
-    public int getActionReason() {
-        return mActionReason;
-    }
-
-    public static class Builder {
-
-        private final int mActionReason;
-        private final AppSnippet mAppSnippet;
-        private boolean mIsAppUpdating;
-        private String mDialogMessage;
-
-        public Builder(int actionReason, @Nullable AppSnippet appSnippet) {
-            mActionReason = actionReason;
-            mAppSnippet = appSnippet;
-        }
-
-        public Builder setAppUpdating(boolean isUpdating) {
-            mIsAppUpdating = isUpdating;
-            return this;
-        }
-
-        public Builder setDialogMessage(@Nullable String message) {
-            mDialogMessage = message;
-            return this;
-        }
-
-        public InstallUserActionRequired build() {
-            return new InstallUserActionRequired(mActionReason, mAppSnippet, mIsAppUpdating,
-                mDialogMessage);
-        }
-    }
-}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/uninstallstagedata/UninstallAborted.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/uninstallstagedata/UninstallAborted.java
deleted file mode 100644
index 9aea6b1..0000000
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/uninstallstagedata/UninstallAborted.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.packageinstaller.v2.model.uninstallstagedata;
-
-import android.app.Activity;
-import com.android.packageinstaller.R;
-
-public class UninstallAborted extends UninstallStage {
-
-    public static final int ABORT_REASON_GENERIC_ERROR = 0;
-    public static final int ABORT_REASON_APP_UNAVAILABLE = 1;
-    public static final int ABORT_REASON_USER_NOT_ALLOWED = 2;
-    private final int mStage = UninstallStage.STAGE_ABORTED;
-    private final int mAbortReason;
-    private final int mDialogTitleResource;
-    private final int mDialogTextResource;
-    private final int mActivityResultCode = Activity.RESULT_FIRST_USER;
-
-    public UninstallAborted(int abortReason) {
-        mAbortReason = abortReason;
-        switch (abortReason) {
-            case ABORT_REASON_APP_UNAVAILABLE -> {
-                mDialogTitleResource = R.string.app_not_found_dlg_title;
-                mDialogTextResource = R.string.app_not_found_dlg_text;
-            }
-            case ABORT_REASON_USER_NOT_ALLOWED -> {
-                mDialogTitleResource = 0;
-                mDialogTextResource = R.string.user_is_not_allowed_dlg_text;
-            }
-            default -> {
-                mDialogTitleResource = 0;
-                mDialogTextResource = R.string.generic_error_dlg_text;
-            }
-        }
-    }
-
-    public int getAbortReason() {
-        return mAbortReason;
-    }
-
-    public int getActivityResultCode() {
-        return mActivityResultCode;
-    }
-
-    public int getDialogTitleResource() {
-        return mDialogTitleResource;
-    }
-
-    public int getDialogTextResource() {
-        return mDialogTextResource;
-    }
-
-    @Override
-    public int getStageCode() {
-        return mStage;
-    }
-}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/uninstallstagedata/UninstallFailed.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/uninstallstagedata/UninstallFailed.java
deleted file mode 100644
index 6ed8883..0000000
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/uninstallstagedata/UninstallFailed.java
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.packageinstaller.v2.model.uninstallstagedata;
-
-import android.app.Activity;
-import android.app.Notification;
-import android.content.Intent;
-
-public class UninstallFailed extends UninstallStage {
-
-    private final int mStage = UninstallStage.STAGE_FAILED;
-    private final boolean mReturnResult;
-    /**
-     * If the caller wants the result back, the intent will hold the uninstall failure status code
-     * and legacy code.
-     */
-    private final Intent mResultIntent;
-    /**
-     * When the user does not request a result back, this notification will be shown indicating the
-     * reason for uninstall failure.
-     */
-    private final Notification mUninstallNotification;
-    /**
-     * ID used to show {@link #mUninstallNotification}
-     */
-    private final int mUninstallId;
-    private final int mActivityResultCode;
-
-    public UninstallFailed(boolean returnResult, Intent resultIntent, int activityResultCode,
-        int uninstallId, Notification uninstallNotification) {
-        mReturnResult = returnResult;
-        mResultIntent = resultIntent;
-        mActivityResultCode = activityResultCode;
-        mUninstallId = uninstallId;
-        mUninstallNotification = uninstallNotification;
-    }
-
-    public boolean returnResult() {
-        return mReturnResult;
-    }
-
-    public Intent getResultIntent() {
-        return mResultIntent;
-    }
-
-    public int getActivityResultCode() {
-        return mActivityResultCode;
-    }
-
-    public Notification getUninstallNotification() {
-        return mUninstallNotification;
-    }
-
-    public int getUninstallId() {
-        return mUninstallId;
-    }
-
-    @Override
-    public int getStageCode() {
-        return mStage;
-    }
-
-    public static class Builder {
-
-        private final boolean mReturnResult;
-        private int mActivityResultCode = Activity.RESULT_CANCELED;
-        /**
-         * See {@link UninstallFailed#mResultIntent}
-         */
-        private Intent mResultIntent = null;
-        /**
-         * See {@link UninstallFailed#mUninstallNotification}
-         */
-        private Notification mUninstallNotification;
-        /**
-         * See {@link UninstallFailed#mUninstallId}
-         */
-        private int mUninstallId;
-
-        public Builder(boolean returnResult) {
-            mReturnResult = returnResult;
-        }
-
-        public Builder setUninstallNotification(int uninstallId, Notification notification) {
-            mUninstallId = uninstallId;
-            mUninstallNotification = notification;
-            return this;
-        }
-
-        public Builder setResultIntent(Intent intent) {
-            mResultIntent = intent;
-            return this;
-        }
-
-        public Builder setActivityResultCode(int resultCode) {
-            mActivityResultCode = resultCode;
-            return this;
-        }
-
-        public UninstallFailed build() {
-            return new UninstallFailed(mReturnResult, mResultIntent, mActivityResultCode,
-                mUninstallId, mUninstallNotification);
-        }
-    }
-}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/uninstallstagedata/UninstallReady.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/uninstallstagedata/UninstallReady.java
deleted file mode 100644
index 0108cb4..0000000
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/uninstallstagedata/UninstallReady.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.packageinstaller.v2.model.uninstallstagedata;
-
-public class UninstallReady extends UninstallStage {
-
-    private final int mStage = UninstallStage.STAGE_READY;
-
-    @Override
-    public int getStageCode() {
-        return mStage;
-    }
-}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/uninstallstagedata/UninstallStage.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/uninstallstagedata/UninstallStage.java
deleted file mode 100644
index 87ca4ec..0000000
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/uninstallstagedata/UninstallStage.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.packageinstaller.v2.model.uninstallstagedata;
-
-public abstract class UninstallStage {
-
-    public static final int STAGE_DEFAULT = -1;
-    public static final int STAGE_ABORTED = 0;
-    public static final int STAGE_READY = 1;
-    public static final int STAGE_USER_ACTION_REQUIRED = 2;
-    public static final int STAGE_UNINSTALLING = 3;
-    public static final int STAGE_SUCCESS = 4;
-    public static final int STAGE_FAILED = 5;
-
-    public abstract int getStageCode();
-}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/uninstallstagedata/UninstallSuccess.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/uninstallstagedata/UninstallSuccess.java
deleted file mode 100644
index 5df6b02..0000000
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/uninstallstagedata/UninstallSuccess.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.packageinstaller.v2.model.uninstallstagedata;
-
-import android.content.Intent;
-
-public class UninstallSuccess extends UninstallStage {
-
-    private final int mStage = UninstallStage.STAGE_SUCCESS;
-    private final String mMessage;
-    private final Intent mResultIntent;
-    private final int mActivityResultCode;
-
-    public UninstallSuccess(Intent resultIntent, int activityResultCode, String message) {
-        mResultIntent = resultIntent;
-        mActivityResultCode = activityResultCode;
-        mMessage = message;
-    }
-
-    public String getMessage() {
-        return mMessage;
-    }
-
-    public Intent getResultIntent() {
-        return mResultIntent;
-    }
-
-    public int getActivityResultCode() {
-        return mActivityResultCode;
-    }
-
-    @Override
-    public int getStageCode() {
-        return mStage;
-    }
-
-    public static class Builder {
-
-        private Intent mResultIntent;
-        private int mActivityResultCode;
-        private String mMessage;
-
-        public Builder() {
-        }
-
-        public Builder setResultIntent(Intent intent) {
-            mResultIntent = intent;
-            return this;
-        }
-
-        public Builder setActivityResultCode(int resultCode) {
-            mActivityResultCode = resultCode;
-            return this;
-        }
-
-        public Builder setMessage(String message) {
-            mMessage = message;
-            return this;
-        }
-
-        public UninstallSuccess build() {
-            return new UninstallSuccess(mResultIntent, mActivityResultCode, mMessage);
-        }
-    }
-}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/uninstallstagedata/UninstallUninstalling.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/uninstallstagedata/UninstallUninstalling.java
deleted file mode 100644
index f5156cb..0000000
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/uninstallstagedata/UninstallUninstalling.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.packageinstaller.v2.model.uninstallstagedata;
-
-public class UninstallUninstalling extends UninstallStage {
-
-    private final int mStage = UninstallStage.STAGE_UNINSTALLING;
-
-    private final CharSequence mAppLabel;
-    private final boolean mIsCloneUser;
-
-    public UninstallUninstalling(CharSequence appLabel, boolean isCloneUser) {
-        mAppLabel = appLabel;
-        mIsCloneUser = isCloneUser;
-    }
-
-    public CharSequence getAppLabel() {
-        return mAppLabel;
-    }
-
-    public boolean isCloneUser() {
-        return mIsCloneUser;
-    }
-
-    @Override
-    public int getStageCode() {
-        return mStage;
-    }
-}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/uninstallstagedata/UninstallUserActionRequired.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/uninstallstagedata/UninstallUserActionRequired.java
deleted file mode 100644
index b600149..0000000
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/uninstallstagedata/UninstallUserActionRequired.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.packageinstaller.v2.model.uninstallstagedata;
-
-public class UninstallUserActionRequired extends UninstallStage {
-
-    private final int mStage = UninstallStage.STAGE_USER_ACTION_REQUIRED;
-    private final String mTitle;
-    private final String mMessage;
-    private final long mAppDataSize;
-
-    public UninstallUserActionRequired(String title, String message, long appDataSize) {
-        mTitle = title;
-        mMessage = message;
-        mAppDataSize = appDataSize;
-    }
-
-    public String getTitle() {
-        return mTitle;
-    }
-
-    public String getMessage() {
-        return mMessage;
-    }
-
-    public long getAppDataSize() {
-        return mAppDataSize;
-    }
-
-    @Override
-    public int getStageCode() {
-        return mStage;
-    }
-
-    public static class Builder {
-
-        private String mTitle;
-        private String mMessage;
-        private long mAppDataSize = 0;
-
-        public Builder setTitle(String title) {
-            mTitle = title;
-            return this;
-        }
-
-        public Builder setMessage(String message) {
-            mMessage = message;
-            return this;
-        }
-
-        public Builder setAppDataSize(long appDataSize) {
-            mAppDataSize = appDataSize;
-            return this;
-        }
-
-        public UninstallUserActionRequired build() {
-            return new UninstallUserActionRequired(mTitle, mMessage, mAppDataSize);
-        }
-    }
-}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/InstallActionListener.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/InstallActionListener.java
deleted file mode 100644
index fdb024f..0000000
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/InstallActionListener.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.packageinstaller.v2.ui;
-
-import android.content.Intent;
-
-public interface InstallActionListener {
-
-    /**
-     * Method to handle a positive response from the user
-     */
-    void onPositiveResponse(int stageCode);
-
-    /**
-     * Method to dispatch intent for toggling "install from unknown sources" setting for a package
-     */
-    void sendUnknownAppsIntent(String packageName);
-
-    /**
-     * Method to handle a negative response from the user
-     */
-    void onNegativeResponse(int stageCode);
-    void openInstalledApp(Intent intent);
-}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/InstallActionListener.kt b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/InstallActionListener.kt
new file mode 100644
index 0000000..1d4d178
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/InstallActionListener.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.packageinstaller.v2.ui
+
+import android.content.Intent
+
+interface InstallActionListener {
+    /**
+     * Method to handle a positive response from the user.
+     */
+    fun onPositiveResponse(reasonCode: Int)
+
+    /**
+     * Method to dispatch intent for toggling "install from unknown sources" setting for a package.
+     */
+    fun sendUnknownAppsIntent(sourcePackageName: String)
+
+    /**
+     * Method to handle a negative response from the user.
+     */
+    fun onNegativeResponse(stageCode: Int)
+
+    fun onNegativeResponse(resultCode: Int, data: Intent?)
+
+    /**
+     * Launch the intent to open the newly installed / updated app.
+     */
+    fun openInstalledApp(intent: Intent?)
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/InstallLaunch.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/InstallLaunch.java
deleted file mode 100644
index d06b4b3..0000000
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/InstallLaunch.java
+++ /dev/null
@@ -1,354 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.packageinstaller.v2.ui;
-
-import static android.content.Intent.CATEGORY_LAUNCHER;
-import static android.content.Intent.FLAG_ACTIVITY_NO_HISTORY;
-import static android.os.Process.INVALID_UID;
-import static com.android.packageinstaller.v2.model.InstallRepository.EXTRA_STAGED_SESSION_ID;
-
-import android.app.Activity;
-import android.app.AppOpsManager;
-import android.content.ActivityNotFoundException;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.UserManager;
-import android.provider.Settings;
-import android.util.Log;
-import android.view.Window;
-import androidx.annotation.Nullable;
-import androidx.fragment.app.DialogFragment;
-import androidx.fragment.app.FragmentActivity;
-import androidx.fragment.app.FragmentManager;
-import androidx.lifecycle.ViewModelProvider;
-import com.android.packageinstaller.R;
-import com.android.packageinstaller.v2.model.InstallRepository;
-import com.android.packageinstaller.v2.model.InstallRepository.CallerInfo;
-import com.android.packageinstaller.v2.model.installstagedata.InstallAborted;
-import com.android.packageinstaller.v2.model.installstagedata.InstallFailed;
-import com.android.packageinstaller.v2.model.installstagedata.InstallInstalling;
-import com.android.packageinstaller.v2.model.installstagedata.InstallStage;
-import com.android.packageinstaller.v2.model.installstagedata.InstallSuccess;
-import com.android.packageinstaller.v2.model.installstagedata.InstallUserActionRequired;
-import com.android.packageinstaller.v2.ui.fragments.AnonymousSourceFragment;
-import com.android.packageinstaller.v2.ui.fragments.ExternalSourcesBlockedFragment;
-import com.android.packageinstaller.v2.ui.fragments.InstallConfirmationFragment;
-import com.android.packageinstaller.v2.ui.fragments.InstallFailedFragment;
-import com.android.packageinstaller.v2.ui.fragments.InstallInstallingFragment;
-import com.android.packageinstaller.v2.ui.fragments.InstallStagingFragment;
-import com.android.packageinstaller.v2.ui.fragments.InstallSuccessFragment;
-import com.android.packageinstaller.v2.ui.fragments.SimpleErrorFragment;
-import com.android.packageinstaller.v2.viewmodel.InstallViewModel;
-import com.android.packageinstaller.v2.viewmodel.InstallViewModelFactory;
-import java.util.ArrayList;
-import java.util.List;
-
-public class InstallLaunch extends FragmentActivity implements InstallActionListener {
-
-    public static final String EXTRA_CALLING_PKG_UID =
-            InstallLaunch.class.getPackageName() + ".callingPkgUid";
-    public static final String EXTRA_CALLING_PKG_NAME =
-            InstallLaunch.class.getPackageName() + ".callingPkgName";
-    private static final String TAG = InstallLaunch.class.getSimpleName();
-    private static final String TAG_DIALOG = "dialog";
-    private final int REQUEST_TRUST_EXTERNAL_SOURCE = 1;
-    private final boolean mLocalLOGV = false;
-    /**
-     * A collection of unknown sources listeners that are actively listening for app ops mode
-     * changes
-     */
-    private final List<UnknownSourcesListener> mActiveUnknownSourcesListeners = new ArrayList<>(1);
-    private InstallViewModel mInstallViewModel;
-    private InstallRepository mInstallRepository;
-    private FragmentManager mFragmentManager;
-    private AppOpsManager mAppOpsManager;
-
-    @Override
-    protected void onCreate(@Nullable Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        this.requestWindowFeature(Window.FEATURE_NO_TITLE);
-
-        mFragmentManager = getSupportFragmentManager();
-        mAppOpsManager = getSystemService(AppOpsManager.class);
-
-        mInstallRepository = new InstallRepository(getApplicationContext());
-        mInstallViewModel = new ViewModelProvider(this,
-                new InstallViewModelFactory(this.getApplication(), mInstallRepository)).get(
-                InstallViewModel.class);
-
-        Intent intent = getIntent();
-        CallerInfo info = new CallerInfo(
-                intent.getStringExtra(EXTRA_CALLING_PKG_NAME),
-                intent.getIntExtra(EXTRA_CALLING_PKG_UID, INVALID_UID));
-        mInstallViewModel.preprocessIntent(intent, info);
-
-        mInstallViewModel.getCurrentInstallStage().observe(this, this::onInstallStageChange);
-    }
-
-    /**
-     * Main controller of the UI. This method shows relevant dialogs based on the install stage
-     */
-    private void onInstallStageChange(InstallStage installStage) {
-        switch (installStage.getStageCode()) {
-            case InstallStage.STAGE_STAGING -> {
-                InstallStagingFragment stagingDialog = new InstallStagingFragment();
-                showDialogInner(stagingDialog);
-                mInstallViewModel.getStagingProgress().observe(this, stagingDialog::setProgress);
-            }
-            case InstallStage.STAGE_ABORTED -> {
-                InstallAborted aborted = (InstallAborted) installStage;
-                switch (aborted.getAbortReason()) {
-                    // TODO: check if any dialog is to be shown for ABORT_REASON_INTERNAL_ERROR
-                    case InstallAborted.ABORT_REASON_DONE,
-                        InstallAborted.ABORT_REASON_INTERNAL_ERROR ->
-                        setResult(aborted.getActivityResultCode(), aborted.getResultIntent(), true);
-                    case InstallAborted.ABORT_REASON_POLICY -> showPolicyRestrictionDialog(aborted);
-                    default -> setResult(RESULT_CANCELED, null, true);
-                }
-            }
-            case InstallStage.STAGE_USER_ACTION_REQUIRED -> {
-                InstallUserActionRequired uar = (InstallUserActionRequired) installStage;
-                switch (uar.getActionReason()) {
-                    case InstallUserActionRequired.USER_ACTION_REASON_INSTALL_CONFIRMATION -> {
-                        InstallConfirmationFragment actionDialog =
-                            new InstallConfirmationFragment(uar);
-                        showDialogInner(actionDialog);
-                    }
-                    case InstallUserActionRequired.USER_ACTION_REASON_UNKNOWN_SOURCE -> {
-                        ExternalSourcesBlockedFragment externalSourceDialog =
-                            new ExternalSourcesBlockedFragment(uar);
-                        showDialogInner(externalSourceDialog);
-                    }
-                    case InstallUserActionRequired.USER_ACTION_REASON_ANONYMOUS_SOURCE -> {
-                        AnonymousSourceFragment anonymousSourceDialog =
-                            new AnonymousSourceFragment();
-                        showDialogInner(anonymousSourceDialog);
-                    }
-                }
-            }
-            case InstallStage.STAGE_INSTALLING -> {
-                InstallInstalling installing = (InstallInstalling) installStage;
-                InstallInstallingFragment installingDialog =
-                    new InstallInstallingFragment(installing);
-                showDialogInner(installingDialog);
-            }
-            case InstallStage.STAGE_SUCCESS -> {
-                InstallSuccess success = (InstallSuccess) installStage;
-                if (success.shouldReturnResult()) {
-                    Intent successIntent = success.getResultIntent();
-                    setResult(Activity.RESULT_OK, successIntent, true);
-                } else {
-                    InstallSuccessFragment successFragment = new InstallSuccessFragment(success);
-                    showDialogInner(successFragment);
-                }
-            }
-            case InstallStage.STAGE_FAILED -> {
-                InstallFailed failed = (InstallFailed) installStage;
-                InstallFailedFragment failedDialog = new InstallFailedFragment(failed);
-                showDialogInner(failedDialog);
-            }
-            default -> {
-                Log.d(TAG, "Unimplemented stage: " + installStage.getStageCode());
-                showDialogInner(null);
-            }
-        }
-    }
-
-    private void showPolicyRestrictionDialog(InstallAborted aborted) {
-        String restriction = aborted.getMessage();
-        Intent adminSupportIntent = aborted.getResultIntent();
-        boolean shouldFinish;
-
-        // If the given restriction is set by an admin, display information about the
-        // admin enforcing the restriction for the affected user. If not enforced by the admin,
-        // show the system dialog.
-        if (adminSupportIntent != null) {
-            if (mLocalLOGV) {
-                Log.i(TAG, "Restriction set by admin, starting " + adminSupportIntent);
-            }
-            startActivity(adminSupportIntent);
-            // Finish the package installer app since the next dialog will not be shown by this app
-            shouldFinish = true;
-        } else {
-            if (mLocalLOGV) {
-                Log.i(TAG, "Restriction set by system: " + restriction);
-            }
-            DialogFragment blockedByPolicyDialog = createDevicePolicyRestrictionDialog(restriction);
-            // Don't finish the package installer app since the next dialog
-            // will be shown by this app
-            shouldFinish = false;
-            showDialogInner(blockedByPolicyDialog);
-        }
-        setResult(RESULT_CANCELED, null, shouldFinish);
-    }
-
-    /**
-     * Create a new dialog based on the install restriction enforced.
-     *
-     * @param restriction The restriction to create the dialog for
-     * @return The dialog
-     */
-    private DialogFragment createDevicePolicyRestrictionDialog(String restriction) {
-        if (mLocalLOGV) {
-            Log.i(TAG, "createDialog(" + restriction + ")");
-        }
-        return switch (restriction) {
-            case UserManager.DISALLOW_INSTALL_APPS ->
-                new SimpleErrorFragment(R.string.install_apps_user_restriction_dlg_text);
-            case UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES,
-                UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY ->
-                new SimpleErrorFragment(R.string.unknown_apps_user_restriction_dlg_text);
-            default -> null;
-        };
-    }
-
-    /**
-     * Replace any visible dialog by the dialog returned by InstallRepository
-     *
-     * @param newDialog The new dialog to display
-     */
-    private void showDialogInner(@Nullable DialogFragment newDialog) {
-        DialogFragment currentDialog = (DialogFragment) mFragmentManager.findFragmentByTag(
-            TAG_DIALOG);
-        if (currentDialog != null) {
-            currentDialog.dismissAllowingStateLoss();
-        }
-        if (newDialog != null) {
-            newDialog.show(mFragmentManager, TAG_DIALOG);
-        }
-    }
-
-    public void setResult(int resultCode, Intent data, boolean shouldFinish) {
-        super.setResult(resultCode, data);
-        if (shouldFinish) {
-            finish();
-        }
-    }
-
-    @Override
-    public void onPositiveResponse(int reasonCode) {
-        switch (reasonCode) {
-            case InstallUserActionRequired.USER_ACTION_REASON_ANONYMOUS_SOURCE ->
-                mInstallViewModel.forcedSkipSourceCheck();
-            case InstallUserActionRequired.USER_ACTION_REASON_INSTALL_CONFIRMATION ->
-                mInstallViewModel.initiateInstall();
-        }
-    }
-
-    @Override
-    public void onNegativeResponse(int stageCode) {
-        if (stageCode == InstallStage.STAGE_USER_ACTION_REQUIRED) {
-            mInstallViewModel.cleanupInstall();
-        }
-        setResult(Activity.RESULT_CANCELED, null, true);
-    }
-
-    @Override
-    public void sendUnknownAppsIntent(String sourcePackageName) {
-        Intent settingsIntent = new Intent();
-        settingsIntent.setAction(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES);
-        final Uri packageUri = Uri.parse("package:" + sourcePackageName);
-        settingsIntent.setData(packageUri);
-        settingsIntent.setFlags(FLAG_ACTIVITY_NO_HISTORY);
-
-        try {
-            registerAppOpChangeListener(new UnknownSourcesListener(sourcePackageName),
-                sourcePackageName);
-            startActivityForResult(settingsIntent, REQUEST_TRUST_EXTERNAL_SOURCE);
-        } catch (ActivityNotFoundException exc) {
-            Log.e(TAG, "Settings activity not found for action: "
-                + Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES);
-        }
-    }
-
-    @Override
-    public void openInstalledApp(Intent intent) {
-        setResult(RESULT_OK, intent, true);
-        if (intent != null && intent.hasCategory(CATEGORY_LAUNCHER)) {
-            startActivity(intent);
-        }
-    }
-
-    private void registerAppOpChangeListener(UnknownSourcesListener listener, String packageName) {
-        mAppOpsManager.startWatchingMode(
-            AppOpsManager.OPSTR_REQUEST_INSTALL_PACKAGES, packageName,
-            listener);
-        mActiveUnknownSourcesListeners.add(listener);
-    }
-
-    private void unregisterAppOpChangeListener(UnknownSourcesListener listener) {
-        mActiveUnknownSourcesListeners.remove(listener);
-        mAppOpsManager.stopWatchingMode(listener);
-    }
-
-    @Override
-    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
-        super.onActivityResult(requestCode, resultCode, data);
-        if (requestCode == REQUEST_TRUST_EXTERNAL_SOURCE) {
-            mInstallViewModel.reattemptInstall();
-        } else {
-            setResult(Activity.RESULT_CANCELED,  null, true);
-        }
-    }
-
-    @Override
-    protected void onDestroy() {
-        super.onDestroy();
-        while (!mActiveUnknownSourcesListeners.isEmpty()) {
-            unregisterAppOpChangeListener(mActiveUnknownSourcesListeners.get(0));
-        }
-    }
-
-    private class UnknownSourcesListener implements AppOpsManager.OnOpChangedListener {
-
-        private final String mOriginatingPackage;
-
-        public UnknownSourcesListener(String originatingPackage) {
-            mOriginatingPackage = originatingPackage;
-        }
-
-        @Override
-        public void onOpChanged(String op, String packageName) {
-            if (!mOriginatingPackage.equals(packageName)) {
-                return;
-            }
-            unregisterAppOpChangeListener(this);
-            mActiveUnknownSourcesListeners.remove(this);
-            if (isDestroyed()) {
-                return;
-            }
-            new Handler(Looper.getMainLooper()).postDelayed(() -> {
-                if (!isDestroyed()) {
-                    // Relaunch Pia to continue installation.
-                    startActivity(getIntent()
-                        .putExtra(EXTRA_STAGED_SESSION_ID, mInstallViewModel.getStagedSessionId()));
-
-                    // If the userId of the root of activity stack is different from current userId,
-                    // starting Pia again lead to duplicate instances of the app in the stack.
-                    // As such, finish the old instance. Old Pia is finished even if the userId of
-                    // the root is the same, since there is no way to determine the difference in
-                    // userIds.
-                    finish();
-                }
-            }, 500);
-        }
-    }
-}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/InstallLaunch.kt b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/InstallLaunch.kt
new file mode 100644
index 0000000..6f8eca3
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/InstallLaunch.kt
@@ -0,0 +1,360 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.packageinstaller.v2.ui
+
+import android.app.Activity
+import android.app.AppOpsManager
+import android.content.ActivityNotFoundException
+import android.content.Intent
+import android.net.Uri
+import android.os.Bundle
+import android.os.Handler
+import android.os.Looper
+import android.os.Process
+import android.os.UserManager
+import android.provider.Settings
+import android.util.Log
+import android.view.Window
+import androidx.activity.result.ActivityResultLauncher
+import androidx.activity.result.contract.ActivityResultContracts
+import androidx.fragment.app.DialogFragment
+import androidx.fragment.app.FragmentActivity
+import androidx.fragment.app.FragmentManager
+import androidx.lifecycle.ViewModelProvider
+import com.android.packageinstaller.R
+import com.android.packageinstaller.v2.model.InstallAborted
+import com.android.packageinstaller.v2.model.InstallFailed
+import com.android.packageinstaller.v2.model.InstallInstalling
+import com.android.packageinstaller.v2.model.InstallRepository
+import com.android.packageinstaller.v2.model.InstallStage
+import com.android.packageinstaller.v2.model.InstallSuccess
+import com.android.packageinstaller.v2.model.InstallUserActionRequired
+import com.android.packageinstaller.v2.ui.fragments.AnonymousSourceFragment
+import com.android.packageinstaller.v2.ui.fragments.ExternalSourcesBlockedFragment
+import com.android.packageinstaller.v2.ui.fragments.InstallConfirmationFragment
+import com.android.packageinstaller.v2.ui.fragments.InstallFailedFragment
+import com.android.packageinstaller.v2.ui.fragments.InstallInstallingFragment
+import com.android.packageinstaller.v2.ui.fragments.InstallStagingFragment
+import com.android.packageinstaller.v2.ui.fragments.InstallSuccessFragment
+import com.android.packageinstaller.v2.ui.fragments.ParseErrorFragment
+import com.android.packageinstaller.v2.ui.fragments.SimpleErrorFragment
+import com.android.packageinstaller.v2.viewmodel.InstallViewModel
+import com.android.packageinstaller.v2.viewmodel.InstallViewModelFactory
+
+class InstallLaunch : FragmentActivity(), InstallActionListener {
+
+    companion object {
+        @JvmField val EXTRA_CALLING_PKG_UID =
+            InstallLaunch::class.java.packageName + ".callingPkgUid"
+        @JvmField val EXTRA_CALLING_PKG_NAME =
+            InstallLaunch::class.java.packageName + ".callingPkgName"
+        private val LOG_TAG = InstallLaunch::class.java.simpleName
+        private const val TAG_DIALOG = "dialog"
+    }
+
+    private val localLOGV = false
+
+    /**
+     * A collection of unknown sources listeners that are actively listening for app ops mode
+     * changes
+     */
+    private val activeUnknownSourcesListeners: MutableList<UnknownSourcesListener> = ArrayList(1)
+    private var installViewModel: InstallViewModel? = null
+    private var installRepository: InstallRepository? = null
+    private var fragmentManager: FragmentManager? = null
+    private var appOpsManager: AppOpsManager? = null
+    private lateinit var unknownAppsIntentLauncher: ActivityResultLauncher<Intent>
+
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        requestWindowFeature(Window.FEATURE_NO_TITLE)
+        fragmentManager = supportFragmentManager
+        appOpsManager = getSystemService(AppOpsManager::class.java)
+        installRepository = InstallRepository(applicationContext)
+        installViewModel = ViewModelProvider(
+            this, InstallViewModelFactory(this.application, installRepository!!)
+        )[InstallViewModel::class.java]
+
+        val intent = intent
+        val info = InstallRepository.CallerInfo(
+            intent.getStringExtra(EXTRA_CALLING_PKG_NAME),
+            intent.getIntExtra(EXTRA_CALLING_PKG_UID, Process.INVALID_UID)
+        )
+        installViewModel!!.preprocessIntent(intent, info)
+        installViewModel!!.currentInstallStage.observe(this) { installStage: InstallStage ->
+            onInstallStageChange(installStage)
+        }
+
+        // Used to launch intent for Settings, to manage "install unknown apps" permission
+        unknownAppsIntentLauncher =
+            registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
+                // Reattempt installation on coming back from Settings, after toggling
+                // "install unknown apps" permission
+                installViewModel!!.reattemptInstall()
+            }
+    }
+
+    /**
+     * Main controller of the UI. This method shows relevant dialogs based on the install stage
+     */
+    private fun onInstallStageChange(installStage: InstallStage) {
+        when (installStage.stageCode) {
+            InstallStage.STAGE_STAGING -> {
+                val stagingDialog = InstallStagingFragment()
+                showDialogInner(stagingDialog)
+                installViewModel!!.stagingProgress.observe(this) { progress: Int ->
+                    stagingDialog.setProgress(progress)
+                }
+            }
+
+            InstallStage.STAGE_ABORTED -> {
+                val aborted = installStage as InstallAborted
+                when (aborted.abortReason) {
+                    InstallAborted.ABORT_REASON_DONE,
+                    InstallAborted.ABORT_REASON_INTERNAL_ERROR -> {
+                        if (aborted.errorDialogType == InstallAborted.DLG_PACKAGE_ERROR) {
+                            val parseErrorDialog = ParseErrorFragment(aborted)
+                            showDialogInner(parseErrorDialog)
+                        } else {
+                            setResult(aborted.activityResultCode, aborted.resultIntent, true)
+                        }
+                    }
+
+                    InstallAborted.ABORT_REASON_POLICY -> showPolicyRestrictionDialog(aborted)
+                    else -> setResult(Activity.RESULT_CANCELED, null, true)
+                }
+            }
+
+            InstallStage.STAGE_USER_ACTION_REQUIRED -> {
+                val uar = installStage as InstallUserActionRequired
+                when (uar.actionReason) {
+                    InstallUserActionRequired.USER_ACTION_REASON_INSTALL_CONFIRMATION -> {
+                        val actionDialog = InstallConfirmationFragment(uar)
+                        showDialogInner(actionDialog)
+                    }
+
+                    InstallUserActionRequired.USER_ACTION_REASON_UNKNOWN_SOURCE -> {
+                        val externalSourceDialog = ExternalSourcesBlockedFragment(uar)
+                        showDialogInner(externalSourceDialog)
+                    }
+
+                    InstallUserActionRequired.USER_ACTION_REASON_ANONYMOUS_SOURCE -> {
+                        val anonymousSourceDialog = AnonymousSourceFragment()
+                        showDialogInner(anonymousSourceDialog)
+                    }
+                }
+            }
+
+            InstallStage.STAGE_INSTALLING -> {
+                val installing = installStage as InstallInstalling
+                val installingDialog = InstallInstallingFragment(installing)
+                showDialogInner(installingDialog)
+            }
+
+            InstallStage.STAGE_SUCCESS -> {
+                val success = installStage as InstallSuccess
+                if (success.shouldReturnResult) {
+                    val successIntent = success.resultIntent
+                    setResult(Activity.RESULT_OK, successIntent, true)
+                } else {
+                    val successFragment = InstallSuccessFragment(success)
+                    showDialogInner(successFragment)
+                }
+            }
+
+            InstallStage.STAGE_FAILED -> {
+                val failed = installStage as InstallFailed
+                val failedDialog = InstallFailedFragment(failed)
+                showDialogInner(failedDialog)
+            }
+
+            else -> {
+                Log.d(LOG_TAG, "Unimplemented stage: " + installStage.stageCode)
+                showDialogInner(null)
+            }
+        }
+    }
+
+    private fun showPolicyRestrictionDialog(aborted: InstallAborted) {
+        val restriction = aborted.message
+        val adminSupportIntent = aborted.resultIntent
+        var shouldFinish: Boolean = false
+
+        // If the given restriction is set by an admin, display information about the
+        // admin enforcing the restriction for the affected user. If not enforced by the admin,
+        // show the system dialog.
+        if (adminSupportIntent != null) {
+            if (localLOGV) {
+                Log.i(LOG_TAG, "Restriction set by admin, starting $adminSupportIntent")
+            }
+            startActivity(adminSupportIntent)
+            // Finish the package installer app since the next dialog will not be shown by this app
+            shouldFinish = true
+        } else {
+            if (localLOGV) {
+                Log.i(LOG_TAG, "Restriction set by system: $restriction")
+            }
+            val blockedByPolicyDialog = createDevicePolicyRestrictionDialog(restriction)
+            // Don't finish the package installer app since the next dialog
+            // will be shown by this app
+            shouldFinish = blockedByPolicyDialog == null
+            showDialogInner(blockedByPolicyDialog)
+        }
+        setResult(Activity.RESULT_CANCELED, null, shouldFinish)
+    }
+
+    /**
+     * Create a new dialog based on the install restriction enforced.
+     *
+     * @param restriction The restriction to create the dialog for
+     * @return The dialog
+     */
+    private fun createDevicePolicyRestrictionDialog(restriction: String?): DialogFragment? {
+        if (localLOGV) {
+            Log.i(LOG_TAG, "createDialog($restriction)")
+        }
+        return when (restriction) {
+            UserManager.DISALLOW_INSTALL_APPS ->
+                SimpleErrorFragment(R.string.install_apps_user_restriction_dlg_text)
+
+            UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES,
+            UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY ->
+                SimpleErrorFragment(R.string.unknown_apps_user_restriction_dlg_text)
+
+            else -> null
+        }
+    }
+
+    /**
+     * Replace any visible dialog by the dialog returned by InstallRepository
+     *
+     * @param newDialog The new dialog to display
+     */
+    private fun showDialogInner(newDialog: DialogFragment?) {
+        val currentDialog = fragmentManager!!.findFragmentByTag(TAG_DIALOG) as DialogFragment?
+        currentDialog?.dismissAllowingStateLoss()
+        newDialog?.show(fragmentManager!!, TAG_DIALOG)
+    }
+
+    fun setResult(resultCode: Int, data: Intent?, shouldFinish: Boolean) {
+        super.setResult(resultCode, data)
+        if (shouldFinish) {
+            finish()
+        }
+    }
+
+    override fun onPositiveResponse(reasonCode: Int) {
+        when (reasonCode) {
+            InstallUserActionRequired.USER_ACTION_REASON_ANONYMOUS_SOURCE ->
+                installViewModel!!.forcedSkipSourceCheck()
+
+            InstallUserActionRequired.USER_ACTION_REASON_INSTALL_CONFIRMATION ->
+                installViewModel!!.initiateInstall()
+        }
+    }
+
+    override fun onNegativeResponse(stageCode: Int) {
+        if (stageCode == InstallStage.STAGE_USER_ACTION_REQUIRED) {
+            installViewModel!!.cleanupInstall()
+        }
+        setResult(Activity.RESULT_CANCELED, null, true)
+    }
+
+    override fun onNegativeResponse(resultCode: Int, data: Intent?) {
+        setResult(resultCode, data, true)
+    }
+
+    override fun sendUnknownAppsIntent(sourcePackageName: String) {
+        val settingsIntent = Intent()
+        settingsIntent.setAction(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES)
+        val packageUri = Uri.parse("package:$sourcePackageName")
+        settingsIntent.setData(packageUri)
+        settingsIntent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY)
+        try {
+            registerAppOpChangeListener(
+                UnknownSourcesListener(sourcePackageName), sourcePackageName
+            )
+            unknownAppsIntentLauncher.launch(settingsIntent)
+        } catch (exc: ActivityNotFoundException) {
+            Log.e(
+                LOG_TAG, "Settings activity not found for action: "
+                    + Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES
+            )
+        }
+    }
+
+    override fun openInstalledApp(intent: Intent?) {
+        setResult(Activity.RESULT_OK, intent, true)
+        if (intent != null && intent.hasCategory(Intent.CATEGORY_LAUNCHER)) {
+            startActivity(intent)
+        }
+    }
+
+    private fun registerAppOpChangeListener(listener: UnknownSourcesListener, packageName: String) {
+        appOpsManager!!.startWatchingMode(
+            AppOpsManager.OPSTR_REQUEST_INSTALL_PACKAGES,
+            packageName,
+            listener
+        )
+        activeUnknownSourcesListeners.add(listener)
+    }
+
+    private fun unregisterAppOpChangeListener(listener: UnknownSourcesListener) {
+        activeUnknownSourcesListeners.remove(listener)
+        appOpsManager!!.stopWatchingMode(listener)
+    }
+
+    override fun onDestroy() {
+        super.onDestroy()
+        while (activeUnknownSourcesListeners.isNotEmpty()) {
+            unregisterAppOpChangeListener(activeUnknownSourcesListeners[0])
+        }
+    }
+
+    private inner class UnknownSourcesListener(private val mOriginatingPackage: String) :
+        AppOpsManager.OnOpChangedListener {
+        override fun onOpChanged(op: String, packageName: String) {
+            if (mOriginatingPackage != packageName) {
+                return
+            }
+            unregisterAppOpChangeListener(this)
+            activeUnknownSourcesListeners.remove(this)
+            if (isDestroyed) {
+                return
+            }
+            Handler(Looper.getMainLooper()).postDelayed({
+                if (!isDestroyed) {
+                    // Relaunch Pia to continue installation.
+                    startActivity(
+                        intent.putExtra(
+                            InstallRepository.EXTRA_STAGED_SESSION_ID,
+                            installViewModel!!.stagedSessionId
+                        )
+                    )
+
+                    // If the userId of the root of activity stack is different from current userId,
+                    // starting Pia again lead to duplicate instances of the app in the stack.
+                    // As such, finish the old instance. Old Pia is finished even if the userId of
+                    // the root is the same, since there is no way to determine the difference in
+                    // userIds.
+                    finish()
+                }
+            }, 500)
+        }
+    }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/UninstallActionListener.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/UninstallActionListener.kt
similarity index 78%
rename from packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/UninstallActionListener.java
rename to packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/UninstallActionListener.kt
index b8a9355..33f5db3 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/UninstallActionListener.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/UninstallActionListener.kt
@@ -14,11 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.packageinstaller.v2.ui;
+package com.android.packageinstaller.v2.ui
 
-public interface UninstallActionListener {
-
-    void onPositiveResponse(boolean keepData);
-
-    void onNegativeResponse();
+interface UninstallActionListener {
+    fun onPositiveResponse(keepData: Boolean)
+    fun onNegativeResponse()
 }
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/UninstallLaunch.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/UninstallLaunch.java
deleted file mode 100644
index 7638e91..0000000
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/UninstallLaunch.java
+++ /dev/null
@@ -1,167 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.packageinstaller.v2.ui;
-
-import static android.os.Process.INVALID_UID;
-import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
-
-import android.app.Activity;
-import android.app.NotificationManager;
-import android.content.Intent;
-import android.os.Bundle;
-import android.util.Log;
-import android.widget.Toast;
-import androidx.annotation.Nullable;
-import androidx.fragment.app.DialogFragment;
-import androidx.fragment.app.FragmentActivity;
-import androidx.fragment.app.FragmentManager;
-import androidx.lifecycle.ViewModelProvider;
-import com.android.packageinstaller.v2.model.UninstallRepository;
-import com.android.packageinstaller.v2.model.UninstallRepository.CallerInfo;
-import com.android.packageinstaller.v2.model.uninstallstagedata.UninstallAborted;
-import com.android.packageinstaller.v2.model.uninstallstagedata.UninstallFailed;
-import com.android.packageinstaller.v2.model.uninstallstagedata.UninstallStage;
-import com.android.packageinstaller.v2.model.uninstallstagedata.UninstallSuccess;
-import com.android.packageinstaller.v2.model.uninstallstagedata.UninstallUninstalling;
-import com.android.packageinstaller.v2.model.uninstallstagedata.UninstallUserActionRequired;
-import com.android.packageinstaller.v2.ui.fragments.UninstallConfirmationFragment;
-import com.android.packageinstaller.v2.ui.fragments.UninstallErrorFragment;
-import com.android.packageinstaller.v2.ui.fragments.UninstallUninstallingFragment;
-import com.android.packageinstaller.v2.viewmodel.UninstallViewModel;
-import com.android.packageinstaller.v2.viewmodel.UninstallViewModelFactory;
-
-public class UninstallLaunch extends FragmentActivity implements UninstallActionListener {
-
-    public static final String EXTRA_CALLING_PKG_UID =
-        UninstallLaunch.class.getPackageName() + ".callingPkgUid";
-    public static final String EXTRA_CALLING_ACTIVITY_NAME =
-        UninstallLaunch.class.getPackageName() + ".callingActivityName";
-    public static final String TAG = UninstallLaunch.class.getSimpleName();
-    private static final String TAG_DIALOG = "dialog";
-
-    private UninstallViewModel mUninstallViewModel;
-    private UninstallRepository mUninstallRepository;
-    private FragmentManager mFragmentManager;
-    private NotificationManager mNotificationManager;
-
-    @Override
-    protected void onCreate(@Nullable Bundle savedInstanceState) {
-        getWindow().addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
-
-        // Never restore any state, esp. never create any fragments. The data in the fragment might
-        // be stale, if e.g. the app was uninstalled while the activity was destroyed.
-        super.onCreate(null);
-
-        mFragmentManager = getSupportFragmentManager();
-        mNotificationManager = getSystemService(NotificationManager.class);
-
-        mUninstallRepository = new UninstallRepository(getApplicationContext());
-        mUninstallViewModel = new ViewModelProvider(this,
-            new UninstallViewModelFactory(this.getApplication(), mUninstallRepository)).get(
-            UninstallViewModel.class);
-
-        Intent intent = getIntent();
-        CallerInfo callerInfo = new CallerInfo(
-            intent.getStringExtra(EXTRA_CALLING_ACTIVITY_NAME),
-            intent.getIntExtra(EXTRA_CALLING_PKG_UID, INVALID_UID));
-        mUninstallViewModel.preprocessIntent(intent, callerInfo);
-
-        mUninstallViewModel.getCurrentUninstallStage().observe(this,
-            this::onUninstallStageChange);
-    }
-
-    /**
-     * Main controller of the UI. This method shows relevant dialogs / fragments based on the
-     * uninstall stage
-     */
-    private void onUninstallStageChange(UninstallStage uninstallStage) {
-        if (uninstallStage.getStageCode() == UninstallStage.STAGE_ABORTED) {
-            UninstallAborted aborted = (UninstallAborted) uninstallStage;
-            if (aborted.getAbortReason() == UninstallAborted.ABORT_REASON_APP_UNAVAILABLE ||
-                aborted.getAbortReason() == UninstallAborted.ABORT_REASON_USER_NOT_ALLOWED) {
-                UninstallErrorFragment errorDialog = new UninstallErrorFragment(aborted);
-                showDialogInner(errorDialog);
-            } else {
-                setResult(aborted.getActivityResultCode(), null, true);
-            }
-        } else if (uninstallStage.getStageCode() == UninstallStage.STAGE_USER_ACTION_REQUIRED) {
-            UninstallUserActionRequired uar = (UninstallUserActionRequired) uninstallStage;
-            UninstallConfirmationFragment confirmationDialog = new UninstallConfirmationFragment(
-                uar);
-            showDialogInner(confirmationDialog);
-        } else if (uninstallStage.getStageCode() == UninstallStage.STAGE_UNINSTALLING) {
-            // TODO: This shows a fragment whether or not user requests a result or not.
-            //  Originally, if the user does not request a result, we used to show a notification.
-            //  And a fragment if the user requests a result back. Should we consolidate and
-            //  show a fragment always?
-            UninstallUninstalling uninstalling = (UninstallUninstalling) uninstallStage;
-            UninstallUninstallingFragment uninstallingDialog = new UninstallUninstallingFragment(
-                uninstalling);
-            showDialogInner(uninstallingDialog);
-        } else if (uninstallStage.getStageCode() == UninstallStage.STAGE_FAILED) {
-            UninstallFailed failed = (UninstallFailed) uninstallStage;
-            if (!failed.returnResult()) {
-                mNotificationManager.notify(failed.getUninstallId(),
-                    failed.getUninstallNotification());
-            }
-            setResult(failed.getActivityResultCode(), failed.getResultIntent(), true);
-        } else if (uninstallStage.getStageCode() == UninstallStage.STAGE_SUCCESS) {
-            UninstallSuccess success = (UninstallSuccess) uninstallStage;
-            if (success.getMessage() != null) {
-                Toast.makeText(this, success.getMessage(), Toast.LENGTH_LONG).show();
-            }
-            setResult(success.getActivityResultCode(), success.getResultIntent(), true);
-        } else {
-            Log.e(TAG, "Invalid stage: " + uninstallStage.getStageCode());
-            showDialogInner(null);
-        }
-    }
-
-    /**
-     * Replace any visible dialog by the dialog returned by InstallRepository
-     *
-     * @param newDialog The new dialog to display
-     */
-    private void showDialogInner(DialogFragment newDialog) {
-        DialogFragment currentDialog = (DialogFragment) mFragmentManager.findFragmentByTag(
-            TAG_DIALOG);
-        if (currentDialog != null) {
-            currentDialog.dismissAllowingStateLoss();
-        }
-        if (newDialog != null) {
-            newDialog.show(mFragmentManager, TAG_DIALOG);
-        }
-    }
-
-    public void setResult(int resultCode, Intent data, boolean shouldFinish) {
-        super.setResult(resultCode, data);
-        if (shouldFinish) {
-            finish();
-        }
-    }
-
-    @Override
-    public void onPositiveResponse(boolean keepData) {
-        mUninstallViewModel.initiateUninstall(keepData);
-    }
-
-    @Override
-    public void onNegativeResponse() {
-        mUninstallViewModel.cancelInstall();
-        setResult(Activity.RESULT_FIRST_USER, null, true);
-    }
-}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/UninstallLaunch.kt b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/UninstallLaunch.kt
new file mode 100644
index 0000000..0050c7e
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/UninstallLaunch.kt
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.packageinstaller.v2.ui
+
+import android.app.Activity
+import android.app.NotificationManager
+import android.content.Intent
+import android.os.Bundle
+import android.os.Process
+import android.util.Log
+import android.view.WindowManager
+import android.widget.Toast
+import androidx.fragment.app.DialogFragment
+import androidx.fragment.app.FragmentActivity
+import androidx.fragment.app.FragmentManager
+import androidx.lifecycle.ViewModelProvider
+import com.android.packageinstaller.v2.model.UninstallAborted
+import com.android.packageinstaller.v2.model.UninstallFailed
+import com.android.packageinstaller.v2.model.UninstallRepository
+import com.android.packageinstaller.v2.model.UninstallStage
+import com.android.packageinstaller.v2.model.UninstallSuccess
+import com.android.packageinstaller.v2.model.UninstallUninstalling
+import com.android.packageinstaller.v2.model.UninstallUserActionRequired
+import com.android.packageinstaller.v2.ui.fragments.UninstallConfirmationFragment
+import com.android.packageinstaller.v2.ui.fragments.UninstallErrorFragment
+import com.android.packageinstaller.v2.ui.fragments.UninstallUninstallingFragment
+import com.android.packageinstaller.v2.viewmodel.UninstallViewModel
+import com.android.packageinstaller.v2.viewmodel.UninstallViewModelFactory
+
+class UninstallLaunch : FragmentActivity(), UninstallActionListener {
+
+    companion object {
+        @JvmField val EXTRA_CALLING_PKG_UID =
+            UninstallLaunch::class.java.packageName + ".callingPkgUid"
+        @JvmField val EXTRA_CALLING_ACTIVITY_NAME =
+            UninstallLaunch::class.java.packageName + ".callingActivityName"
+        val LOG_TAG = UninstallLaunch::class.java.simpleName
+        private const val TAG_DIALOG = "dialog"
+    }
+
+    private var uninstallViewModel: UninstallViewModel? = null
+    private var uninstallRepository: UninstallRepository? = null
+    private var fragmentManager: FragmentManager? = null
+    private var notificationManager: NotificationManager? = null
+    override fun onCreate(savedInstanceState: Bundle?) {
+        window.addSystemFlags(WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS)
+
+        // Never restore any state, esp. never create any fragments. The data in the fragment might
+        // be stale, if e.g. the app was uninstalled while the activity was destroyed.
+        super.onCreate(null)
+        fragmentManager = supportFragmentManager
+        notificationManager = getSystemService(NotificationManager::class.java)
+
+        uninstallRepository = UninstallRepository(applicationContext)
+        uninstallViewModel = ViewModelProvider(
+            this, UninstallViewModelFactory(this.application, uninstallRepository!!)
+        ).get(UninstallViewModel::class.java)
+
+        val intent = intent
+        val callerInfo = UninstallRepository.CallerInfo(
+            intent.getStringExtra(EXTRA_CALLING_ACTIVITY_NAME),
+            intent.getIntExtra(EXTRA_CALLING_PKG_UID, Process.INVALID_UID)
+        )
+        uninstallViewModel!!.preprocessIntent(intent, callerInfo)
+        uninstallViewModel!!.currentUninstallStage.observe(this) { uninstallStage: UninstallStage ->
+            onUninstallStageChange(uninstallStage)
+        }
+    }
+
+    /**
+     * Main controller of the UI. This method shows relevant dialogs / fragments based on the
+     * uninstall stage
+     */
+    private fun onUninstallStageChange(uninstallStage: UninstallStage) {
+        when (uninstallStage.stageCode) {
+            UninstallStage.STAGE_ABORTED -> {
+                val aborted = uninstallStage as UninstallAborted
+                if (aborted.abortReason == UninstallAborted.ABORT_REASON_APP_UNAVAILABLE ||
+                    aborted.abortReason == UninstallAborted.ABORT_REASON_USER_NOT_ALLOWED
+                ) {
+                    val errorDialog = UninstallErrorFragment(aborted)
+                    showDialogInner(errorDialog)
+                } else {
+                    setResult(aborted.activityResultCode, null, true)
+                }
+            }
+
+            UninstallStage.STAGE_USER_ACTION_REQUIRED -> {
+                val uar = uninstallStage as UninstallUserActionRequired
+                val confirmationDialog = UninstallConfirmationFragment(uar)
+                showDialogInner(confirmationDialog)
+            }
+
+            UninstallStage.STAGE_UNINSTALLING -> {
+                // TODO: This shows a fragment whether or not user requests a result or not.
+                //  Originally, if the user does not request a result, we used to show a notification.
+                //  And a fragment if the user requests a result back. Should we consolidate and
+                //  show a fragment always?
+                val uninstalling = uninstallStage as UninstallUninstalling
+                val uninstallingDialog = UninstallUninstallingFragment(uninstalling)
+                showDialogInner(uninstallingDialog)
+            }
+
+            UninstallStage.STAGE_FAILED -> {
+                val failed = uninstallStage as UninstallFailed
+                if (!failed.returnResult) {
+                    notificationManager!!.notify(
+                        failed.uninstallNotificationId!!, failed.uninstallNotification
+                    )
+                }
+                setResult(failed.activityResultCode, failed.resultIntent, true)
+            }
+
+            UninstallStage.STAGE_SUCCESS -> {
+                val success = uninstallStage as UninstallSuccess
+                if (success.message != null) {
+                    Toast.makeText(this, success.message, Toast.LENGTH_LONG).show()
+                }
+                setResult(success.activityResultCode, success.resultIntent, true)
+            }
+
+            else -> {
+                Log.e(LOG_TAG, "Invalid stage: " + uninstallStage.stageCode)
+                showDialogInner(null)
+            }
+        }
+    }
+
+    /**
+     * Replace any visible dialog by the dialog returned by InstallRepository
+     *
+     * @param newDialog The new dialog to display
+     */
+    private fun showDialogInner(newDialog: DialogFragment?) {
+        val currentDialog = fragmentManager!!.findFragmentByTag(TAG_DIALOG) as DialogFragment?
+        currentDialog?.dismissAllowingStateLoss()
+        newDialog?.show(fragmentManager!!, TAG_DIALOG)
+    }
+
+    fun setResult(resultCode: Int, data: Intent?, shouldFinish: Boolean) {
+        super.setResult(resultCode, data)
+        if (shouldFinish) {
+            finish()
+        }
+    }
+
+    override fun onPositiveResponse(keepData: Boolean) {
+        uninstallViewModel!!.initiateUninstall(keepData)
+    }
+
+    override fun onNegativeResponse() {
+        uninstallViewModel!!.cancelInstall()
+        setResult(Activity.RESULT_FIRST_USER, null, true)
+    }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/AnonymousSourceFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/AnonymousSourceFragment.java
index 6d6fcc9..b29cb2a 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/AnonymousSourceFragment.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/AnonymousSourceFragment.java
@@ -24,8 +24,8 @@
 import androidx.annotation.NonNull;
 import androidx.fragment.app.DialogFragment;
 import com.android.packageinstaller.R;
-import com.android.packageinstaller.v2.model.installstagedata.InstallStage;
-import com.android.packageinstaller.v2.model.installstagedata.InstallUserActionRequired;
+import com.android.packageinstaller.v2.model.InstallStage;
+import com.android.packageinstaller.v2.model.InstallUserActionRequired;
 import com.android.packageinstaller.v2.ui.InstallActionListener;
 
 /**
@@ -34,7 +34,10 @@
 public class AnonymousSourceFragment extends DialogFragment {
 
     public static String TAG = AnonymousSourceFragment.class.getSimpleName();
+    @NonNull
     private InstallActionListener mInstallActionListener;
+    @NonNull
+    private AlertDialog mDialog;
 
     @Override
     public void onAttach(@NonNull Context context) {
@@ -45,7 +48,7 @@
     @NonNull
     @Override
     public Dialog onCreateDialog(Bundle savedInstanceState) {
-        return new AlertDialog.Builder(getActivity())
+       mDialog = new AlertDialog.Builder(requireContext())
             .setMessage(R.string.anonymous_source_warning)
             .setPositiveButton(R.string.anonymous_source_continue,
                 ((dialog, which) -> mInstallActionListener.onPositiveResponse(
@@ -53,6 +56,7 @@
             .setNegativeButton(R.string.cancel,
                 ((dialog, which) -> mInstallActionListener.onNegativeResponse(
                     InstallStage.STAGE_USER_ACTION_REQUIRED))).create();
+       return mDialog;
     }
 
     @Override
@@ -60,4 +64,24 @@
         super.onCancel(dialog);
         mInstallActionListener.onNegativeResponse(InstallStage.STAGE_USER_ACTION_REQUIRED);
     }
+
+    @Override
+    public void onStart() {
+        super.onStart();
+        mDialog.getButton(DialogInterface.BUTTON_POSITIVE).setFilterTouchesWhenObscured(true);
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+        // This prevents tapjacking since an overlay activity started in front of Pia will
+        // cause Pia to be paused.
+        mDialog.getButton(DialogInterface.BUTTON_POSITIVE).setEnabled(false);
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        mDialog.getButton(DialogInterface.BUTTON_POSITIVE).setEnabled(true);
+    }
 }
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/ExternalSourcesBlockedFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/ExternalSourcesBlockedFragment.java
index 4cdce52..2314d6b 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/ExternalSourcesBlockedFragment.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/ExternalSourcesBlockedFragment.java
@@ -25,7 +25,7 @@
 import androidx.annotation.Nullable;
 import androidx.fragment.app.DialogFragment;
 import com.android.packageinstaller.R;
-import com.android.packageinstaller.v2.model.installstagedata.InstallUserActionRequired;
+import com.android.packageinstaller.v2.model.InstallUserActionRequired;
 import com.android.packageinstaller.v2.ui.InstallActionListener;
 
 /**
@@ -35,8 +35,12 @@
 public class ExternalSourcesBlockedFragment extends DialogFragment {
 
     private final String TAG = ExternalSourcesBlockedFragment.class.getSimpleName();
+    @NonNull
     private final InstallUserActionRequired mDialogData;
+    @NonNull
     private InstallActionListener mInstallActionListener;
+    @NonNull
+    private AlertDialog mDialog;
 
     public ExternalSourcesBlockedFragment(InstallUserActionRequired dialogData) {
         mDialogData = dialogData;
@@ -51,7 +55,7 @@
     @NonNull
     @Override
     public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
-        return new AlertDialog.Builder(requireContext())
+        mDialog = new AlertDialog.Builder(requireContext())
             .setTitle(mDialogData.getAppLabel())
             .setIcon(mDialogData.getAppIcon())
             .setMessage(R.string.untrusted_external_source_warning)
@@ -62,6 +66,7 @@
                 (dialog, which) -> mInstallActionListener.onNegativeResponse(
                     mDialogData.getStageCode()))
             .create();
+        return mDialog;
     }
 
     @Override
@@ -69,4 +74,24 @@
         super.onCancel(dialog);
         mInstallActionListener.onNegativeResponse(mDialogData.getStageCode());
     }
+
+    @Override
+    public void onStart() {
+        super.onStart();
+        mDialog.getButton(DialogInterface.BUTTON_POSITIVE).setFilterTouchesWhenObscured(true);
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+        // This prevents tapjacking since an overlay activity started in front of Pia will
+        // cause Pia to be paused.
+        mDialog.getButton(DialogInterface.BUTTON_POSITIVE).setEnabled(false);
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        mDialog.getButton(DialogInterface.BUTTON_POSITIVE).setEnabled(true);
+    }
 }
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallConfirmationFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallConfirmationFragment.java
index 6398aef..dbe32cc 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallConfirmationFragment.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallConfirmationFragment.java
@@ -28,7 +28,7 @@
 import androidx.annotation.Nullable;
 import androidx.fragment.app.DialogFragment;
 import com.android.packageinstaller.R;
-import com.android.packageinstaller.v2.model.installstagedata.InstallUserActionRequired;
+import com.android.packageinstaller.v2.model.InstallUserActionRequired;
 import com.android.packageinstaller.v2.ui.InstallActionListener;
 
 /**
@@ -42,6 +42,8 @@
     private final InstallUserActionRequired mDialogData;
     @NonNull
     private InstallActionListener mInstallActionListener;
+    @NonNull
+    private AlertDialog mDialog;
 
     public InstallConfirmationFragment(@NonNull InstallUserActionRequired dialogData) {
         mDialogData = dialogData;
@@ -58,20 +60,29 @@
     public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
         View dialogView = getLayoutInflater().inflate(R.layout.install_content_view, null);
 
-        AlertDialog dialog = new AlertDialog.Builder(requireContext())
+        int positiveBtnTextRes;
+        if (mDialogData.isAppUpdating()) {
+            if (mDialogData.getDialogMessage() != null) {
+                positiveBtnTextRes = R.string.update_anyway;
+            } else {
+                positiveBtnTextRes = R.string.update;
+            }
+        } else {
+            positiveBtnTextRes = R.string.install;
+        }
+
+        mDialog = new AlertDialog.Builder(requireContext())
             .setIcon(mDialogData.getAppIcon())
             .setTitle(mDialogData.getAppLabel())
             .setView(dialogView)
-            .setPositiveButton(mDialogData.isAppUpdating() ? R.string.update : R.string.install,
+            .setPositiveButton(positiveBtnTextRes,
                 (dialogInt, which) -> mInstallActionListener.onPositiveResponse(
                     InstallUserActionRequired.USER_ACTION_REASON_INSTALL_CONFIRMATION))
             .setNegativeButton(R.string.cancel,
                 (dialogInt, which) -> mInstallActionListener.onNegativeResponse(
                     mDialogData.getStageCode()))
-
             .create();
 
-        // TODO: Dynamically change positive button text to update anyway
         TextView viewToEnable;
         if (mDialogData.isAppUpdating()) {
             viewToEnable = dialogView.requireViewById(R.id.install_confirm_question_update);
@@ -84,7 +95,7 @@
         }
         viewToEnable.setVisibility(View.VISIBLE);
 
-        return dialog;
+        return mDialog;
     }
 
     @Override
@@ -92,4 +103,24 @@
         super.onCancel(dialog);
         mInstallActionListener.onNegativeResponse(mDialogData.getStageCode());
     }
+
+    @Override
+    public void onStart() {
+        super.onStart();
+        mDialog.getButton(DialogInterface.BUTTON_POSITIVE).setFilterTouchesWhenObscured(true);
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+        // This prevents tapjacking since an overlay activity started in front of Pia will
+        // cause Pia to be paused.
+        mDialog.getButton(DialogInterface.BUTTON_POSITIVE).setEnabled(false);
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        mDialog.getButton(DialogInterface.BUTTON_POSITIVE).setEnabled(true);
+    }
 }
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallFailedFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallFailedFragment.java
index d45cd76..4667a7a 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallFailedFragment.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallFailedFragment.java
@@ -28,7 +28,7 @@
 import androidx.annotation.Nullable;
 import androidx.fragment.app.DialogFragment;
 import com.android.packageinstaller.R;
-import com.android.packageinstaller.v2.model.installstagedata.InstallFailed;
+import com.android.packageinstaller.v2.model.InstallFailed;
 import com.android.packageinstaller.v2.ui.InstallActionListener;
 
 /**
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallInstallingFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallInstallingFragment.java
index 9f60f96..7327b5d 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallInstallingFragment.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallInstallingFragment.java
@@ -25,7 +25,7 @@
 import androidx.annotation.Nullable;
 import androidx.fragment.app.DialogFragment;
 import com.android.packageinstaller.R;
-import com.android.packageinstaller.v2.model.installstagedata.InstallInstalling;
+import com.android.packageinstaller.v2.model.InstallInstalling;
 
 /**
  * Dialog to show when an install is in progress.
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallSuccessFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallSuccessFragment.java
index ab6a932..b2a65faa 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallSuccessFragment.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallSuccessFragment.java
@@ -29,8 +29,8 @@
 import androidx.annotation.Nullable;
 import androidx.fragment.app.DialogFragment;
 import com.android.packageinstaller.R;
-import com.android.packageinstaller.v2.model.installstagedata.InstallStage;
-import com.android.packageinstaller.v2.model.installstagedata.InstallSuccess;
+import com.android.packageinstaller.v2.model.InstallStage;
+import com.android.packageinstaller.v2.model.InstallSuccess;
 import com.android.packageinstaller.v2.ui.InstallActionListener;
 import java.util.List;
 
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/ParseErrorFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/ParseErrorFragment.java
new file mode 100644
index 0000000..68d48d6
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/ParseErrorFragment.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.packageinstaller.v2.ui.fragments;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import androidx.annotation.NonNull;
+import androidx.fragment.app.DialogFragment;
+import com.android.packageinstaller.R;
+import com.android.packageinstaller.v2.model.InstallAborted;
+import com.android.packageinstaller.v2.ui.InstallActionListener;
+
+public class ParseErrorFragment extends DialogFragment {
+
+    private static final String TAG = ParseErrorFragment.class.getSimpleName();
+    private final InstallAborted mDialogData;
+    private InstallActionListener mInstallActionListener;
+
+    public ParseErrorFragment(InstallAborted dialogData) {
+        mDialogData = dialogData;
+    }
+
+    @Override
+    public void onAttach(@NonNull Context context) {
+        super.onAttach(context);
+        mInstallActionListener = (InstallActionListener) context;
+    }
+
+    @NonNull
+    @Override
+    public Dialog onCreateDialog(Bundle savedInstanceState) {
+        return new AlertDialog.Builder(getActivity())
+            .setMessage(R.string.Parse_error_dlg_text)
+            .setPositiveButton(R.string.ok,
+                (dialog, which) ->
+                    mInstallActionListener.onNegativeResponse(
+                        mDialogData.getActivityResultCode(), mDialogData.getResultIntent()))
+            .create();
+    }
+
+    @Override
+    public void onCancel(DialogInterface dialog) {
+        super.onCancel(dialog);
+        mInstallActionListener.onNegativeResponse(
+            mDialogData.getActivityResultCode(), mDialogData.getResultIntent());
+    }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/SimpleErrorFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/SimpleErrorFragment.java
index 47fd67f..58b8b2d 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/SimpleErrorFragment.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/SimpleErrorFragment.java
@@ -24,7 +24,7 @@
 import androidx.annotation.NonNull;
 import androidx.fragment.app.DialogFragment;
 import com.android.packageinstaller.R;
-import com.android.packageinstaller.v2.model.installstagedata.InstallStage;
+import com.android.packageinstaller.v2.model.InstallStage;
 import com.android.packageinstaller.v2.ui.InstallActionListener;
 
 public class SimpleErrorFragment extends DialogFragment {
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/UninstallConfirmationFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/UninstallConfirmationFragment.java
index 1b0885e..32ac4a6 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/UninstallConfirmationFragment.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/UninstallConfirmationFragment.java
@@ -30,7 +30,7 @@
 import androidx.annotation.Nullable;
 import androidx.fragment.app.DialogFragment;
 import com.android.packageinstaller.R;
-import com.android.packageinstaller.v2.model.uninstallstagedata.UninstallUserActionRequired;
+import com.android.packageinstaller.v2.model.UninstallUserActionRequired;
 import com.android.packageinstaller.v2.ui.UninstallActionListener;
 
 /**
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/UninstallErrorFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/UninstallErrorFragment.java
index 305daba..eb7183d 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/UninstallErrorFragment.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/UninstallErrorFragment.java
@@ -25,7 +25,7 @@
 import androidx.annotation.Nullable;
 import androidx.fragment.app.DialogFragment;
 import com.android.packageinstaller.R;
-import com.android.packageinstaller.v2.model.uninstallstagedata.UninstallAborted;
+import com.android.packageinstaller.v2.model.UninstallAborted;
 import com.android.packageinstaller.v2.ui.UninstallActionListener;
 
 /**
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/UninstallUninstallingFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/UninstallUninstallingFragment.java
index 23cc421..835efc6 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/UninstallUninstallingFragment.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/UninstallUninstallingFragment.java
@@ -22,7 +22,7 @@
 import androidx.annotation.NonNull;
 import androidx.fragment.app.DialogFragment;
 import com.android.packageinstaller.R;
-import com.android.packageinstaller.v2.model.uninstallstagedata.UninstallUninstalling;
+import com.android.packageinstaller.v2.model.UninstallUninstalling;
 
 /**
  * Dialog to show that the app is uninstalling.
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/viewmodel/InstallViewModel.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/viewmodel/InstallViewModel.java
deleted file mode 100644
index 04a0622..0000000
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/viewmodel/InstallViewModel.java
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.packageinstaller.v2.viewmodel;
-
-import android.app.Application;
-import android.content.Intent;
-import androidx.annotation.NonNull;
-import androidx.lifecycle.AndroidViewModel;
-import androidx.lifecycle.MediatorLiveData;
-import androidx.lifecycle.MutableLiveData;
-import com.android.packageinstaller.v2.model.InstallRepository;
-import com.android.packageinstaller.v2.model.InstallRepository.CallerInfo;
-import com.android.packageinstaller.v2.model.installstagedata.InstallStage;
-import com.android.packageinstaller.v2.model.installstagedata.InstallStaging;
-
-
-public class InstallViewModel extends AndroidViewModel {
-
-    private static final String TAG = InstallViewModel.class.getSimpleName();
-    private final InstallRepository mRepository;
-    private final MediatorLiveData<InstallStage> mCurrentInstallStage = new MediatorLiveData<>(
-            new InstallStaging());
-
-    public InstallViewModel(@NonNull Application application, InstallRepository repository) {
-        super(application);
-        mRepository = repository;
-    }
-
-    public MutableLiveData<InstallStage> getCurrentInstallStage() {
-        return mCurrentInstallStage;
-    }
-
-    public void preprocessIntent(Intent intent, CallerInfo callerInfo) {
-        InstallStage stage = mRepository.performPreInstallChecks(intent, callerInfo);
-        if (stage.getStageCode() == InstallStage.STAGE_ABORTED) {
-            mCurrentInstallStage.setValue(stage);
-        } else {
-            // Since staging is an async operation, we will get the staging result later in time.
-            // Result of the file staging will be set in InstallRepository#mStagingResult.
-            // As such, mCurrentInstallStage will need to add another MutableLiveData
-            // as a data source
-            mRepository.stageForInstall();
-            mCurrentInstallStage.addSource(mRepository.getStagingResult(), installStage -> {
-                if (installStage.getStageCode() != InstallStage.STAGE_READY) {
-                    mCurrentInstallStage.setValue(installStage);
-                } else {
-                    checkIfAllowedAndInitiateInstall();
-                }
-            });
-        }
-    }
-
-    public MutableLiveData<Integer> getStagingProgress() {
-        return mRepository.getStagingProgress();
-    }
-
-    private void checkIfAllowedAndInitiateInstall() {
-        InstallStage stage = mRepository.requestUserConfirmation();
-        mCurrentInstallStage.setValue(stage);
-    }
-
-    public void forcedSkipSourceCheck() {
-        InstallStage stage = mRepository.forcedSkipSourceCheck();
-        mCurrentInstallStage.setValue(stage);
-    }
-
-    public void cleanupInstall() {
-        mRepository.cleanupInstall();
-    }
-
-    public void reattemptInstall() {
-        InstallStage stage = mRepository.reattemptInstall();
-        mCurrentInstallStage.setValue(stage);
-    }
-
-    public void initiateInstall() {
-        // Since installing is an async operation, we will get the install result later in time.
-        // Result of the installation will be set in InstallRepository#mInstallResult.
-        // As such, mCurrentInstallStage will need to add another MutableLiveData as a data source
-        mRepository.initiateInstall();
-        mCurrentInstallStage.addSource(mRepository.getInstallResult(), installStage -> {
-            if (installStage != null) {
-                mCurrentInstallStage.setValue(installStage);
-            }
-        });
-    }
-
-    public int getStagedSessionId() {
-        return mRepository.getStagedSessionId();
-    }
-}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/viewmodel/InstallViewModel.kt b/packages/PackageInstaller/src/com/android/packageinstaller/v2/viewmodel/InstallViewModel.kt
new file mode 100644
index 0000000..072fb2d
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/viewmodel/InstallViewModel.kt
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.packageinstaller.v2.viewmodel
+
+import android.app.Application
+import android.content.Intent
+import androidx.lifecycle.AndroidViewModel
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MediatorLiveData
+import androidx.lifecycle.MutableLiveData
+import com.android.packageinstaller.v2.model.InstallRepository
+import com.android.packageinstaller.v2.model.InstallStage
+import com.android.packageinstaller.v2.model.InstallStaging
+
+class InstallViewModel(application: Application, val repository: InstallRepository) :
+    AndroidViewModel(application) {
+
+    companion object {
+        private val LOG_TAG = InstallViewModel::class.java.simpleName
+    }
+
+    private val _currentInstallStage = MediatorLiveData<InstallStage>(InstallStaging())
+    val currentInstallStage: MutableLiveData<InstallStage>
+        get() = _currentInstallStage
+
+    fun preprocessIntent(intent: Intent, callerInfo: InstallRepository.CallerInfo) {
+        val stage = repository.performPreInstallChecks(intent, callerInfo)
+        if (stage.stageCode == InstallStage.STAGE_ABORTED) {
+            _currentInstallStage.value = stage
+        } else {
+            // Since staging is an async operation, we will get the staging result later in time.
+            // Result of the file staging will be set in InstallRepository#mStagingResult.
+            // As such, mCurrentInstallStage will need to add another MutableLiveData
+            // as a data source
+            repository.stageForInstall()
+            _currentInstallStage.addSource(repository.stagingResult) { installStage: InstallStage ->
+                if (installStage.stageCode != InstallStage.STAGE_READY) {
+                    _currentInstallStage.value = installStage
+                } else {
+                    checkIfAllowedAndInitiateInstall()
+                }
+            }
+        }
+    }
+
+    val stagingProgress: LiveData<Int>
+        get() = repository.stagingProgress
+
+    private fun checkIfAllowedAndInitiateInstall() {
+        val stage = repository.requestUserConfirmation()
+        _currentInstallStage.value = stage
+    }
+
+    fun forcedSkipSourceCheck() {
+        val stage = repository.forcedSkipSourceCheck()
+        _currentInstallStage.value = stage
+    }
+
+    fun cleanupInstall() {
+        repository.cleanupInstall()
+    }
+
+    fun reattemptInstall() {
+        val stage = repository.reattemptInstall()
+        _currentInstallStage.value = stage
+    }
+
+    fun initiateInstall() {
+        // Since installing is an async operation, we will get the install result later in time.
+        // Result of the installation will be set in InstallRepository#mInstallResult.
+        // As such, mCurrentInstallStage will need to add another MutableLiveData as a data source
+        repository.initiateInstall()
+        _currentInstallStage.addSource(repository.installResult) { installStage: InstallStage? ->
+            if (installStage != null) {
+                _currentInstallStage.value = installStage
+            }
+        }
+    }
+
+    val stagedSessionId: Int
+        get() = repository.stagedSessionId
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/viewmodel/InstallViewModelFactory.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/viewmodel/InstallViewModelFactory.java
deleted file mode 100644
index ef459e6..0000000
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/viewmodel/InstallViewModelFactory.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.packageinstaller.v2.viewmodel;
-
-import android.app.Application;
-import androidx.annotation.NonNull;
-import androidx.lifecycle.ViewModel;
-import androidx.lifecycle.ViewModelProvider;
-import com.android.packageinstaller.v2.model.InstallRepository;
-
-public class InstallViewModelFactory extends ViewModelProvider.AndroidViewModelFactory {
-
-    private final InstallRepository mRepository;
-    private final Application mApplication;
-
-    public InstallViewModelFactory(Application application, InstallRepository repository) {
-        // Calling super class' ctor ensures that create method is called correctly and the right
-        // ctor of InstallViewModel is used. If we fail to do that, the default ctor:
-        // InstallViewModel(application) is used, and repository isn't initialized in the viewmodel
-        super(application);
-        mApplication = application;
-        mRepository = repository;
-    }
-
-    @NonNull
-    @Override
-    @SuppressWarnings("unchecked")
-    public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
-        return (T) new InstallViewModel(mApplication, mRepository);
-    }
-}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/viewmodel/InstallViewModelFactory.kt b/packages/PackageInstaller/src/com/android/packageinstaller/v2/viewmodel/InstallViewModelFactory.kt
new file mode 100644
index 0000000..07b2f4f
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/viewmodel/InstallViewModelFactory.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.packageinstaller.v2.viewmodel
+
+import android.app.Application
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+import com.android.packageinstaller.v2.model.InstallRepository
+
+class InstallViewModelFactory(val application: Application, val repository: InstallRepository) :
+    ViewModelProvider.AndroidViewModelFactory(application) {
+
+    // Calling super class' ctor ensures that create method is called correctly and the right
+    // ctor of InstallViewModel is used. If we fail to do that, the default ctor:
+    // InstallViewModel(application) is used, and repository isn't initialized in the viewmodel
+    override fun <T : ViewModel> create(modelClass: Class<T>): T {
+        return InstallViewModel(application, repository) as T
+    }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/viewmodel/UninstallViewModel.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/viewmodel/UninstallViewModel.java
deleted file mode 100644
index 3f7bce8..0000000
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/viewmodel/UninstallViewModel.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.packageinstaller.v2.viewmodel;
-
-import android.app.Application;
-import android.content.Intent;
-import androidx.annotation.NonNull;
-import androidx.lifecycle.AndroidViewModel;
-import androidx.lifecycle.MediatorLiveData;
-import androidx.lifecycle.MutableLiveData;
-import com.android.packageinstaller.v2.model.UninstallRepository;
-import com.android.packageinstaller.v2.model.UninstallRepository.CallerInfo;
-import com.android.packageinstaller.v2.model.uninstallstagedata.UninstallStage;
-
-public class UninstallViewModel extends AndroidViewModel {
-
-    private static final String TAG = UninstallViewModel.class.getSimpleName();
-    private final UninstallRepository mRepository;
-    private final MediatorLiveData<UninstallStage> mCurrentUninstallStage =
-        new MediatorLiveData<>();
-
-    public UninstallViewModel(@NonNull Application application, UninstallRepository repository) {
-        super(application);
-        mRepository = repository;
-    }
-
-    public MutableLiveData<UninstallStage> getCurrentUninstallStage() {
-        return mCurrentUninstallStage;
-    }
-
-    public void preprocessIntent(Intent intent, CallerInfo callerInfo) {
-        UninstallStage stage = mRepository.performPreUninstallChecks(intent, callerInfo);
-        if (stage.getStageCode() != UninstallStage.STAGE_ABORTED) {
-            stage = mRepository.generateUninstallDetails();
-        }
-        mCurrentUninstallStage.setValue(stage);
-    }
-
-    public void initiateUninstall(boolean keepData) {
-        mRepository.initiateUninstall(keepData);
-        // Since uninstall is an async operation, we will get the uninstall result later in time.
-        // Result of the uninstall will be set in UninstallRepository#mUninstallResult.
-        // As such, mCurrentUninstallStage will need to add another MutableLiveData
-        // as a data source
-        mCurrentUninstallStage.addSource(mRepository.getUninstallResult(), uninstallStage -> {
-            if (uninstallStage != null) {
-                mCurrentUninstallStage.setValue(uninstallStage);
-            }
-        });
-    }
-
-    public void cancelInstall() {
-        mRepository.cancelInstall();
-    }
-}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/viewmodel/UninstallViewModel.kt b/packages/PackageInstaller/src/com/android/packageinstaller/v2/viewmodel/UninstallViewModel.kt
new file mode 100644
index 0000000..80886e9
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/viewmodel/UninstallViewModel.kt
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.packageinstaller.v2.viewmodel
+
+import android.app.Application
+import android.content.Intent
+import androidx.lifecycle.AndroidViewModel
+import androidx.lifecycle.MediatorLiveData
+import androidx.lifecycle.MutableLiveData
+import com.android.packageinstaller.v2.model.UninstallRepository
+import com.android.packageinstaller.v2.model.UninstallStage
+
+class UninstallViewModel(application: Application, val repository: UninstallRepository) :
+    AndroidViewModel(application) {
+
+    companion object {
+        private val LOG_TAG = UninstallViewModel::class.java.simpleName
+    }
+
+    private val _currentUninstallStage = MediatorLiveData<UninstallStage>()
+    val currentUninstallStage: MutableLiveData<UninstallStage>
+        get() = _currentUninstallStage
+
+    fun preprocessIntent(intent: Intent, callerInfo: UninstallRepository.CallerInfo) {
+        var stage = repository.performPreUninstallChecks(intent, callerInfo)
+        if (stage.stageCode != UninstallStage.STAGE_ABORTED) {
+            stage = repository.generateUninstallDetails()
+        }
+        _currentUninstallStage.value = stage
+    }
+
+    fun initiateUninstall(keepData: Boolean) {
+        repository.initiateUninstall(keepData)
+        // Since uninstall is an async operation, we will get the uninstall result later in time.
+        // Result of the uninstall will be set in UninstallRepository#mUninstallResult.
+        // As such, _currentUninstallStage will need to add another MutableLiveData
+        // as a data source
+        _currentUninstallStage.addSource(repository.uninstallResult) { uninstallStage: UninstallStage? ->
+            if (uninstallStage != null) {
+                _currentUninstallStage.value = uninstallStage
+            }
+        }
+    }
+
+    fun cancelInstall() {
+        repository.cancelInstall()
+    }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/viewmodel/UninstallViewModelFactory.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/viewmodel/UninstallViewModelFactory.java
deleted file mode 100644
index cd9845e..0000000
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/viewmodel/UninstallViewModelFactory.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.packageinstaller.v2.viewmodel;
-
-import android.app.Application;
-import androidx.annotation.NonNull;
-import androidx.lifecycle.ViewModel;
-import androidx.lifecycle.ViewModelProvider;
-import com.android.packageinstaller.v2.model.UninstallRepository;
-
-public class UninstallViewModelFactory extends ViewModelProvider.AndroidViewModelFactory {
-
-    private final UninstallRepository mRepository;
-    private final Application mApplication;
-
-    public UninstallViewModelFactory(Application application, UninstallRepository repository) {
-        // Calling super class' ctor ensures that create method is called correctly and the right
-        // ctor of UninstallViewModel is used. If we fail to do that, the default ctor:
-        // UninstallViewModel(application) is used, and repository isn't initialized in
-        // the viewmodel
-        super(application);
-        mApplication = application;
-        mRepository = repository;
-    }
-
-    @NonNull
-    @Override
-    @SuppressWarnings("unchecked")
-    public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
-        return (T) new UninstallViewModel(mApplication, mRepository);
-    }
-}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/viewmodel/UninstallViewModelFactory.kt b/packages/PackageInstaller/src/com/android/packageinstaller/v2/viewmodel/UninstallViewModelFactory.kt
new file mode 100644
index 0000000..0a316e7
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/viewmodel/UninstallViewModelFactory.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.packageinstaller.v2.viewmodel
+
+import android.app.Application
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+import com.android.packageinstaller.v2.model.UninstallRepository
+
+class UninstallViewModelFactory(val application: Application, val repository: UninstallRepository) :
+    ViewModelProvider.AndroidViewModelFactory(application) {
+
+    // Calling super class' ctor ensures that create method is called correctly and the right
+    // ctor of UninstallViewModel is used. If we fail to do that, the default ctor:
+    // UninstallViewModel(application) is used, and repository isn't initialized in
+    // the viewmodel
+    override fun <T : ViewModel> create(modelClass: Class<T>): T {
+        return UninstallViewModel(application, repository) as T
+    }
+}
diff --git a/packages/SettingsLib/AdaptiveIcon/Android.bp b/packages/SettingsLib/AdaptiveIcon/Android.bp
index df72a92..044ba87 100644
--- a/packages/SettingsLib/AdaptiveIcon/Android.bp
+++ b/packages/SettingsLib/AdaptiveIcon/Android.bp
@@ -15,9 +15,12 @@
     resource_dirs: ["res"],
 
     static_libs: [
-          "androidx.annotation_annotation",
-          "SettingsLibTile"
+        "androidx.annotation_annotation",
+        "SettingsLibTile",
     ],
 
     min_sdk_version: "21",
+    lint: {
+        baseline_filename: "lint-baseline.xml",
+    },
 }
diff --git a/packages/SettingsLib/AdaptiveIcon/lint-baseline.xml b/packages/SettingsLib/AdaptiveIcon/lint-baseline.xml
index 7f16517..8127e1a 100644
--- a/packages/SettingsLib/AdaptiveIcon/lint-baseline.xml
+++ b/packages/SettingsLib/AdaptiveIcon/lint-baseline.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="5" by="lint 4.1.0" client="cli" variant="all" version="4.1.0">
+<issues format="6" by="lint 8.4.0-alpha01" type="baseline" client="" dependencies="true" name="" variant="all" version="8.4.0-alpha01">
 
     <issue
         id="NewApi"
@@ -8,7 +8,7 @@
         errorLine2="                                 ~~~~~~~~">
         <location
             file="frameworks/base/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveIcon.java"
-            line="78"
+            line="79"
             column="34"/>
     </issue>
 
@@ -19,7 +19,7 @@
         errorLine2="                                   ~~~~~~~~">
         <location
             file="frameworks/base/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveIcon.java"
-            line="90"
+            line="91"
             column="36"/>
     </issue>
 
@@ -30,7 +30,7 @@
         errorLine2="                                             ~~~~~~~~~~~~~~~">
         <location
             file="frameworks/base/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveOutlineDrawable.java"
-            line="43"
+            line="45"
             column="46"/>
     </issue>
 
@@ -41,7 +41,7 @@
         errorLine2="        ~~~~~">
         <location
             file="frameworks/base/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveOutlineDrawable.java"
-            line="65"
+            line="67"
             column="9"/>
     </issue>
 
@@ -52,7 +52,7 @@
         errorLine2="        ~~~~~">
         <location
             file="frameworks/base/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveOutlineDrawable.java"
-            line="72"
+            line="74"
             column="9"/>
     </issue>
 
@@ -63,7 +63,7 @@
         errorLine2="        ~~~~~~~~~~~">
         <location
             file="frameworks/base/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveOutlineDrawable.java"
-            line="80"
+            line="82"
             column="9"/>
     </issue>
 
@@ -74,8 +74,8 @@
         errorLine2="                         ~~~~~~~~">
         <location
             file="frameworks/base/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveOutlineDrawable.java"
-            line="105"
+            line="107"
             column="26"/>
     </issue>
 
-</issues>
+</issues>
\ No newline at end of file
diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp
index 2501869..5da4b95 100644
--- a/packages/SettingsLib/Android.bp
+++ b/packages/SettingsLib/Android.bp
@@ -60,6 +60,9 @@
         "src/**/*.java",
         "src/**/*.kt",
     ],
+    lint: {
+        extra_check_modules: ["SettingsLibLintChecker"],
+    },
 }
 
 // NOTE: Keep this module in sync with ./common.mk
diff --git a/packages/SettingsLib/EmergencyNumber/Android.bp b/packages/SettingsLib/EmergencyNumber/Android.bp
index 986baf7..bd0dbdc 100644
--- a/packages/SettingsLib/EmergencyNumber/Android.bp
+++ b/packages/SettingsLib/EmergencyNumber/Android.bp
@@ -17,4 +17,7 @@
     ],
     sdk_version: "system_current",
     min_sdk_version: "21",
+    lint: {
+        baseline_filename: "lint-baseline.xml",
+    },
 }
diff --git a/packages/SettingsLib/EmergencyNumber/lint-baseline.xml b/packages/SettingsLib/EmergencyNumber/lint-baseline.xml
index e9c687f..13bf5f5 100644
--- a/packages/SettingsLib/EmergencyNumber/lint-baseline.xml
+++ b/packages/SettingsLib/EmergencyNumber/lint-baseline.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="5" by="lint 4.1.0" client="cli" variant="all" version="4.1.0">
+<issues format="6" by="lint 8.4.0-alpha01" type="baseline" client="" dependencies="true" name="" variant="all" version="8.4.0-alpha01">
 
     <issue
         id="NewApi"
@@ -8,7 +8,7 @@
         errorLine2="                                        ~~~~~~~~~~~~~~~~">
         <location
             file="frameworks/base/packages/SettingsLib/EmergencyNumber/src/com/android/settingslib/emergencynumber/EmergencyNumberUtils.java"
-            line="77"
+            line="81"
             column="41"/>
     </issue>
 
@@ -19,7 +19,7 @@
         errorLine2="                                            ~~~~~~~~~~~~~~~~">
         <location
             file="frameworks/base/packages/SettingsLib/EmergencyNumber/src/com/android/settingslib/emergencynumber/EmergencyNumberUtils.java"
-            line="78"
+            line="82"
             column="45"/>
     </issue>
 
@@ -30,18 +30,18 @@
         errorLine2="                                                             ~~~~~~~~~~~~~~~~~~~~">
         <location
             file="frameworks/base/packages/SettingsLib/EmergencyNumber/src/com/android/settingslib/emergencynumber/EmergencyNumberUtils.java"
-            line="78"
+            line="82"
             column="62"/>
     </issue>
 
     <issue
         id="NewApi"
         message="Call requires API level 29 (current min is 21): `android.telephony.TelephonyManager#getEmergencyNumberList`"
-        errorLine1="        Map&lt;Integer, List&lt;EmergencyNumber>> allLists = mTelephonyManager.getEmergencyNumberList("
+        errorLine1="        Map&lt;Integer, List&lt;EmergencyNumber&gt;&gt; allLists = mTelephonyManager.getEmergencyNumberList("
         errorLine2="                                                                         ~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="frameworks/base/packages/SettingsLib/EmergencyNumber/src/com/android/settingslib/emergencynumber/EmergencyNumberUtils.java"
-            line="173"
+            line="177"
             column="74"/>
     </issue>
 
@@ -52,7 +52,7 @@
         errorLine2="                                        ~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="frameworks/base/packages/SettingsLib/EmergencyNumber/src/com/android/settingslib/emergencynumber/EmergencyNumberUtils.java"
-            line="196"
+            line="200"
             column="41"/>
     </issue>
 
@@ -63,7 +63,7 @@
         errorLine2="                                                                    ~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="frameworks/base/packages/SettingsLib/EmergencyNumber/src/com/android/settingslib/emergencynumber/EmergencyNumberUtils.java"
-            line="219"
+            line="223"
             column="69"/>
     </issue>
 
@@ -74,7 +74,7 @@
         errorLine2="                                        ~~~~~~~~~">
         <location
             file="frameworks/base/packages/SettingsLib/EmergencyNumber/src/com/android/settingslib/emergencynumber/EmergencyNumberUtils.java"
-            line="234"
+            line="238"
             column="41"/>
     </issue>
 
@@ -85,8 +85,8 @@
         errorLine2="                                                   ~~~~~~~~~~~~~~~~~">
         <location
             file="frameworks/base/packages/SettingsLib/EmergencyNumber/src/com/android/settingslib/emergencynumber/EmergencyNumberUtils.java"
-            line="251"
+            line="255"
             column="52"/>
     </issue>
 
-</issues>
+</issues>
\ No newline at end of file
diff --git a/packages/SettingsLib/LintChecker/Android.bp b/packages/SettingsLib/LintChecker/Android.bp
new file mode 100644
index 0000000..eb489b1
--- /dev/null
+++ b/packages/SettingsLib/LintChecker/Android.bp
@@ -0,0 +1,33 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+java_library_host {
+    name: "SettingsLibLintChecker",
+    srcs: ["src/**/*.kt"],
+    plugins: ["auto_service_plugin"],
+    libs: [
+        "auto_service_annotations",
+        "lint_api",
+    ],
+    kotlincflags: ["-Xjvm-default=all"],
+}
diff --git a/packages/SettingsLib/LintChecker/src/com/android/settingslib/tools/lint/NullabilityAnnotationsDetector.kt b/packages/SettingsLib/LintChecker/src/com/android/settingslib/tools/lint/NullabilityAnnotationsDetector.kt
new file mode 100644
index 0000000..1f06261
--- /dev/null
+++ b/packages/SettingsLib/LintChecker/src/com/android/settingslib/tools/lint/NullabilityAnnotationsDetector.kt
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.tools.lint
+
+import com.android.tools.lint.client.api.UElementHandler
+import com.android.tools.lint.detector.api.Category
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Implementation
+import com.android.tools.lint.detector.api.Issue
+import com.android.tools.lint.detector.api.JavaContext
+import com.android.tools.lint.detector.api.LintFix
+import com.android.tools.lint.detector.api.Scope
+import com.android.tools.lint.detector.api.Severity
+import com.intellij.psi.PsiModifier
+import com.intellij.psi.PsiPrimitiveType
+import com.intellij.psi.PsiType
+import org.jetbrains.uast.UAnnotated
+import org.jetbrains.uast.UElement
+import org.jetbrains.uast.UMethod
+
+class NullabilityAnnotationsDetector : Detector(), Detector.UastScanner {
+    override fun getApplicableUastTypes(): List<Class<out UElement>> = listOf(UMethod::class.java)
+
+    override fun createUastHandler(context: JavaContext): UElementHandler? {
+        if (!context.isJavaFile()) return null
+
+        return object : UElementHandler() {
+            override fun visitMethod(node: UMethod) {
+                if (node.isPublic() && node.name != ANONYMOUS_CONSTRUCTOR) {
+                    node.verifyMethod()
+                    node.verifyMethodParameters()
+                }
+            }
+
+            private fun UMethod.isPublic() = modifierList.hasModifierProperty(PsiModifier.PUBLIC)
+
+            private fun UMethod.verifyMethod() {
+                if (isConstructor) return
+                if (returnType.isPrimitive()) return
+                checkAnnotation(METHOD_MSG)
+            }
+
+            private fun UMethod.verifyMethodParameters() {
+                for (parameter in uastParameters) {
+                    if (parameter.type.isPrimitive()) continue
+                    parameter.checkAnnotation(PARAMETER_MSG)
+                }
+            }
+
+            private fun PsiType?.isPrimitive() = this is PsiPrimitiveType
+
+            private fun UAnnotated.checkAnnotation(message: String) {
+                val oldAnnotation = findOldNullabilityAnnotation()
+                val oldAnnotationName = oldAnnotation?.qualifiedName?.substringAfterLast('.')
+
+                if (oldAnnotationName != null) {
+                    val annotation = "androidx.annotation.$oldAnnotationName"
+                    reportIssue(
+                        REQUIRE_NULLABILITY_ISSUE,
+                        "Prefer $annotation",
+                        LintFix.create()
+                                .replace()
+                                .range(context.getLocation(oldAnnotation))
+                                .with("@$annotation")
+                                .autoFix()
+                                .build()
+                    )
+                } else if (!hasNullabilityAnnotation()) {
+                    reportIssue(REQUIRE_NULLABILITY_ISSUE, message)
+                }
+            }
+
+            private fun UElement.reportIssue(
+                issue: Issue,
+                message: String,
+                quickfixData: LintFix? = null,
+            ) {
+                context.report(
+                    issue = issue,
+                    scope = this,
+                    location = context.getNameLocation(this),
+                    message = message,
+                    quickfixData = quickfixData,
+                )
+            }
+
+            private fun UAnnotated.findOldNullabilityAnnotation() =
+                uAnnotations.find { it.qualifiedName in oldAnnotations }
+
+            private fun UAnnotated.hasNullabilityAnnotation() =
+                uAnnotations.any { it.qualifiedName in validAnnotations }
+        }
+    }
+
+    private fun JavaContext.isJavaFile() = psiFile?.fileElementType.toString().startsWith("java")
+
+    companion object {
+        private val validAnnotations = arrayOf("androidx.annotation.NonNull",
+            "androidx.annotation.Nullable")
+
+        private val oldAnnotations = arrayOf("android.annotation.NonNull",
+            "android.annotation.Nullable",
+        )
+
+        private const val ANONYMOUS_CONSTRUCTOR = "<anon-init>"
+
+        private const val METHOD_MSG =
+                "Java public method return with non-primitive type must add androidx annotation. " +
+                        "Example: @NonNull | @Nullable Object functionName() {}"
+
+        private const val PARAMETER_MSG =
+                "Java public method parameter with non-primitive type must add androidx " +
+                        "annotation. Example: functionName(@NonNull Context context, " +
+                        "@Nullable Object obj) {}"
+
+        internal val REQUIRE_NULLABILITY_ISSUE = Issue
+            .create(
+                id = "RequiresNullabilityAnnotation",
+                briefDescription = "Requires nullability annotation for function",
+                explanation = "All public java APIs should specify nullability annotations for " +
+                        "methods and parameters.",
+                category = Category.CUSTOM_LINT_CHECKS,
+                priority = 3,
+                severity = Severity.WARNING,
+                androidSpecific = true,
+                implementation = Implementation(
+                  NullabilityAnnotationsDetector::class.java,
+                  Scope.JAVA_FILE_SCOPE,
+                ),
+            )
+    }
+}
\ No newline at end of file
diff --git a/packages/SettingsLib/LintChecker/src/com/android/settingslib/tools/lint/SettingsLintIssueRegistry.kt b/packages/SettingsLib/LintChecker/src/com/android/settingslib/tools/lint/SettingsLintIssueRegistry.kt
new file mode 100644
index 0000000..e0ab24a
--- /dev/null
+++ b/packages/SettingsLib/LintChecker/src/com/android/settingslib/tools/lint/SettingsLintIssueRegistry.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.tools.lint
+
+import com.android.tools.lint.client.api.IssueRegistry
+import com.android.tools.lint.detector.api.CURRENT_API
+import com.google.auto.service.AutoService
+
+@AutoService(IssueRegistry::class)
+class SettingsLintIssueRegistry : IssueRegistry() {
+    override val issues = listOf(NullabilityAnnotationsDetector.REQUIRE_NULLABILITY_ISSUE)
+
+    override val api: Int = CURRENT_API
+}
\ No newline at end of file
diff --git a/packages/SettingsLib/MainSwitchPreference/lint-baseline.xml b/packages/SettingsLib/MainSwitchPreference/lint-baseline.xml
deleted file mode 100644
index cfa64a4..0000000
--- a/packages/SettingsLib/MainSwitchPreference/lint-baseline.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.0.0-dev" type="baseline" dependencies="true" variant="all" version="8.0.0-dev">
-
-    <issue
-        id="NewApi"
-        message="`@android:dimen/config_restrictedIconSize` requires API level 29 (current min is 28)"
-        errorLine1='    &lt;dimen name="settingslib_restricted_icon_size"&gt;@android:dimen/config_restrictedIconSize&lt;/dimen&gt;'
-        errorLine2="                                                   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SettingsLib/MainSwitchPreference/res/values/dimens.xml"
-            line="21"
-            column="52"/>
-    </issue>
-
-</issues>
\ No newline at end of file
diff --git a/packages/SettingsLib/RestrictedLockUtils/Android.bp b/packages/SettingsLib/RestrictedLockUtils/Android.bp
index 028489d..3b04bd9 100644
--- a/packages/SettingsLib/RestrictedLockUtils/Android.bp
+++ b/packages/SettingsLib/RestrictedLockUtils/Android.bp
@@ -30,4 +30,7 @@
         "//apex_available:platform",
         "com.android.permission",
     ],
+    lint: {
+        baseline_filename: "lint-baseline.xml",
+    },
 }
diff --git a/packages/SettingsLib/RestrictedLockUtils/lint-baseline.xml b/packages/SettingsLib/RestrictedLockUtils/lint-baseline.xml
index 26d05a6..45a07fe 100644
--- a/packages/SettingsLib/RestrictedLockUtils/lint-baseline.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/lint-baseline.xml
@@ -1,38 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.0.0-dev" type="baseline" dependencies="true" variant="all" version="8.0.0-dev">
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 24 (current min is 23): `android.os.UserHandle#of`"
-        errorLine1="        context.startActivityAsUser(intent, UserHandle.of(targetUserId));"
-        errorLine2="                                                       ~~">
-        <location
-            file="frameworks/base/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java"
-            line="97"
-            column="56"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 24 (current min is 23): `android.os.UserHandle#of`"
-        errorLine1="        return um.getUserProfiles().contains(UserHandle.of(userId));"
-        errorLine2="                                                        ~~">
-        <location
-            file="frameworks/base/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java"
-            line="140"
-            column="57"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 26 (current min is 23): `android.app.admin.DevicePolicyManager#getDeviceOwnerComponentOnAnyUser`"
-        errorLine1="            adminComponent = dpm.getDeviceOwnerComponentOnAnyUser();"
-        errorLine2="                                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java"
-            line="75"
-            column="34"/>
-    </issue>
+<issues format="6" by="lint 8.4.0-alpha01" type="baseline" client="" dependencies="true" name="" variant="all" version="8.4.0-alpha01">
 
     <issue
         id="NewApi"
@@ -58,6 +25,28 @@
 
     <issue
         id="NewApi"
+        message="Call requires API level 26 (current min is 23): `android.app.admin.DevicePolicyManager#getDeviceOwnerComponentOnAnyUser`"
+        errorLine1="            adminComponent = dpm.getDeviceOwnerComponentOnAnyUser();"
+        errorLine2="                                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="frameworks/base/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java"
+            line="75"
+            column="34"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 24 (current min is 23): `android.os.UserHandle#of`"
+        errorLine1="        context.startActivityAsUser(intent, UserHandle.of(targetUserId));"
+        errorLine2="                                                       ~~">
+        <location
+            file="frameworks/base/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java"
+            line="97"
+            column="56"/>
+    </issue>
+
+    <issue
+        id="NewApi"
         message="Call requires API level 29 (current min is 23): `android.content.Context#startActivityAsUser`"
         errorLine1="        context.startActivityAsUser(intent, UserHandle.of(targetUserId));"
         errorLine2="                ~~~~~~~~~~~~~~~~~~~">
@@ -67,4 +56,15 @@
             column="17"/>
     </issue>
 
+    <issue
+        id="NewApi"
+        message="Call requires API level 24 (current min is 23): `android.os.UserHandle#of`"
+        errorLine1="        return um.getUserProfiles().contains(UserHandle.of(userId));"
+        errorLine2="                                                        ~~">
+        <location
+            file="frameworks/base/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java"
+            line="120"
+            column="57"/>
+    </issue>
+
 </issues>
\ No newline at end of file
diff --git a/packages/SettingsLib/SchedulesProvider/Android.bp b/packages/SettingsLib/SchedulesProvider/Android.bp
index 2d93e4e..22e4e94 100644
--- a/packages/SettingsLib/SchedulesProvider/Android.bp
+++ b/packages/SettingsLib/SchedulesProvider/Android.bp
@@ -14,9 +14,12 @@
     srcs: ["src/**/*.java"],
 
     static_libs: [
-          "androidx.annotation_annotation",
+        "androidx.annotation_annotation",
     ],
 
     sdk_version: "system_current",
     min_sdk_version: "21",
+    lint: {
+        baseline_filename: "lint-baseline.xml",
+    },
 }
diff --git a/packages/SettingsLib/SchedulesProvider/lint-baseline.xml b/packages/SettingsLib/SchedulesProvider/lint-baseline.xml
index 0744710..db6a882 100644
--- a/packages/SettingsLib/SchedulesProvider/lint-baseline.xml
+++ b/packages/SettingsLib/SchedulesProvider/lint-baseline.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.0.0-dev" type="baseline" dependencies="true" variant="all" version="8.0.0-dev">
+<issues format="6" by="lint 8.4.0-alpha01" type="baseline" client="" dependencies="true" name="" variant="all" version="8.4.0-alpha01">
 
     <issue
         id="NewApi"
diff --git a/packages/SettingsLib/SearchProvider/Android.bp b/packages/SettingsLib/SearchProvider/Android.bp
index c07a802..c385d38 100644
--- a/packages/SettingsLib/SearchProvider/Android.bp
+++ b/packages/SettingsLib/SearchProvider/Android.bp
@@ -15,4 +15,7 @@
 
     sdk_version: "system_current",
     min_sdk_version: "21",
+    lint: {
+        baseline_filename: "lint-baseline.xml",
+    },
 }
diff --git a/packages/SettingsLib/SearchProvider/lint-baseline.xml b/packages/SettingsLib/SearchProvider/lint-baseline.xml
index 53346e0..3cfca1d 100644
--- a/packages/SettingsLib/SearchProvider/lint-baseline.xml
+++ b/packages/SettingsLib/SearchProvider/lint-baseline.xml
@@ -1,27 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.0.0-dev" type="baseline" dependencies="true" variant="all" version="8.0.0-dev">
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 23 (current min is 21): `new android.provider.SearchIndexableResource`"
-        errorLine1="            super("
-        errorLine2="            ~~~~~">
-        <location
-            file="frameworks/base/packages/SettingsLib/SearchProvider/src/com/android/settingslib/searchprovider/SettingsXmlIndexProvider.java"
-            line="107"
-            column="13"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Class requires API level 23 (current min is 21): `android.provider.SearchIndexableResource`"
-        errorLine1="    public static final class SearchIndexableIntentResource extends SearchIndexableResource {"
-        errorLine2="                                                                    ~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SettingsLib/SearchProvider/src/com/android/settingslib/searchprovider/SettingsXmlIndexProvider.java"
-            line="97"
-            column="69"/>
-    </issue>
+<issues format="6" by="lint 8.4.0-alpha01" type="baseline" client="" dependencies="true" name="" variant="all" version="8.4.0-alpha01">
 
     <issue
         id="NewApi"
@@ -36,6 +14,39 @@
 
     <issue
         id="NewApi"
+        message="Field requires API level 23 (current min is 21): `android.provider.SearchIndexablesContract#INDEXABLES_XML_RES_COLUMNS`"
+        errorLine1="        final MatrixCursor cursor = new MatrixCursor(INDEXABLES_XML_RES_COLUMNS);"
+        errorLine2="                                                     ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="frameworks/base/packages/SettingsLib/SearchProvider/src/com/android/settingslib/searchprovider/SettingsXmlIndexProvider.java"
+            line="45"
+            column="54"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 23 (current min is 21): `android.provider.SearchIndexableData#rank`"
+        errorLine1="                    .add(XmlResource.COLUMN_RANK, indexableResource.rank)"
+        errorLine2="                                                  ~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="frameworks/base/packages/SettingsLib/SearchProvider/src/com/android/settingslib/searchprovider/SettingsXmlIndexProvider.java"
+            line="50"
+            column="51"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 23 (current min is 21): `android.provider.SearchIndexableResource#xmlResId`"
+        errorLine1="                    .add(XmlResource.COLUMN_XML_RESID, indexableResource.xmlResId)"
+        errorLine2="                                                       ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="frameworks/base/packages/SettingsLib/SearchProvider/src/com/android/settingslib/searchprovider/SettingsXmlIndexProvider.java"
+            line="51"
+            column="56"/>
+    </issue>
+
+    <issue
+        id="NewApi"
         message="Field requires API level 23 (current min is 21): `android.provider.SearchIndexableData#className`"
         errorLine1="                    .add(XmlResource.COLUMN_CLASS_NAME, indexableResource.className)"
         errorLine2="                                                        ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -58,6 +69,39 @@
 
     <issue
         id="NewApi"
+        message="Field requires API level 23 (current min is 21): `android.provider.SearchIndexableData#intentTargetClass`"
+        errorLine1="                            indexableResource.intentTargetClass);"
+        errorLine2="                            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="frameworks/base/packages/SettingsLib/SearchProvider/src/com/android/settingslib/searchprovider/SettingsXmlIndexProvider.java"
+            line="56"
+            column="29"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Class requires API level 23 (current min is 21): `android.provider.SearchIndexableResource`"
+        errorLine1="    public static final class SearchIndexableIntentResource extends SearchIndexableResource {"
+        errorLine2="                                                                    ~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="frameworks/base/packages/SettingsLib/SearchProvider/src/com/android/settingslib/searchprovider/SettingsXmlIndexProvider.java"
+            line="97"
+            column="69"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 23 (current min is 21): `new android.provider.SearchIndexableResource`"
+        errorLine1="            super("
+        errorLine2="            ~~~~~">
+        <location
+            file="frameworks/base/packages/SettingsLib/SearchProvider/src/com/android/settingslib/searchprovider/SettingsXmlIndexProvider.java"
+            line="107"
+            column="13"/>
+    </issue>
+
+    <issue
+        id="NewApi"
         message="Field requires API level 23 (current min is 21): `android.provider.SearchIndexableData#intentAction`"
         errorLine1='                this.intentAction = "android.intent.action.MAIN";'
         errorLine2="                ~~~~~~~~~~~~~~~~~">
@@ -81,17 +125,6 @@
     <issue
         id="NewApi"
         message="Field requires API level 23 (current min is 21): `android.provider.SearchIndexableData#intentTargetClass`"
-        errorLine1="                            indexableResource.intentTargetClass);"
-        errorLine2="                            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SettingsLib/SearchProvider/src/com/android/settingslib/searchprovider/SettingsXmlIndexProvider.java"
-            line="56"
-            column="29"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Field requires API level 23 (current min is 21): `android.provider.SearchIndexableData#intentTargetClass`"
         errorLine1="            this.intentTargetClass = className;"
         errorLine2="            ~~~~~~~~~~~~~~~~~~~~~~">
         <location
@@ -100,37 +133,4 @@
             column="13"/>
     </issue>
 
-    <issue
-        id="NewApi"
-        message="Field requires API level 23 (current min is 21): `android.provider.SearchIndexableData#rank`"
-        errorLine1="                    .add(XmlResource.COLUMN_RANK, indexableResource.rank)"
-        errorLine2="                                                  ~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SettingsLib/SearchProvider/src/com/android/settingslib/searchprovider/SettingsXmlIndexProvider.java"
-            line="50"
-            column="51"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Field requires API level 23 (current min is 21): `android.provider.SearchIndexableResource#xmlResId`"
-        errorLine1="                    .add(XmlResource.COLUMN_XML_RESID, indexableResource.xmlResId)"
-        errorLine2="                                                       ~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SettingsLib/SearchProvider/src/com/android/settingslib/searchprovider/SettingsXmlIndexProvider.java"
-            line="51"
-            column="56"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Field requires API level 23 (current min is 21): `android.provider.SearchIndexablesContract#INDEXABLES_XML_RES_COLUMNS`"
-        errorLine1="        final MatrixCursor cursor = new MatrixCursor(INDEXABLES_XML_RES_COLUMNS);"
-        errorLine2="                                                     ~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SettingsLib/SearchProvider/src/com/android/settingslib/searchprovider/SettingsXmlIndexProvider.java"
-            line="45"
-            column="54"/>
-    </issue>
-
 </issues>
\ 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 f4edb36..460a6f7 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
@@ -24,7 +24,8 @@
 import com.android.settingslib.spa.gallery.button.ActionButtonPageProvider
 import com.android.settingslib.spa.gallery.card.CardPageProvider
 import com.android.settingslib.spa.gallery.chart.ChartPageProvider
-import com.android.settingslib.spa.gallery.dialog.AlertDialogPageProvider
+import com.android.settingslib.spa.gallery.dialog.DialogMainPageProvider
+import com.android.settingslib.spa.gallery.dialog.NavDialogProvider
 import com.android.settingslib.spa.gallery.editor.EditorMainPageProvider
 import com.android.settingslib.spa.gallery.editor.SettingsExposedDropdownMenuBoxPageProvider
 import com.android.settingslib.spa.gallery.editor.SettingsExposedDropdownMenuCheckBoxProvider
@@ -91,7 +92,8 @@
                 ProgressBarPageProvider,
                 LoadingBarPageProvider,
                 ChartPageProvider,
-                AlertDialogPageProvider,
+                DialogMainPageProvider,
+                NavDialogProvider,
                 ItemListPageProvider,
                 ItemOperatePageProvider,
                 OperateListPageProvider,
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/dialog/AlertDialogPage.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/dialog/DialogMainPageProvider.kt
similarity index 84%
rename from packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/dialog/AlertDialogPage.kt
rename to packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/dialog/DialogMainPageProvider.kt
index 1545a3e..4e3fcee 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/dialog/AlertDialogPage.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/dialog/DialogMainPageProvider.kt
@@ -28,10 +28,10 @@
 import com.android.settingslib.spa.widget.preference.Preference
 import com.android.settingslib.spa.widget.preference.PreferenceModel
 
-private const val TITLE = "AlertDialogPage"
+private const val TITLE = "Category: Dialog"
 
-object AlertDialogPageProvider : SettingsPageProvider {
-    override val name = "AlertDialogPage"
+object DialogMainPageProvider : SettingsPageProvider {
+    override val name = "DialogMain"
     private val owner = createSettingsPage()
 
     override fun buildEntry(arguments: Bundle?): List<SettingsEntry> = listOf(
@@ -47,6 +47,12 @@
                 override val onClick = alertDialogPresenter::open
             })
         }.build(),
+        SettingsEntryBuilder.create("NavDialog", owner).setUiLayoutFn {
+            Preference(object : PreferenceModel {
+                override val title = "Navigate to Dialog"
+                override val onClick = navigator(route = NavDialogProvider.name)
+            })
+        }.build(),
     )
 
     fun buildInjectEntry() = SettingsEntryBuilder.createInject(owner)
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/dialog/NavDialogProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/dialog/NavDialogProvider.kt
new file mode 100644
index 0000000..6f79911
--- /dev/null
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/dialog/NavDialogProvider.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spa.gallery.dialog
+
+import android.os.Bundle
+import androidx.compose.runtime.Composable
+import com.android.settingslib.spa.framework.common.SettingsPageProvider
+import com.android.settingslib.spa.widget.dialog.SettingsDialogCard
+
+object NavDialogProvider : SettingsPageProvider {
+    override val name = "NavDialog"
+    override val navType = SettingsPageProvider.NavType.Dialog
+
+    @Composable
+    override fun Page(arguments: Bundle?) {
+        SettingsDialogCard("Example Nav Dialog") {}
+    }
+}
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePageProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePageProvider.kt
index 6a2e598..1f028d5 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePageProvider.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePageProvider.kt
@@ -30,7 +30,7 @@
 import com.android.settingslib.spa.gallery.button.ActionButtonPageProvider
 import com.android.settingslib.spa.gallery.card.CardPageProvider
 import com.android.settingslib.spa.gallery.chart.ChartPageProvider
-import com.android.settingslib.spa.gallery.dialog.AlertDialogPageProvider
+import com.android.settingslib.spa.gallery.dialog.DialogMainPageProvider
 import com.android.settingslib.spa.gallery.editor.EditorMainPageProvider
 import com.android.settingslib.spa.gallery.itemList.OperateListPageProvider
 import com.android.settingslib.spa.gallery.page.ArgumentPageModel
@@ -71,7 +71,7 @@
             ProgressBarPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
             LoadingBarPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
             ChartPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
-            AlertDialogPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
+            DialogMainPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
             EditorMainPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
             CardPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
             CopyablePageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
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 9f8c868..5605485 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
@@ -22,7 +22,6 @@
 import androidx.activity.ComponentActivity
 import androidx.activity.compose.setContent
 import androidx.annotation.VisibleForTesting
-import androidx.compose.foundation.layout.Box
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.runtime.LaunchedEffect
@@ -30,15 +29,19 @@
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.saveable.rememberSaveable
 import androidx.core.view.WindowCompat
+import androidx.navigation.NavBackStackEntry
 import androidx.navigation.NavGraph.Companion.findStartDestination
+import androidx.navigation.NavGraphBuilder
 import androidx.navigation.compose.NavHost
 import androidx.navigation.compose.composable
+import androidx.navigation.compose.dialog
 import androidx.navigation.compose.rememberNavController
 import com.android.settingslib.spa.R
 import com.android.settingslib.spa.framework.common.LogCategory
 import com.android.settingslib.spa.framework.common.NullPageProvider
 import com.android.settingslib.spa.framework.common.SettingsPage
 import com.android.settingslib.spa.framework.common.SettingsPageProvider
+import com.android.settingslib.spa.framework.common.SettingsPageProvider.NavType
 import com.android.settingslib.spa.framework.common.SettingsPageProviderRepository
 import com.android.settingslib.spa.framework.common.SpaEnvironmentFactory
 import com.android.settingslib.spa.framework.common.createSettingsPage
@@ -127,27 +130,31 @@
     allProvider: Collection<SettingsPageProvider>,
     content: @Composable (SettingsPage) -> Unit,
 ) {
-    // TODO(b/298520326): Remove Box after the issue is fixed.
-    // Wrap the top level node into a Box to workaround an issue of Compose 1.6.0-alpha03.
-    Box {
-        NavHost(
-            navController = navController,
-            startDestination = NullPageProvider.name,
-        ) {
-            composable(NullPageProvider.name) {}
-            for (spp in allProvider) {
-                animatedComposable(
-                    route = spp.name + spp.parameter.navRoute(),
-                    arguments = spp.parameter,
-                ) { navBackStackEntry ->
-                    val page = remember { spp.createSettingsPage(navBackStackEntry.arguments) }
-                    content(page)
-                }
+    NavHost(
+        navController = navController,
+        startDestination = NullPageProvider.name,
+    ) {
+        composable(NullPageProvider.name) {}
+        for (spp in allProvider) {
+            destination(spp) { navBackStackEntry ->
+                val page = remember { spp.createSettingsPage(navBackStackEntry.arguments) }
+                content(page)
             }
         }
     }
 }
 
+private fun NavGraphBuilder.destination(
+    spp: SettingsPageProvider,
+    content: @Composable (NavBackStackEntry) -> Unit,
+) {
+    val route = spp.name + spp.parameter.navRoute()
+    when (spp.navType) {
+        NavType.Page -> animatedComposable(route, spp.parameter) { content(it) }
+        NavType.Dialog -> dialog(route, spp.parameter) { content(it) }
+    }
+}
+
 @Composable
 private fun NavControllerWrapperImpl.InitialDestination(
     initialIntent: Intent?,
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 18f964e..81bee5e 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
@@ -34,6 +34,14 @@
     /** The page provider name, needs to be *unique* and *stable*. */
     val name: String
 
+    enum class NavType {
+        Page,
+        Dialog,
+    }
+
+    val navType: NavType
+        get() = NavType.Page
+
     /** The display name of this page provider, for better readability. */
     val displayName: String
         get() = name
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt
index c143390..b7f2c1e 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt
@@ -34,6 +34,15 @@
         end = itemPaddingEnd,
         bottom = itemPaddingVertical,
     )
+    val textFieldPadding = PaddingValues(
+        start = itemPaddingStart,
+        end = itemPaddingEnd,
+    )
+    val menuFieldPadding = PaddingValues(
+        start = itemPaddingStart,
+        end = itemPaddingEnd,
+        bottom = itemPaddingVertical,
+    )
     val itemPaddingAround = 8.dp
     val itemDividerHeight = 32.dp
 
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/dialog/SettingsDialog.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/dialog/SettingsDialog.kt
index 8b172da..f08e740 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/dialog/SettingsDialog.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/dialog/SettingsDialog.kt
@@ -19,10 +19,13 @@
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.AlertDialogDefaults
 import androidx.compose.material3.Card
+import androidx.compose.material3.CardDefaults
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.window.Dialog
+import androidx.navigation.compose.NavHost
 import com.android.settingslib.spa.framework.theme.SettingsDimension
 import com.android.settingslib.spa.framework.theme.SettingsShape
 import com.android.settingslib.spa.widget.ui.SettingsTitle
@@ -34,13 +37,27 @@
     content: @Composable () -> Unit,
 ) {
     Dialog(onDismissRequest = onDismissRequest) {
-        Card(shape = SettingsShape.CornerExtraLarge) {
-            Column(modifier = Modifier.padding(vertical = SettingsDimension.itemPaddingAround)) {
-                Box(modifier = Modifier.padding(SettingsDimension.dialogItemPadding)) {
-                    SettingsTitle(title = title, useMediumWeight = true)
-                }
-                content()
+        SettingsDialogCard(title, content)
+    }
+}
+
+/**
+ * Card for dialog, suitable for independent dialog in the [NavHost].
+ */
+@Composable
+fun SettingsDialogCard(
+    title: String,
+    content: @Composable () -> Unit,
+) {
+    Card(
+        shape = SettingsShape.CornerExtraLarge,
+        colors = CardDefaults.cardColors(containerColor = AlertDialogDefaults.containerColor),
+    ) {
+        Column(modifier = Modifier.padding(vertical = SettingsDimension.itemPaddingAround)) {
+            Box(modifier = Modifier.padding(SettingsDimension.dialogItemPadding)) {
+                SettingsTitle(title = title, useMediumWeight = true)
             }
+            content()
         }
     }
 }
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuBox.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuBox.kt
index 0d6c064..f6692a3 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuBox.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuBox.kt
@@ -51,7 +51,7 @@
         onExpandedChange = { expanded = it },
         modifier = Modifier
             .width(350.dp)
-            .padding(SettingsDimension.itemPadding),
+            .padding(SettingsDimension.menuFieldPadding),
     ) {
         OutlinedTextField(
             // The `menuAnchor` modifier must be passed to the text field for correctness.
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuCheckBox.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuCheckBox.kt
index 5d248e1..ba8e354 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuCheckBox.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuCheckBox.kt
@@ -63,7 +63,7 @@
         onExpandedChange = { expanded = it },
         modifier = Modifier
             .width(350.dp)
-            .padding(SettingsDimension.itemPadding)
+            .padding(SettingsDimension.menuFieldPadding)
             .onSizeChanged { dropDownWidth = it.width },
     ) {
         OutlinedTextField(
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsOutlinedTextField.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsOutlinedTextField.kt
index e0dd4e1..2ce3c66 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsOutlinedTextField.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsOutlinedTextField.kt
@@ -42,7 +42,7 @@
     OutlinedTextField(
         modifier = Modifier
             .fillMaxWidth()
-            .padding(SettingsDimension.itemPadding),
+            .padding(SettingsDimension.textFieldPadding),
         value = value,
         onValueChange = onTextChange,
         label = {
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsTextFieldPassword.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsTextFieldPassword.kt
index 0757df3..3102a00 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsTextFieldPassword.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsTextFieldPassword.kt
@@ -52,7 +52,7 @@
     var visibility by remember { mutableStateOf(false) }
     OutlinedTextField(
         modifier = Modifier
-            .padding(SettingsDimension.itemPadding)
+            .padding(SettingsDimension.menuFieldPadding)
             .fillMaxWidth(),
         value = value,
         onValueChange = onTextChange,
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/CheckboxPreference.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/CheckboxPreference.kt
new file mode 100644
index 0000000..93d77d4
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/CheckboxPreference.kt
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.preference
+
+import androidx.compose.foundation.LocalIndication
+import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.selection.toggleable
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.outlined.AirplanemodeActive
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.semantics.Role
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.Dp
+import com.android.settingslib.spa.framework.theme.SettingsDimension
+import com.android.settingslib.spa.framework.theme.SettingsTheme
+import com.android.settingslib.spa.framework.util.EntryHighlight
+import com.android.settingslib.spa.framework.util.wrapOnSwitchWithLog
+import com.android.settingslib.spa.widget.ui.SettingsCheckbox
+import com.android.settingslib.spa.widget.ui.SettingsIcon
+
+/**
+ * The widget model for [CheckboxPreference] widget.
+ */
+interface CheckboxPreferenceModel {
+    /**
+     * The title of this [CheckboxPreference].
+     */
+    val title: String
+
+    /**
+     * The summary of this [CheckboxPreference].
+     */
+    val summary: () -> String
+        get() = { "" }
+
+    /**
+     * The icon of this [Preference].
+     *
+     * Default is `null` which means no icon.
+     */
+    val icon: (@Composable () -> Unit)?
+        get() = null
+
+    /**
+     * Indicates whether this [CheckboxPreference] is checked.
+     *
+     * This can be `null` during the data loading before the data is available.
+     */
+    val checked: () -> Boolean?
+
+    /**
+     * Indicates whether this [CheckboxPreference] is changeable.
+     *
+     * Not changeable [CheckboxPreference] will be displayed in disabled style.
+     */
+    val changeable: () -> Boolean
+        get() = { true }
+
+    /**
+     * The checkbox change handler of this [CheckboxPreference].
+     *
+     * If `null`, this [CheckboxPreference] is not [toggleable].
+     */
+    val onCheckedChange: ((newChecked: Boolean) -> Unit)?
+}
+
+/**
+ * CheckboxPreference widget.
+ *
+ * Data is provided through [CheckboxPreferenceModel].
+ */
+@Composable
+fun CheckboxPreference(model: CheckboxPreferenceModel) {
+    EntryHighlight {
+        InternalCheckboxPreference(
+            title = model.title,
+            summary = model.summary,
+            icon = model.icon,
+            checked = model.checked(),
+            changeable = model.changeable(),
+            onCheckedChange = model.onCheckedChange,
+        )
+    }
+}
+
+@Composable
+internal fun InternalCheckboxPreference(
+    title: String,
+    summary: () -> String = { "" },
+    icon: @Composable (() -> Unit)? = null,
+    checked: Boolean?,
+    changeable: Boolean = true,
+    paddingStart: Dp = SettingsDimension.itemPaddingStart,
+    paddingEnd: Dp = SettingsDimension.itemPaddingEnd,
+    paddingVertical: Dp = SettingsDimension.itemPaddingVertical,
+    onCheckedChange: ((newChecked: Boolean) -> Unit)?,
+) {
+    val indication = LocalIndication.current
+    val onChangeWithLog = wrapOnSwitchWithLog(onCheckedChange)
+    val interactionSource = remember { MutableInteractionSource() }
+    val modifier = remember(checked, changeable) {
+        if (checked != null && onChangeWithLog != null) {
+            Modifier.toggleable(
+                value = checked,
+                interactionSource = interactionSource,
+                indication = indication,
+                enabled = changeable,
+                role = Role.Checkbox,
+                onValueChange = onChangeWithLog,
+            )
+        } else Modifier
+    }
+    BasePreference(
+        title = title,
+        summary = summary,
+        modifier = modifier,
+        enabled = { changeable },
+        paddingStart = paddingStart,
+        paddingEnd = paddingEnd,
+        paddingVertical = paddingVertical,
+        icon = icon,
+    ) {
+        Spacer(Modifier.width(SettingsDimension.itemPaddingEnd))
+        SettingsCheckbox(
+            checked = checked,
+            changeable = { changeable },
+            // The onCheckedChange is handled on the whole CheckboxPreference.
+            // DO NOT set it on SettingsCheckbox.
+            onCheckedChange = null,
+            interactionSource = interactionSource,
+        )
+    }
+}
+
+@Preview
+@Composable
+private fun CheckboxPreferencePreview() {
+    SettingsTheme {
+        Column {
+            InternalCheckboxPreference(
+                title = "Use Dark theme",
+                checked = true,
+                onCheckedChange = {},
+            )
+            InternalCheckboxPreference(
+                title = "Use Dark theme",
+                summary = { "Summary" },
+                checked = false,
+                onCheckedChange = {},
+            )
+            InternalCheckboxPreference(
+                title = "Use Dark theme",
+                summary = { "Summary" },
+                checked = true,
+                onCheckedChange = {},
+                icon = @Composable {
+                    SettingsIcon(imageVector = Icons.Outlined.AirplanemodeActive)
+                },
+            )
+        }
+    }
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Checkbox.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Checkbox.kt
new file mode 100644
index 0000000..7a9d46c
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Checkbox.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.ui
+
+import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.material3.Checkbox
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import com.android.settingslib.spa.framework.util.wrapOnSwitchWithLog
+
+@Composable
+internal fun SettingsCheckbox(
+    checked: Boolean?,
+    changeable: () -> Boolean,
+    onCheckedChange: ((newChecked: Boolean) -> Unit)? = null,
+    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
+) {
+    if (checked != null) {
+        Checkbox(
+            checked = checked,
+            onCheckedChange = wrapOnSwitchWithLog(onCheckedChange),
+            enabled = changeable(),
+            interactionSource = interactionSource,
+        )
+    } else {
+        Checkbox(
+            checked = false,
+            onCheckedChange = null,
+            enabled = false,
+            interactionSource = interactionSource,
+        )
+    }
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Spinner.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Spinner.kt
index a9974dc..514ad669 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Spinner.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Spinner.kt
@@ -39,6 +39,9 @@
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.semantics.Role
+import androidx.compose.ui.semantics.role
+import androidx.compose.ui.semantics.semantics
 import androidx.compose.ui.unit.dp
 import com.android.settingslib.spa.framework.theme.SettingsDimension
 import com.android.settingslib.spa.framework.theme.SettingsTheme
@@ -68,6 +71,7 @@
     ) {
         val contentPadding = PaddingValues(horizontal = SettingsDimension.itemPaddingEnd)
         Button(
+            modifier = Modifier.semantics { role = Role.DropdownList },
             onClick = { expanded = true },
             colors = ButtonDefaults.buttonColors(
                 containerColor = SettingsTheme.colorScheme.spinnerHeaderContainer,
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/BrowseActivityTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/BrowseActivityTest.kt
index 92d3411..8cbd964 100644
--- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/BrowseActivityTest.kt
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/BrowseActivityTest.kt
@@ -18,6 +18,7 @@
 
 import android.content.Context
 import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.hasText
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onAllNodesWithText
 import androidx.compose.ui.test.onNodeWithText
@@ -29,12 +30,14 @@
 import com.android.settingslib.spa.framework.common.SettingsPage
 import com.android.settingslib.spa.framework.common.SpaEnvironmentFactory
 import com.android.settingslib.spa.framework.common.createSettingsPage
+import com.android.settingslib.spa.tests.testutils.SppDialog
 import com.android.settingslib.spa.tests.testutils.SpaEnvironmentForTest
 import com.android.settingslib.spa.tests.testutils.SpaLoggerForTest
 import com.android.settingslib.spa.tests.testutils.SppDisabled
 import com.android.settingslib.spa.tests.testutils.SppHome
 import com.android.settingslib.spa.testutils.waitUntil
-import com.google.common.truth.Truth
+import com.android.settingslib.spa.testutils.waitUntilExists
+import com.google.common.truth.Truth.assertThat
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -106,11 +109,31 @@
         composeTestRule.onNodeWithText(sppDisabled.getTitle(null)).assertDoesNotExist()
         spaLogger.verifyPageEvent(pageDisabled.id, 0, 0)
     }
+
+    @Test
+    fun browseContent_dialog() {
+        val spaEnvironment = SpaEnvironmentForTest(
+            context = context,
+            rootPages = listOf(SppHome.createSettingsPage()),
+            logger = spaLogger,
+        )
+        SpaEnvironmentFactory.reset(spaEnvironment)
+        val sppRepository by spaEnvironment.pageProviderRepository
+
+        composeTestRule.setContent {
+            BrowseContent(
+                sppRepository = sppRepository,
+                isPageEnabled = SettingsPage::isEnabled,
+                initialIntent = null,
+            )
+        }
+        composeTestRule.onNodeWithText(SppDialog.name).performClick()
+
+        composeTestRule.waitUntilExists(hasText(SppDialog.CONTENT))
+    }
 }
 
 private fun SpaLoggerForTest.verifyPageEvent(id: String, entryCount: Int, leaveCount: Int) {
-    Truth.assertThat(getEventCount(id, LogEvent.PAGE_ENTER, LogCategory.FRAMEWORK))
-        .isEqualTo(entryCount)
-    Truth.assertThat(getEventCount(id, LogEvent.PAGE_LEAVE, LogCategory.FRAMEWORK))
-        .isEqualTo(leaveCount)
+    assertThat(getEventCount(id, LogEvent.PAGE_ENTER, LogCategory.FRAMEWORK)).isEqualTo(entryCount)
+    assertThat(getEventCount(id, LogEvent.PAGE_LEAVE, LogCategory.FRAMEWORK)).isEqualTo(leaveCount)
 }
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/common/SettingsEntryRepositoryTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/common/SettingsEntryRepositoryTest.kt
index b139f28..0a1c05f 100644
--- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/common/SettingsEntryRepositoryTest.kt
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/common/SettingsEntryRepositoryTest.kt
@@ -22,6 +22,7 @@
 import com.android.settingslib.spa.framework.util.genEntryId
 import com.android.settingslib.spa.framework.util.genPageId
 import com.android.settingslib.spa.tests.testutils.SpaEnvironmentForTest
+import com.android.settingslib.spa.tests.testutils.SppDialog
 import com.android.settingslib.spa.tests.testutils.SppHome
 import com.android.settingslib.spa.tests.testutils.SppLayer1
 import com.android.settingslib.spa.tests.testutils.SppLayer2
@@ -39,26 +40,21 @@
     @Test
     fun testGetPageWithEntry() {
         val pageWithEntry = entryRepository.getAllPageWithEntry()
-        assertThat(pageWithEntry.size).isEqualTo(3)
-        assertThat(
-            entryRepository.getPageWithEntry(genPageId("SppHome"))
-                ?.entries?.size
-        ).isEqualTo(1)
-        assertThat(
-            entryRepository.getPageWithEntry(genPageId("SppLayer1"))
-                ?.entries?.size
-        ).isEqualTo(3)
-        assertThat(
-            entryRepository.getPageWithEntry(genPageId("SppLayer2"))
-                ?.entries?.size
-        ).isEqualTo(2)
+
+        assertThat(pageWithEntry).hasSize(4)
+        assertThat(entryRepository.getPageWithEntry(genPageId("SppHome"))?.entries)
+            .hasSize(2)
+        assertThat(entryRepository.getPageWithEntry(genPageId("SppLayer1"))?.entries)
+            .hasSize(3)
+        assertThat(entryRepository.getPageWithEntry(genPageId("SppLayer2"))?.entries)
+            .hasSize(2)
         assertThat(entryRepository.getPageWithEntry(genPageId("SppWithParam"))).isNull()
     }
 
     @Test
     fun testGetEntry() {
         val entry = entryRepository.getAllEntries()
-        assertThat(entry.size).isEqualTo(7)
+        assertThat(entry).hasSize(8)
         assertThat(
             entryRepository.getEntry(
                 genEntryId(
@@ -91,6 +87,16 @@
         ).isNotNull()
         assertThat(
             entryRepository.getEntry(
+                genEntryId(
+                    "INJECT",
+                    SppDialog.createSettingsPage(),
+                    SppHome.createSettingsPage(),
+                    SppDialog.createSettingsPage(),
+                )
+            )
+        ).isNotNull()
+        assertThat(
+            entryRepository.getEntry(
                 genEntryId("Layer1Entry1", SppLayer1.createSettingsPage())
             )
         ).isNotNull()
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/common/SettingsPageProviderRepositoryTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/common/SettingsPageProviderRepositoryTest.kt
index 8576573..169c541 100644
--- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/common/SettingsPageProviderRepositoryTest.kt
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/common/SettingsPageProviderRepositoryTest.kt
@@ -25,39 +25,55 @@
 @RunWith(AndroidJUnit4::class)
 class SettingsPageProviderRepositoryTest {
     @Test
-    fun getStartPageTest() {
-        val sppRepoEmpty = SettingsPageProviderRepository(emptyList(), emptyList())
+    fun rootPages_empty() {
+        val sppRepoEmpty = SettingsPageProviderRepository(emptyList())
+
         assertThat(sppRepoEmpty.getDefaultStartPage()).isEqualTo("")
         assertThat(sppRepoEmpty.getAllRootPages()).isEmpty()
-
-        val nullPage = NullPageProvider.createSettingsPage()
-        val sppRepoNull =
-            SettingsPageProviderRepository(emptyList(), listOf(nullPage))
-        assertThat(sppRepoNull.getDefaultStartPage()).isEqualTo("NULL")
-        assertThat(sppRepoNull.getAllRootPages()).contains(nullPage)
-
-        val rootPage1 = createSettingsPage(sppName = "Spp1", displayName = "Spp1")
-        val rootPage2 = createSettingsPage(sppName = "Spp2", displayName = "Spp2")
-        val sppRepo = SettingsPageProviderRepository(emptyList(), listOf(rootPage1, rootPage2))
-        val allRoots = sppRepo.getAllRootPages()
-        assertThat(sppRepo.getDefaultStartPage()).isEqualTo("Spp1")
-        assertThat(allRoots.size).isEqualTo(2)
-        assertThat(allRoots).contains(rootPage1)
-        assertThat(allRoots).contains(rootPage2)
     }
 
     @Test
-    fun getProviderTest() {
-        val sppRepoEmpty = SettingsPageProviderRepository(emptyList(), emptyList())
+    fun rootPages_single() {
+        val nullPage = NullPageProvider.createSettingsPage()
+
+        val sppRepoNull = SettingsPageProviderRepository(
+            allPageProviders = emptyList(),
+            rootPages = listOf(nullPage),
+        )
+
+        assertThat(sppRepoNull.getDefaultStartPage()).isEqualTo("NULL")
+        assertThat(sppRepoNull.getAllRootPages()).containsExactly(nullPage)
+    }
+
+    @Test
+    fun rootPages_twoPages() {
+        val rootPage1 = createSettingsPage(sppName = "Spp1", displayName = "Spp1")
+        val rootPage2 = createSettingsPage(sppName = "Spp2", displayName = "Spp2")
+
+        val sppRepo = SettingsPageProviderRepository(
+            allPageProviders = emptyList(),
+            rootPages = listOf(rootPage1, rootPage2),
+        )
+
+        assertThat(sppRepo.getDefaultStartPage()).isEqualTo("Spp1")
+        assertThat(sppRepo.getAllRootPages()).containsExactly(rootPage1, rootPage2)
+    }
+
+    @Test
+    fun getProviderOrNull_empty() {
+        val sppRepoEmpty = SettingsPageProviderRepository(emptyList())
         assertThat(sppRepoEmpty.getAllProviders()).isEmpty()
         assertThat(sppRepoEmpty.getProviderOrNull("Spp")).isNull()
+    }
 
+    @Test
+    fun getProviderOrNull_single() {
         val sppRepo = SettingsPageProviderRepository(listOf(
             object : SettingsPageProvider {
                 override val name = "Spp"
             }
-        ), emptyList())
-        assertThat(sppRepo.getAllProviders().size).isEqualTo(1)
+        ))
+        assertThat(sppRepo.getAllProviders()).hasSize(1)
         assertThat(sppRepo.getProviderOrNull("Spp")).isNotNull()
         assertThat(sppRepo.getProviderOrNull("SppUnknown")).isNull()
     }
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/tests/testutils/SpaEnvironmentForTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/tests/testutils/SpaEnvironmentForTest.kt
index 2755b4e..22a5ca3 100644
--- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/tests/testutils/SpaEnvironmentForTest.kt
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/tests/testutils/SpaEnvironmentForTest.kt
@@ -21,6 +21,8 @@
 import android.content.Context
 import android.content.Intent
 import android.os.Bundle
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
 import androidx.navigation.NavType
 import androidx.navigation.navArgument
 import com.android.settingslib.spa.framework.BrowseActivity
@@ -88,6 +90,7 @@
         val owner = this.createSettingsPage()
         return listOf(
             SppLayer1.buildInject().setLink(fromPage = owner).build(),
+            SppDialog.buildInject().setLink(fromPage = owner).build(),
         )
     }
 }
@@ -160,6 +163,21 @@
     }
 }
 
+object SppDialog : SettingsPageProvider {
+    override val name = "SppDialog"
+    override val navType = SettingsPageProvider.NavType.Dialog
+
+    const val CONTENT = "SppDialog Content"
+
+    @Composable
+    override fun Page(arguments: Bundle?) {
+        Text(CONTENT)
+    }
+
+    fun buildInject() = SettingsEntryBuilder.createInject(this.createSettingsPage())
+        .setMacro { SimplePreferenceMacro(title = name, clickRoute = name) }
+}
+
 object SppForSearch : SettingsPageProvider {
     override val name = "SppForSearch"
 
@@ -223,6 +241,7 @@
                         navArgument("rt_param") { type = NavType.StringType },
                     )
                 },
+                SppDialog,
             ),
             rootPages
         )
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/CheckboxPreferenceTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/CheckboxPreferenceTest.kt
new file mode 100644
index 0000000..7375923
--- /dev/null
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/CheckboxPreferenceTest.kt
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spa.widget.preference
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.assertIsOff
+import androidx.compose.ui.test.assertIsOn
+import androidx.compose.ui.test.isToggleable
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithText
+import androidx.compose.ui.test.performClick
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class CheckboxPreferenceTest {
+    @get:Rule
+    val composeTestRule = createComposeRule()
+
+    @Test
+    fun title_displayed() {
+        composeTestRule.setContent {
+            testCheckboxPreference(changeable = true)
+        }
+
+        composeTestRule.onNodeWithText("CheckboxPreference").assertIsDisplayed()
+    }
+
+    @Test
+    fun toggleable_initialStateIsCorrect() {
+        composeTestRule.setContent {
+            testCheckboxPreference(changeable = true)
+        }
+
+        composeTestRule.onNode(isToggleable()).assertIsOff()
+    }
+
+    @Test
+    fun click_changeable_withEffect() {
+        composeTestRule.setContent {
+            testCheckboxPreference(changeable = true)
+        }
+
+        composeTestRule.onNodeWithText("CheckboxPreference").performClick()
+        composeTestRule.onNode(isToggleable()).assertIsOn()
+    }
+
+    @Test
+    fun click_notChangeable_noEffect() {
+        composeTestRule.setContent {
+            testCheckboxPreference(changeable = false)
+        }
+
+        composeTestRule.onNodeWithText("CheckboxPreference").performClick()
+        composeTestRule.onNode(isToggleable()).assertIsOff()
+    }
+}
+
+@Composable
+private fun testCheckboxPreference(changeable: Boolean) {
+    var checked by rememberSaveable { mutableStateOf(false) }
+    CheckboxPreference(remember {
+        object : CheckboxPreferenceModel {
+            override val title = "CheckboxPreference"
+            override val checked = { checked }
+            override val changeable = { changeable }
+            override val onCheckedChange = { newChecked: Boolean -> checked = newChecked }
+        }
+    })
+}
\ No newline at end of file
diff --git a/packages/SettingsLib/Tile/Android.bp b/packages/SettingsLib/Tile/Android.bp
index eb9e329..19c59dd 100644
--- a/packages/SettingsLib/Tile/Android.bp
+++ b/packages/SettingsLib/Tile/Android.bp
@@ -14,8 +14,11 @@
     srcs: ["src/**/*.java"],
 
     static_libs: [
-          "androidx.annotation_annotation",
+        "androidx.annotation_annotation",
     ],
 
     min_sdk_version: "21",
+    lint: {
+        baseline_filename: "lint-baseline.xml",
+    },
 }
diff --git a/packages/SettingsLib/Tile/lint-baseline.xml b/packages/SettingsLib/Tile/lint-baseline.xml
index 326ec0d..56b1bca 100644
--- a/packages/SettingsLib/Tile/lint-baseline.xml
+++ b/packages/SettingsLib/Tile/lint-baseline.xml
@@ -1,55 +1,59 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="5" by="lint 4.1.0" client="cli" variant="all" version="4.1.0">
+<issues format="6" by="lint 8.4.0-alpha01" type="baseline" client="" dependencies="true" name="" variant="all" version="8.4.0-alpha01">
 
     <issue
         id="NewApi"
-        message="Call requires API level 24 (current min is 21): `java.lang.Iterable#forEach`"
-        errorLine1="        controllers.forEach(controller -&gt; {"
-        errorLine2="                    ~~~~~~~">
-        <location
-            file="frameworks/base/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/SwitchesProvider.java"
-            line="79"
-            column="21"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 23 (current min is 21): `android.graphics.drawable.Icon#createWithResource`">
+        message="Call requires API level 29 (current min is 21): `android.os.Parcel#writeBoolean`"
+        errorLine1="        dest.writeBoolean(this instanceof ProviderTile);"
+        errorLine2="             ~~~~~~~~~~~~">
         <location
             file="frameworks/base/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java"
-            line="312"/>
+            line="114"
+            column="14"/>
     </issue>
 
     <issue
         id="NewApi"
-        message="Call requires API level 23 (current min is 21): `android.graphics.drawable.Icon#setTint`">
+        message="Call requires API level 23 (current min is 21): `android.graphics.drawable.Icon#createWithResource`"
+        errorLine1="            final Icon icon = Icon.createWithResource(componentInfo.packageName, iconResId);"
+        errorLine2="                                   ~~~~~~~~~~~~~~~~~~">
         <location
             file="frameworks/base/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java"
-            line="318"/>
+            line="326"
+            column="36"/>
     </issue>
 
     <issue
         id="NewApi"
-        message="Call requires API level 29 (current min is 21): `android.os.Parcel#readBoolean`">
+        message="Call requires API level 23 (current min is 21): `android.graphics.drawable.Icon#setTint`"
+        errorLine1="                icon.setTint(tintColor);"
+        errorLine2="                     ~~~~~~~">
         <location
             file="frameworks/base/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java"
-            line="373"/>
+            line="332"
+            column="22"/>
     </issue>
 
     <issue
         id="NewApi"
-        message="Call requires API level 29 (current min is 21): `android.os.Parcel#writeBoolean`">
+        message="Call requires API level 29 (current min is 21): `android.os.Parcel#readBoolean`"
+        errorLine1="            final boolean isProviderTile = source.readBoolean();"
+        errorLine2="                                                  ~~~~~~~~~~~">
         <location
             file="frameworks/base/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java"
-            line="108"/>
+            line="387"
+            column="51"/>
     </issue>
 
     <issue
         id="NewApi"
-        message="Call requires API level 31 (current min is 21): `android.content.Context#getAttributionSource`">
+        message="Call requires API level 31 (current min is 21): `android.content.Context#getAttributionSource`"
+        errorLine1="            return provider.call(context.getAttributionSource(),"
+        errorLine2="                                         ~~~~~~~~~~~~~~~~~~~~">
         <location
             file="frameworks/base/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java"
-            line="565"/>
+            line="601"
+            column="42"/>
     </issue>
 
 </issues>
\ No newline at end of file
diff --git a/packages/SettingsLib/Utils/Android.bp b/packages/SettingsLib/Utils/Android.bp
index c7ad24c..d5a56c8 100644
--- a/packages/SettingsLib/Utils/Android.bp
+++ b/packages/SettingsLib/Utils/Android.bp
@@ -31,4 +31,7 @@
         "com.android.healthfitness",
         "com.android.permission",
     ],
+    lint: {
+        baseline_filename: "lint-baseline.xml",
+    },
 }
diff --git a/packages/SettingsLib/Utils/lint-baseline.xml b/packages/SettingsLib/Utils/lint-baseline.xml
index 3fcd56c..2f6cc3a 100644
--- a/packages/SettingsLib/Utils/lint-baseline.xml
+++ b/packages/SettingsLib/Utils/lint-baseline.xml
@@ -1,26 +1,15 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.0.0-dev" type="baseline" dependencies="true" variant="all" version="8.0.0-dev">
+<issues format="6" by="lint 8.4.0-alpha01" type="baseline" client="" dependencies="true" name="" variant="all" version="8.4.0-alpha01">
 
     <issue
         id="NewApi"
-        message="Call requires API level 24 (current min is 21): `android.os.UserHandle#of`"
-        errorLine1="                            mContext.getPackageName(), 0, UserHandle.of(managedUserId)"
-        errorLine2="                                                                     ~~">
+        message="Call requires API level 24 (current min is 23): `android.os.UserManager#isManagedProfile`"
+        errorLine1="        return context.getSystemService(UserManager.class).isManagedProfile(userId)"
+        errorLine2="                                                           ~~~~~~~~~~~~~~~~">
         <location
-            file="frameworks/base/packages/SettingsLib/Utils/src/com/android/settingslib/utils/WorkPolicyUtils.java"
-            line="119"
-            column="70"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 24 (current min is 21): `android.os.UserHandle#of`"
-        errorLine1="                        intent, 0, UserHandle.of(managedUserId));"
-        errorLine2="                                              ~~">
-        <location
-            file="frameworks/base/packages/SettingsLib/Utils/src/com/android/settingslib/utils/WorkPolicyUtils.java"
-            line="150"
-            column="47"/>
+            file="frameworks/base/packages/SettingsLib/Utils/src/com/android/settingslib/utils/applications/AppUtils.java"
+            line="62"
+            column="60"/>
     </issue>
 
     <issue
@@ -36,35 +25,13 @@
 
     <issue
         id="NewApi"
-        message="Call requires API level 24 (current min is 21): `android.os.UserManager#isManagedProfile`"
-        errorLine1="            if (mUserManager.isManagedProfile(id)) {"
-        errorLine2="                             ~~~~~~~~~~~~~~~~">
+        message="Call requires API level 29 (current min is 21): `android.content.Context#startActivityAsUser`"
+        errorLine1="            activityContext.startActivityAsUser(intent, UserHandle.of(userId));"
+        errorLine2="                            ~~~~~~~~~~~~~~~~~~~">
         <location
             file="frameworks/base/packages/SettingsLib/Utils/src/com/android/settingslib/utils/WorkPolicyUtils.java"
-            line="173"
-            column="30"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 24 (current min is 23): `android.os.UserManager#isManagedProfile`"
-        errorLine1="        return context.getSystemService(UserManager.class).isManagedProfile(userId)"
-        errorLine2="                                                           ~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SettingsLib/Utils/src/com/android/settingslib/utils/applications/AppUtils.java"
-            line="62"
-            column="60"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 26 (current min is 21): `android.app.admin.DevicePolicyManager#getDeviceOwnerComponentOnAnyUser`"
-        errorLine1="        return mDevicePolicyManager.getDeviceOwnerComponentOnAnyUser();"
-        errorLine2="                                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SettingsLib/Utils/src/com/android/settingslib/utils/WorkPolicyUtils.java"
-            line="163"
-            column="37"/>
+            line="80"
+            column="29"/>
     </issue>
 
     <issue
@@ -80,13 +47,13 @@
 
     <issue
         id="NewApi"
-        message="Call requires API level 29 (current min is 21): `android.content.Context#startActivityAsUser`"
-        errorLine1="            activityContext.startActivityAsUser(intent, UserHandle.of(userId));"
-        errorLine2="                            ~~~~~~~~~~~~~~~~~~~">
+        message="Call requires API level 24 (current min is 21): `android.os.UserHandle#of`"
+        errorLine1="                            mContext.getPackageName(), 0, UserHandle.of(managedUserId)"
+        errorLine2="                                                                     ~~">
         <location
             file="frameworks/base/packages/SettingsLib/Utils/src/com/android/settingslib/utils/WorkPolicyUtils.java"
-            line="80"
-            column="29"/>
+            line="119"
+            column="70"/>
     </issue>
 
     <issue
@@ -102,6 +69,28 @@
 
     <issue
         id="NewApi"
+        message="Call requires API level 24 (current min is 21): `android.os.UserHandle#of`"
+        errorLine1="                        intent, 0, UserHandle.of(managedUserId));"
+        errorLine2="                                              ~~">
+        <location
+            file="frameworks/base/packages/SettingsLib/Utils/src/com/android/settingslib/utils/WorkPolicyUtils.java"
+            line="150"
+            column="47"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 26 (current min is 21): `android.app.admin.DevicePolicyManager#getDeviceOwnerComponentOnAnyUser`"
+        errorLine1="        return mDevicePolicyManager.getDeviceOwnerComponentOnAnyUser();"
+        errorLine2="                                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="frameworks/base/packages/SettingsLib/Utils/src/com/android/settingslib/utils/WorkPolicyUtils.java"
+            line="163"
+            column="37"/>
+    </issue>
+
+    <issue
+        id="NewApi"
         message="Call requires API level 30 (current min is 21): `android.os.UserManager#getAllProfiles`"
         errorLine1="        List&lt;UserHandle&gt; allProfiles = mUserManager.getAllProfiles();"
         errorLine2="                                                    ~~~~~~~~~~~~~~">
@@ -111,4 +100,15 @@
             column="53"/>
     </issue>
 
+    <issue
+        id="NewApi"
+        message="Call requires API level 24 (current min is 21): `android.os.UserManager#isManagedProfile`"
+        errorLine1="            if (mUserManager.isManagedProfile(id)) {"
+        errorLine2="                             ~~~~~~~~~~~~~~~~">
+        <location
+            file="frameworks/base/packages/SettingsLib/Utils/src/com/android/settingslib/utils/WorkPolicyUtils.java"
+            line="173"
+            column="30"/>
+    </issue>
+
 </issues>
\ No newline at end of file
diff --git a/packages/SettingsLib/lint-baseline.xml b/packages/SettingsLib/lint-baseline.xml
deleted file mode 100644
index d6a23fd..0000000
--- a/packages/SettingsLib/lint-baseline.xml
+++ /dev/null
@@ -1,204 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<issues format="5" by="lint 4.1.0" client="cli" variant="all" version="4.1.0">
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.bluetooth.BluetoothDevice#setAlias`">
-        <location
-            file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java"
-            line="584"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.net.wifi.WifiInfo#getSubscriptionId`">
-        <location
-            file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java"
-            line="248"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.net.wifi.WifiInfo#getSubscriptionId`">
-        <location
-            file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java"
-            line="278"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.net.wifi.WifiManager#registerSubsystemRestartTrackingCallback`">
-        <location
-            file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/connectivity/ConnectivitySubsystemsRecoveryManager.java"
-            line="201"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.net.wifi.WifiManager#unregisterSubsystemRestartTrackingCallback`">
-        <location
-            file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/connectivity/ConnectivitySubsystemsRecoveryManager.java"
-            line="208"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.os.UserManager#isUserForeground`">
-        <location
-            file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/enterprise/ManagedDeviceActionDisabledByAdminController.java"
-            line="78"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.TelephonyManager#isDataCapable`">
-        <location
-            file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/Utils.java"
-            line="498"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.TelephonyManager#isDataCapable`">
-        <location
-            file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java"
-            line="225"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.TelephonyManager#registerTelephonyCallback`">
-        <location
-            file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/connectivity/ConnectivitySubsystemsRecoveryManager.java"
-            line="215"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.TelephonyManager#registerTelephonyCallback`">
-        <location
-            file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java"
-            line="86"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.TelephonyManager#unregisterTelephonyCallback`">
-        <location
-            file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/connectivity/ConnectivitySubsystemsRecoveryManager.java"
-            line="222"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.TelephonyManager#unregisterTelephonyCallback`">
-        <location
-            file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java"
-            line="88"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 34 (current min is 30): `android.os.UserManager#isAdminUser`">
-        <location
-            file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/development/AbstractEnableAdbPreferenceController.java"
-            line="66"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 34 (current min is 30): `android.os.UserManager#isAdminUser`">
-        <location
-            file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/development/DevelopmentSettingsEnabler.java"
-            line="49"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 34 (current min is 30): `android.os.UserManager#isAdminUser`">
-        <location
-            file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractSimStatusImeiInfoPreferenceController.java"
-            line="33"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Class requires API level 31 (current min is 30): `android.net.wifi.WifiManager.SubsystemRestartTrackingCallback`">
-        <location
-            file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/connectivity/ConnectivitySubsystemsRecoveryManager.java"
-            line="64"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Class requires API level 31 (current min is 30): `android.telephony.TelephonyCallback.ActiveDataSubscriptionIdListener`">
-        <location
-            file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java"
-            line="125"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Class requires API level 31 (current min is 30): `android.telephony.TelephonyCallback.CarrierNetworkListener`">
-        <location
-            file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java"
-            line="124"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Class requires API level 31 (current min is 30): `android.telephony.TelephonyCallback.DataActivityListener`">
-        <location
-            file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java"
-            line="123"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Class requires API level 31 (current min is 30): `android.telephony.TelephonyCallback.DataConnectionStateListener`">
-        <location
-            file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java"
-            line="122"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Class requires API level 31 (current min is 30): `android.telephony.TelephonyCallback.DisplayInfoListener`">
-        <location
-            file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java"
-            line="126"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Class requires API level 31 (current min is 30): `android.telephony.TelephonyCallback.ServiceStateListener`">
-        <location
-            file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java"
-            line="120"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Class requires API level 31 (current min is 30): `android.telephony.TelephonyCallback.SignalStrengthsListener`">
-        <location
-            file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java"
-            line="121"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Class requires API level 31 (current min is 30): `android.telephony.TelephonyCallback`">
-        <location
-            file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/connectivity/ConnectivitySubsystemsRecoveryManager.java"
-            line="79"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Class requires API level 31 (current min is 30): `android.telephony.TelephonyCallback`">
-        <location
-            file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java"
-            line="119"/>
-    </issue>
-
-</issues>
\ No newline at end of file
diff --git a/packages/SettingsLib/res/values-da/arrays.xml b/packages/SettingsLib/res/values-da/arrays.xml
index 9d783ec..3c5135b 100644
--- a/packages/SettingsLib/res/values-da/arrays.xml
+++ b/packages/SettingsLib/res/values-da/arrays.xml
@@ -26,7 +26,7 @@
     <item msgid="6050951078202663628">"Opretter forbindelse..."</item>
     <item msgid="8356618438494652335">"Godkender..."</item>
     <item msgid="2837871868181677206">"Henter IP-adresse…"</item>
-    <item msgid="4613015005934755724">"Tilsluttet"</item>
+    <item msgid="4613015005934755724">"Forbundet"</item>
     <item msgid="3763530049995655072">"Sat på pause"</item>
     <item msgid="7852381437933824454">"Afbryder ..."</item>
     <item msgid="5046795712175415059">"Afbrudt"</item>
diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml
index 2a6a247..0d0f0c6 100644
--- a/packages/SettingsLib/res/values-da/strings.xml
+++ b/packages/SettingsLib/res/values-da/strings.xml
@@ -85,15 +85,15 @@
     <string name="bluetooth_disconnected" msgid="7739366554710388701">"Afbrudt"</string>
     <string name="bluetooth_disconnecting" msgid="7638892134401574338">"Afbryder ..."</string>
     <string name="bluetooth_connecting" msgid="5871702668260192755">"Opretter forbindelse..."</string>
-    <string name="bluetooth_connected" msgid="8065345572198502293">"Tilsluttet <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
+    <string name="bluetooth_connected" msgid="8065345572198502293">"Forbundet med <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
     <string name="bluetooth_pairing" msgid="4269046942588193600">"Parrer..."</string>
-    <string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"Tilsluttet <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> (ingen telefon)"</string>
-    <string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"Tilsluttet <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> (ingen medier)"</string>
-    <string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"Tilsluttet <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> (ingen telefon eller medier)"</string>
-    <string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"Tilsluttet <xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g> – batteriniveau <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
-    <string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"Tilsluttet <xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g> (ingen telefon) – batteriniveau <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
-    <string name="bluetooth_connected_no_a2dp_battery_level" msgid="6499078454894324287">"Tilsluttet <xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g> (ingen medier) – batteriniveau <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
-    <string name="bluetooth_connected_no_headset_no_a2dp_battery_level" msgid="8477440576953067242">"Tilsluttet <xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g> (ingen telefon eller medier) – batteriniveau <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+    <string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"Forbundet med <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> (ingen telefon)"</string>
+    <string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"Forbundet med <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> (ingen medier)"</string>
+    <string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"Forbundet med <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> (ingen telefon eller medier)"</string>
+    <string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"Forbundet med <xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g> – batteriniveau <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+    <string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"Forbundet med <xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g> (ingen telefon) – batteriniveau <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+    <string name="bluetooth_connected_no_a2dp_battery_level" msgid="6499078454894324287">"Forbundet med <xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g> (ingen medier) – batteriniveau <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+    <string name="bluetooth_connected_no_headset_no_a2dp_battery_level" msgid="8477440576953067242">"Forbundet med <xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g> (ingen telefon eller medier) – batteriniveau <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="bluetooth_active_battery_level" msgid="3450745316700494425">"Aktiv, <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> batteri"</string>
     <string name="bluetooth_active_battery_level_untethered" msgid="2706188607604205362">"Aktivt, V: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> batteri, H: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> batteri"</string>
     <string name="bluetooth_battery_level" msgid="2893696778200201555">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> batteri"</string>
@@ -129,7 +129,7 @@
     <string name="bluetooth_sap_profile_summary_connected" msgid="1280297388033001037">"Sluttet til SAP"</string>
     <string name="bluetooth_opp_profile_summary_not_connected" msgid="3959741824627764954">"Ikke forbundet til filoverførselsserver"</string>
     <string name="bluetooth_hid_profile_summary_connected" msgid="3923653977051684833">"Forbundet til inputenhed"</string>
-    <string name="bluetooth_pan_user_profile_summary_connected" msgid="380469653827505727">"Tilsluttet enhed for at få internetadgang"</string>
+    <string name="bluetooth_pan_user_profile_summary_connected" msgid="380469653827505727">"Forbundet med enhed for at få internetadgang"</string>
     <string name="bluetooth_pan_nap_profile_summary_connected" msgid="3744773111299503493">"Deler lokal internetforbindelse med enhed"</string>
     <string name="bluetooth_pan_profile_summary_use_for" msgid="7422039765025340313">"Brug til internetadgang"</string>
     <string name="bluetooth_map_profile_summary_use_for" msgid="4453622103977592583">"Brug til kort"</string>
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index 364ba54..5d2210c 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -155,8 +155,8 @@
     <string name="bluetooth_talkback_headphone" msgid="8613073829180337091">"Entzungailua"</string>
     <string name="bluetooth_talkback_input_peripheral" msgid="5133944817800149942">"Idazteko gailua"</string>
     <string name="bluetooth_talkback_bluetooth" msgid="1143241359781999989">"Bluetooth bidezko gailua"</string>
-    <string name="accessibility_wifi_off" msgid="1195445715254137155">"Desaktibatuta dago Wi-Fi konexioa."</string>
-    <string name="accessibility_no_wifi" msgid="5297119459491085771">"Deskonektatu egin da Wi-Fi konexioa."</string>
+    <string name="accessibility_wifi_off" msgid="1195445715254137155">"Desaktibatuta dago wifi-konexioa."</string>
+    <string name="accessibility_no_wifi" msgid="5297119459491085771">"Deskonektatu egin da wifi-konexioa."</string>
     <string name="accessibility_wifi_one_bar" msgid="6025652717281815212">"Wi-Fi sarearen barra bat."</string>
     <string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Wi-Fi sarearen bi barra."</string>
     <string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Wi-Fi sarearen hiru barra."</string>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index a5bfe5e..0815268 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -84,7 +84,7 @@
     <string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="bluetooth_disconnected" msgid="7739366554710388701">"Frakoblet"</string>
     <string name="bluetooth_disconnecting" msgid="7638892134401574338">"Kobler fra…"</string>
-    <string name="bluetooth_connecting" msgid="5871702668260192755">"Kobler til…"</string>
+    <string name="bluetooth_connecting" msgid="5871702668260192755">"Kobler til …"</string>
     <string name="bluetooth_connected" msgid="8065345572198502293">"Koblet til <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
     <string name="bluetooth_pairing" msgid="4269046942588193600">"Kobler til …"</string>
     <string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"Koblet til (ingen telefon) <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml
index d8e1152..6c4ddf2 100644
--- a/packages/SettingsLib/res/values-ne/strings.xml
+++ b/packages/SettingsLib/res/values-ne/strings.xml
@@ -335,7 +335,7 @@
     <string name="verify_apps_over_usb_title" msgid="6031809675604442636">"USB à€źà€Ÿ à€à€Șà€•à„‹ à€Șà„à€·à„à€Ÿà€ż à€—à€°à€żà€Żà„‹à€žà„"</string>
     <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"à€čà€Ÿà€šà€żà€•à€Ÿà€°à€• à€”à„à€Żà€”à€čà€Ÿà€° à€Șà€€à„à€€à€Ÿ à€Čà€—à€Ÿà€‰à€š ADB/ADT à€Źà€Ÿà€Ÿ à€‡à€šà„à€žà„à€Ÿà€Č à€—à€°à€żà€à€•à€Ÿ à€à€Șà€•à„‹ à€œà€Ÿà€à€š à€—à€°à€żà€Żà„‹à€žà„"</string>
     <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"à€šà€Ÿà€źà€•à€°à€Ł à€šà€—à€°à€żà€à€•à€Ÿ à€Źà„à€Čà„à€Ÿà„à€„ à€Ąà€żà€­à€Ÿà€‡à€ž (à€źà„à€Żà€Ÿà€• à€à€Ąà„à€°à„‡à€ž à€­à€à€•à€Ÿ à€źà€Ÿà€€à„à€°) à€Šà„‡à€–à€Ÿà€‡à€šà„‡ à€›"</string>
-    <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"à€Żà€žà€Čà„‡ à€°à€żà€źà„‹à€Ÿ à€Ąà€żà€­à€Ÿà€‡à€žà€źà€Ÿ à€…à€€à„à€Żà€Ÿà€§à€żà€• à€ à„‚à€Čà„‹ à€”à€Ÿ à€…à€šà€żà€Żà€šà„à€€à„à€°à€żà€€ à€­à„‹à€Čà„à€Żà„à€ź à€Źà€œà„à€šà„‡à€•à„‹ à€œà€žà„à€€à€Ÿ à€…à€”à€žà„à€„à€Ÿà€źà€Ÿ à€Źà„à€Čà„à€Ÿà„à€„à€•à„‹ à€šà€żà€°à€Șà„‡à€•à„à€· à€­à„‹à€Čà„à€Żà„à€ź à€…à€« à€—à€°à„à€›à„€"</string>
+    <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"à€Żà€žà€Čà„‡ à€°à€żà€źà„‹à€Ÿ à€Ąà€żà€­à€Ÿà€‡à€žà€źà€Ÿ à€…à€€à„à€Żà€§à€żà€• à€ à„‚à€Čà„‹ à€”à€Ÿ à€…à€šà€żà€Żà€šà„à€€à„à€°à€żà€€ à€­à„‹à€Čà„à€Żà„à€ź à€Źà€œà„à€šà„‡à€•à„‹ à€œà€žà„à€€à€Ÿ à€…à€”à€žà„à€„à€Ÿà€źà€Ÿ à€Źà„à€Čà„à€Ÿà„à€„à€•à„‹ à€šà€żà€°à€Șà„‡à€•à„à€· à€­à„‹à€Čà„à€Żà„à€ź à€…à€« à€—à€°à„à€›à„€"</string>
     <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"à€Źà„à€Čà„à€Ÿà„à€„ Gabeldorsche à€žà„à€”à€żà€§à€Ÿà€•à„‹ à€žà„à€Ÿà„à€Żà€Ÿà€• à€…à€š à€—à€°à€żà€Żà„‹à€žà„à„€"</string>
     <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"à€Żà€žà€Čà„‡ à€Șà€°à€żà€·à„à€•à„ƒà€€ à€œà€Ąà€Ÿà€šà€•à„‹ à€žà„à€”à€żà€§à€Ÿ à€žà€•à„à€·à€ź à€Șà€Ÿà€°à„à€›à„€"</string>
     <string name="enable_terminal_title" msgid="3834790541986303654">"à€žà„à€„à€Ÿà€šà„€à€Ż à€Ÿà€°à„à€źà€żà€šà€Č"</string>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index 1cdb69c..a94c313 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -140,8 +140,8 @@
     <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Tumia kwa kuingiza"</string>
     <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"Tumia kwa visaidizi vya kusikia"</string>
     <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Tumia kwa ajili ya LE_AUDIO"</string>
-    <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Oanisha"</string>
-    <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"OANISHA"</string>
+    <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Unganisha"</string>
+    <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"UNGANISHA"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"Ghairi"</string>
     <string name="bluetooth_pairing_will_share_phonebook" msgid="3064334458659165176">"Kuoanisha hutoa ruhusa ya kufikiwa kwa unaowasiliana nao na rekodi ya simu zilizopigwa unapounganishwa."</string>
     <string name="bluetooth_pairing_error_message" msgid="6626399020672335565">"Haikuwezakulinganisha na <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
@@ -237,10 +237,10 @@
     <string name="adb_wireless_error" msgid="721958772149779856">"Hitilafu"</string>
     <string name="adb_wireless_settings" msgid="2295017847215680229">"Utatuzi usiotumia waya"</string>
     <string name="adb_wireless_list_empty_off" msgid="1713707973837255490">"Ili kuangalia na kutumia vifaa vinavyopatikana, washa utatuzi usiotumia waya"</string>
-    <string name="adb_pair_method_qrcode_title" msgid="6982904096137468634">"Oanisha kifaa ukitumia msimbo wa QR"</string>
-    <string name="adb_pair_method_qrcode_summary" msgid="7130694277228970888">"Oanisha vifaa vipya ukitumia kichanganuzi cha Msimbo wa QR"</string>
-    <string name="adb_pair_method_code_title" msgid="1122590300445142904">"Oanisha kifaa ukitumia msimbo wa kuoanisha"</string>
-    <string name="adb_pair_method_code_summary" msgid="6370414511333685185">"Oanisha vifaa vipya ukitumia msimbo wa tarakimu sita"</string>
+    <string name="adb_pair_method_qrcode_title" msgid="6982904096137468634">"Unganisha kifaa ukitumia msimbo wa QR"</string>
+    <string name="adb_pair_method_qrcode_summary" msgid="7130694277228970888">"Unganisha vifaa vipya ukitumia kichanganuzi cha Msimbo wa QR"</string>
+    <string name="adb_pair_method_code_title" msgid="1122590300445142904">"Unganisha kifaa ukitumia msimbo wa kuunganisha"</string>
+    <string name="adb_pair_method_code_summary" msgid="6370414511333685185">"Unganisha vifaa vipya ukitumia msimbo wa tarakimu sita"</string>
     <string name="adb_paired_devices_title" msgid="5268997341526217362">"Vifaa vilivyooanishwa"</string>
     <string name="adb_wireless_device_connected_summary" msgid="3039660790249148713">"Vilivyounganishwa kwa sasa"</string>
     <string name="adb_wireless_device_details_title" msgid="7129369670526565786">"Maelezo ya kifaa"</string>
@@ -248,16 +248,16 @@
     <string name="adb_device_fingerprint_title_format" msgid="291504822917843701">"Alama bainifu ya kifaa: <xliff:g id="FINGERPRINT_PARAM">%1$s</xliff:g>"</string>
     <string name="adb_wireless_connection_failed_title" msgid="664211177427438438">"Imeshindwa kuunganisha"</string>
     <string name="adb_wireless_connection_failed_message" msgid="9213896700171602073">"Hakikisha kuwa <xliff:g id="DEVICE_NAME">%1$s</xliff:g> kimeunganishwa kwenye mtandao sahihi"</string>
-    <string name="adb_pairing_device_dialog_title" msgid="7141739231018530210">"Oanisha na kifaa"</string>
+    <string name="adb_pairing_device_dialog_title" msgid="7141739231018530210">"Unganisha na kifaa"</string>
     <string name="adb_pairing_device_dialog_pairing_code_label" msgid="3639239786669722731">"Msimbo wa kuoanisha wa Wi-Fi"</string>
     <string name="adb_pairing_device_dialog_failed_title" msgid="3426758947882091735">"Imeshindwa kuoanisha"</string>
     <string name="adb_pairing_device_dialog_failed_msg" msgid="6611097519661997148">"Hakikisha kuwa kifaa kimeunganishwa kwenye mtandao mmoja."</string>
-    <string name="adb_wireless_qrcode_summary" msgid="8051414549011801917">"Oanisha kifaa kupitia Wi-Fi kwa kuchanganua msimbo wa QR"</string>
+    <string name="adb_wireless_qrcode_summary" msgid="8051414549011801917">"Unganisha kifaa kupitia Wi-Fi kwa kuchanganua msimbo wa QR"</string>
     <string name="adb_wireless_verifying_qrcode_text" msgid="6123192424916029207">"Inaoanisha kifaa…"</string>
     <string name="adb_qrcode_pairing_device_failed_msg" msgid="6936292092592914132">"Imeshindwa kuoanisha kifaa. Huenda msimbo wa QR haukuwa sahihi au kifaa hakijaunganishwa kwenye mtandao mmoja."</string>
     <string name="adb_wireless_ip_addr_preference_title" msgid="8335132107715311730">"Anwani ya IP na Mlango"</string>
     <string name="adb_wireless_qrcode_pairing_title" msgid="1906409667944674707">"Changanua msimbo wa QR"</string>
-    <string name="adb_wireless_qrcode_pairing_description" msgid="6014121407143607851">"Oanisha kifaa kupitia Wi-Fi kwa kuchanganua msimbo wa QR"</string>
+    <string name="adb_wireless_qrcode_pairing_description" msgid="6014121407143607851">"Unganisha kifaa kupitia Wi-Fi kwa kuchanganua msimbo wa QR"</string>
     <string name="adb_wireless_no_network_msg" msgid="2365795244718494658">"Tafadhali unganisha kwenye mtandao wa Wi-Fi"</string>
     <string name="keywords_adb_wireless" msgid="6507505581882171240">"adb, tatua, dev"</string>
     <string name="bugreport_in_power" msgid="8664089072534638709">"Njia ya mkato ya kuripoti hitilafu"</string>
diff --git a/packages/SettingsLib/search/Android.bp b/packages/SettingsLib/search/Android.bp
index 202e7fe..390c9d2e 100644
--- a/packages/SettingsLib/search/Android.bp
+++ b/packages/SettingsLib/search/Android.bp
@@ -24,6 +24,9 @@
 
     sdk_version: "system_current",
     min_sdk_version: "21",
+    lint: {
+        baseline_filename: "lint-baseline.xml",
+    },
 }
 
 java_plugin {
diff --git a/packages/SettingsLib/search/lint-baseline.xml b/packages/SettingsLib/search/lint-baseline.xml
index 7ec512b..61cdb05 100644
--- a/packages/SettingsLib/search/lint-baseline.xml
+++ b/packages/SettingsLib/search/lint-baseline.xml
@@ -1,16 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.0.0-dev" type="baseline" dependencies="true" variant="all" version="8.0.0-dev">
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 23 (current min is 21): `new android.provider.SearchIndexableData`"
-        errorLine1="        super(context);"
-        errorLine2="        ~~~~~">
-        <location
-            file="frameworks/base/packages/SettingsLib/search/src/com/android/settingslib/search/SearchIndexableRaw.java"
-            line="62"
-            column="9"/>
-    </issue>
+<issues format="6" by="lint 8.4.0-alpha01" type="baseline" client="" dependencies="true" name="" variant="all" version="8.4.0-alpha01">
 
     <issue
         id="NewApi"
@@ -23,4 +12,15 @@
             column="41"/>
     </issue>
 
+    <issue
+        id="NewApi"
+        message="Call requires API level 23 (current min is 21): `new android.provider.SearchIndexableData`"
+        errorLine1="        super(context);"
+        errorLine2="        ~~~~~">
+        <location
+            file="frameworks/base/packages/SettingsLib/search/src/com/android/settingslib/search/SearchIndexableRaw.java"
+            line="62"
+            column="9"/>
+    </issue>
+
 </issues>
\ No newline at end of file
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
index 8e5396f..d902457 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
@@ -19,6 +19,7 @@
 import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_NONE;
 import static android.app.admin.DevicePolicyManager.MTE_NOT_CONTROLLED_BY_POLICY;
 import static android.app.admin.DevicePolicyManager.PROFILE_KEYGUARD_FEATURES_AFFECT_OWNER;
+import static android.app.role.RoleManager.ROLE_FINANCED_DEVICE_KIOSK;
 
 import static com.android.settingslib.Utils.getColorAttrDefaultColor;
 
@@ -27,6 +28,7 @@
 import android.app.AppGlobals;
 import android.app.AppOpsManager;
 import android.app.admin.DevicePolicyManager;
+import android.app.role.RoleManager;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -70,6 +72,10 @@
     private static final boolean DEBUG = Log.isLoggable(LOG_TAG, Log.DEBUG);
     private static final Set<String> ECM_KEYS = new ArraySet<>();
 
+    // TODO(b/281701062): reference role name from role manager once its exposed.
+    private static final String ROLE_DEVICE_LOCK_CONTROLLER =
+            "android.app.role.SYSTEM_FINANCED_DEVICE_CONTROLLER";
+
     static {
         if (android.security.Flags.extendEcmToAllSettings()) {
             ECM_KEYS.add(AppOpsManager.OPSTR_SYSTEM_ALERT_WINDOW);
@@ -476,16 +482,27 @@
     }
 
     /**
-     * Check if {@param packageName} is restricted by the profile or device owner from using
-     * metered data.
+     * Check if user control over metered data usage of {@code packageName} is disabled by the
+     * profile or device owner.
      *
      * @return EnforcedAdmin object containing the enforced admin component and admin user details,
-     * or {@code null} if the {@param packageName} is not restricted.
+     * or {@code null} if the user control is not disabled.
      */
-    public static EnforcedAdmin checkIfMeteredDataRestricted(Context context,
+    public static EnforcedAdmin checkIfMeteredDataUsageUserControlDisabled(Context context,
             String packageName, int userId) {
+        RoleManager roleManager = context.getSystemService(RoleManager.class);
+        UserHandle userHandle = getUserHandleOf(userId);
+        if (roleManager.getRoleHoldersAsUser(ROLE_FINANCED_DEVICE_KIOSK, userHandle)
+                .contains(packageName)
+                || roleManager.getRoleHoldersAsUser(ROLE_DEVICE_LOCK_CONTROLLER, userHandle)
+                .contains(packageName)) {
+            // There is no actual device admin for a financed device, but metered data usage
+            // control should still be disabled for both controller and kiosk apps.
+            return new EnforcedAdmin();
+        }
+
         final EnforcedAdmin enforcedAdmin = getProfileOrDeviceOwner(context,
-                getUserHandleOf(userId));
+                userHandle);
         if (enforcedAdmin == null) {
             return null;
         }
diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.java
index 5bc27195..943e3fc 100644
--- a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.java
@@ -70,10 +70,8 @@
     @Override
     public void displayPreference(PreferenceScreen screen) {
         super.displayPreference(screen);
-        if (isAvailable()) {
-            mWifiMacAddress = screen.findPreference(KEY_WIFI_MAC_ADDRESS);
-            updateConnectivity();
-        }
+        mWifiMacAddress = screen.findPreference(KEY_WIFI_MAC_ADDRESS);
+        updateConnectivity();
     }
 
     @Override
@@ -84,16 +82,16 @@
     @SuppressLint("HardwareIds")
     @Override
     protected void updateConnectivity() {
+        if (mWifiManager == null || mWifiMacAddress == null) {
+            return;
+        }
+
         final String[] macAddresses = mWifiManager.getFactoryMacAddresses();
         String macAddress = null;
         if (macAddresses != null && macAddresses.length > 0) {
             macAddress = macAddresses[0];
         }
 
-        if (mWifiMacAddress == null) {
-            return;
-        }
-
         if (TextUtils.isEmpty(macAddress) || macAddress.equals(WifiInfo.DEFAULT_MAC_ADDRESS)) {
             mWifiMacAddress.setSummary(R.string.status_unavailable);
         } else {
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java
index 9560b8d..cdb8740 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java
@@ -19,6 +19,7 @@
 
 import android.bluetooth.BluetoothClass;
 import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothHearingAid;
 import android.content.Context;
 import android.graphics.drawable.Drawable;
 import android.media.AudioManager;
@@ -42,18 +43,16 @@
     BluetoothMediaDevice(
             Context context,
             CachedBluetoothDevice device,
-            MediaRoute2Info info,
-            String packageName) {
-        this(context, device, info, packageName, null);
+            MediaRoute2Info info) {
+        this(context, device, info, null);
     }
 
     BluetoothMediaDevice(
             Context context,
             CachedBluetoothDevice device,
             MediaRoute2Info info,
-            String packageName,
             RouteListingPreference.Item item) {
-        super(context, info, packageName, item);
+        super(context, info, item);
         mCachedDevice = device;
         mAudioManager = context.getSystemService(AudioManager.class);
         initDeviceRecord();
@@ -100,7 +99,12 @@
 
     @Override
     public String getId() {
-        return MediaDeviceUtils.getId(mCachedDevice);
+        if (mCachedDevice.isHearingAidDevice()) {
+            if (mCachedDevice.getHiSyncId() != BluetoothHearingAid.HI_SYNC_ID_INVALID) {
+                return Long.toString(mCachedDevice.getHiSyncId());
+            }
+        }
+        return mCachedDevice.getAddress();
     }
 
     /**
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/ComplexMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/ComplexMediaDevice.java
index 4e0ebd1..338fb87 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/ComplexMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/ComplexMediaDevice.java
@@ -34,9 +34,8 @@
     ComplexMediaDevice(
             Context context,
             MediaRoute2Info info,
-            String packageName,
             RouteListingPreference.Item item) {
-        super(context, info, packageName, item);
+        super(context, info, item);
     }
 
     // MediaRoute2Info.getName was made public on API 34, but exists since API 30.
@@ -63,7 +62,7 @@
 
     @Override
     public String getId() {
-        return MediaDeviceUtils.getId(mRouteInfo);
+        return mRouteInfo.getId();
     }
 
     public boolean isConnected() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java
index 012cbc0..1347dd1 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java
@@ -45,14 +45,13 @@
     InfoMediaDevice(
             Context context,
             MediaRoute2Info info,
-            String packageName,
             RouteListingPreference.Item item) {
-        super(context, info, packageName, item);
+        super(context, info, item);
         initDeviceRecord();
     }
 
-    InfoMediaDevice(Context context, MediaRoute2Info info, String packageName) {
-        this(context, info, packageName, null);
+    InfoMediaDevice(Context context, MediaRoute2Info info) {
+        this(context, info, null);
     }
 
     @Override
@@ -118,7 +117,7 @@
 
     @Override
     public String getId() {
-        return MediaDeviceUtils.getId(mRouteInfo);
+        return mRouteInfo.getId();
     }
 
     public boolean isConnected() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
index ba40a50..581c7de 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
@@ -98,14 +98,15 @@
     private final Map<String, RouteListingPreference.Item> mPreferenceItemMap =
             new ConcurrentHashMap<>();
 
-    public InfoMediaManager(Context context, String packageName, Notification notification,
+    public InfoMediaManager(
+            Context context,
+            @NonNull String packageName,
+            Notification notification,
             LocalBluetoothManager localBluetoothManager) {
         super(context, notification);
 
         mBluetoothManager = localBluetoothManager;
-        if (!TextUtils.isEmpty(packageName)) {
-            mPackageName = packageName;
-        }
+        mPackageName = packageName;
     }
 
     /** Creates an instance of InfoMediaManager. */
@@ -114,6 +115,14 @@
             String packageName,
             Notification notification,
             LocalBluetoothManager localBluetoothManager) {
+
+        // The caller is only interested in system routes (headsets, built-in speakers, etc), and is
+        // not interested in a specific app's routing. The media routing APIs still require a
+        // package name, so we use the package name of the calling app.
+        if (TextUtils.isEmpty(packageName)) {
+            packageName = context.getPackageName();
+        }
+
         if (Flags.useMediaRouter2ForInfoMediaManager()) {
             try {
                 return new RouterInfoMediaManager(
@@ -374,7 +383,7 @@
         for (MediaRoute2Info route : getSelectableRoutes(info)) {
             deviceList.add(
                     new InfoMediaDevice(
-                            mContext, route, mPackageName, mPreferenceItemMap.get(route.getId())));
+                            mContext, route, mPreferenceItemMap.get(route.getId())));
         }
         return deviceList;
     }
@@ -401,7 +410,7 @@
         for (MediaRoute2Info route : getDeselectableRoutes(info)) {
             deviceList.add(
                     new InfoMediaDevice(
-                            mContext, route, mPackageName, mPreferenceItemMap.get(route.getId())));
+                            mContext, route, mPreferenceItemMap.get(route.getId())));
             Log.d(TAG, route.getName() + " is deselectable for " + mPackageName);
         }
         return deviceList;
@@ -425,7 +434,7 @@
         for (MediaRoute2Info route : getSelectedRoutes(info)) {
             deviceList.add(
                     new InfoMediaDevice(
-                            mContext, route, mPackageName, mPreferenceItemMap.get(route.getId())));
+                            mContext, route, mPreferenceItemMap.get(route.getId())));
         }
         return deviceList;
     }
@@ -624,7 +633,6 @@
                         new InfoMediaDevice(
                                 mContext,
                                 route,
-                                mPackageName,
                                 mPreferenceItemMap.get(route.getId()));
                 break;
             case TYPE_BUILTIN_SPEAKER:
@@ -641,7 +649,6 @@
                         new PhoneMediaDevice(
                                 mContext,
                                 route,
-                                mPackageName,
                                 mPreferenceItemMap.getOrDefault(route.getId(), null));
                 break;
             case TYPE_HEARING_AID:
@@ -657,7 +664,6 @@
                                     mContext,
                                     cachedDevice,
                                     route,
-                                    mPackageName,
                                     mPreferenceItemMap.getOrDefault(route.getId(), null));
                 }
                 break;
@@ -666,7 +672,6 @@
                         new ComplexMediaDevice(
                                 mContext,
                                 route,
-                                mPackageName,
                                 mPreferenceItemMap.get(route.getId()));
                 break;
             default:
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
index dbc3bf7..5925492 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
@@ -184,10 +184,6 @@
             return false;
         }
 
-        if (mCurrentConnectedDevice != null) {
-            mCurrentConnectedDevice.disconnect();
-        }
-
         device.setState(MediaDeviceState.STATE_CONNECTING);
         mInfoMediaManager.connectToDevice(device);
         return true;
@@ -567,7 +563,7 @@
                 final CachedBluetoothDevice cachedDevice =
                         cachedDeviceManager.findDevice(device);
                 if (isBondedMediaDevice(cachedDevice) && isMutingExpectedDevice(cachedDevice)) {
-                    return new BluetoothMediaDevice(mContext, cachedDevice, null, mPackageName);
+                    return new BluetoothMediaDevice(mContext, cachedDevice, null);
                 }
             }
             return null;
@@ -614,7 +610,7 @@
             mDisconnectedMediaDevices.clear();
             for (CachedBluetoothDevice cachedDevice : cachedBluetoothDeviceList) {
                 final MediaDevice mediaDevice =
-                        new BluetoothMediaDevice(mContext, cachedDevice, null, mPackageName);
+                        new BluetoothMediaDevice(mContext, cachedDevice, null);
                 if (!mMediaDevices.contains(mediaDevice)) {
                     cachedDevice.registerCallback(mDeviceAttributeChangeCallback);
                     mDisconnectedMediaDevices.add(mediaDevice);
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
index c8e4c0c..0c4cf76 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
@@ -121,16 +121,13 @@
     protected final Context mContext;
     protected final MediaRoute2Info mRouteInfo;
     protected final RouteListingPreference.Item mItem;
-    protected final String mPackageName;
 
     MediaDevice(
             Context context,
             MediaRoute2Info info,
-            String packageName,
             RouteListingPreference.Item item) {
         mContext = context;
         mRouteInfo = info;
-        mPackageName = packageName;
         mItem = item;
         setType(info);
     }
@@ -399,12 +396,6 @@
     }
 
     /**
-     * Stop transfer MediaDevice
-     */
-    public void disconnect() {
-    }
-
-    /**
      * Set current device's state
      */
     public void setState(@LocalMediaManager.MediaDeviceState int state) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaDeviceUtils.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaDeviceUtils.java
deleted file mode 100644
index b3a52b9..0000000
--- a/packages/SettingsLib/src/com/android/settingslib/media/MediaDeviceUtils.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright 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.settingslib.media;
-
-import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothHearingAid;
-import android.media.MediaRoute2Info;
-
-import com.android.settingslib.bluetooth.CachedBluetoothDevice;
-
-/**
- * MediaDeviceUtils provides utility function for MediaDevice
- */
-public class MediaDeviceUtils {
-    /**
-     * Use CachedBluetoothDevice address to represent unique id
-     *
-     * @param cachedDevice the CachedBluetoothDevice
-     * @return CachedBluetoothDevice address
-     */
-    public static String getId(CachedBluetoothDevice cachedDevice) {
-        if (cachedDevice.isHearingAidDevice()) {
-            if (cachedDevice.getHiSyncId() != BluetoothHearingAid.HI_SYNC_ID_INVALID) {
-                return Long.toString(cachedDevice.getHiSyncId());
-            }
-        }
-        return cachedDevice.getAddress();
-    }
-
-    /**
-     * Use BluetoothDevice address to represent unique id
-     *
-     * @param bluetoothDevice the BluetoothDevice
-     * @return BluetoothDevice address
-     */
-    public static String getId(BluetoothDevice bluetoothDevice) {
-        return bluetoothDevice.getAddress();
-    }
-
-    /**
-     * Use MediaRoute2Info id to represent unique id
-     *
-     * @param route the MediaRoute2Info
-     * @return MediaRoute2Info id
-     */
-    public static String getId(MediaRoute2Info route) {
-        return route.getId();
-    }
-}
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
index 0676ce5..d6f1eab 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
@@ -117,16 +117,15 @@
         return name.toString();
     }
 
-    PhoneMediaDevice(Context context, MediaRoute2Info info, String packageName) {
-        this(context, info, packageName, null);
+    PhoneMediaDevice(Context context, MediaRoute2Info info) {
+        this(context, info, null);
     }
 
     PhoneMediaDevice(
             Context context,
             MediaRoute2Info info,
-            String packageName,
             RouteListingPreference.Item item) {
-        super(context, info, packageName, item);
+        super(context, info, item);
         mDeviceIconUtil = new DeviceIconUtil(mContext);
         initDeviceRecord();
     }
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/RouterInfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/RouterInfoMediaManager.java
index 5298a46..8a122fc 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/RouterInfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/RouterInfoMediaManager.java
@@ -71,11 +71,6 @@
             LocalBluetoothManager localBluetoothManager) throws PackageNotAvailableException {
         super(context, packageName, notification, localBluetoothManager);
 
-        // TODO: b/291277292 - Change optional package name for a mandatory uid.
-        if (packageName == null) {
-            packageName = context.getPackageName();
-        }
-
         mRouter = MediaRouter2.getInstance(context, packageName);
 
         if (mRouter == null) {
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/BluetoothMediaDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/BluetoothMediaDeviceTest.java
index f50802a..7061742 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/BluetoothMediaDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/BluetoothMediaDeviceTest.java
@@ -39,6 +39,8 @@
 @RunWith(RobolectricTestRunner.class)
 public class BluetoothMediaDeviceTest {
 
+    private static final String TEST_ADDRESS = "11:22:33:44:55:66";
+
     @Mock
     private CachedBluetoothDevice mDevice;
 
@@ -54,7 +56,7 @@
         when(mDevice.isActiveDevice(BluetoothProfile.HEARING_AID)).thenReturn(true);
         when(mDevice.isActiveDevice(BluetoothProfile.LE_AUDIO)).thenReturn(true);
 
-        mBluetoothMediaDevice = new BluetoothMediaDevice(mContext, mDevice, null, null, null);
+        mBluetoothMediaDevice = new BluetoothMediaDevice(mContext, mDevice, null, null);
     }
 
     @Test
@@ -111,4 +113,10 @@
 
         assertThat(mBluetoothMediaDevice.getIcon() instanceof BitmapDrawable).isFalse();
     }
+
+    @Test
+    public void getId_returnsCachedBluetoothDeviceAddress() {
+        when(mDevice.getAddress()).thenReturn(TEST_ADDRESS);
+        assertThat(mBluetoothMediaDevice.getId()).isEqualTo(TEST_ADDRESS);
+    }
 }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaDeviceTest.java
index a072c17..0665308 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaDeviceTest.java
@@ -65,7 +65,7 @@
         MockitoAnnotations.initMocks(this);
         mContext = RuntimeEnvironment.application;
 
-        mInfoMediaDevice = new InfoMediaDevice(mContext, mRouteInfo, TEST_PACKAGE_NAME);
+        mInfoMediaDevice = new InfoMediaDevice(mContext, mRouteInfo);
     }
 
     @Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
index 2252b69..f0330c4 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
@@ -547,7 +547,7 @@
     @Test
     public void connectDeviceWithoutPackageName_noSession_returnFalse() {
         final MediaRoute2Info info = mock(MediaRoute2Info.class);
-        final MediaDevice device = new InfoMediaDevice(mContext, info, TEST_PACKAGE_NAME);
+        final MediaDevice device = new InfoMediaDevice(mContext, info);
 
         final List<RoutingSessionInfo> infos = new ArrayList<>();
 
@@ -623,7 +623,7 @@
         routingSessionInfos.add(info);
 
         final MediaRoute2Info route2Info = mock(MediaRoute2Info.class);
-        final MediaDevice device = new InfoMediaDevice(mContext, route2Info, TEST_PACKAGE_NAME);
+        final MediaDevice device = new InfoMediaDevice(mContext, route2Info);
 
         final List<String> list = new ArrayList<>();
         list.add(TEST_ID);
@@ -644,7 +644,7 @@
         routingSessionInfos.add(info);
 
         final MediaRoute2Info route2Info = mock(MediaRoute2Info.class);
-        final MediaDevice device = new InfoMediaDevice(mContext, route2Info, TEST_PACKAGE_NAME);
+        final MediaDevice device = new InfoMediaDevice(mContext, route2Info);
 
         final List<String> list = new ArrayList<>();
         list.add("fake_id");
@@ -674,7 +674,7 @@
         routingSessionInfos.add(info);
 
         final MediaRoute2Info route2Info = mock(MediaRoute2Info.class);
-        final MediaDevice device = new InfoMediaDevice(mContext, route2Info, TEST_PACKAGE_NAME);
+        final MediaDevice device = new InfoMediaDevice(mContext, route2Info);
 
         final List<String> list = new ArrayList<>();
         list.add(TEST_ID);
@@ -695,7 +695,7 @@
         routingSessionInfos.add(info);
 
         final MediaRoute2Info route2Info = mock(MediaRoute2Info.class);
-        final MediaDevice device = new InfoMediaDevice(mContext, route2Info, TEST_PACKAGE_NAME);
+        final MediaDevice device = new InfoMediaDevice(mContext, route2Info);
 
         final List<String> list = new ArrayList<>();
         list.add("fake_id");
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
index 926b41a..9a7d4f1 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
@@ -116,8 +116,8 @@
         when(mLocalProfileManager.getA2dpProfile()).thenReturn(mA2dpProfile);
         when(mLocalProfileManager.getHearingAidProfile()).thenReturn(mHapProfile);
 
-        mInfoMediaDevice1 = spy(new InfoMediaDevice(mContext, mRouteInfo1, TEST_PACKAGE_NAME));
-        mInfoMediaDevice2 = new InfoMediaDevice(mContext, mRouteInfo2, TEST_PACKAGE_NAME);
+        mInfoMediaDevice1 = spy(new InfoMediaDevice(mContext, mRouteInfo1));
+        mInfoMediaDevice2 = new InfoMediaDevice(mContext, mRouteInfo2);
         mLocalMediaManager = new LocalMediaManager(mContext, mLocalBluetoothManager,
                 mInfoMediaManager, "com.test.packagename");
         mLocalMediaManager.mAudioManager = mAudioManager;
@@ -147,7 +147,6 @@
         mLocalMediaManager.registerCallback(mCallback);
         assertThat(mLocalMediaManager.connectDevice(device)).isTrue();
 
-        verify(currentDevice).disconnect();
         verify(mInfoMediaManager).connectToDevice(device);
     }
 
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java
index 18055d9..098ab16 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java
@@ -171,17 +171,17 @@
 
         mBluetoothMediaDevice1 =
                 new BluetoothMediaDevice(
-                        mContext, mCachedDevice1, mBluetoothRouteInfo1, TEST_PACKAGE_NAME);
+                        mContext, mCachedDevice1, mBluetoothRouteInfo1);
         mBluetoothMediaDevice2 =
                 new BluetoothMediaDevice(
-                        mContext, mCachedDevice2, mBluetoothRouteInfo2, TEST_PACKAGE_NAME);
+                        mContext, mCachedDevice2, mBluetoothRouteInfo2);
         mBluetoothMediaDevice3 =
                 new BluetoothMediaDevice(
-                        mContext, mCachedDevice3, mBluetoothRouteInfo3, TEST_PACKAGE_NAME);
-        mInfoMediaDevice1 = new InfoMediaDevice(mContext, mRouteInfo1, TEST_PACKAGE_NAME);
-        mInfoMediaDevice2 = new InfoMediaDevice(mContext, mRouteInfo2, TEST_PACKAGE_NAME);
-        mInfoMediaDevice3 = new InfoMediaDevice(mContext, mRouteInfo3, TEST_PACKAGE_NAME);
-        mPhoneMediaDevice = new PhoneMediaDevice(mContext, mPhoneRouteInfo, TEST_PACKAGE_NAME);
+                        mContext, mCachedDevice3, mBluetoothRouteInfo3);
+        mInfoMediaDevice1 = new InfoMediaDevice(mContext, mRouteInfo1);
+        mInfoMediaDevice2 = new InfoMediaDevice(mContext, mRouteInfo2);
+        mInfoMediaDevice3 = new InfoMediaDevice(mContext, mRouteInfo3);
+        mPhoneMediaDevice = new PhoneMediaDevice(mContext, mPhoneRouteInfo);
     }
 
     @Test
@@ -316,7 +316,7 @@
         when(phoneRouteInfo.getType()).thenReturn(TYPE_WIRED_HEADPHONES);
 
         final PhoneMediaDevice phoneMediaDevice =
-                new PhoneMediaDevice(mContext, phoneRouteInfo, TEST_PACKAGE_NAME);
+                new PhoneMediaDevice(mContext, phoneRouteInfo);
 
         mMediaDevices.add(mBluetoothMediaDevice1);
         mMediaDevices.add(phoneMediaDevice);
@@ -332,7 +332,7 @@
         when(phoneRouteInfo.getType()).thenReturn(TYPE_WIRED_HEADPHONES);
 
         final PhoneMediaDevice phoneMediaDevice =
-                new PhoneMediaDevice(mContext, phoneRouteInfo, TEST_PACKAGE_NAME);
+                new PhoneMediaDevice(mContext, phoneRouteInfo);
 
         mMediaDevices.add(mInfoMediaDevice1);
         mMediaDevices.add(phoneMediaDevice);
@@ -483,7 +483,7 @@
     public void getFeatures_noRouteInfo_returnEmptyList() {
         mBluetoothMediaDevice1 =
                 new BluetoothMediaDevice(
-                        mContext, mCachedDevice1, null /* MediaRoute2Info */, TEST_PACKAGE_NAME);
+                        mContext, mCachedDevice1, /* MediaRoute2Info */ null);
 
         assertThat(mBluetoothMediaDevice1.getFeatures().size()).isEqualTo(0);
     }
@@ -498,10 +498,9 @@
                         mContext,
                         mCachedDevice1,
                         null /* MediaRoute2Info */,
-                        TEST_PACKAGE_NAME,
                         mItem);
         mPhoneMediaDevice =
-                new PhoneMediaDevice(mContext, mPhoneRouteInfo, TEST_PACKAGE_NAME, mItem);
+                new PhoneMediaDevice(mContext, mPhoneRouteInfo, mItem);
 
         assertThat(mBluetoothMediaDevice1.getSelectionBehavior()).isEqualTo(
                 SELECTION_BEHAVIOR_TRANSFER);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceUtilsTest.java
deleted file mode 100644
index 30a6ad2..0000000
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceUtilsTest.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settingslib.media;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.when;
-
-import android.bluetooth.BluetoothDevice;
-import android.media.MediaRoute2Info;
-
-import com.android.settingslib.bluetooth.CachedBluetoothDevice;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
-
-@RunWith(RobolectricTestRunner.class)
-public class MediaDeviceUtilsTest {
-
-    private static final String TEST_ADDRESS = "11:22:33:44:55:66";
-    private static final String TEST_ROUTE_ID = "test_route_id";
-
-    @Mock
-    private CachedBluetoothDevice mCachedDevice;
-    @Mock
-    private BluetoothDevice mBluetoothDevice;
-    @Mock
-    private MediaRoute2Info mRouteInfo;
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-    }
-
-    @Test
-    public void getId_returnCachedBluetoothDeviceAddress() {
-        when(mCachedDevice.getAddress()).thenReturn(TEST_ADDRESS);
-
-        final String id = MediaDeviceUtils.getId(mCachedDevice);
-
-        assertThat(id).isEqualTo(TEST_ADDRESS);
-    }
-
-    @Test
-    public void getId_returnBluetoothDeviceAddress() {
-        when(mBluetoothDevice.getAddress()).thenReturn(TEST_ADDRESS);
-
-        final String id = MediaDeviceUtils.getId(mBluetoothDevice);
-
-        assertThat(id).isEqualTo(TEST_ADDRESS);
-    }
-
-    @Test
-    public void getId_returnRouteInfoId() {
-        when(mRouteInfo.getId()).thenReturn(TEST_ROUTE_ID);
-
-        final String id = MediaDeviceUtils.getId(mRouteInfo);
-
-        assertThat(id).isEqualTo(TEST_ROUTE_ID);
-    }
-}
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
index e5dbe5f..add3134 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
@@ -69,6 +69,7 @@
         Settings.Global.PRIVATE_DNS_SPECIFIER,
         Settings.Global.SOFT_AP_TIMEOUT_ENABLED,
         Settings.Global.ZEN_DURATION,
+        Settings.Global.MUTE_ALARM_STREAM_WITH_RINGER_MODE_USER_PREFERENCE,
         Settings.Global.REVERSE_CHARGING_AUTO_ON,
         Settings.Global.CHARGING_VIBRATION_ENABLED,
         Settings.Global.AWARE_ALLOWED,
@@ -92,6 +93,7 @@
         Settings.Global.Wearable.CLOCKWORK_AUTO_TIME,
         Settings.Global.Wearable.CLOCKWORK_AUTO_TIME_ZONE,
         Settings.Global.Wearable.CLOCKWORK_24HR_TIME,
+        Settings.Global.Wearable.CONSISTENT_NOTIFICATION_BLOCKING_ENABLED,
         Settings.Global.Wearable.MUTE_WHEN_OFF_BODY_ENABLED,
         Settings.Global.Wearable.AMBIENT_ENABLED,
         Settings.Global.Wearable.AMBIENT_TILT_TO_WAKE,
@@ -112,5 +114,7 @@
         Settings.Global.Wearable.SCREENSHOT_ENABLED,
         Settings.Global.Wearable.SCREEN_UNLOCK_SOUND_ENABLED,
         Settings.Global.Wearable.CHARGING_SOUNDS_ENABLED,
+        Settings.Global.Wearable.WRIST_DETECTION_AUTO_LOCKING_ENABLED,
+        Settings.Global.FORCE_ENABLE_PSS_PROFILING,
     };
 }
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
index 59c3cd3..e7d7bb0 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
@@ -19,93 +19,105 @@
 import android.compat.annotation.UnsupportedAppUsage;
 import android.provider.Settings;
 
+import com.android.server.display.feature.flags.Flags;
+
+import java.util.ArrayList;
+import java.util.List;
+
 /** Information about the system settings to back up */
 public class SystemSettings {
 
     /**
-     * Settings to backup.
+     * Settings to back up.
      *
      * NOTE: Settings are backed up and restored in the order they appear
      *       in this array. If you have one setting depending on another,
      *       make sure that they are ordered appropriately.
      */
     @UnsupportedAppUsage
-    public static final String[] SETTINGS_TO_BACKUP = {
-        Settings.System.STAY_ON_WHILE_PLUGGED_IN,   // moved to global
-        Settings.System.WIFI_USE_STATIC_IP,
-        Settings.System.WIFI_STATIC_IP,
-        Settings.System.WIFI_STATIC_GATEWAY,
-        Settings.System.WIFI_STATIC_NETMASK,
-        Settings.System.WIFI_STATIC_DNS1,
-        Settings.System.WIFI_STATIC_DNS2,
-        Settings.System.BLUETOOTH_DISCOVERABILITY,
-        Settings.System.BLUETOOTH_DISCOVERABILITY_TIMEOUT,
-        Settings.System.FONT_SCALE,
-        Settings.System.DIM_SCREEN,
-        Settings.System.SCREEN_OFF_TIMEOUT,
-        Settings.System.SCREEN_BRIGHTNESS_MODE,
-        Settings.System.ADAPTIVE_SLEEP,             // moved to secure
-        Settings.System.APPLY_RAMPING_RINGER,
-        Settings.System.VIBRATE_INPUT_DEVICES,
-        Settings.System.MODE_RINGER_STREAMS_AFFECTED,
-        Settings.System.TEXT_AUTO_REPLACE,
-        Settings.System.TEXT_AUTO_CAPS,
-        Settings.System.TEXT_AUTO_PUNCTUATE,
-        Settings.System.TEXT_SHOW_PASSWORD,
-        Settings.System.AUTO_TIME,                  // moved to global
-        Settings.System.AUTO_TIME_ZONE,             // moved to global
-        Settings.System.TIME_12_24,
-        Settings.System.DTMF_TONE_WHEN_DIALING,
-        Settings.System.DTMF_TONE_TYPE_WHEN_DIALING,
-        Settings.System.HEARING_AID,
-        Settings.System.TTY_MODE,
-        Settings.System.MASTER_MONO,
-        Settings.System.MASTER_BALANCE,
-        Settings.System.FOLD_LOCK_BEHAVIOR,
-        Settings.System.SOUND_EFFECTS_ENABLED,
-        Settings.System.HAPTIC_FEEDBACK_ENABLED,
-        Settings.System.POWER_SOUNDS_ENABLED,       // moved to global
-        Settings.System.DOCK_SOUNDS_ENABLED,        // moved to global
-        Settings.System.LOCKSCREEN_SOUNDS_ENABLED,
-        Settings.System.SHOW_WEB_SUGGESTIONS,
-        Settings.System.SIP_CALL_OPTIONS,
-        Settings.System.SIP_RECEIVE_CALLS,
-        Settings.System.POINTER_SPEED,
-        Settings.System.VIBRATE_ON,
-        Settings.System.VIBRATE_WHEN_RINGING,
-        Settings.System.RINGTONE,
-        Settings.System.LOCK_TO_APP_ENABLED,
-        Settings.System.NOTIFICATION_SOUND,
-        Settings.System.ACCELEROMETER_ROTATION,
-        Settings.System.SHOW_BATTERY_PERCENT,
-        Settings.System.ALARM_VIBRATION_INTENSITY,
-        Settings.System.MEDIA_VIBRATION_INTENSITY,
-        Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
-        Settings.System.RING_VIBRATION_INTENSITY,
-        Settings.System.HAPTIC_FEEDBACK_INTENSITY,
-        Settings.System.HARDWARE_HAPTIC_FEEDBACK_INTENSITY,
-        Settings.System.KEYBOARD_VIBRATION_ENABLED,
-        Settings.System.HAPTIC_FEEDBACK_ENABLED,
-        Settings.System.DISPLAY_COLOR_MODE_VENDOR_HINT, // must precede DISPLAY_COLOR_MODE
-        Settings.System.DISPLAY_COLOR_MODE,
-        Settings.System.ALARM_ALERT,
-        Settings.System.NOTIFICATION_LIGHT_PULSE,
-        Settings.System.WEAR_ACCESSIBILITY_GESTURE_ENABLED,
-        Settings.System.CLOCKWORK_BLUETOOTH_SETTINGS_PREF,
-        Settings.System.UNREAD_NOTIFICATION_DOT_INDICATOR,
-        Settings.System.AUTO_LAUNCH_MEDIA_CONTROLS,
-        Settings.System.LOCALE_PREFERENCES,
-        Settings.System.TOUCHPAD_POINTER_SPEED,
-        Settings.System.TOUCHPAD_NATURAL_SCROLLING,
-        Settings.System.TOUCHPAD_TAP_TO_CLICK,
-        Settings.System.TOUCHPAD_RIGHT_CLICK_ZONE,
-        Settings.System.CAMERA_FLASH_NOTIFICATION,
-        Settings.System.SCREEN_FLASH_NOTIFICATION,
-        Settings.System.SCREEN_FLASH_NOTIFICATION_COLOR,
-        Settings.System.PEAK_REFRESH_RATE,
-        Settings.System.MIN_REFRESH_RATE,
-        Settings.System.NOTIFICATION_COOLDOWN_ENABLED,
-        Settings.System.NOTIFICATION_COOLDOWN_ALL,
-        Settings.System.NOTIFICATION_COOLDOWN_VIBRATE_UNLOCKED,
-    };
+    public static final String[] SETTINGS_TO_BACKUP = getSettingsToBackUp();
+
+    private static String[] getSettingsToBackUp() {
+        List<String> settings = new ArrayList<>(List.of(
+                Settings.System.STAY_ON_WHILE_PLUGGED_IN,   // moved to global
+                Settings.System.WIFI_USE_STATIC_IP,
+                Settings.System.WIFI_STATIC_IP,
+                Settings.System.WIFI_STATIC_GATEWAY,
+                Settings.System.WIFI_STATIC_NETMASK,
+                Settings.System.WIFI_STATIC_DNS1,
+                Settings.System.WIFI_STATIC_DNS2,
+                Settings.System.BLUETOOTH_DISCOVERABILITY,
+                Settings.System.BLUETOOTH_DISCOVERABILITY_TIMEOUT,
+                Settings.System.FONT_SCALE,
+                Settings.System.DIM_SCREEN,
+                Settings.System.SCREEN_OFF_TIMEOUT,
+                Settings.System.SCREEN_BRIGHTNESS_MODE,
+                Settings.System.ADAPTIVE_SLEEP,             // moved to secure
+                Settings.System.APPLY_RAMPING_RINGER,
+                Settings.System.VIBRATE_INPUT_DEVICES,
+                Settings.System.MODE_RINGER_STREAMS_AFFECTED,
+                Settings.System.TEXT_AUTO_REPLACE,
+                Settings.System.TEXT_AUTO_CAPS,
+                Settings.System.TEXT_AUTO_PUNCTUATE,
+                Settings.System.TEXT_SHOW_PASSWORD,
+                Settings.System.AUTO_TIME,                  // moved to global
+                Settings.System.AUTO_TIME_ZONE,             // moved to global
+                Settings.System.TIME_12_24,
+                Settings.System.DTMF_TONE_WHEN_DIALING,
+                Settings.System.DTMF_TONE_TYPE_WHEN_DIALING,
+                Settings.System.HEARING_AID,
+                Settings.System.TTY_MODE,
+                Settings.System.MASTER_MONO,
+                Settings.System.MASTER_BALANCE,
+                Settings.System.FOLD_LOCK_BEHAVIOR,
+                Settings.System.SOUND_EFFECTS_ENABLED,
+                Settings.System.HAPTIC_FEEDBACK_ENABLED,
+                Settings.System.POWER_SOUNDS_ENABLED,       // moved to global
+                Settings.System.DOCK_SOUNDS_ENABLED,        // moved to global
+                Settings.System.LOCKSCREEN_SOUNDS_ENABLED,
+                Settings.System.SHOW_WEB_SUGGESTIONS,
+                Settings.System.SIP_CALL_OPTIONS,
+                Settings.System.SIP_RECEIVE_CALLS,
+                Settings.System.POINTER_SPEED,
+                Settings.System.VIBRATE_ON,
+                Settings.System.VIBRATE_WHEN_RINGING,
+                Settings.System.RINGTONE,
+                Settings.System.LOCK_TO_APP_ENABLED,
+                Settings.System.NOTIFICATION_SOUND,
+                Settings.System.ACCELEROMETER_ROTATION,
+                Settings.System.SHOW_BATTERY_PERCENT,
+                Settings.System.ALARM_VIBRATION_INTENSITY,
+                Settings.System.MEDIA_VIBRATION_INTENSITY,
+                Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
+                Settings.System.RING_VIBRATION_INTENSITY,
+                Settings.System.HAPTIC_FEEDBACK_INTENSITY,
+                Settings.System.HARDWARE_HAPTIC_FEEDBACK_INTENSITY,
+                Settings.System.KEYBOARD_VIBRATION_ENABLED,
+                Settings.System.HAPTIC_FEEDBACK_ENABLED,
+                Settings.System.DISPLAY_COLOR_MODE_VENDOR_HINT, // must precede DISPLAY_COLOR_MODE
+                Settings.System.DISPLAY_COLOR_MODE,
+                Settings.System.ALARM_ALERT,
+                Settings.System.NOTIFICATION_LIGHT_PULSE,
+                Settings.System.WEAR_ACCESSIBILITY_GESTURE_ENABLED,
+                Settings.System.CLOCKWORK_BLUETOOTH_SETTINGS_PREF,
+                Settings.System.UNREAD_NOTIFICATION_DOT_INDICATOR,
+                Settings.System.AUTO_LAUNCH_MEDIA_CONTROLS,
+                Settings.System.LOCALE_PREFERENCES,
+                Settings.System.TOUCHPAD_POINTER_SPEED,
+                Settings.System.TOUCHPAD_NATURAL_SCROLLING,
+                Settings.System.TOUCHPAD_TAP_TO_CLICK,
+                Settings.System.TOUCHPAD_RIGHT_CLICK_ZONE,
+                Settings.System.CAMERA_FLASH_NOTIFICATION,
+                Settings.System.SCREEN_FLASH_NOTIFICATION,
+                Settings.System.SCREEN_FLASH_NOTIFICATION_COLOR,
+                Settings.System.NOTIFICATION_COOLDOWN_ENABLED,
+                Settings.System.NOTIFICATION_COOLDOWN_ALL,
+                Settings.System.NOTIFICATION_COOLDOWN_VIBRATE_UNLOCKED
+        ));
+        if (Flags.backUpSmoothDisplayAndForcePeakRefreshRate()) {
+            settings.add(Settings.System.PEAK_REFRESH_RATE);
+            settings.add(Settings.System.MIN_REFRESH_RATE);
+        }
+        return settings.toArray(new String[0]);
+    }
 }
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
index 3027c5f..c0a0760 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
@@ -210,6 +210,8 @@
         VALIDATORS.put(Global.POWER_BUTTON_LONG_PRESS_DURATION_MS, NONE_NEGATIVE_LONG_VALIDATOR);
         VALIDATORS.put(Global.STYLUS_EVER_USED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Global.MUTE_ALARM_STREAM_WITH_RINGER_MODE, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(
+                Global.MUTE_ALARM_STREAM_WITH_RINGER_MODE_USER_PREFERENCE, BOOLEAN_VALIDATOR);
 
         VALIDATORS.put(Global.Wearable.HAS_PAY_TOKENS, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Global.Wearable.GMS_CHECKIN_TIMEOUT_MIN, ANY_INTEGER_VALIDATOR);
@@ -447,5 +449,9 @@
         VALIDATORS.put(Global.Wearable.WEAR_LAUNCHER_UI_MODE, ANY_INTEGER_VALIDATOR);
         VALIDATORS.put(Global.Wearable.WEAR_POWER_ANOMALY_SERVICE_ENABLED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Global.Wearable.CONNECTIVITY_KEEP_DATA_ON, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(Global.Wearable.WRIST_DETECTION_AUTO_LOCKING_ENABLED, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(
+                Global.Wearable.CONSISTENT_NOTIFICATION_BLOCKING_ENABLED, ANY_INTEGER_VALIDATOR);
+        VALIDATORS.put(Global.FORCE_ENABLE_PSS_PROFILING, BOOLEAN_VALIDATOR);
     }
 }
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/WritableNamespacePrefixes.java b/packages/SettingsProvider/src/com/android/providers/settings/WritableNamespacePrefixes.java
index bd99a8b..74fd828 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/WritableNamespacePrefixes.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/WritableNamespacePrefixes.java
@@ -99,7 +99,6 @@
                 "kiwi",
                 "latency_tracker",
                 "launcher",
-                "launcher_lily",
                 "leaked_animator",
                 "lmkd_native",
                 "location",
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 6ad10cc..d26c5ff 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -33,6 +33,8 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.server.display.feature.flags.Flags;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -53,59 +55,6 @@
     public static final String HYBRID_SYSUI_BATTERY_WARNING_FLAGS =
             "hybrid_sysui_battery_warning_flags";
 
-    /**
-     * The following denylists contain settings that should *not* be backed up and restored to
-     * another device.  As a general rule, anything that is not user configurable should be
-     * denied (and conversely, things that *are* user configurable *should* be backed up)
-     */
-    private static final Set<String> BACKUP_DENY_LIST_SYSTEM_SETTINGS =
-            newHashSet(
-                    Settings.System.ADVANCED_SETTINGS, // candidate for backup?
-                    Settings.System.ALARM_ALERT_CACHE, // internal cache
-                    Settings.System.APPEND_FOR_LAST_AUDIBLE, // suffix deprecated since API 2
-                    Settings.System.EGG_MODE, // I am the lolrus
-                    Settings.System.END_BUTTON_BEHAVIOR, // bug?
-                    Settings.System
-                            .HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY, // candidate for backup?
-                    Settings.System.LOCKSCREEN_DISABLED, // ?
-                    Settings.System.MEDIA_BUTTON_RECEIVER, // candidate for backup?
-                    Settings.System.MUTE_STREAMS_AFFECTED, //  candidate for backup?
-                    Settings.System.NOTIFICATION_SOUND_CACHE, // internal cache
-                    Settings.System.POINTER_LOCATION, // backup candidate?
-                    Settings.System.DEBUG_ENABLE_ENHANCED_CALL_BLOCKING, // used for testing only
-                    Settings.System.RINGTONE_CACHE, // internal cache
-                    Settings.System.SCREEN_BRIGHTNESS, // removed in P
-                    Settings.System.SETUP_WIZARD_HAS_RUN, // Only used by SuW
-                    Settings.System.SHOW_GTALK_SERVICE_STATUS, // candidate for backup?
-                    Settings.System.SHOW_TOUCHES,
-                    Settings.System.SHOW_KEY_PRESSES,
-                    Settings.System.SHOW_ROTARY_INPUT,
-                    Settings.System.SIP_ADDRESS_ONLY, // value, not a setting
-                    Settings.System.SIP_ALWAYS, // value, not a setting
-                    Settings.System.SYSTEM_LOCALES, // bug?
-                    Settings.System.USER_ROTATION, // backup candidate?
-                    Settings.System.VIBRATE_IN_SILENT, // deprecated?
-                    Settings.System.VOLUME_ACCESSIBILITY, // used internally, changing value will
-                                                          // not change volume
-                    Settings.System.VOLUME_ALARM, // deprecated since API 2?
-                    Settings.System.VOLUME_ASSISTANT, // candidate for backup?
-                    Settings.System.VOLUME_BLUETOOTH_SCO, // deprecated since API 2?
-                    Settings.System.VOLUME_MASTER, // candidate for backup?
-                    Settings.System.VOLUME_MUSIC, // deprecated since API 2?
-                    Settings.System.VOLUME_NOTIFICATION, // deprecated since API 2?
-                    Settings.System.VOLUME_RING, // deprecated since API 2?
-                    Settings.System.VOLUME_SYSTEM, // deprecated since API 2?
-                    Settings.System.VOLUME_VOICE, // deprecated since API 2?
-                    Settings.System.WHEN_TO_MAKE_WIFI_CALLS, // bug?
-                    Settings.System.WINDOW_ORIENTATION_LISTENER_LOG, // used for debugging only
-                    Settings.System.SCREEN_BRIGHTNESS_FLOAT,
-                    Settings.System.SCREEN_BRIGHTNESS_FOR_ALS,
-                    Settings.System.WEAR_ACCESSIBILITY_GESTURE_ENABLED_DURING_OOBE,
-                    Settings.System.WEAR_TTS_PREWARM_ENABLED,
-                    Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ,
-                    Settings.System.MULTI_AUDIO_FOCUS_ENABLED // form-factor/OEM specific
-                    );
-
     private static final Set<String> BACKUP_DENY_LIST_GLOBAL_SETTINGS =
             newHashSet(
                     Settings.Global.ACTIVITY_MANAGER_CONSTANTS,
@@ -737,6 +686,7 @@
                  Settings.Secure.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS,
                  Settings.Secure.CONTENT_CAPTURE_ENABLED,
                  Settings.Secure.DEFAULT_INPUT_METHOD,
+                 Settings.Secure.DEFAULT_DEVICE_INPUT_METHOD,
                  Settings.Secure.DEVICE_PAIRED,
                  Settings.Secure.DIALER_DEFAULT_APPLICATION,
                  Settings.Secure.DISABLED_PRINT_SERVICES,
@@ -862,7 +812,7 @@
         checkSettingsBackedUpOrDenied(
                 getCandidateSettings(Settings.System.class),
                 newHashSet(SystemSettings.SETTINGS_TO_BACKUP),
-                BACKUP_DENY_LIST_SYSTEM_SETTINGS);
+                getBackUpDenyListSystemSettings());
     }
 
     @Test
@@ -917,6 +867,7 @@
                         Settings.Secure.NEARBY_SHARING_SLICE_URI,
                         Settings.Secure.NOTIFIED_NON_ACCESSIBILITY_CATEGORY_SERVICES,
                         Settings.Secure.ONE_HANDED_TUTORIAL_SHOW_COUNT,
+                        Settings.Secure.PRIVATE_SPACE_AUTO_LOCK,
                         Settings.Secure.RELEASE_COMPRESS_BLOCKS_ON_INSTALL,
                         Settings.Secure.SCREENSAVER_COMPLICATIONS_ENABLED,
                         Settings.Secure.SHOW_QR_CODE_SCANNER_SETTING,
@@ -937,6 +888,69 @@
         checkSettingsBackedUpOrDenied(allSettings, keys, BACKUP_DENY_LIST_SECURE_SETTINGS);
     }
 
+    /**
+     * The following denylists contain settings that should *not* be backed up and restored to
+     * another device.  As a general rule, anything that is not user configurable should be
+     * denied (and conversely, things that *are* user configurable *should* be backed up)
+     */
+    private static Set<String> getBackUpDenyListSystemSettings() {
+        Set<String> settings =
+                newHashSet(
+                        Settings.System.ADVANCED_SETTINGS, // candidate for backup?
+                        Settings.System.ALARM_ALERT_CACHE, // internal cache
+                        Settings.System.APPEND_FOR_LAST_AUDIBLE, // suffix deprecated since API 2
+                        Settings.System.EGG_MODE, // I am the lolrus
+                        Settings.System.END_BUTTON_BEHAVIOR, // bug?
+                        Settings.System
+                                .HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY,
+                        // candidate for backup?
+                        Settings.System.LOCKSCREEN_DISABLED, // ?
+                        Settings.System.MEDIA_BUTTON_RECEIVER, // candidate for backup?
+                        Settings.System.MUTE_STREAMS_AFFECTED, //  candidate for backup?
+                        Settings.System.NOTIFICATION_SOUND_CACHE, // internal cache
+                        Settings.System.POINTER_LOCATION, // backup candidate?
+                        Settings.System.DEBUG_ENABLE_ENHANCED_CALL_BLOCKING,
+                        // used for testing only
+                        Settings.System.RINGTONE_CACHE, // internal cache
+                        Settings.System.SCREEN_BRIGHTNESS, // removed in P
+                        Settings.System.SETUP_WIZARD_HAS_RUN, // Only used by SuW
+                        Settings.System.SHOW_GTALK_SERVICE_STATUS, // candidate for backup?
+                        Settings.System.SHOW_TOUCHES,
+                        Settings.System.SHOW_KEY_PRESSES,
+                        Settings.System.SHOW_ROTARY_INPUT,
+                        Settings.System.SIP_ADDRESS_ONLY, // value, not a setting
+                        Settings.System.SIP_ALWAYS, // value, not a setting
+                        Settings.System.SYSTEM_LOCALES, // bug?
+                        Settings.System.USER_ROTATION, // backup candidate?
+                        Settings.System.VIBRATE_IN_SILENT, // deprecated?
+                        Settings.System.VOLUME_ACCESSIBILITY,
+                        // used internally, changing value will
+                        // not change volume
+                        Settings.System.VOLUME_ALARM, // deprecated since API 2?
+                        Settings.System.VOLUME_ASSISTANT, // candidate for backup?
+                        Settings.System.VOLUME_BLUETOOTH_SCO, // deprecated since API 2?
+                        Settings.System.VOLUME_MASTER, // candidate for backup?
+                        Settings.System.VOLUME_MUSIC, // deprecated since API 2?
+                        Settings.System.VOLUME_NOTIFICATION, // deprecated since API 2?
+                        Settings.System.VOLUME_RING, // deprecated since API 2?
+                        Settings.System.VOLUME_SYSTEM, // deprecated since API 2?
+                        Settings.System.VOLUME_VOICE, // deprecated since API 2?
+                        Settings.System.WHEN_TO_MAKE_WIFI_CALLS, // bug?
+                        Settings.System.WINDOW_ORIENTATION_LISTENER_LOG, // used for debugging only
+                        Settings.System.SCREEN_BRIGHTNESS_FLOAT,
+                        Settings.System.SCREEN_BRIGHTNESS_FOR_ALS,
+                        Settings.System.WEAR_ACCESSIBILITY_GESTURE_ENABLED_DURING_OOBE,
+                        Settings.System.WEAR_TTS_PREWARM_ENABLED,
+                        Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ,
+                        Settings.System.MULTI_AUDIO_FOCUS_ENABLED // form-factor/OEM specific
+                );
+        if (!Flags.backUpSmoothDisplayAndForcePeakRefreshRate()) {
+            settings.add(Settings.System.MIN_REFRESH_RATE);
+            settings.add(Settings.System.PEAK_REFRESH_RATE);
+        }
+        return settings;
+    }
+
     private static void checkSettingsBackedUpOrDenied(
             Set<String> settings, Set<String> settingsToBackup, Set<String> denylist) {
         Set<String> settingsNotBackedUp = difference(settings, settingsToBackup);
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 477c42e..507d9c4 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -810,6 +810,9 @@
     <uses-permission android:name="android.permission.FOREGROUND_SERVICE_FILE_MANAGEMENT" />
 
     <!-- Permission required for CTS test - CtsAppFgsTestCases -->
+    <uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PROCESSING" />
+
+    <!-- Permission required for CTS test - CtsAppFgsTestCases -->
     <uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />
 
     <!-- Permissions required for CTS test - CtsAppFgsTestCases -->
diff --git a/packages/Shell/res/values-hi/strings.xml b/packages/Shell/res/values-hi/strings.xml
index 42b635a..1a4b009 100644
--- a/packages/Shell/res/values-hi/strings.xml
+++ b/packages/Shell/res/values-hi/strings.xml
@@ -21,7 +21,7 @@
     <string name="bugreport_in_progress_title" msgid="4311705936714972757">"à€—à€Ąà€Œà€Źà€Ąà€Œà„€ à€•à„€ à€°à€żà€Șà„‹à€°à„à€Ÿ <xliff:g id="ID">#%d</xliff:g> à€€à„ˆà€Żà€Ÿà€° à€•à„€ à€œà€Ÿ à€°à€čà„€ à€čà„ˆ"</string>
     <string name="bugreport_finished_title" msgid="4429132808670114081">"à€—à€Ąà€Œà€Źà€Ąà€Œà„€ à€•à„€ à€°à€żà€Șà„‹à€°à„à€Ÿ <xliff:g id="ID">#%d</xliff:g> à€•à„ˆà€Șà„à€šà€° à€•à„€ à€—à€ˆ"</string>
     <string name="bugreport_updating_title" msgid="4423539949559634214">"à€—à€Ąà€Œà€Źà€Ąà€Œà„€ à€•à„€ à€°à€żà€Șà„‹à€°à„à€Ÿ à€źà„‡à€‚ à€Șà„‚à€°à„€ à€œà€Ÿà€šà€•à€Ÿà€°à„€ à€œà„‹à€Ąà€Œà„€ à€œà€Ÿ à€°à€čà„€ à€čà„ˆ"</string>
-    <string name="bugreport_updating_wait" msgid="3322151947853929470">"à€•à„ƒà€Șà€Żà€Ÿ à€Șà„à€°à€€à„€à€•à„à€·à€Ÿ à€•à€°à„‡à€‚…"</string>
+    <string name="bugreport_updating_wait" msgid="3322151947853929470">"à€•à„ƒà€Șà€Żà€Ÿ à€‡à€‚à€€à€œà€Œà€Ÿà€° à€•à€°à„‡à€‚…"</string>
     <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"à€—à€Ąà€Œà€Źà€Ąà€Œà„€ à€•à„€ à€°à€żà€Șà„‹à€°à„à€Ÿ à€„à„‹à€Ąà€Œà„€ à€čà„€ à€Šà„‡à€° à€źà„‡à€‚ à€«à€Œà„‹à€š à€Șà€° à€Šà€żà€–à€Ÿà€ˆ à€Šà„‡à€—à„€"</string>
     <string name="bugreport_finished_text" product="tv" msgid="5758325479058638893">"à€…à€Șà€šà„€ à€—à€Ąà€Œà€Źà€Ąà€Œà„€ à€•à„€ à€°à€żà€Șà„‹à€°à„à€Ÿ à€¶à„‡à€Żà€° à€•à€°à€šà„‡ à€•à„‡ à€Čà€żà€ à€Ÿà„ˆà€Ș à€•à€°à„‡à€‚"</string>
     <string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"à€…à€Șà€šà„€ à€—à€Ąà€Œà€Źà€Ąà€Œà„€ à€•à„€ à€°à€żà€Șà„‹à€°à„à€Ÿ à€¶à„‡à€Żà€° à€•à€°à€šà„‡ à€•à„‡ à€Čà€żà€ à€Ÿà„ˆà€Ș à€•à€°à„‡à€‚"</string>
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index cc5dfc6..d61ae7e 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -65,6 +65,7 @@
 
                 "androidx.compose.runtime_runtime",
                 "androidx.compose.material3_material3",
+                "androidx.compose.material_material-icons-extended",
                 "androidx.activity_activity-compose",
                 "androidx.compose.animation_animation-graphics",
             ],
@@ -156,7 +157,7 @@
         "SystemUI-res",
         "WifiTrackerLib",
         "WindowManager-Shell",
-        "SystemUIAnimationLib",
+        "PlatformAnimationLib",
         "SystemUICommon",
         "SystemUICustomizationLib",
         "SystemUILogLib",
@@ -273,7 +274,7 @@
     static_libs: [
         "SystemUI-res",
         "WifiTrackerLib",
-        "SystemUIAnimationLib",
+        "PlatformAnimationLib",
         "SystemUIPluginLib",
         "SystemUISharedLib",
         "SystemUICustomizationLib",
@@ -437,6 +438,7 @@
         "androidx.core_core-animation-testing",
         "androidx.test.ext.junit",
         "inline-mockito-robolectric-prebuilt",
+        "platform-parametric-runner-lib",
     ],
     libs: [
         "android.test.runner",
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index a03fa9b..7443e4c 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -84,6 +84,7 @@
     <uses-permission android:name="android.permission.READ_WIFI_CREDENTIAL"/>
     <uses-permission android:name="android.permission.LOCATION_HARDWARE" />
     <uses-permission android:name="android.permission.NETWORK_FACTORY" />
+    <uses-permission android:name="android.permission.SATELLITE_COMMUNICATION" />
     <!-- Physical hardware -->
     <uses-permission android:name="android.permission.MANAGE_USB" />
     <uses-permission android:name="android.permission.CONTROL_DISPLAY_BRIGHTNESS" />
diff --git a/packages/SystemUI/aconfig/Android.bp b/packages/SystemUI/aconfig/Android.bp
index 7f16ca5..03f9d74 100644
--- a/packages/SystemUI/aconfig/Android.bp
+++ b/packages/SystemUI/aconfig/Android.bp
@@ -25,6 +25,7 @@
         "//frameworks/base/packages/SystemUI:__subpackages__",
         "//frameworks/libs/systemui/tracinglib:__subpackages__",
         "//platform_testing:__subpackages__",
+        "//cts:__subpackages__",
     ],
 }
 
diff --git a/packages/SystemUI/aconfig/accessibility.aconfig b/packages/SystemUI/aconfig/accessibility.aconfig
index 21263a9..f7b1a26 100644
--- a/packages/SystemUI/aconfig/accessibility.aconfig
+++ b/packages/SystemUI/aconfig/accessibility.aconfig
@@ -10,6 +10,13 @@
 }
 
 flag {
+    name: "floating_menu_drag_to_hide"
+    namespace: "accessibility"
+    description: "Allows users to hide the FAB then use notification to dismiss or bring it back."
+    bug: "298718415"
+}
+
+flag {
     name: "floating_menu_ime_displacement_animation"
     namespace: "accessibility"
     description: "Adds an animation for when the FAB is displaced by an IME becoming visible."
@@ -28,4 +35,4 @@
     namespace: "accessibility"
     description: "Animates the floating menu's transition between curved and jagged edges."
     bug: "281140482"
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/aconfig/predictive_back.aconfig b/packages/SystemUI/aconfig/predictive_back.aconfig
new file mode 100644
index 0000000..d0e6b28
--- /dev/null
+++ b/packages/SystemUI/aconfig/predictive_back.aconfig
@@ -0,0 +1,29 @@
+package: "com.android.systemui"
+
+flag {
+    name: "predictive_back_sysui"
+    namespace: "systemui"
+    description: "Predictive Back Dispatching for SysUI"
+    bug: "309545085"
+}
+
+flag {
+    name: "predictive_back_animate_shade"
+    namespace: "systemui"
+    description: "Enable Shade Animations"
+    bug: "309545085"
+}
+
+flag {
+    name: "predictive_back_animate_bouncer"
+    namespace: "systemui"
+    description: "Enable Predictive Back Animation in Bouncer"
+    bug: "309545085"
+}
+
+flag {
+    name: "predictive_back_animate_dialogs"
+    namespace: "systemui"
+    description: "Enable Predictive Back Animation for SysUI dialogs"
+    bug: "309545085"
+}
\ No newline at end of file
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 98a2d9f..5fbffbe 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -145,6 +145,16 @@
 }
 
 flag {
+    name: "enable_background_keyguard_ondrawn_callback"
+    namespace: "systemui"
+    description: "Calls the onDrawn keyguard in the background, without being blocked by main"
+        "thread work. This results in the screen to turn on earlier when the main thread is stuck. "
+        "Note that, even after this callback is called, we're waiting for all windows to finish "
+        " drawing."
+    bug: "295873557"
+}
+
+flag {
     name: "qs_new_pipeline"
     namespace: "systemui"
     description: "Use the new pipeline for Quick Settings. Should have no behavior changes."
@@ -155,7 +165,7 @@
    name: "qs_new_tiles"
    namespace: "systemui"
    description: "Use the new tiles in the Quick Settings. Should have no behavior changes."
-   bug: "241772429"
+   bug: "311147395"
 }
 
 flag {
@@ -205,6 +215,20 @@
 }
 
 flag {
+   name: "pss_task_switcher"
+   namespace: "systemui"
+   description: "Enable the task switcher feature for partial screen sharing"
+   bug: "317208379"
+}
+
+flag {
+   name: "revamped_bouncer_messages"
+   namespace: "systemui"
+   description: "Change the bouncer message to be a 2-line more descriptive message"
+   bug: "236891644"
+}
+
+flag {
    name: "rest_to_unlock"
    namespace: "systemui"
    description: "Require prolonged touch for fingerprint authentication"
@@ -234,6 +258,15 @@
 }
 
 flag {
+   name: "centralized_status_bar_dimens_refactor"
+   namespace: "systemui"
+   description: "Refactors shade header and keyguard status bar to read status bar dimens from a"
+        " central place, instead of reading resources directly. This is to take into account display"
+        " cutouts and other special cases. "
+   bug: "317199366"
+}
+
+flag {
   name: "enable_layout_tracing"
   namespace: "systemui"
   description: "Enables detailed traversal slices during measure and layout in perfetto traces"
@@ -270,6 +303,13 @@
 }
 
 flag {
+    name: "new_volume_panel"
+    namespace: "systemui"
+    description: "Switches to the new volume panel (without Slices)."
+    bug: "202262476"
+}
+
+flag {
     name: "screenshare_notification_hiding"
     namespace: "systemui"
     description: "Enable hiding of notifications during screenshare"
@@ -277,8 +317,22 @@
 }
 
 flag {
+   name: "run_fingerprint_detect_on_dismissible_keyguard"
+   namespace: "systemui"
+   description: "Run fingerprint detect instead of authenticate if the keyguard is dismissible."
+   bug: "311145851"
+}
+
+flag {
    name: "bluetooth_qs_tile_dialog_auto_on_toggle"
    namespace: "systemui"
    description: "Displays the auto on toggle in the bluetooth QS tile dialog"
    bug: "316985153"
 }
+
+flag {
+   name: "smartspace_relocate_to_bottom"
+   namespace: "systemui"
+   description: "Relocate Smartspace to bottom of the Lock Screen"
+   bug: "316212788"
+}
diff --git a/packages/SystemUI/animation/core/Android.bp b/packages/SystemUI/animation/Android.bp
similarity index 98%
rename from packages/SystemUI/animation/core/Android.bp
rename to packages/SystemUI/animation/Android.bp
index 8438051..872187a 100644
--- a/packages/SystemUI/animation/core/Android.bp
+++ b/packages/SystemUI/animation/Android.bp
@@ -23,7 +23,7 @@
 
 android_library {
 
-    name: "SystemUIAnimationLib",
+    name: "PlatformAnimationLib",
     use_resource_processor: true,
 
     srcs: [
diff --git a/packages/SystemUI/animation/core/AndroidManifest.xml b/packages/SystemUI/animation/AndroidManifest.xml
similarity index 100%
rename from packages/SystemUI/animation/core/AndroidManifest.xml
rename to packages/SystemUI/animation/AndroidManifest.xml
diff --git a/packages/SystemUI/animation/core/build.gradle b/packages/SystemUI/animation/build.gradle
similarity index 81%
rename from packages/SystemUI/animation/core/build.gradle
rename to packages/SystemUI/animation/build.gradle
index 12637f4..939455f 100644
--- a/packages/SystemUI/animation/core/build.gradle
+++ b/packages/SystemUI/animation/build.gradle
@@ -5,8 +5,8 @@
 android {
     sourceSets {
         main {
-            java.srcDirs = ["${SYS_UI_DIR}/animation/core/src/com/android/systemui/surfaceeffects/"]
-            manifest.srcFile "${SYS_UI_DIR}/animation/core/AndroidManifest.xml"
+            java.srcDirs = ["${SYS_UI_DIR}/animation/src/com/android/systemui/surfaceeffects/"]
+            manifest.srcFile "${SYS_UI_DIR}/animation/AndroidManifest.xml"
         }
     }
 
diff --git a/packages/SystemUI/animation/core/res/anim/launch_dialog_enter.xml b/packages/SystemUI/animation/res/anim/launch_dialog_enter.xml
similarity index 100%
rename from packages/SystemUI/animation/core/res/anim/launch_dialog_enter.xml
rename to packages/SystemUI/animation/res/anim/launch_dialog_enter.xml
diff --git a/packages/SystemUI/animation/core/res/anim/launch_dialog_exit.xml b/packages/SystemUI/animation/res/anim/launch_dialog_exit.xml
similarity index 100%
rename from packages/SystemUI/animation/core/res/anim/launch_dialog_exit.xml
rename to packages/SystemUI/animation/res/anim/launch_dialog_exit.xml
diff --git a/packages/SystemUI/animation/core/res/values/ids.xml b/packages/SystemUI/animation/res/values/ids.xml
similarity index 100%
rename from packages/SystemUI/animation/core/res/values/ids.xml
rename to packages/SystemUI/animation/res/values/ids.xml
diff --git a/packages/SystemUI/animation/core/res/values/styles.xml b/packages/SystemUI/animation/res/values/styles.xml
similarity index 100%
rename from packages/SystemUI/animation/core/res/values/styles.xml
rename to packages/SystemUI/animation/res/values/styles.xml
diff --git a/packages/SystemUI/animation/core/src/com/android/systemui/animation/ActivityLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
similarity index 100%
rename from packages/SystemUI/animation/core/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
rename to packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
diff --git a/packages/SystemUI/animation/core/src/com/android/systemui/animation/AnimationFeatureFlags.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/AnimationFeatureFlags.kt
similarity index 100%
rename from packages/SystemUI/animation/core/src/com/android/systemui/animation/AnimationFeatureFlags.kt
rename to packages/SystemUI/animation/src/com/android/systemui/animation/AnimationFeatureFlags.kt
diff --git a/packages/SystemUI/animation/core/src/com/android/systemui/animation/DelegateLaunchAnimatorController.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/DelegateLaunchAnimatorController.kt
similarity index 100%
rename from packages/SystemUI/animation/core/src/com/android/systemui/animation/DelegateLaunchAnimatorController.kt
rename to packages/SystemUI/animation/src/com/android/systemui/animation/DelegateLaunchAnimatorController.kt
diff --git a/packages/SystemUI/animation/core/src/com/android/systemui/animation/DialogLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
similarity index 100%
rename from packages/SystemUI/animation/core/src/com/android/systemui/animation/DialogLaunchAnimator.kt
rename to packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
diff --git a/packages/SystemUI/animation/core/src/com/android/systemui/animation/Expandable.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/Expandable.kt
similarity index 100%
rename from packages/SystemUI/animation/core/src/com/android/systemui/animation/Expandable.kt
rename to packages/SystemUI/animation/src/com/android/systemui/animation/Expandable.kt
diff --git a/packages/SystemUI/animation/core/src/com/android/systemui/animation/FontInterpolator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/FontInterpolator.kt
similarity index 100%
rename from packages/SystemUI/animation/core/src/com/android/systemui/animation/FontInterpolator.kt
rename to packages/SystemUI/animation/src/com/android/systemui/animation/FontInterpolator.kt
diff --git a/packages/SystemUI/animation/core/src/com/android/systemui/animation/FontVariationUtils.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/FontVariationUtils.kt
similarity index 100%
rename from packages/SystemUI/animation/core/src/com/android/systemui/animation/FontVariationUtils.kt
rename to packages/SystemUI/animation/src/com/android/systemui/animation/FontVariationUtils.kt
diff --git a/packages/SystemUI/animation/core/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
similarity index 99%
rename from packages/SystemUI/animation/core/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
rename to packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
index b738e2b..efdbfdb 100644
--- a/packages/SystemUI/animation/core/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
@@ -494,7 +494,7 @@
             }
 
             for (i in 0 until drawable.numberOfLayers) {
-                (drawable.getDrawable(i) as? GradientDrawable)?.cornerRadii = radii
+                applyBackgroundRadii(drawable.getDrawable(i), radii)
             }
         }
 
diff --git a/packages/SystemUI/animation/core/src/com/android/systemui/animation/LaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchAnimator.kt
similarity index 100%
rename from packages/SystemUI/animation/core/src/com/android/systemui/animation/LaunchAnimator.kt
rename to packages/SystemUI/animation/src/com/android/systemui/animation/LaunchAnimator.kt
diff --git a/packages/SystemUI/animation/core/src/com/android/systemui/animation/LaunchableView.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchableView.kt
similarity index 100%
rename from packages/SystemUI/animation/core/src/com/android/systemui/animation/LaunchableView.kt
rename to packages/SystemUI/animation/src/com/android/systemui/animation/LaunchableView.kt
diff --git a/packages/SystemUI/animation/core/src/com/android/systemui/animation/RemoteAnimationDelegate.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteAnimationDelegate.kt
similarity index 100%
rename from packages/SystemUI/animation/core/src/com/android/systemui/animation/RemoteAnimationDelegate.kt
rename to packages/SystemUI/animation/src/com/android/systemui/animation/RemoteAnimationDelegate.kt
diff --git a/packages/SystemUI/animation/core/src/com/android/systemui/animation/ShadeInterpolation.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ShadeInterpolation.kt
similarity index 100%
rename from packages/SystemUI/animation/core/src/com/android/systemui/animation/ShadeInterpolation.kt
rename to packages/SystemUI/animation/src/com/android/systemui/animation/ShadeInterpolation.kt
diff --git a/packages/SystemUI/animation/core/src/com/android/systemui/animation/TextAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt
similarity index 93%
rename from packages/SystemUI/animation/core/src/com/android/systemui/animation/TextAnimator.kt
rename to packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt
index dc54240..8dc7495 100644
--- a/packages/SystemUI/animation/core/src/com/android/systemui/animation/TextAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt
@@ -42,9 +42,9 @@
                 return baseTypeface
             }
 
-            val axes =
-                FontVariationAxis.fromFontVariationSettings(fVar)?.toMutableList()
-                    ?: mutableListOf()
+            val axes = FontVariationAxis.fromFontVariationSettings(fVar)
+                ?.toMutableList()
+                ?: mutableListOf()
             axes.removeIf { !baseTypeface.isSupportedAxes(it.getOpenTypeTagValue()) }
             if (axes.isEmpty()) {
                 return baseTypeface
@@ -206,14 +206,12 @@
      *
      * Here is an example of font runs: "fin. ç”‚ă‚ă‚Š"
      *
-     * ```
      * Characters :    f      i      n      .      _      甂     わ     り
      * Code Points: \u0066 \u0069 \u006E \u002E \u0020 \u7D42 \u308F \u308A
      * Font Runs  : <-- Roboto-Regular.ttf          --><-- NotoSans-CJK.otf -->
      *                  runStart = 0, runLength = 5        runStart = 5, runLength = 3
      * Glyph IDs  :      194        48     7      8     4367   1039   1002
      * Glyph Index:       0          1     2      3       0      1      2
-     * ```
      *
      * In this example, the "fi" is converted into ligature form, thus the single glyph ID is
      * assigned for two characters, f and i.
@@ -245,21 +243,22 @@
     /**
      * Set text style with animation.
      *
-     * By passing -1 to weight, the view preserve the current weight. By passing -1 to textSize, the
-     * view preserve the current text size. Bu passing -1 to duration, the default text animation,
-     * 1000ms, is used. By passing false to animate, the text will be updated without animation.
+     * By passing -1 to weight, the view preserve the current weight.
+     * By passing -1 to textSize, the view preserve the current text size.
+     * Bu passing -1 to duration, the default text animation, 1000ms, is used.
+     * By passing false to animate, the text will be updated without animation.
      *
      * @param fvar an optional text fontVariationSettings.
      * @param textSize an optional font size.
-     * @param colors an optional colors array that must be the same size as numLines passed to the
-     *   TextInterpolator
+     * @param colors an optional colors array that must be the same size as numLines passed to
+     *               the TextInterpolator
      * @param strokeWidth an optional paint stroke width
      * @param animate an optional boolean indicating true for showing style transition as animation,
-     *   false for immediate style transition. True by default.
+     *                false for immediate style transition. True by default.
      * @param duration an optional animation duration in milliseconds. This is ignored if animate is
-     *   false.
+     *                 false.
      * @param interpolator an optional time interpolator. If null is passed, last set interpolator
-     *   will be used. This is ignored if animate is false.
+     *                     will be used. This is ignored if animate is false.
      */
     fun setTextStyle(
         fvar: String? = "",
@@ -325,8 +324,7 @@
     }
 
     /**
-     * Set text style with animation. Similar as fun setTextStyle( fvar: String? = "", textSize:
-     * ```
+     * Set text style with animation. Similar as
      * fun setTextStyle(
      *      fvar: String? = "",
      *      textSize: Float = -1f,
@@ -338,7 +336,6 @@
      *      delay: Long = 0,
      *      onAnimationEnd: Runnable? = null
      * )
-     * ```
      *
      * @param weight an optional style value for `wght` in fontVariationSettings.
      * @param width an optional style value for `wdth` in fontVariationSettings.
@@ -359,13 +356,12 @@
         delay: Long = 0,
         onAnimationEnd: Runnable? = null
     ) {
-        val fvar =
-            fontVariationUtils.updateFontVariation(
-                weight = weight,
-                width = width,
-                opticalSize = opticalSize,
-                roundness = roundness,
-            )
+        val fvar = fontVariationUtils.updateFontVariation(
+            weight = weight,
+            width = width,
+            opticalSize = opticalSize,
+            roundness = roundness,
+        )
         setTextStyle(
             fvar = fvar,
             textSize = textSize,
diff --git a/packages/SystemUI/animation/core/src/com/android/systemui/animation/TextInterpolator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt
similarity index 100%
rename from packages/SystemUI/animation/core/src/com/android/systemui/animation/TextInterpolator.kt
rename to packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt
diff --git a/packages/SystemUI/animation/core/src/com/android/systemui/animation/ViewDialogLaunchAnimatorController.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ViewDialogLaunchAnimatorController.kt
similarity index 100%
rename from packages/SystemUI/animation/core/src/com/android/systemui/animation/ViewDialogLaunchAnimatorController.kt
rename to packages/SystemUI/animation/src/com/android/systemui/animation/ViewDialogLaunchAnimatorController.kt
diff --git a/packages/SystemUI/animation/core/src/com/android/systemui/animation/ViewHierarchyAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ViewHierarchyAnimator.kt
similarity index 92%
rename from packages/SystemUI/animation/core/src/com/android/systemui/animation/ViewHierarchyAnimator.kt
rename to packages/SystemUI/animation/src/com/android/systemui/animation/ViewHierarchyAnimator.kt
index a9c53d1..00d9056 100644
--- a/packages/SystemUI/animation/core/src/com/android/systemui/animation/ViewHierarchyAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ViewHierarchyAnimator.kt
@@ -196,9 +196,9 @@
          *
          * @param includeFadeIn true if the animator should also fade in the view and child views.
          * @param fadeInInterpolator the interpolator to use when fading in the view. Unused if
-         *   [includeFadeIn] is false.
-         * @param onAnimationEnd an optional runnable that will be run once the animation finishes
-         *   successfully. Will not be run if the animation is cancelled.
+         *     [includeFadeIn] is false.
+         * @param onAnimationEnd an optional runnable that will be run once the animation
+         *    finishes successfully. Will not be run if the animation is cancelled.
          */
         @JvmOverloads
         fun animateAddition(
@@ -241,10 +241,7 @@
                 // First, fade in the container view
                 val containerDuration = duration / 6
                 createAndStartFadeInAnimator(
-                    rootView,
-                    containerDuration,
-                    startDelay = 0,
-                    interpolator = fadeInInterpolator
+                    rootView, containerDuration, startDelay = 0, interpolator = fadeInInterpolator
                 )
 
                 // Then, fade in the child views
@@ -400,7 +397,7 @@
          * included by specifying [includeMargins].
          *
          * @param onAnimationEnd an optional runnable that will be run once the animation finishes
-         *   successfully. Will not be run if the animation is cancelled.
+         *    successfully. Will not be run if the animation is cancelled.
          */
         @JvmOverloads
         fun animateRemoval(
@@ -619,7 +616,6 @@
          * not newly introduced margins are included.
          *
          * Base case
-         *
          * ```
          *     1) origin=TOP
          *         x---------x    x---------x    x---------x    x---------x    x---------x
@@ -640,11 +636,9 @@
          *                                         x-----x       x-------x     |         |
          *                                                                     x---------x
          * ```
-         *
          * In case the start and end values differ in the direction of the origin, and
          * [ignorePreviousValues] is false, the previous values are used and a translation is
          * included in addition to the view expansion.
-         *
          * ```
          *     origin=TOP_LEFT - (0,0,0,0) -> (30,30,70,70)
          *         x
@@ -783,7 +777,8 @@
             includeMargins: Boolean = false,
         ): Map<Bound, Int> {
             val marginAdjustment =
-                if (includeMargins && (rootView.layoutParams is ViewGroup.MarginLayoutParams)) {
+                if (includeMargins &&
+                    (rootView.layoutParams is ViewGroup.MarginLayoutParams)) {
                     val marginLp = rootView.layoutParams as ViewGroup.MarginLayoutParams
                     DimenHolder(
                         left = marginLp.leftMargin,
@@ -791,9 +786,9 @@
                         right = marginLp.rightMargin,
                         bottom = marginLp.bottomMargin
                     )
-                } else {
-                    DimenHolder(0, 0, 0, 0)
-                }
+            } else {
+                DimenHolder(0, 0, 0, 0)
+            }
 
             // These are the end values to use *if* this bound is part of the destination.
             val endLeft = left - marginAdjustment.left
@@ -810,69 +805,60 @@
             //  - If destination=BOTTOM_LEFT, then endBottom == endTop AND endLeft == endRight.
 
             return when (destination) {
-                Hotspot.TOP ->
-                    mapOf(
-                        Bound.TOP to endTop,
-                        Bound.BOTTOM to endTop,
-                        Bound.LEFT to left,
-                        Bound.RIGHT to right,
-                    )
-                Hotspot.TOP_RIGHT ->
-                    mapOf(
-                        Bound.TOP to endTop,
-                        Bound.BOTTOM to endTop,
-                        Bound.RIGHT to endRight,
-                        Bound.LEFT to endRight,
-                    )
-                Hotspot.RIGHT ->
-                    mapOf(
-                        Bound.RIGHT to endRight,
-                        Bound.LEFT to endRight,
-                        Bound.TOP to top,
-                        Bound.BOTTOM to bottom,
-                    )
-                Hotspot.BOTTOM_RIGHT ->
-                    mapOf(
-                        Bound.BOTTOM to endBottom,
-                        Bound.TOP to endBottom,
-                        Bound.RIGHT to endRight,
-                        Bound.LEFT to endRight,
-                    )
-                Hotspot.BOTTOM ->
-                    mapOf(
-                        Bound.BOTTOM to endBottom,
-                        Bound.TOP to endBottom,
-                        Bound.LEFT to left,
-                        Bound.RIGHT to right,
-                    )
-                Hotspot.BOTTOM_LEFT ->
-                    mapOf(
-                        Bound.BOTTOM to endBottom,
-                        Bound.TOP to endBottom,
-                        Bound.LEFT to endLeft,
-                        Bound.RIGHT to endLeft,
-                    )
-                Hotspot.LEFT ->
-                    mapOf(
-                        Bound.LEFT to endLeft,
-                        Bound.RIGHT to endLeft,
-                        Bound.TOP to top,
-                        Bound.BOTTOM to bottom,
-                    )
-                Hotspot.TOP_LEFT ->
-                    mapOf(
-                        Bound.TOP to endTop,
-                        Bound.BOTTOM to endTop,
-                        Bound.LEFT to endLeft,
-                        Bound.RIGHT to endLeft,
-                    )
-                Hotspot.CENTER ->
-                    mapOf(
-                        Bound.LEFT to (endLeft + endRight) / 2,
-                        Bound.RIGHT to (endLeft + endRight) / 2,
-                        Bound.TOP to (endTop + endBottom) / 2,
-                        Bound.BOTTOM to (endTop + endBottom) / 2,
-                    )
+                Hotspot.TOP -> mapOf(
+                    Bound.TOP to endTop,
+                    Bound.BOTTOM to endTop,
+                    Bound.LEFT to left,
+                    Bound.RIGHT to right,
+                )
+                Hotspot.TOP_RIGHT -> mapOf(
+                    Bound.TOP to endTop,
+                    Bound.BOTTOM to endTop,
+                    Bound.RIGHT to endRight,
+                    Bound.LEFT to endRight,
+                )
+                Hotspot.RIGHT -> mapOf(
+                    Bound.RIGHT to endRight,
+                    Bound.LEFT to endRight,
+                    Bound.TOP to top,
+                    Bound.BOTTOM to bottom,
+                )
+                Hotspot.BOTTOM_RIGHT -> mapOf(
+                    Bound.BOTTOM to endBottom,
+                    Bound.TOP to endBottom,
+                    Bound.RIGHT to endRight,
+                    Bound.LEFT to endRight,
+                )
+                Hotspot.BOTTOM -> mapOf(
+                    Bound.BOTTOM to endBottom,
+                    Bound.TOP to endBottom,
+                    Bound.LEFT to left,
+                    Bound.RIGHT to right,
+                )
+                Hotspot.BOTTOM_LEFT -> mapOf(
+                    Bound.BOTTOM to endBottom,
+                    Bound.TOP to endBottom,
+                    Bound.LEFT to endLeft,
+                    Bound.RIGHT to endLeft,
+                )
+                Hotspot.LEFT -> mapOf(
+                    Bound.LEFT to endLeft,
+                    Bound.RIGHT to endLeft,
+                    Bound.TOP to top,
+                    Bound.BOTTOM to bottom,
+                )
+                Hotspot.TOP_LEFT -> mapOf(
+                    Bound.TOP to endTop,
+                    Bound.BOTTOM to endTop,
+                    Bound.LEFT to endLeft,
+                    Bound.RIGHT to endLeft,
+                )
+                Hotspot.CENTER -> mapOf(
+                    Bound.LEFT to (endLeft + endRight) / 2,
+                    Bound.RIGHT to (endLeft + endRight) / 2,
+                    Bound.TOP to (endTop + endBottom) / 2,
+                    Bound.BOTTOM to (endTop + endBottom) / 2,
+                )
             }
         }
 
@@ -1097,13 +1083,11 @@
             animator.startDelay = startDelay
             animator.duration = duration
             animator.interpolator = interpolator
-            animator.addListener(
-                object : AnimatorListenerAdapter() {
-                    override fun onAnimationEnd(animation: Animator) {
-                        view.setTag(R.id.tag_alpha_animator, null /* tag */)
-                    }
+            animator.addListener(object : AnimatorListenerAdapter() {
+                override fun onAnimationEnd(animation: Animator) {
+                    view.setTag(R.id.tag_alpha_animator, null /* tag */)
                 }
-            )
+            })
 
             (view.getTag(R.id.tag_alpha_animator) as? ObjectAnimator)?.cancel()
             view.setTag(R.id.tag_alpha_animator, animator)
diff --git a/packages/SystemUI/animation/core/src/com/android/systemui/animation/ViewRootSync.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ViewRootSync.kt
similarity index 100%
rename from packages/SystemUI/animation/core/src/com/android/systemui/animation/ViewRootSync.kt
rename to packages/SystemUI/animation/src/com/android/systemui/animation/ViewRootSync.kt
diff --git a/packages/SystemUI/animation/core/src/com/android/systemui/animation/back/BackAnimationSpec.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/back/BackAnimationSpec.kt
similarity index 100%
rename from packages/SystemUI/animation/core/src/com/android/systemui/animation/back/BackAnimationSpec.kt
rename to packages/SystemUI/animation/src/com/android/systemui/animation/back/BackAnimationSpec.kt
diff --git a/packages/SystemUI/animation/core/src/com/android/systemui/animation/back/BackAnimationSpecForSysUi.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/back/BackAnimationSpecForSysUi.kt
similarity index 100%
rename from packages/SystemUI/animation/core/src/com/android/systemui/animation/back/BackAnimationSpecForSysUi.kt
rename to packages/SystemUI/animation/src/com/android/systemui/animation/back/BackAnimationSpecForSysUi.kt
diff --git a/packages/SystemUI/animation/core/src/com/android/systemui/animation/back/BackTransformation.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/back/BackTransformation.kt
similarity index 100%
rename from packages/SystemUI/animation/core/src/com/android/systemui/animation/back/BackTransformation.kt
rename to packages/SystemUI/animation/src/com/android/systemui/animation/back/BackTransformation.kt
diff --git a/packages/SystemUI/animation/core/src/com/android/systemui/animation/back/OnBackAnimationCallbackExtension.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/back/OnBackAnimationCallbackExtension.kt
similarity index 100%
rename from packages/SystemUI/animation/core/src/com/android/systemui/animation/back/OnBackAnimationCallbackExtension.kt
rename to packages/SystemUI/animation/src/com/android/systemui/animation/back/OnBackAnimationCallbackExtension.kt
diff --git a/packages/SystemUI/animation/core/src/com/android/systemui/animation/view/LaunchableFrameLayout.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/view/LaunchableFrameLayout.kt
similarity index 100%
rename from packages/SystemUI/animation/core/src/com/android/systemui/animation/view/LaunchableFrameLayout.kt
rename to packages/SystemUI/animation/src/com/android/systemui/animation/view/LaunchableFrameLayout.kt
diff --git a/packages/SystemUI/animation/core/src/com/android/systemui/animation/view/LaunchableImageView.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/view/LaunchableImageView.kt
similarity index 100%
rename from packages/SystemUI/animation/core/src/com/android/systemui/animation/view/LaunchableImageView.kt
rename to packages/SystemUI/animation/src/com/android/systemui/animation/view/LaunchableImageView.kt
diff --git a/packages/SystemUI/animation/core/src/com/android/systemui/animation/view/LaunchableLinearLayout.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/view/LaunchableLinearLayout.kt
similarity index 100%
rename from packages/SystemUI/animation/core/src/com/android/systemui/animation/view/LaunchableLinearLayout.kt
rename to packages/SystemUI/animation/src/com/android/systemui/animation/view/LaunchableLinearLayout.kt
diff --git a/packages/SystemUI/animation/core/src/com/android/systemui/animation/view/LaunchableTextView.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/view/LaunchableTextView.kt
similarity index 100%
rename from packages/SystemUI/animation/core/src/com/android/systemui/animation/view/LaunchableTextView.kt
rename to packages/SystemUI/animation/src/com/android/systemui/animation/view/LaunchableTextView.kt
diff --git a/packages/SystemUI/animation/core/src/com/android/systemui/surfaceeffects/ripple/MultiRippleController.kt b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/MultiRippleController.kt
similarity index 100%
rename from packages/SystemUI/animation/core/src/com/android/systemui/surfaceeffects/ripple/MultiRippleController.kt
rename to packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/MultiRippleController.kt
diff --git a/packages/SystemUI/animation/core/src/com/android/systemui/surfaceeffects/ripple/MultiRippleView.kt b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/MultiRippleView.kt
similarity index 100%
rename from packages/SystemUI/animation/core/src/com/android/systemui/surfaceeffects/ripple/MultiRippleView.kt
rename to packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/MultiRippleView.kt
diff --git a/packages/SystemUI/animation/core/src/com/android/systemui/surfaceeffects/ripple/RippleAnimation.kt b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/RippleAnimation.kt
similarity index 100%
rename from packages/SystemUI/animation/core/src/com/android/systemui/surfaceeffects/ripple/RippleAnimation.kt
rename to packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/RippleAnimation.kt
diff --git a/packages/SystemUI/animation/core/src/com/android/systemui/surfaceeffects/ripple/RippleAnimationConfig.kt b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/RippleAnimationConfig.kt
similarity index 100%
rename from packages/SystemUI/animation/core/src/com/android/systemui/surfaceeffects/ripple/RippleAnimationConfig.kt
rename to packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/RippleAnimationConfig.kt
diff --git a/packages/SystemUI/animation/core/src/com/android/systemui/surfaceeffects/ripple/RippleShader.kt b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/RippleShader.kt
similarity index 100%
rename from packages/SystemUI/animation/core/src/com/android/systemui/surfaceeffects/ripple/RippleShader.kt
rename to packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/RippleShader.kt
diff --git a/packages/SystemUI/animation/core/src/com/android/systemui/surfaceeffects/ripple/RippleView.kt b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/RippleView.kt
similarity index 100%
rename from packages/SystemUI/animation/core/src/com/android/systemui/surfaceeffects/ripple/RippleView.kt
rename to packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/RippleView.kt
diff --git a/packages/SystemUI/animation/core/src/com/android/systemui/surfaceeffects/shaders/SolidColorShader.kt b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/shaders/SolidColorShader.kt
similarity index 100%
rename from packages/SystemUI/animation/core/src/com/android/systemui/surfaceeffects/shaders/SolidColorShader.kt
rename to packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/shaders/SolidColorShader.kt
diff --git a/packages/SystemUI/animation/core/src/com/android/systemui/surfaceeffects/shaders/SparkleShader.kt b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/shaders/SparkleShader.kt
similarity index 100%
rename from packages/SystemUI/animation/core/src/com/android/systemui/surfaceeffects/shaders/SparkleShader.kt
rename to packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/shaders/SparkleShader.kt
diff --git a/packages/SystemUI/animation/core/src/com/android/systemui/surfaceeffects/shaderutil/SdfShaderLibrary.kt b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/shaderutil/SdfShaderLibrary.kt
similarity index 100%
rename from packages/SystemUI/animation/core/src/com/android/systemui/surfaceeffects/shaderutil/SdfShaderLibrary.kt
rename to packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/shaderutil/SdfShaderLibrary.kt
diff --git a/packages/SystemUI/animation/core/src/com/android/systemui/surfaceeffects/shaderutil/ShaderUtilLibrary.kt b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/shaderutil/ShaderUtilLibrary.kt
similarity index 89%
rename from packages/SystemUI/animation/core/src/com/android/systemui/surfaceeffects/shaderutil/ShaderUtilLibrary.kt
rename to packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/shaderutil/ShaderUtilLibrary.kt
index 23fcb69..867bbb7d 100644
--- a/packages/SystemUI/animation/core/src/com/android/systemui/surfaceeffects/shaderutil/ShaderUtilLibrary.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/shaderutil/ShaderUtilLibrary.kt
@@ -65,12 +65,33 @@
             return dest;
         }
 
-        // Return range [-1, 1].
+        // Integer mod. GLSL es 1.0 doesn't have integer mod :(
+        int imod(int a, int b) {
+            return a - (b * (a / b));
+        }
+
+        ivec3 imod(ivec3 a, int b) {
+            return ivec3(imod(a.x, b), imod(a.y, b), imod(a.z, b));
+        }
+
+        // Integer based hash function with the return range of [-1, 1].
         vec3 hash(vec3 p) {
-            p = fract(p * vec3(.3456, .1234, .9876));
-            p += dot(p, p.yxz + 43.21);
-            p = (p.xxy + p.yxx) * p.zyx;
-            return (fract(sin(p) * 4567.1234567) - .5) * 2.;
+            ivec3 v = ivec3(p);
+            v = v * 1671731 + 10139267;
+
+            v.x += v.y * v.z;
+            v.y += v.z * v.x;
+            v.z += v.x * v.y;
+
+            ivec3 v2 = v / 65536; // v >> 16
+            v = imod((10 - imod((v + v2), 10)), 10); // v ^ v2
+
+            v.x += v.y * v.z;
+            v.y += v.z * v.x;
+            v.z += v.x * v.y;
+
+            // Use sin and cos to map the range to [-1, 1].
+            return vec3(sin(float(v.x)), cos(float(v.y)), sin(float(v.z)));
         }
 
         // Skew factors (non-uniform).
diff --git a/packages/SystemUI/animation/core/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseAnimationConfig.kt b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseAnimationConfig.kt
similarity index 100%
rename from packages/SystemUI/animation/core/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseAnimationConfig.kt
rename to packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseAnimationConfig.kt
diff --git a/packages/SystemUI/animation/core/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseController.kt b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseController.kt
similarity index 100%
rename from packages/SystemUI/animation/core/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseController.kt
rename to packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseController.kt
diff --git a/packages/SystemUI/animation/core/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseShader.kt b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseShader.kt
similarity index 100%
rename from packages/SystemUI/animation/core/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseShader.kt
rename to packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseShader.kt
diff --git a/packages/SystemUI/animation/core/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseView.kt b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseView.kt
similarity index 100%
rename from packages/SystemUI/animation/core/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseView.kt
rename to packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseView.kt
diff --git a/packages/SystemUI/animation/core/src/com/android/systemui/util/AnimatorExtensions.kt b/packages/SystemUI/animation/src/com/android/systemui/util/AnimatorExtensions.kt
similarity index 100%
rename from packages/SystemUI/animation/core/src/com/android/systemui/util/AnimatorExtensions.kt
rename to packages/SystemUI/animation/src/com/android/systemui/util/AnimatorExtensions.kt
diff --git a/packages/SystemUI/animation/core/src/com/android/systemui/util/Dialog.kt b/packages/SystemUI/animation/src/com/android/systemui/util/Dialog.kt
similarity index 100%
rename from packages/SystemUI/animation/core/src/com/android/systemui/util/Dialog.kt
rename to packages/SystemUI/animation/src/com/android/systemui/util/Dialog.kt
diff --git a/packages/SystemUI/animation/core/src/com/android/systemui/util/Dimension.kt b/packages/SystemUI/animation/src/com/android/systemui/util/Dimension.kt
similarity index 100%
rename from packages/SystemUI/animation/core/src/com/android/systemui/util/Dimension.kt
rename to packages/SystemUI/animation/src/com/android/systemui/util/Dimension.kt
diff --git a/packages/SystemUI/compose/core/Android.bp b/packages/SystemUI/compose/core/Android.bp
index 42d088f..9a4347d 100644
--- a/packages/SystemUI/compose/core/Android.bp
+++ b/packages/SystemUI/compose/core/Android.bp
@@ -30,7 +30,7 @@
     ],
 
     static_libs: [
-        "SystemUIAnimationLib",
+        "PlatformAnimationLib",
 
         "androidx.compose.runtime_runtime",
         "androidx.compose.material3_material3",
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/ui/platform/DensityAwareComposeView.kt b/packages/SystemUI/compose/core/src/com/android/compose/ui/platform/DensityAwareComposeView.kt
new file mode 100644
index 0000000..dff8753
--- /dev/null
+++ b/packages/SystemUI/compose/core/src/com/android/compose/ui/platform/DensityAwareComposeView.kt
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.compose.ui.platform
+
+import android.content.Context
+import android.content.res.Configuration
+import android.util.AttributeSet
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.ui.platform.AbstractComposeView
+
+/**
+ * A ComposeView that recreates its composition if the display size or font scale was changed.
+ *
+ * TODO(b/317317814): Remove this workaround.
+ */
+class DensityAwareComposeView(context: Context) : OpenComposeView(context) {
+    private var lastDensityDpi: Int = -1
+    private var lastFontScale: Float = -1f
+
+    override fun onAttachedToWindow() {
+        super.onAttachedToWindow()
+
+        val configuration = context.resources.configuration
+        lastDensityDpi = configuration.densityDpi
+        lastFontScale = configuration.fontScale
+    }
+
+    override fun dispatchConfigurationChanged(newConfig: Configuration) {
+        super.dispatchConfigurationChanged(newConfig)
+
+        // If the density or font scale changed, we dispose then recreate the composition. Note that
+        // we do this here after dispatching the new configuration to children (instead of doing
+        // this in onConfigurationChanged()) because the new configuration should first be
+        // dispatched to the AndroidComposeView that holds the current density before we recreate
+        // the composition.
+        val densityDpi = newConfig.densityDpi
+        val fontScale = newConfig.fontScale
+        if (densityDpi != lastDensityDpi || fontScale != lastFontScale) {
+            lastDensityDpi = densityDpi
+            lastFontScale = fontScale
+
+            disposeComposition()
+            if (isAttachedToWindow) {
+                createComposition()
+            }
+        }
+    }
+}
+
+/** A fork of [androidx.compose.ui.platform.ComposeView] that is open and can be subclassed. */
+open class OpenComposeView
+internal constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) :
+    AbstractComposeView(context, attrs, defStyleAttr) {
+
+    private val content = mutableStateOf<(@Composable () -> Unit)?>(null)
+
+    @Suppress("RedundantVisibilityModifier")
+    protected override var shouldCreateCompositionOnAttachedToWindow: Boolean = false
+
+    @Composable
+    override fun Content() {
+        content.value?.invoke()
+    }
+
+    override fun getAccessibilityClassName(): CharSequence {
+        return javaClass.name
+    }
+
+    /**
+     * Set the Jetpack Compose UI content for this view. Initial composition will occur when the
+     * view becomes attached to a window or when [createComposition] is called, whichever comes
+     * first.
+     */
+    fun setContent(content: @Composable () -> Unit) {
+        shouldCreateCompositionOnAttachedToWindow = true
+        this.content.value = content
+        if (isAttachedToWindow) {
+            createComposition()
+        }
+    }
+}
diff --git a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/compose/ComposeFacade.kt b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/compose/ComposeFacade.kt
index 5055ee1..d31547b 100644
--- a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/compose/ComposeFacade.kt
+++ b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/compose/ComposeFacade.kt
@@ -27,6 +27,7 @@
 import androidx.compose.ui.unit.dp
 import androidx.lifecycle.LifecycleOwner
 import com.android.compose.theme.PlatformTheme
+import com.android.compose.ui.platform.DensityAwareComposeView
 import com.android.systemui.common.ui.compose.windowinsets.CutoutLocation
 import com.android.systemui.common.ui.compose.windowinsets.DisplayCutout
 import com.android.systemui.common.ui.compose.windowinsets.DisplayCutoutProvider
@@ -84,7 +85,7 @@
         viewModel: FooterActionsViewModel,
         qsVisibilityLifecycleOwner: LifecycleOwner,
     ): View {
-        return ComposeView(context).apply {
+        return DensityAwareComposeView(context).apply {
             setContent { PlatformTheme { FooterActions(viewModel, qsVisibilityLifecycleOwner) } }
         }
     }
diff --git a/packages/SystemUI/compose/features/Android.bp b/packages/SystemUI/compose/features/Android.bp
index 796abf4b..5ab2235 100644
--- a/packages/SystemUI/compose/features/Android.bp
+++ b/packages/SystemUI/compose/features/Android.bp
@@ -38,6 +38,7 @@
         "androidx.compose.runtime_runtime",
         "androidx.compose.animation_animation-graphics",
         "androidx.compose.material3_material3",
+        "androidx.compose.material_material-icons-extended",
         "androidx.activity_activity-compose",
     ],
 
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt
index 1d7c7d63..6591543 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt
@@ -19,7 +19,6 @@
 package com.android.systemui.bouncer.ui.composable
 
 import android.app.AlertDialog
-import android.app.Dialog
 import android.content.DialogInterface
 import androidx.compose.animation.Crossfade
 import androidx.compose.animation.core.animateFloatAsState
@@ -135,7 +134,7 @@
         }
 
         Dialog(
-            viewModel = viewModel,
+            bouncerViewModel = viewModel,
             dialogFactory = dialogFactory,
         )
     }
@@ -666,32 +665,30 @@
 
 @Composable
 private fun Dialog(
-    viewModel: BouncerViewModel,
+    bouncerViewModel: BouncerViewModel,
     dialogFactory: BouncerDialogFactory,
 ) {
-    val dialogMessage: String? by viewModel.dialogMessage.collectAsState()
-    var dialog: Dialog? by remember { mutableStateOf(null) }
+    val dialogViewModel by bouncerViewModel.dialogViewModel.collectAsState()
+    var dialog: AlertDialog? by remember { mutableStateOf(null) }
 
-    if (dialogMessage != null) {
+    dialogViewModel?.let { viewModel ->
         if (dialog == null) {
-            dialog =
-                dialogFactory().apply {
-                    setMessage(dialogMessage)
-                    setButton(
-                        DialogInterface.BUTTON_NEUTRAL,
-                        context.getString(R.string.ok),
-                    ) { _, _ ->
-                        viewModel.onDialogDismissed()
-                    }
-                    setCancelable(false)
-                    setCanceledOnTouchOutside(false)
-                    show()
-                }
+            dialog = dialogFactory()
         }
-    } else {
-        dialog?.dismiss()
-        dialog = null
+        dialog?.apply {
+            setMessage(viewModel.text)
+            setButton(DialogInterface.BUTTON_NEUTRAL, context.getString(R.string.ok)) { _, _ ->
+                viewModel.onDismiss()
+            }
+            setCancelable(false)
+            setCanceledOnTouchOutside(false)
+            show()
+        }
     }
+        ?: {
+            dialog?.dismiss()
+            dialog = null
+        }
 }
 
 /** Renders the UI of the user switcher that's displayed on large screens next to the bouncer UI. */
@@ -772,7 +769,7 @@
 }
 
 /**
- * Renders the dropdowm menu that displays the actual users and/or user actions that can be
+ * Renders the dropdown menu that displays the actual users and/or user actions that can be
  * selected.
  */
 @Composable
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
index 249b3e1..2fea6a5 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
@@ -7,7 +7,6 @@
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.fillMaxHeight
 import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.offset
 import androidx.compose.foundation.layout.width
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.filled.Close
@@ -21,10 +20,8 @@
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Alignment
-import androidx.compose.ui.ExperimentalComposeUiApi
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.input.pointer.pointerInteropFilter
 import androidx.compose.ui.unit.dp
 import com.android.compose.animation.scene.Edge
 import com.android.compose.animation.scene.ElementKey
@@ -33,15 +30,14 @@
 import com.android.compose.animation.scene.SceneKey
 import com.android.compose.animation.scene.SceneScope
 import com.android.compose.animation.scene.SceneTransitionLayout
-import com.android.compose.animation.scene.SceneTransitionLayoutState
 import com.android.compose.animation.scene.Swipe
 import com.android.compose.animation.scene.SwipeDirection
 import com.android.compose.animation.scene.observableTransitionState
 import com.android.compose.animation.scene.transitions
+import com.android.compose.animation.scene.updateSceneTransitionLayoutState
 import com.android.systemui.communal.shared.model.CommunalSceneKey
 import com.android.systemui.communal.shared.model.ObservableCommunalTransitionState
 import com.android.systemui.communal.ui.viewmodel.BaseCommunalViewModel
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.transform
 
@@ -66,7 +62,6 @@
  * This is a temporary container to allow the communal UI to use [SceneTransitionLayout] for gesture
  * handling and transitions before the full Flexiglass layout is ready.
  */
-@OptIn(ExperimentalComposeUiApi::class, ExperimentalCoroutinesApi::class)
 @Composable
 fun CommunalContainer(
     modifier: Modifier = Modifier,
@@ -76,9 +71,15 @@
         viewModel.currentScene
             .transform { value -> emit(value.toTransitionSceneKey()) }
             .collectAsState(TransitionSceneKey.Blank)
-    val sceneTransitionLayoutState = remember { SceneTransitionLayoutState(currentScene) }
-    // Don't show hub mode UI if keyguard is present. This is important since we're in the shade,
-    // which can be opened from many locations.
+    val sceneTransitionLayoutState =
+        updateSceneTransitionLayoutState(
+            currentScene,
+            onChangeScene = { viewModel.onSceneChanged(it.toCommunalSceneKey()) },
+            transitions = sceneTransitions,
+        )
+
+    // Don't show hub mode UI if keyguard is not present. This is important since we're in the
+    // shade, which can be opened from many locations.
     val isKeyguardShowing by viewModel.isKeyguardVisible.collectAsState(initial = false)
 
     // Failsafe to hide the whole SceneTransitionLayout in case of bugginess.
@@ -96,59 +97,30 @@
         onDispose { viewModel.setTransitionState(null) }
     }
 
-    Box(modifier = modifier.fillMaxSize()) {
-        SceneTransitionLayout(
-            modifier = Modifier.fillMaxSize(),
-            currentScene = currentScene,
-            onChangeScene = { sceneKey -> viewModel.onSceneChanged(sceneKey.toCommunalSceneKey()) },
-            transitions = sceneTransitions,
-            state = sceneTransitionLayoutState,
-            edgeDetector = FixedSizeEdgeDetector(ContainerDimensions.EdgeSwipeSize)
+    SceneTransitionLayout(
+        state = sceneTransitionLayoutState,
+        modifier = modifier.fillMaxSize(),
+        edgeDetector = FixedSizeEdgeDetector(ContainerDimensions.EdgeSwipeSize),
+    ) {
+        scene(
+            TransitionSceneKey.Blank,
+            userActions =
+                mapOf(
+                    Swipe(SwipeDirection.Left, fromEdge = Edge.Right) to TransitionSceneKey.Communal
+                )
         ) {
-            scene(
-                TransitionSceneKey.Blank,
-                userActions =
-                    mapOf(
-                        Swipe(SwipeDirection.Left, fromEdge = Edge.Right) to
-                            TransitionSceneKey.Communal
-                    )
-            ) {
-                BlankScene { showSceneTransitionLayout = false }
-            }
-
-            scene(
-                TransitionSceneKey.Communal,
-                userActions =
-                    mapOf(
-                        Swipe(SwipeDirection.Right, fromEdge = Edge.Left) to
-                            TransitionSceneKey.Blank
-                    ),
-            ) {
-                CommunalScene(viewModel, modifier = modifier)
-            }
+            BlankScene { showSceneTransitionLayout = false }
         }
 
-        // TODO(b/308813166): remove once CommunalContainer is moved lower in z-order and doesn't
-        //  block touches anymore.
-        Box(
-            modifier =
-                Modifier.fillMaxSize()
-                    // Offsetting to the left so that edge swipe to open the hub still works. This
-                    // does mean that the very right edge of the hub won't refresh the screen
-                    // timeout, but should be good enough for a temporary solution.
-                    .offset(x = -ContainerDimensions.EdgeSwipeSize)
-                    .pointerInteropFilter {
-                        viewModel.onUserActivity()
-                        if (
-                            sceneTransitionLayoutState.transitionState.currentScene ==
-                                TransitionSceneKey.Blank
-                        ) {
-                            viewModel.onOuterTouch(it)
-                            return@pointerInteropFilter true
-                        }
-                        false
-                    }
-        )
+        scene(
+            TransitionSceneKey.Communal,
+            userActions =
+                mapOf(
+                    Swipe(SwipeDirection.Right, fromEdge = Edge.Left) to TransitionSceneKey.Blank
+                ),
+        ) {
+            CommunalScene(viewModel, modifier = modifier)
+        }
     }
 }
 
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
index 55fc3a2..d76f0ff 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
@@ -16,16 +16,17 @@
 
 package com.android.systemui.communal.ui.compose
 
+import android.appwidget.AppWidgetHostView
 import android.os.Bundle
 import android.util.SizeF
 import android.widget.FrameLayout
-import androidx.compose.animation.core.animateDpAsState
 import androidx.compose.foundation.BorderStroke
 import androidx.compose.foundation.ExperimentalFoundationApi
 import androidx.compose.foundation.background
 import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.BoxScope
+import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.PaddingValues
 import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.Spacer
@@ -44,6 +45,8 @@
 import androidx.compose.material.icons.filled.Add
 import androidx.compose.material.icons.filled.Edit
 import androidx.compose.material.icons.outlined.Delete
+import androidx.compose.material.icons.outlined.TouchApp
+import androidx.compose.material.icons.outlined.Widgets
 import androidx.compose.material3.Button
 import androidx.compose.material3.ButtonColors
 import androidx.compose.material3.ButtonDefaults
@@ -51,6 +54,7 @@
 import androidx.compose.material3.CardDefaults
 import androidx.compose.material3.Icon
 import androidx.compose.material3.IconButton
+import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.OutlinedButton
 import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
@@ -71,11 +75,14 @@
 import androidx.compose.ui.platform.LocalConfiguration
 import androidx.compose.ui.platform.LocalDensity
 import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.style.TextAlign
 import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.IntOffset
 import androidx.compose.ui.unit.IntSize
 import androidx.compose.ui.unit.LayoutDirection
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.viewinterop.AndroidView
+import androidx.compose.ui.window.Popup
 import com.android.compose.theme.LocalAndroidColorScheme
 import com.android.systemui.communal.domain.model.CommunalContentModel
 import com.android.systemui.communal.shared.model.CommunalContentSize
@@ -93,13 +100,16 @@
     onEditDone: (() -> Unit)? = null,
 ) {
     val communalContent by viewModel.communalContent.collectAsState(initial = emptyList())
+    val isPopupOnDismissCtaShowing by
+        viewModel.isPopupOnDismissCtaShowing.collectAsState(initial = false)
     var removeButtonCoordinates: LayoutCoordinates? by remember { mutableStateOf(null) }
     var toolbarSize: IntSize? by remember { mutableStateOf(null) }
     var gridCoordinates: LayoutCoordinates? by remember { mutableStateOf(null) }
     var isDraggingToRemove by remember { mutableStateOf(false) }
 
     Box(
-        modifier = modifier.fillMaxSize().background(Color.White),
+        modifier =
+            modifier.fillMaxSize().background(LocalAndroidColorScheme.current.outlineVariant),
     ) {
         CommunalHubLazyGrid(
             communalContent = communalContent,
@@ -110,7 +120,8 @@
                 isDraggingToRemove =
                     checkForDraggingToRemove(it, removeButtonCoordinates, gridCoordinates)
                 isDraggingToRemove
-            }
+            },
+            onOpenWidgetPicker = onOpenWidgetPicker,
         )
 
         if (viewModel.isEditMode && onOpenWidgetPicker != null && onEditDone != null) {
@@ -127,6 +138,10 @@
             }
         }
 
+        if (isPopupOnDismissCtaShowing) {
+            PopupOnDismissCtaTile(viewModel::onHidePopupAfterDismissCta)
+        }
+
         // This spacer covers the edge of the LazyHorizontalGrid and prevents it from receiving
         // touches, so that the SceneTransitionLayout can intercept the touches and allow an edge
         // swipe back to the blank scene.
@@ -147,13 +162,14 @@
     contentPadding: PaddingValues,
     setGridCoordinates: (coordinates: LayoutCoordinates) -> Unit,
     updateDragPositionForRemove: (offset: Offset) -> Boolean,
+    onOpenWidgetPicker: (() -> Unit)? = null,
 ) {
     var gridModifier = Modifier.align(Alignment.CenterStart)
     val gridState = rememberLazyGridState()
     var list = communalContent
     var dragDropState: GridDragDropState? = null
     if (viewModel.isEditMode && viewModel is CommunalEditModeViewModel) {
-        val contentListState = rememberContentListState(communalContent, viewModel)
+        val contentListState = rememberContentListState(list, viewModel)
         list = contentListState.list
         // for drag & drop operations within the communal hub grid
         dragDropState =
@@ -165,7 +181,7 @@
         gridModifier =
             gridModifier
                 .fillMaxSize()
-                .dragContainer(dragDropState, beforeContentPadding(contentPadding))
+                .dragContainer(dragDropState, beforeContentPadding(contentPadding), viewModel)
                 .onGloballyPositioned { setGridCoordinates(it) }
         // for widgets dropped from other activities
         val dragAndDropTargetState =
@@ -194,6 +210,7 @@
         items(
             count = list.size,
             key = { index -> list[index].key },
+            contentType = { index -> list[index].key },
             span = { index -> GridItemSpan(list[index].size.span) },
         ) { index ->
             val cardModifier = Modifier.width(Dimensions.CardWidth)
@@ -203,15 +220,18 @@
                     list[index].size.dp().value,
                 )
             if (viewModel.isEditMode && dragDropState != null) {
-                DraggableItem(dragDropState = dragDropState, enabled = true, index = index) {
-                    isDragging ->
-                    val elevation by animateDpAsState(if (isDragging) 4.dp else 1.dp)
+                DraggableItem(
+                    dragDropState = dragDropState,
+                    enabled = list[index] is CommunalContentModel.Widget,
+                    index = index,
+                    size = size
+                ) { _ ->
                     CommunalContent(
                         modifier = cardModifier,
-                        elevation = elevation,
                         model = list[index],
                         viewModel = viewModel,
                         size = size,
+                        onOpenWidgetPicker = onOpenWidgetPicker,
                     )
                 }
             } else {
@@ -252,16 +272,11 @@
         horizontalArrangement = Arrangement.SpaceBetween,
         verticalAlignment = Alignment.CenterVertically
     ) {
-        val buttonContentPadding =
-            PaddingValues(
-                vertical = Dimensions.ToolbarButtonPaddingVertical,
-                horizontal = Dimensions.ToolbarButtonPaddingHorizontal,
-            )
         val spacerModifier = Modifier.width(Dimensions.ToolbarButtonSpaceBetween)
         Button(
             onClick = onOpenWidgetPicker,
-            colors = filledSecondaryButtonColors(),
-            contentPadding = buttonContentPadding
+            colors = filledButtonColors(),
+            contentPadding = Dimensions.ButtonPadding
         ) {
             Icon(Icons.Default.Add, stringResource(R.string.button_to_open_widget_editor))
             Spacer(spacerModifier)
@@ -270,25 +285,40 @@
             )
         }
 
-        val buttonColors =
-            if (isDraggingToRemove) filledButtonColors() else ButtonDefaults.outlinedButtonColors()
-        OutlinedButton(
-            onClick = {},
-            colors = buttonColors,
-            contentPadding = buttonContentPadding,
-            modifier = Modifier.onGloballyPositioned { setRemoveButtonCoordinates(it) },
-        ) {
-            Icon(Icons.Outlined.Delete, stringResource(R.string.button_to_open_widget_editor))
-            Spacer(spacerModifier)
-            Text(
-                text = stringResource(R.string.button_to_remove_widget),
-            )
+        val colors = LocalAndroidColorScheme.current
+        if (isDraggingToRemove) {
+            Button(
+                // Button is disabled to make it non-clickable
+                enabled = false,
+                onClick = {},
+                colors =
+                    ButtonDefaults.buttonColors(
+                        disabledContainerColor = colors.primary,
+                        disabledContentColor = colors.onPrimary,
+                    ),
+                contentPadding = Dimensions.ButtonPadding,
+                modifier = Modifier.onGloballyPositioned { setRemoveButtonCoordinates(it) }
+            ) {
+                RemoveButtonContent(spacerModifier)
+            }
+        } else {
+            OutlinedButton(
+                // Button is disabled to make it non-clickable
+                enabled = false,
+                onClick = {},
+                colors = ButtonDefaults.outlinedButtonColors(disabledContentColor = colors.primary),
+                border = BorderStroke(width = 1.0.dp, color = colors.primary),
+                contentPadding = Dimensions.ButtonPadding,
+                modifier = Modifier.onGloballyPositioned { setRemoveButtonCoordinates(it) }
+            ) {
+                RemoveButtonContent(spacerModifier)
+            }
         }
 
         Button(
             onClick = onEditDone,
             colors = filledButtonColors(),
-            contentPadding = buttonContentPadding
+            contentPadding = Dimensions.ButtonPadding
         ) {
             Text(
                 text = stringResource(R.string.hub_mode_editing_exit_button_text),
@@ -298,6 +328,47 @@
 }
 
 @Composable
+private fun PopupOnDismissCtaTile(onHidePopupAfterDismissCta: () -> Unit) {
+    Popup(
+        alignment = Alignment.TopCenter,
+        offset = IntOffset(0, 40),
+        onDismissRequest = onHidePopupAfterDismissCta
+    ) {
+        val colors = LocalAndroidColorScheme.current
+        Row(
+            modifier =
+                Modifier.height(56.dp)
+                    .background(colors.secondary, RoundedCornerShape(50.dp))
+                    .padding(16.dp),
+            horizontalArrangement = Arrangement.Center,
+            verticalAlignment = Alignment.CenterVertically,
+        ) {
+            Icon(
+                imageVector = Icons.Outlined.TouchApp,
+                contentDescription = stringResource(R.string.popup_on_dismiss_cta_tile_text),
+                tint = colors.onSecondary,
+                modifier = Modifier.size(20.dp)
+            )
+            Spacer(modifier = Modifier.size(8.dp))
+            Text(
+                text = stringResource(R.string.popup_on_dismiss_cta_tile_text),
+                style = MaterialTheme.typography.titleSmall,
+                color = colors.onSecondary,
+            )
+        }
+    }
+}
+
+@Composable
+private fun RemoveButtonContent(spacerModifier: Modifier) {
+    Icon(Icons.Outlined.Delete, stringResource(R.string.button_to_remove_widget))
+    Spacer(spacerModifier)
+    Text(
+        text = stringResource(R.string.button_to_remove_widget),
+    )
+}
+
+@Composable
 private fun filledButtonColors(): ButtonColors {
     val colors = LocalAndroidColorScheme.current
     return ButtonDefaults.buttonColors(
@@ -307,25 +378,20 @@
 }
 
 @Composable
-private fun filledSecondaryButtonColors(): ButtonColors {
-    val colors = LocalAndroidColorScheme.current
-    return ButtonDefaults.buttonColors(
-        containerColor = colors.secondary,
-        contentColor = colors.onSecondary,
-    )
-}
-
-@Composable
 private fun CommunalContent(
     model: CommunalContentModel,
     viewModel: BaseCommunalViewModel,
     size: SizeF,
     modifier: Modifier = Modifier,
-    elevation: Dp = 0.dp,
+    onOpenWidgetPicker: (() -> Unit)? = null,
 ) {
     when (model) {
-        is CommunalContentModel.Widget -> WidgetContent(model, size, elevation, modifier)
+        is CommunalContentModel.Widget -> WidgetContent(viewModel, model, size, modifier)
         is CommunalContentModel.WidgetPlaceholder -> WidgetPlaceholderContent(size)
+        is CommunalContentModel.CtaTileInViewMode ->
+            CtaTileInViewModeContent(viewModel, size, modifier)
+        is CommunalContentModel.CtaTileInEditMode ->
+            CtaTileInEditModeContent(size, modifier, onOpenWidgetPicker)
         is CommunalContentModel.Smartspace -> SmartspaceContent(model, modifier)
         is CommunalContentModel.Tutorial -> TutorialContent(modifier)
         is CommunalContentModel.Umo -> Umo(viewModel, modifier)
@@ -343,24 +409,144 @@
     ) {}
 }
 
+/** Presents a CTA tile at the end of the grid, to customize the hub. */
 @Composable
-private fun WidgetContent(
-    model: CommunalContentModel.Widget,
+private fun CtaTileInViewModeContent(
+    viewModel: BaseCommunalViewModel,
     size: SizeF,
-    elevation: Dp,
     modifier: Modifier = Modifier,
 ) {
+    val colors = LocalAndroidColorScheme.current
     Card(
         modifier = modifier.height(size.height.dp),
-        elevation = CardDefaults.cardElevation(draggedElevation = elevation),
+        colors =
+            CardDefaults.cardColors(
+                containerColor = colors.primary,
+                contentColor = colors.onPrimary,
+            ),
+        shape = RoundedCornerShape(80.dp, 40.dp, 80.dp, 40.dp)
+    ) {
+        Column(
+            modifier = Modifier.fillMaxSize().padding(horizontal = 82.dp),
+            verticalArrangement =
+                Arrangement.spacedBy(Dimensions.Spacing, Alignment.CenterVertically),
+            horizontalAlignment = Alignment.CenterHorizontally,
+        ) {
+            Icon(
+                imageVector = Icons.Outlined.Widgets,
+                contentDescription = stringResource(R.string.cta_label_to_open_widget_picker),
+                modifier = Modifier.size(Dimensions.IconSize),
+            )
+            Text(
+                text = stringResource(R.string.cta_label_to_edit_widget),
+                style = MaterialTheme.typography.titleLarge,
+                textAlign = TextAlign.Center,
+            )
+            Row(
+                modifier = Modifier.fillMaxWidth(),
+                horizontalArrangement = Arrangement.Center,
+            ) {
+                OutlinedButton(
+                    colors =
+                        ButtonDefaults.buttonColors(
+                            contentColor = colors.onPrimary,
+                        ),
+                    border = BorderStroke(width = 1.0.dp, color = colors.primaryContainer),
+                    contentPadding = Dimensions.ButtonPadding,
+                    onClick = viewModel::onDismissCtaTile,
+                ) {
+                    Text(
+                        text = stringResource(R.string.cta_tile_button_to_dismiss),
+                    )
+                }
+                Spacer(modifier = Modifier.size(Dimensions.Spacing))
+                Button(
+                    colors =
+                        ButtonDefaults.buttonColors(
+                            containerColor = colors.primaryContainer,
+                            contentColor = colors.onPrimaryContainer,
+                        ),
+                    contentPadding = Dimensions.ButtonPadding,
+                    onClick = viewModel::onOpenWidgetEditor
+                ) {
+                    Text(
+                        text = stringResource(R.string.cta_tile_button_to_open_widget_editor),
+                    )
+                }
+            }
+        }
+    }
+}
+
+/** Presents a CTA tile at the end of the hub in edit mode, to add more widgets. */
+@Composable
+private fun CtaTileInEditModeContent(
+    size: SizeF,
+    modifier: Modifier = Modifier,
+    onOpenWidgetPicker: (() -> Unit)? = null,
+) {
+    if (onOpenWidgetPicker == null) {
+        throw IllegalArgumentException("onOpenWidgetPicker should not be null.")
+    }
+    val colors = LocalAndroidColorScheme.current
+    Card(
+        modifier = modifier.height(size.height.dp),
+        colors = CardDefaults.cardColors(containerColor = Color.Transparent),
+        border = BorderStroke(1.dp, colors.primary),
+        shape = RoundedCornerShape(200.dp),
+        onClick = onOpenWidgetPicker,
+    ) {
+        Column(
+            modifier = Modifier.fillMaxSize(),
+            verticalArrangement =
+                Arrangement.spacedBy(Dimensions.Spacing, Alignment.CenterVertically),
+            horizontalAlignment = Alignment.CenterHorizontally,
+        ) {
+            Icon(
+                imageVector = Icons.Outlined.Widgets,
+                contentDescription = stringResource(R.string.cta_label_to_open_widget_picker),
+                tint = colors.primary,
+                modifier = Modifier.size(Dimensions.IconSize),
+            )
+            Text(
+                text = stringResource(R.string.cta_label_to_open_widget_picker),
+                style = MaterialTheme.typography.titleLarge,
+                color = colors.primary,
+                textAlign = TextAlign.Center,
+            )
+        }
+    }
+}
+
+@Composable
+private fun WidgetContent(
+    viewModel: BaseCommunalViewModel,
+    model: CommunalContentModel.Widget,
+    size: SizeF,
+    modifier: Modifier = Modifier,
+) {
+    Box(
+        modifier = modifier.height(size.height.dp),
+        contentAlignment = Alignment.Center,
     ) {
         AndroidView(
             modifier = modifier,
             factory = { context ->
-                model.appWidgetHost
-                    .createView(context, model.appWidgetId, model.providerInfo)
-                    .apply { updateAppWidgetSize(Bundle.EMPTY, listOf(size)) }
+                // The AppWidgetHostView will inherit the interaction handler from the
+                // AppWidgetHost. So set the interaction handler here before creating the view, and
+                // then clear it after the view is created. This is a workaround due to the fact
+                // that the interaction handler cannot be specified when creating the view,
+                // and there are race conditions if it is set after the view is created.
+                model.appWidgetHost.setInteractionHandler(viewModel.getInteractionHandler())
+                val view =
+                    model.appWidgetHost
+                        .createView(context, model.appWidgetId, model.providerInfo)
+                        .apply { updateAppWidgetSize(Bundle.EMPTY, listOf(size)) }
+                model.appWidgetHost.setInteractionHandler(null)
+                view
             },
+            // For reusing composition in lazy lists.
+            onReset = {},
         )
     }
 }
@@ -373,10 +559,10 @@
     AndroidView(
         modifier = modifier,
         factory = { context ->
-            FrameLayout(context).apply { addView(model.remoteViews.apply(context, this)) }
+            AppWidgetHostView(context).apply { updateAppWidget(model.remoteViews) }
         },
         // For reusing composition in lazy lists.
-        onReset = {}
+        onReset = {},
     )
 }
 
@@ -484,4 +670,10 @@
     val ToolbarButtonPaddingHorizontal = 24.dp
     val ToolbarButtonPaddingVertical = 16.dp
     val ToolbarButtonSpaceBetween = 8.dp
+    val ButtonPadding =
+        PaddingValues(
+            vertical = ToolbarButtonPaddingVertical,
+            horizontal = ToolbarButtonPaddingHorizontal,
+        )
+    val IconSize = 48.dp
 }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt
index 0d460aa8..1138221 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.communal.ui.compose
 
+import android.util.SizeF
 import androidx.compose.foundation.ExperimentalFoundationApi
 import androidx.compose.foundation.gestures.detectDragGesturesAfterLongPress
 import androidx.compose.foundation.gestures.scrollBy
@@ -39,6 +40,7 @@
 import androidx.compose.ui.unit.toSize
 import androidx.compose.ui.zIndex
 import com.android.systemui.communal.ui.compose.extensions.plus
+import com.android.systemui.communal.ui.viewmodel.BaseCommunalViewModel
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.channels.Channel
 import kotlinx.coroutines.launch
@@ -206,7 +208,8 @@
 
 fun Modifier.dragContainer(
     dragDropState: GridDragDropState,
-    beforeContentPadding: ContentPaddingInPx
+    beforeContentPadding: ContentPaddingInPx,
+    viewModel: BaseCommunalViewModel,
 ): Modifier {
     return pointerInput(dragDropState, beforeContentPadding) {
         detectDragGesturesAfterLongPress(
@@ -219,9 +222,16 @@
                     offset,
                     Offset(beforeContentPadding.startPadding, beforeContentPadding.topPadding)
                 )
+                viewModel.onReorderWidgetStart()
             },
-            onDragEnd = { dragDropState.onDragInterrupted() },
-            onDragCancel = { dragDropState.onDragInterrupted() }
+            onDragEnd = {
+                dragDropState.onDragInterrupted()
+                viewModel.onReorderWidgetEnd()
+            },
+            onDragCancel = {
+                dragDropState.onDragInterrupted()
+                viewModel.onReorderWidgetCancel()
+            }
         )
     }
 }
@@ -233,6 +243,7 @@
     dragDropState: GridDragDropState,
     index: Int,
     enabled: Boolean,
+    size: SizeF,
     modifier: Modifier = Modifier,
     content: @Composable (isDragging: Boolean) -> Unit
 ) {
@@ -250,7 +261,11 @@
         } else {
             Modifier.animateItemPlacement()
         }
-    Box(modifier = modifier.then(draggingModifier), propagateMinConstraints = true) {
-        content(dragging)
+
+    Box(modifier) {
+        if (dragging) {
+            WidgetPlaceholderContent(size)
+        }
+        Box(modifier = draggingModifier, propagateMinConstraints = true) { content(dragging) }
     }
 }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenLongPress.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenLongPress.kt
new file mode 100644
index 0000000..472484a
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenLongPress.kt
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:OptIn(ExperimentalFoundationApi::class)
+
+package com.android.systemui.keyguard.ui.composable
+
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.combinedClickable
+import androidx.compose.foundation.gestures.awaitEachGesture
+import androidx.compose.foundation.gestures.awaitFirstDown
+import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.BoxScope
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Rect
+import androidx.compose.ui.input.pointer.pointerInput
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardLongPressViewModel
+
+/** Container for lockscreen content that handles long-press to bring up the settings menu. */
+@Composable
+fun LockscreenLongPress(
+    viewModel: KeyguardLongPressViewModel,
+    modifier: Modifier = Modifier,
+    content: @Composable BoxScope.(onSettingsMenuPlaces: (coordinates: Rect?) -> Unit) -> Unit,
+) {
+    val isEnabled: Boolean by viewModel.isLongPressHandlingEnabled.collectAsState(initial = false)
+    val (settingsMenuBounds, setSettingsMenuBounds) = remember { mutableStateOf<Rect?>(null) }
+    val interactionSource = remember { MutableInteractionSource() }
+
+    Box(
+        modifier =
+            modifier
+                .combinedClickable(
+                    enabled = isEnabled,
+                    onLongClick = viewModel::onLongPress,
+                    onClick = {},
+                    interactionSource = interactionSource,
+                    // Passing null for the indication removes the ripple effect.
+                    indication = null,
+                )
+                .pointerInput(settingsMenuBounds) {
+                    awaitEachGesture {
+                        val pointerInputChange = awaitFirstDown()
+                        if (settingsMenuBounds?.contains(pointerInputChange.position) == false) {
+                            viewModel.onTouchedOutside()
+                        }
+                    }
+                },
+    ) {
+        content(setSettingsMenuBounds)
+    }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt
index 93f31ec..ff53ff2 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt
@@ -14,36 +14,15 @@
  * limitations under the License.
  */
 
-@file:OptIn(ExperimentalFoundationApi::class)
-
 package com.android.systemui.keyguard.ui.composable
 
-import android.view.View
-import android.view.ViewGroup
-import androidx.compose.foundation.ExperimentalFoundationApi
-import androidx.compose.foundation.combinedClickable
-import androidx.compose.foundation.gestures.awaitEachGesture
-import androidx.compose.foundation.gestures.awaitFirstDown
-import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
-import androidx.compose.runtime.getValue
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.geometry.Rect
-import androidx.compose.ui.graphics.toComposeRect
-import androidx.compose.ui.input.pointer.pointerInput
-import androidx.compose.ui.layout.Layout
-import androidx.compose.ui.viewinterop.AndroidView
-import androidx.core.view.isVisible
 import com.android.compose.animation.scene.SceneScope
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.keyguard.qualifiers.KeyguardRootView
-import com.android.systemui.keyguard.ui.viewmodel.KeyguardLongPressViewModel
 import com.android.systemui.keyguard.ui.viewmodel.LockscreenSceneViewModel
-import com.android.systemui.notifications.ui.composable.NotificationStack
-import com.android.systemui.res.R
 import com.android.systemui.scene.shared.model.Direction
 import com.android.systemui.scene.shared.model.Edge
 import com.android.systemui.scene.shared.model.SceneKey
@@ -58,9 +37,6 @@
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.stateIn
 
-/** Set this to `true` to use the LockscreenContent replacement of KeyguardRootView. */
-private val UseLockscreenContent = false
-
 /** The lock screen scene shows when the device is locked. */
 @SysUISingleton
 class LockscreenScene
@@ -68,7 +44,6 @@
 constructor(
     @Application private val applicationScope: CoroutineScope,
     private val viewModel: LockscreenSceneViewModel,
-    @KeyguardRootView private val viewProvider: () -> @JvmSuppressWildcards View,
     private val lockscreenContent: Lazy<LockscreenContent>,
 ) : ComposableScene {
     override val key = SceneKey.Lockscreen
@@ -93,8 +68,6 @@
         modifier: Modifier,
     ) {
         LockscreenScene(
-            viewProvider = viewProvider,
-            viewModel = viewModel,
             lockscreenContent = lockscreenContent,
             modifier = modifier,
         )
@@ -115,99 +88,13 @@
 }
 
 @Composable
-private fun SceneScope.LockscreenScene(
-    viewProvider: () -> View,
-    viewModel: LockscreenSceneViewModel,
+private fun LockscreenScene(
     lockscreenContent: Lazy<LockscreenContent>,
     modifier: Modifier = Modifier,
 ) {
-    fun findSettingsMenu(): View {
-        return viewProvider().requireViewById(R.id.keyguard_settings_button)
-    }
-
-    Box(
-        modifier = modifier,
-    ) {
-        LongPressSurface(
-            viewModel = viewModel.longPress,
-            isSettingsMenuVisible = { findSettingsMenu().isVisible },
-            settingsMenuBounds = {
-                val bounds = android.graphics.Rect()
-                findSettingsMenu().getHitRect(bounds)
-                bounds.toComposeRect()
-            },
-            modifier = Modifier.fillMaxSize(),
+    lockscreenContent
+        .get()
+        .Content(
+            modifier = modifier.fillMaxSize(),
         )
-
-        if (UseLockscreenContent) {
-            lockscreenContent
-                .get()
-                .Content(
-                    modifier = Modifier.fillMaxSize(),
-                )
-        } else {
-            AndroidView(
-                factory = { _ ->
-                    val keyguardRootView = viewProvider()
-                    // Remove the KeyguardRootView from any parent it might already have in legacy
-                    // code just in case (a view can't have two parents).
-                    (keyguardRootView.parent as? ViewGroup)?.removeView(keyguardRootView)
-                    keyguardRootView
-                },
-                modifier = Modifier.fillMaxSize(),
-            )
-        }
-
-        val notificationStackPosition by viewModel.keyguardRoot.notificationBounds.collectAsState()
-
-        Layout(
-            modifier = Modifier.fillMaxSize(),
-            content = {
-                NotificationStack(
-                    viewModel = viewModel.notifications,
-                    isScrimVisible = false,
-                )
-            }
-        ) { measurables, constraints ->
-            check(measurables.size == 1)
-            val height = notificationStackPosition.height.toInt()
-            val childConstraints = constraints.copy(minHeight = height, maxHeight = height)
-            val placeable = measurables[0].measure(childConstraints)
-            layout(constraints.maxWidth, constraints.maxHeight) {
-                val start = (constraints.maxWidth - placeable.measuredWidth) / 2
-                placeable.placeRelative(x = start, y = notificationStackPosition.top.toInt())
-            }
-        }
-    }
-}
-
-@Composable
-private fun LongPressSurface(
-    viewModel: KeyguardLongPressViewModel,
-    isSettingsMenuVisible: () -> Boolean,
-    settingsMenuBounds: () -> Rect,
-    modifier: Modifier = Modifier,
-) {
-    val isEnabled: Boolean by viewModel.isLongPressHandlingEnabled.collectAsState(initial = false)
-
-    Box(
-        modifier =
-            modifier
-                .combinedClickable(
-                    enabled = isEnabled,
-                    onLongClick = viewModel::onLongPress,
-                    onClick = {},
-                )
-                .pointerInput(Unit) {
-                    awaitEachGesture {
-                        val pointerInputChange = awaitFirstDown()
-                        if (
-                            isSettingsMenuVisible() &&
-                                !settingsMenuBounds().contains(pointerInputChange.position)
-                        ) {
-                            viewModel.onTouchedOutside()
-                        }
-                    }
-                },
-    )
 }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenSceneBlueprintModule.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenSceneBlueprintModule.kt
index 9abb50c..3677cab 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenSceneBlueprintModule.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenSceneBlueprintModule.kt
@@ -20,6 +20,7 @@
 import com.android.systemui.keyguard.ui.composable.blueprint.DefaultBlueprintModule
 import com.android.systemui.keyguard.ui.composable.blueprint.ShortcutsBesideUdfpsBlueprintModule
 import com.android.systemui.keyguard.ui.composable.blueprint.SplitShadeBlueprintModule
+import com.android.systemui.keyguard.ui.composable.section.OptionalSectionModule
 import dagger.Module
 
 @Module(
@@ -27,6 +28,7 @@
         [
             CommunalBlueprintModule::class,
             DefaultBlueprintModule::class,
+            OptionalSectionModule::class,
             ShortcutsBesideUdfpsBlueprintModule::class,
             SplitShadeBlueprintModule::class,
         ],
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/BurnInState.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/BurnInState.kt
new file mode 100644
index 0000000..c418490
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/BurnInState.kt
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.composable.blueprint
+
+import androidx.compose.foundation.layout.WindowInsets
+import androidx.compose.foundation.layout.displayCutout
+import androidx.compose.foundation.layout.systemBars
+import androidx.compose.foundation.layout.union
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.ui.platform.LocalDensity
+import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
+import com.android.systemui.keyguard.ui.viewmodel.BurnInParameters
+import com.android.systemui.plugins.clocks.ClockController
+import kotlin.math.min
+import kotlin.math.roundToInt
+
+/** Produces a [BurnInState] that can be used to query the `LockscreenBurnInViewModel` flows. */
+@Composable
+fun rememberBurnIn(
+    clockInteractor: KeyguardClockInteractor,
+): BurnInState {
+    val clock by clockInteractor.currentClock.collectAsState()
+
+    val (smartspaceTop, onSmartspaceTopChanged) = remember { mutableStateOf<Float?>(null) }
+    val (smallClockTop, onSmallClockTopChanged) = remember { mutableStateOf<Float?>(null) }
+
+    val topmostTop =
+        when {
+            smartspaceTop != null && smallClockTop != null -> min(smartspaceTop, smallClockTop)
+            smartspaceTop != null -> smartspaceTop
+            smallClockTop != null -> smallClockTop
+            else -> 0f
+        }.roundToInt()
+
+    val params = rememberBurnInParameters(clock, topmostTop)
+
+    return remember(params, onSmartspaceTopChanged, onSmallClockTopChanged) {
+        BurnInState(
+            parameters = params,
+            onSmartspaceTopChanged = onSmartspaceTopChanged,
+            onSmallClockTopChanged = onSmallClockTopChanged,
+        )
+    }
+}
+
+@Composable
+private fun rememberBurnInParameters(
+    clock: ClockController?,
+    topmostTop: Int,
+): BurnInParameters {
+    val density = LocalDensity.current
+    val topInset = WindowInsets.systemBars.union(WindowInsets.displayCutout).getTop(density)
+
+    return remember(clock, topInset, topmostTop) {
+        BurnInParameters(
+            clockControllerProvider = { clock },
+            topInset = topInset,
+            statusViewTop = topmostTop,
+        )
+    }
+}
+
+data class BurnInState(
+    /** Parameters for use with the `LockscreenBurnInViewModel. */
+    val parameters: BurnInParameters,
+    /**
+     * Callback to invoke when the top coordinate of the smartspace element is updated, pass `null`
+     * when the element is not shown.
+     */
+    val onSmartspaceTopChanged: (Float?) -> Unit,
+    /**
+     * Callback to invoke when the top coordinate of the small clock element is updated, pass `null`
+     * when the element is not shown.
+     */
+    val onSmallClockTopChanged: (Float?) -> Unit,
+)
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/CommunalBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/CommunalBlueprint.kt
index 7eddaaf..86124c6 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/CommunalBlueprint.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/CommunalBlueprint.kt
@@ -24,24 +24,35 @@
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
 import com.android.compose.animation.scene.SceneScope
+import com.android.systemui.keyguard.ui.composable.LockscreenLongPress
+import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel
 import dagger.Binds
 import dagger.Module
 import dagger.multibindings.IntoSet
 import javax.inject.Inject
 
 /** Renders the lockscreen scene when showing the communal glanceable hub. */
-class CommunalBlueprint @Inject constructor() : LockscreenSceneBlueprint {
+class CommunalBlueprint
+@Inject
+constructor(
+    private val viewModel: LockscreenContentViewModel,
+) : LockscreenSceneBlueprint {
 
     override val id: String = "communal"
 
     @Composable
     override fun SceneScope.Content(modifier: Modifier) {
-        Box(modifier.background(Color.Black)) {
-            Text(
-                text = "TODO(b/316211368): communal blueprint",
-                color = Color.White,
-                modifier = Modifier.align(Alignment.Center),
-            )
+        LockscreenLongPress(
+            viewModel = viewModel.longPress,
+            modifier = modifier,
+        ) { _ ->
+            Box(modifier.background(Color.Black)) {
+                Text(
+                    text = "TODO(b/316211368): communal blueprint",
+                    color = Color.White,
+                    modifier = Modifier.align(Alignment.Center),
+                )
+            }
         }
     }
 }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt
index 7314453..84d4246 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt
@@ -17,23 +17,28 @@
 package com.android.systemui.keyguard.ui.composable.blueprint
 
 import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.layout.Layout
 import androidx.compose.ui.unit.IntRect
 import com.android.compose.animation.scene.SceneScope
+import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
+import com.android.systemui.keyguard.ui.composable.LockscreenLongPress
 import com.android.systemui.keyguard.ui.composable.section.AmbientIndicationSection
 import com.android.systemui.keyguard.ui.composable.section.BottomAreaSection
 import com.android.systemui.keyguard.ui.composable.section.ClockSection
 import com.android.systemui.keyguard.ui.composable.section.LockSection
 import com.android.systemui.keyguard.ui.composable.section.NotificationSection
+import com.android.systemui.keyguard.ui.composable.section.SettingsMenuSection
 import com.android.systemui.keyguard.ui.composable.section.SmartSpaceSection
 import com.android.systemui.keyguard.ui.composable.section.StatusBarSection
 import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel
 import dagger.Binds
 import dagger.Module
 import dagger.multibindings.IntoSet
+import java.util.Optional
 import javax.inject.Inject
 
 /**
@@ -49,8 +54,10 @@
     private val smartSpaceSection: SmartSpaceSection,
     private val notificationSection: NotificationSection,
     private val lockSection: LockSection,
-    private val ambientIndicationSection: AmbientIndicationSection,
+    private val ambientIndicationSectionOptional: Optional<AmbientIndicationSection>,
     private val bottomAreaSection: BottomAreaSection,
+    private val settingsMenuSection: SettingsMenuSection,
+    private val clockInteractor: KeyguardClockInteractor,
 ) : LockscreenSceneBlueprint {
 
     override val id: String = "default"
@@ -58,103 +65,129 @@
     @Composable
     override fun SceneScope.Content(modifier: Modifier) {
         val isUdfpsVisible = viewModel.isUdfpsVisible
+        val burnIn = rememberBurnIn(clockInteractor)
 
-        Layout(
-            content = {
-                // Constrained to above the lock icon.
-                Column(
-                    modifier = Modifier.fillMaxWidth(),
-                ) {
-                    with(statusBarSection) { StatusBar(modifier = Modifier.fillMaxWidth()) }
-                    with(clockSection) { SmallClock(modifier = Modifier.fillMaxWidth()) }
-                    with(smartSpaceSection) { SmartSpace(modifier = Modifier.fillMaxWidth()) }
-                    with(clockSection) { LargeClock(modifier = Modifier.fillMaxWidth()) }
-                    with(notificationSection) {
-                        Notifications(modifier = Modifier.fillMaxWidth().weight(1f))
-                    }
-                    if (!isUdfpsVisible) {
-                        with(ambientIndicationSection) {
-                            AmbientIndication(modifier = Modifier.fillMaxWidth())
-                        }
-                    }
-                }
-
-                with(lockSection) { LockIcon() }
-
-                // Aligned to bottom and constrained to below the lock icon.
-                Column(modifier = Modifier.fillMaxWidth()) {
-                    if (isUdfpsVisible) {
-                        with(ambientIndicationSection) {
-                            AmbientIndication(modifier = Modifier.fillMaxWidth())
-                        }
-                    }
-
-                    with(bottomAreaSection) { IndicationArea(modifier = Modifier.fillMaxWidth()) }
-                }
-
-                // Aligned to bottom and NOT constrained by the lock icon.
-                with(bottomAreaSection) {
-                    Shortcut(isStart = true, applyPadding = true)
-                    Shortcut(isStart = false, applyPadding = true)
-                }
-            },
+        LockscreenLongPress(
+            viewModel = viewModel.longPress,
             modifier = modifier,
-        ) { measurables, constraints ->
-            check(measurables.size == 5)
-            val (
-                aboveLockIconMeasurable,
-                lockIconMeasurable,
-                belowLockIconMeasurable,
-                startShortcutMeasurable,
-                endShortcutMeasurable,
-            ) = measurables
+        ) { onSettingsMenuPlaced ->
+            Layout(
+                content = {
+                    // Constrained to above the lock icon.
+                    Column(
+                        modifier = Modifier.fillMaxWidth(),
+                    ) {
+                        with(statusBarSection) { StatusBar(modifier = Modifier.fillMaxWidth()) }
+                        with(clockSection) {
+                            SmallClock(
+                                onTopChanged = burnIn.onSmallClockTopChanged,
+                                modifier = Modifier.fillMaxWidth(),
+                            )
+                        }
+                        with(smartSpaceSection) {
+                            SmartSpace(
+                                burnInParams = burnIn.parameters,
+                                onTopChanged = burnIn.onSmartspaceTopChanged,
+                                modifier = Modifier.fillMaxWidth(),
+                            )
+                        }
+                        with(clockSection) { LargeClock(modifier = Modifier.fillMaxWidth()) }
+                        with(notificationSection) {
+                            Notifications(modifier = Modifier.fillMaxWidth().weight(1f))
+                        }
+                        if (!isUdfpsVisible && ambientIndicationSectionOptional.isPresent) {
+                            with(ambientIndicationSectionOptional.get()) {
+                                AmbientIndication(modifier = Modifier.fillMaxWidth())
+                            }
+                        }
+                    }
 
-            val noMinConstraints =
-                constraints.copy(
-                    minWidth = 0,
-                    minHeight = 0,
-                )
-            val lockIconPlaceable = lockIconMeasurable.measure(noMinConstraints)
-            val lockIconBounds =
-                IntRect(
-                    left = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Left],
-                    top = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Top],
-                    right = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Right],
-                    bottom = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Bottom],
-                )
+                    with(lockSection) { LockIcon() }
 
-            val aboveLockIconPlaceable =
-                aboveLockIconMeasurable.measure(
-                    noMinConstraints.copy(maxHeight = lockIconBounds.top)
-                )
-            val belowLockIconPlaceable =
-                belowLockIconMeasurable.measure(
-                    noMinConstraints.copy(maxHeight = constraints.maxHeight - lockIconBounds.bottom)
-                )
-            val startShortcutPleaceable = startShortcutMeasurable.measure(noMinConstraints)
-            val endShortcutPleaceable = endShortcutMeasurable.measure(noMinConstraints)
+                    // Aligned to bottom and constrained to below the lock icon.
+                    Column(modifier = Modifier.fillMaxWidth()) {
+                        if (isUdfpsVisible && ambientIndicationSectionOptional.isPresent) {
+                            with(ambientIndicationSectionOptional.get()) {
+                                AmbientIndication(modifier = Modifier.fillMaxWidth())
+                            }
+                        }
 
-            layout(constraints.maxWidth, constraints.maxHeight) {
-                aboveLockIconPlaceable.place(
-                    x = 0,
-                    y = 0,
-                )
-                lockIconPlaceable.place(
-                    x = lockIconBounds.left,
-                    y = lockIconBounds.top,
-                )
-                belowLockIconPlaceable.place(
-                    x = 0,
-                    y = constraints.maxHeight - belowLockIconPlaceable.height,
-                )
-                startShortcutPleaceable.place(
-                    x = 0,
-                    y = constraints.maxHeight - startShortcutPleaceable.height,
-                )
-                endShortcutPleaceable.place(
-                    x = constraints.maxWidth - endShortcutPleaceable.width,
-                    y = constraints.maxHeight - endShortcutPleaceable.height,
-                )
+                        with(bottomAreaSection) {
+                            IndicationArea(modifier = Modifier.fillMaxWidth())
+                        }
+                    }
+
+                    // Aligned to bottom and NOT constrained by the lock icon.
+                    with(bottomAreaSection) {
+                        Shortcut(isStart = true, applyPadding = true)
+                        Shortcut(isStart = false, applyPadding = true)
+                    }
+                    with(settingsMenuSection) { SettingsMenu(onSettingsMenuPlaced) }
+                },
+                modifier = Modifier.fillMaxSize(),
+            ) { measurables, constraints ->
+                check(measurables.size == 6)
+                val aboveLockIconMeasurable = measurables[0]
+                val lockIconMeasurable = measurables[1]
+                val belowLockIconMeasurable = measurables[2]
+                val startShortcutMeasurable = measurables[3]
+                val endShortcutMeasurable = measurables[4]
+                val settingsMenuMeasurable = measurables[5]
+
+                val noMinConstraints =
+                    constraints.copy(
+                        minWidth = 0,
+                        minHeight = 0,
+                    )
+                val lockIconPlaceable = lockIconMeasurable.measure(noMinConstraints)
+                val lockIconBounds =
+                    IntRect(
+                        left = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Left],
+                        top = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Top],
+                        right = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Right],
+                        bottom = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Bottom],
+                    )
+
+                val aboveLockIconPlaceable =
+                    aboveLockIconMeasurable.measure(
+                        noMinConstraints.copy(maxHeight = lockIconBounds.top)
+                    )
+                val belowLockIconPlaceable =
+                    belowLockIconMeasurable.measure(
+                        noMinConstraints.copy(
+                            maxHeight = constraints.maxHeight - lockIconBounds.bottom
+                        )
+                    )
+                val startShortcutPleaceable = startShortcutMeasurable.measure(noMinConstraints)
+                val endShortcutPleaceable = endShortcutMeasurable.measure(noMinConstraints)
+                val settingsMenuPlaceable = settingsMenuMeasurable.measure(noMinConstraints)
+
+                layout(constraints.maxWidth, constraints.maxHeight) {
+                    aboveLockIconPlaceable.place(
+                        x = 0,
+                        y = 0,
+                    )
+                    lockIconPlaceable.place(
+                        x = lockIconBounds.left,
+                        y = lockIconBounds.top,
+                    )
+                    belowLockIconPlaceable.place(
+                        x = 0,
+                        y = constraints.maxHeight - belowLockIconPlaceable.height,
+                    )
+                    startShortcutPleaceable.place(
+                        x = 0,
+                        y = constraints.maxHeight - startShortcutPleaceable.height,
+                    )
+                    endShortcutPleaceable.place(
+                        x = constraints.maxWidth - endShortcutPleaceable.width,
+                        y = constraints.maxHeight - endShortcutPleaceable.height,
+                    )
+                    settingsMenuPlaceable.place(
+                        x = (constraints.maxWidth - settingsMenuPlaceable.width) / 2,
+                        y = constraints.maxHeight - settingsMenuPlaceable.height,
+                    )
+                }
             }
         }
     }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt
index 4c119c7..4148462 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt
@@ -17,23 +17,28 @@
 package com.android.systemui.keyguard.ui.composable.blueprint
 
 import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.layout.Layout
 import androidx.compose.ui.unit.IntRect
 import com.android.compose.animation.scene.SceneScope
+import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
+import com.android.systemui.keyguard.ui.composable.LockscreenLongPress
 import com.android.systemui.keyguard.ui.composable.section.AmbientIndicationSection
 import com.android.systemui.keyguard.ui.composable.section.BottomAreaSection
 import com.android.systemui.keyguard.ui.composable.section.ClockSection
 import com.android.systemui.keyguard.ui.composable.section.LockSection
 import com.android.systemui.keyguard.ui.composable.section.NotificationSection
+import com.android.systemui.keyguard.ui.composable.section.SettingsMenuSection
 import com.android.systemui.keyguard.ui.composable.section.SmartSpaceSection
 import com.android.systemui.keyguard.ui.composable.section.StatusBarSection
 import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel
 import dagger.Binds
 import dagger.Module
 import dagger.multibindings.IntoSet
+import java.util.Optional
 import javax.inject.Inject
 
 /**
@@ -49,8 +54,10 @@
     private val smartSpaceSection: SmartSpaceSection,
     private val notificationSection: NotificationSection,
     private val lockSection: LockSection,
-    private val ambientIndicationSection: AmbientIndicationSection,
+    private val ambientIndicationSectionOptional: Optional<AmbientIndicationSection>,
     private val bottomAreaSection: BottomAreaSection,
+    private val settingsMenuSection: SettingsMenuSection,
+    private val clockInteractor: KeyguardClockInteractor,
 ) : LockscreenSceneBlueprint {
 
     override val id: String = "shortcuts-besides-udfps"
@@ -58,106 +65,136 @@
     @Composable
     override fun SceneScope.Content(modifier: Modifier) {
         val isUdfpsVisible = viewModel.isUdfpsVisible
+        val burnIn = rememberBurnIn(clockInteractor)
 
-        Layout(
-            content = {
-                // Constrained to above the lock icon.
-                Column(
-                    modifier = Modifier.fillMaxWidth(),
-                ) {
-                    with(statusBarSection) { StatusBar(modifier = Modifier.fillMaxWidth()) }
-                    with(clockSection) { SmallClock(modifier = Modifier.fillMaxWidth()) }
-                    with(smartSpaceSection) { SmartSpace(modifier = Modifier.fillMaxWidth()) }
-                    with(clockSection) { LargeClock(modifier = Modifier.fillMaxWidth()) }
-                    with(notificationSection) {
-                        Notifications(modifier = Modifier.fillMaxWidth().weight(1f))
-                    }
-                    if (!isUdfpsVisible) {
-                        with(ambientIndicationSection) {
-                            AmbientIndication(modifier = Modifier.fillMaxWidth())
-                        }
-                    }
-                }
-
-                // Constrained to the left of the lock icon (in left-to-right layouts).
-                with(bottomAreaSection) { Shortcut(isStart = true, applyPadding = false) }
-
-                with(lockSection) { LockIcon() }
-
-                // Constrained to the right of the lock icon (in left-to-right layouts).
-                with(bottomAreaSection) { Shortcut(isStart = false, applyPadding = false) }
-
-                // Aligned to bottom and constrained to below the lock icon.
-                Column(modifier = Modifier.fillMaxWidth()) {
-                    if (isUdfpsVisible) {
-                        with(ambientIndicationSection) {
-                            AmbientIndication(modifier = Modifier.fillMaxWidth())
-                        }
-                    }
-
-                    with(bottomAreaSection) { IndicationArea(modifier = Modifier.fillMaxWidth()) }
-                }
-            },
+        LockscreenLongPress(
+            viewModel = viewModel.longPress,
             modifier = modifier,
-        ) { measurables, constraints ->
-            check(measurables.size == 5)
-            val (
-                aboveLockIconMeasurable,
-                startSideShortcutMeasurable,
-                lockIconMeasurable,
-                endSideShortcutMeasurable,
-                belowLockIconMeasurable,
-            ) = measurables
+        ) { onSettingsMenuPlaced ->
+            Layout(
+                content = {
+                    // Constrained to above the lock icon.
+                    Column(
+                        modifier = Modifier.fillMaxWidth(),
+                    ) {
+                        with(statusBarSection) { StatusBar(modifier = Modifier.fillMaxWidth()) }
+                        with(clockSection) {
+                            SmallClock(
+                                onTopChanged = burnIn.onSmallClockTopChanged,
+                                modifier = Modifier.fillMaxWidth(),
+                            )
+                        }
+                        with(smartSpaceSection) {
+                            SmartSpace(
+                                burnInParams = burnIn.parameters,
+                                onTopChanged = burnIn.onSmartspaceTopChanged,
+                                modifier = Modifier.fillMaxWidth(),
+                            )
+                        }
+                        with(clockSection) { LargeClock(modifier = Modifier.fillMaxWidth()) }
+                        with(notificationSection) {
+                            Notifications(modifier = Modifier.fillMaxWidth().weight(1f))
+                        }
+                        if (!isUdfpsVisible && ambientIndicationSectionOptional.isPresent) {
+                            with(ambientIndicationSectionOptional.get()) {
+                                AmbientIndication(modifier = Modifier.fillMaxWidth())
+                            }
+                        }
+                    }
 
-            val noMinConstraints =
-                constraints.copy(
-                    minWidth = 0,
-                    minHeight = 0,
-                )
+                    // Constrained to the left of the lock icon (in left-to-right layouts).
+                    with(bottomAreaSection) { Shortcut(isStart = true, applyPadding = false) }
 
-            val lockIconPlaceable = lockIconMeasurable.measure(noMinConstraints)
-            val lockIconBounds =
-                IntRect(
-                    left = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Left],
-                    top = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Top],
-                    right = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Right],
-                    bottom = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Bottom],
-                )
+                    with(lockSection) { LockIcon() }
 
-            val aboveLockIconPlaceable =
-                aboveLockIconMeasurable.measure(
-                    noMinConstraints.copy(maxHeight = lockIconBounds.top)
-                )
-            val startSideShortcutPlaceable = startSideShortcutMeasurable.measure(noMinConstraints)
-            val endSideShortcutPlaceable = endSideShortcutMeasurable.measure(noMinConstraints)
-            val belowLockIconPlaceable =
-                belowLockIconMeasurable.measure(
-                    noMinConstraints.copy(maxHeight = constraints.maxHeight - lockIconBounds.bottom)
-                )
+                    // Constrained to the right of the lock icon (in left-to-right layouts).
+                    with(bottomAreaSection) { Shortcut(isStart = false, applyPadding = false) }
 
-            layout(constraints.maxWidth, constraints.maxHeight) {
-                aboveLockIconPlaceable.place(
-                    x = 0,
-                    y = 0,
-                )
-                startSideShortcutPlaceable.placeRelative(
-                    x = lockIconBounds.left / 2 - startSideShortcutPlaceable.width / 2,
-                    y = lockIconBounds.center.y - startSideShortcutPlaceable.height / 2,
-                )
-                lockIconPlaceable.place(
-                    x = lockIconBounds.left,
-                    y = lockIconBounds.top,
-                )
-                endSideShortcutPlaceable.placeRelative(
-                    x =
-                        lockIconBounds.right + (constraints.maxWidth - lockIconBounds.right) / 2 -
-                            endSideShortcutPlaceable.width / 2,
-                    y = lockIconBounds.center.y - endSideShortcutPlaceable.height / 2,
-                )
-                belowLockIconPlaceable.place(
-                    x = 0,
-                    y = constraints.maxHeight - belowLockIconPlaceable.height,
-                )
+                    // Aligned to bottom and constrained to below the lock icon.
+                    Column(modifier = Modifier.fillMaxWidth()) {
+                        if (isUdfpsVisible && ambientIndicationSectionOptional.isPresent) {
+                            with(ambientIndicationSectionOptional.get()) {
+                                AmbientIndication(modifier = Modifier.fillMaxWidth())
+                            }
+                        }
+
+                        with(bottomAreaSection) {
+                            IndicationArea(modifier = Modifier.fillMaxWidth())
+                        }
+                    }
+
+                    // Aligned to bottom and NOT constrained by the lock icon.
+                    with(settingsMenuSection) { SettingsMenu(onSettingsMenuPlaced) }
+                },
+                modifier = Modifier.fillMaxSize(),
+            ) { measurables, constraints ->
+                check(measurables.size == 6)
+                val aboveLockIconMeasurable = measurables[0]
+                val startSideShortcutMeasurable = measurables[1]
+                val lockIconMeasurable = measurables[2]
+                val endSideShortcutMeasurable = measurables[3]
+                val belowLockIconMeasurable = measurables[4]
+                val settingsMenuMeasurable = measurables[5]
+
+                val noMinConstraints =
+                    constraints.copy(
+                        minWidth = 0,
+                        minHeight = 0,
+                    )
+
+                val lockIconPlaceable = lockIconMeasurable.measure(noMinConstraints)
+                val lockIconBounds =
+                    IntRect(
+                        left = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Left],
+                        top = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Top],
+                        right = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Right],
+                        bottom = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Bottom],
+                    )
+
+                val aboveLockIconPlaceable =
+                    aboveLockIconMeasurable.measure(
+                        noMinConstraints.copy(maxHeight = lockIconBounds.top)
+                    )
+                val startSideShortcutPlaceable =
+                    startSideShortcutMeasurable.measure(noMinConstraints)
+                val endSideShortcutPlaceable = endSideShortcutMeasurable.measure(noMinConstraints)
+                val belowLockIconPlaceable =
+                    belowLockIconMeasurable.measure(
+                        noMinConstraints.copy(
+                            maxHeight = constraints.maxHeight - lockIconBounds.bottom
+                        )
+                    )
+                val settingsMenuPlaceable = settingsMenuMeasurable.measure(noMinConstraints)
+
+                layout(constraints.maxWidth, constraints.maxHeight) {
+                    aboveLockIconPlaceable.place(
+                        x = 0,
+                        y = 0,
+                    )
+                    startSideShortcutPlaceable.placeRelative(
+                        x = lockIconBounds.left / 2 - startSideShortcutPlaceable.width / 2,
+                        y = lockIconBounds.center.y - startSideShortcutPlaceable.height / 2,
+                    )
+                    lockIconPlaceable.place(
+                        x = lockIconBounds.left,
+                        y = lockIconBounds.top,
+                    )
+                    endSideShortcutPlaceable.placeRelative(
+                        x =
+                            lockIconBounds.right +
+                                (constraints.maxWidth - lockIconBounds.right) / 2 -
+                                endSideShortcutPlaceable.width / 2,
+                        y = lockIconBounds.center.y - endSideShortcutPlaceable.height / 2,
+                    )
+                    belowLockIconPlaceable.place(
+                        x = 0,
+                        y = constraints.maxHeight - belowLockIconPlaceable.height,
+                    )
+                    settingsMenuPlaceable.place(
+                        x = (constraints.maxWidth - settingsMenuPlaceable.width) / 2,
+                        y = constraints.maxHeight - settingsMenuPlaceable.height,
+                    )
+                }
             }
         }
     }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/SplitShadeBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/SplitShadeBlueprint.kt
index 7545d5f..fdf1166 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/SplitShadeBlueprint.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/SplitShadeBlueprint.kt
@@ -24,6 +24,8 @@
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
 import com.android.compose.animation.scene.SceneScope
+import com.android.systemui.keyguard.ui.composable.LockscreenLongPress
+import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel
 import dagger.Binds
 import dagger.Module
 import dagger.multibindings.IntoSet
@@ -33,18 +35,27 @@
  * Renders the lockscreen scene when showing with a split shade (e.g. unfolded foldable and/or
  * tablet form factor).
  */
-class SplitShadeBlueprint @Inject constructor() : LockscreenSceneBlueprint {
+class SplitShadeBlueprint
+@Inject
+constructor(
+    private val viewModel: LockscreenContentViewModel,
+) : LockscreenSceneBlueprint {
 
     override val id: String = "split-shade"
 
     @Composable
     override fun SceneScope.Content(modifier: Modifier) {
-        Box(modifier.background(Color.Black)) {
-            Text(
-                text = "TODO(b/316211368): split shade blueprint",
-                color = Color.White,
-                modifier = Modifier.align(Alignment.Center),
-            )
+        LockscreenLongPress(
+            viewModel = viewModel.longPress,
+            modifier = modifier,
+        ) { _ ->
+            Box(modifier.background(Color.Black)) {
+                Text(
+                    text = "TODO(b/316211368): split shade blueprint",
+                    color = Color.White,
+                    modifier = Modifier.align(Alignment.Center),
+                )
+            }
         }
     }
 }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/modifier/BurnInModifiers.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/modifier/BurnInModifiers.kt
new file mode 100644
index 0000000..f9dd04b
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/modifier/BurnInModifiers.kt
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.composable.modifier
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.graphicsLayer
+import androidx.compose.ui.layout.boundsInWindow
+import androidx.compose.ui.layout.onPlaced
+import com.android.systemui.keyguard.ui.viewmodel.AodBurnInViewModel
+import com.android.systemui.keyguard.ui.viewmodel.BurnInParameters
+import com.android.systemui.keyguard.ui.viewmodel.BurnInScaleViewModel
+
+/**
+ * Modifies the composable to account for anti-burn in translation, alpha, and scaling.
+ *
+ * Please override [isClock] as `true` if the composable is an element that's part of a clock.
+ */
+@Composable
+fun Modifier.burnInAware(
+    viewModel: AodBurnInViewModel,
+    params: BurnInParameters,
+    isClock: Boolean = false,
+): Modifier {
+    val translationX by viewModel.translationX(params).collectAsState(initial = 0f)
+    val translationY by viewModel.translationY(params).collectAsState(initial = 0f)
+    val alpha by viewModel.alpha.collectAsState(initial = 1f)
+    val scaleViewModel by viewModel.scale(params).collectAsState(initial = BurnInScaleViewModel())
+
+    return this.graphicsLayer {
+        val scale =
+            when {
+                scaleViewModel.scaleClockOnly && isClock -> scaleViewModel.scale
+                !scaleViewModel.scaleClockOnly -> scaleViewModel.scale
+                else -> 1f
+            }
+
+        this.translationX = translationX
+        this.translationY = translationY
+        this.alpha = alpha
+        this.scaleX = scale
+        this.scaleY = scale
+    }
+}
+
+/** Reports the "top" coordinate of the modified composable to the given [consumer]. */
+@Composable
+fun Modifier.onTopPlacementChanged(
+    consumer: (Float) -> Unit,
+): Modifier {
+    return onPlaced { coordinates -> consumer(coordinates.boundsInWindow().top) }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/AmbientIndicationSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/AmbientIndicationSection.kt
index 0e7ac5e..af9a195 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/AmbientIndicationSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/AmbientIndicationSection.kt
@@ -16,36 +16,11 @@
 
 package com.android.systemui.keyguard.ui.composable.section
 
-import androidx.compose.foundation.background
-import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
-import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.Color
-import com.android.compose.animation.scene.ElementKey
 import com.android.compose.animation.scene.SceneScope
-import javax.inject.Inject
 
-class AmbientIndicationSection @Inject constructor() {
-    @Composable
-    fun SceneScope.AmbientIndication(modifier: Modifier = Modifier) {
-        MovableElement(
-            key = AmbientIndicationElementKey,
-            modifier = modifier,
-        ) {
-            Box(
-                modifier = Modifier.fillMaxWidth().background(Color.Green),
-            ) {
-                Text(
-                    text = "TODO(b/316211368): Ambient indication",
-                    color = Color.White,
-                    modifier = Modifier.align(Alignment.Center),
-                )
-            }
-        }
-    }
+/** Defines interface for classes that can render the ambient indication area. */
+interface AmbientIndicationSection {
+    @Composable fun SceneScope.AmbientIndication(modifier: Modifier)
 }
-
-private val AmbientIndicationElementKey = ElementKey("AmbientIndication")
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/BottomAreaSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/BottomAreaSection.kt
index db20f65..8bd0d45 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/BottomAreaSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/BottomAreaSection.kt
@@ -35,10 +35,10 @@
 import com.android.systemui.keyguard.ui.binder.KeyguardIndicationAreaBinder
 import com.android.systemui.keyguard.ui.binder.KeyguardQuickAffordanceViewBinder
 import com.android.systemui.keyguard.ui.view.KeyguardIndicationArea
+import com.android.systemui.keyguard.ui.viewmodel.AodAlphaViewModel
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardIndicationAreaViewModel
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordanceViewModel
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordancesCombinedViewModel
-import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
 import com.android.systemui.plugins.FalsingManager
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.KeyguardIndicationController
@@ -55,7 +55,7 @@
     private val vibratorHelper: VibratorHelper,
     private val indicationController: KeyguardIndicationController,
     private val indicationAreaViewModel: KeyguardIndicationAreaViewModel,
-    private val keyguardRootViewModel: KeyguardRootViewModel,
+    private val alphaViewModel: AodAlphaViewModel,
 ) {
     /**
      * Renders a single lockscreen shortcut.
@@ -74,20 +74,22 @@
             key = if (isStart) StartButtonElementKey else EndButtonElementKey,
             modifier = modifier,
         ) {
-            Shortcut(
-                viewId = if (isStart) R.id.start_button else R.id.end_button,
-                viewModel = if (isStart) viewModel.startButton else viewModel.endButton,
-                transitionAlpha = viewModel.transitionAlpha,
-                falsingManager = falsingManager,
-                vibratorHelper = vibratorHelper,
-                indicationController = indicationController,
-                modifier =
-                    if (applyPadding) {
-                        Modifier.shortcutPadding()
-                    } else {
-                        Modifier
-                    }
-            )
+            content {
+                Shortcut(
+                    viewId = if (isStart) R.id.start_button else R.id.end_button,
+                    viewModel = if (isStart) viewModel.startButton else viewModel.endButton,
+                    transitionAlpha = viewModel.transitionAlpha,
+                    falsingManager = falsingManager,
+                    vibratorHelper = vibratorHelper,
+                    indicationController = indicationController,
+                    modifier =
+                        if (applyPadding) {
+                            Modifier.shortcutPadding()
+                        } else {
+                            Modifier
+                        }
+                )
+            }
         }
     }
 
@@ -99,11 +101,13 @@
             key = IndicationAreaElementKey,
             modifier = modifier.shortcutPadding(),
         ) {
-            IndicationArea(
-                indicationAreaViewModel = indicationAreaViewModel,
-                keyguardRootViewModel = keyguardRootViewModel,
-                indicationController = indicationController,
-            )
+            content {
+                IndicationArea(
+                    indicationAreaViewModel = indicationAreaViewModel,
+                    alphaViewModel = alphaViewModel,
+                    indicationController = indicationController,
+                )
+            }
         }
     }
 
@@ -179,7 +183,7 @@
     @Composable
     private fun IndicationArea(
         indicationAreaViewModel: KeyguardIndicationAreaViewModel,
-        keyguardRootViewModel: KeyguardRootViewModel,
+        alphaViewModel: AodAlphaViewModel,
         indicationController: KeyguardIndicationController,
         modifier: Modifier = Modifier,
     ) {
@@ -192,7 +196,7 @@
                     KeyguardIndicationAreaBinder.bind(
                         view = view,
                         viewModel = indicationAreaViewModel,
-                        keyguardRootViewModel = keyguardRootViewModel,
+                        aodAlphaViewModel = alphaViewModel,
                         indicationController = indicationController,
                     )
                 )
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/ClockSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/ClockSection.kt
index eaf8063..f021bb6 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/ClockSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/ClockSection.kt
@@ -16,41 +16,73 @@
 
 package com.android.systemui.keyguard.ui.composable.section
 
-import androidx.compose.foundation.background
-import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.material3.Text
+import androidx.compose.foundation.layout.padding
 import androidx.compose.runtime.Composable
-import androidx.compose.ui.Alignment
+import androidx.compose.runtime.DisposableEffect
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.LocalView
+import androidx.compose.ui.res.dimensionResource
+import androidx.compose.ui.viewinterop.AndroidView
 import com.android.compose.animation.scene.ElementKey
 import com.android.compose.animation.scene.SceneScope
+import com.android.compose.modifiers.padding
+import com.android.keyguard.KeyguardClockSwitch
+import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
+import com.android.systemui.keyguard.ui.composable.modifier.onTopPlacementChanged
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
+import com.android.systemui.res.R
 import javax.inject.Inject
 
 class ClockSection
 @Inject
 constructor(
     private val viewModel: KeyguardClockViewModel,
+    private val clockInteractor: KeyguardClockInteractor,
 ) {
+
     @Composable
-    fun SceneScope.SmallClock(modifier: Modifier = Modifier) {
-        if (viewModel.useLargeClock) {
+    fun SceneScope.SmallClock(
+        onTopChanged: (top: Float?) -> Unit,
+        modifier: Modifier = Modifier,
+    ) {
+        val clockSize by viewModel.clockSize.collectAsState()
+        val currentClock by viewModel.currentClock.collectAsState()
+        viewModel.clock = currentClock
+
+        if (clockSize != KeyguardClockSwitch.SMALL) {
+            onTopChanged(null)
             return
         }
 
+        if (currentClock?.smallClock?.view == null) {
+            return
+        }
+
+        val view = LocalView.current
+
+        DisposableEffect(view) {
+            clockInteractor.clockEventController.registerListeners(view)
+
+            onDispose { clockInteractor.clockEventController.unregisterListeners() }
+        }
+
         MovableElement(
             key = ClockElementKey,
             modifier = modifier,
         ) {
-            Box(
-                modifier = Modifier.fillMaxWidth().background(Color.Magenta),
-            ) {
-                Text(
-                    text = "TODO(b/316211368): Small clock",
-                    color = Color.White,
-                    modifier = Modifier.align(Alignment.Center),
+            content {
+                AndroidView(
+                    factory = { checkNotNull(currentClock).smallClock.view },
+                    modifier =
+                        Modifier.padding(
+                                horizontal =
+                                    dimensionResource(R.dimen.keyguard_affordance_horizontal_offset)
+                            )
+                            .padding(top = { viewModel.getSmallClockTopMargin(view.context) })
+                            .onTopPlacementChanged(onTopChanged),
                 )
             }
         }
@@ -58,21 +90,36 @@
 
     @Composable
     fun SceneScope.LargeClock(modifier: Modifier = Modifier) {
-        if (!viewModel.useLargeClock) {
+        val clockSize by viewModel.clockSize.collectAsState()
+        val currentClock by viewModel.currentClock.collectAsState()
+        viewModel.clock = currentClock
+
+        if (clockSize != KeyguardClockSwitch.LARGE) {
             return
         }
 
+        if (currentClock?.largeClock?.view == null) {
+            return
+        }
+
+        val view = LocalView.current
+
+        DisposableEffect(view) {
+            clockInteractor.clockEventController.registerListeners(view)
+
+            onDispose { clockInteractor.clockEventController.unregisterListeners() }
+        }
+
         MovableElement(
             key = ClockElementKey,
             modifier = modifier,
         ) {
-            Box(
-                modifier = Modifier.fillMaxWidth().background(Color.Blue),
-            ) {
-                Text(
-                    text = "TODO(b/316211368): Large clock",
-                    color = Color.White,
-                    modifier = Modifier.align(Alignment.Center),
+            content {
+                AndroidView(
+                    factory = { checkNotNull(currentClock).largeClock.view },
+                    modifier =
+                        Modifier.fillMaxWidth()
+                            .padding(top = { viewModel.getLargeClockTopMargin(view.context) })
                 )
             }
         }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt
index d93863d..2a6bea7 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt
@@ -19,25 +19,33 @@
 import android.content.Context
 import android.util.DisplayMetrics
 import android.view.WindowManager
-import androidx.compose.foundation.background
-import androidx.compose.foundation.layout.Box
-import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
-import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.layout.layout
 import androidx.compose.ui.platform.LocalContext
 import androidx.compose.ui.unit.Constraints
 import androidx.compose.ui.unit.IntOffset
 import androidx.compose.ui.unit.IntRect
+import androidx.compose.ui.viewinterop.AndroidView
 import com.android.compose.animation.scene.ElementKey
 import com.android.compose.animation.scene.SceneScope
+import com.android.keyguard.LockIconView
+import com.android.keyguard.LockIconViewController
+import com.android.systemui.Flags.keyguardBottomAreaRefactor
 import com.android.systemui.biometrics.AuthController
+import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
 import com.android.systemui.flags.FeatureFlagsClassic
 import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.ui.binder.DeviceEntryIconViewBinder
 import com.android.systemui.keyguard.ui.composable.blueprint.BlueprintAlignmentLines
+import com.android.systemui.keyguard.ui.view.DeviceEntryIconView
+import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryBackgroundViewModel
+import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryForegroundViewModel
+import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryIconViewModel
+import com.android.systemui.plugins.FalsingManager
 import com.android.systemui.res.R
+import com.android.systemui.statusbar.VibratorHelper
+import dagger.Lazy
 import javax.inject.Inject
 
 class LockSection
@@ -46,48 +54,70 @@
     private val windowManager: WindowManager,
     private val authController: AuthController,
     private val featureFlags: FeatureFlagsClassic,
+    private val lockIconViewController: Lazy<LockIconViewController>,
+    private val deviceEntryIconViewModel: Lazy<DeviceEntryIconViewModel>,
+    private val deviceEntryForegroundViewModel: Lazy<DeviceEntryForegroundViewModel>,
+    private val deviceEntryBackgroundViewModel: Lazy<DeviceEntryBackgroundViewModel>,
+    private val falsingManager: Lazy<FalsingManager>,
+    private val vibratorHelper: Lazy<VibratorHelper>,
 ) {
     @Composable
     fun SceneScope.LockIcon(modifier: Modifier = Modifier) {
-        MovableElement(
-            key = LockIconElementKey,
-            modifier = modifier,
-        ) {
-            val context = LocalContext.current
-            Box(
-                modifier =
-                    Modifier.background(Color.Red).layout { measurable, _ ->
-                        val lockIconBounds = lockIconBounds(context)
-                        val placeable =
-                            measurable.measure(
-                                Constraints.fixed(
-                                    width = lockIconBounds.width,
-                                    height = lockIconBounds.height,
-                                )
-                            )
-                        layout(
-                            width = placeable.width,
-                            height = placeable.height,
-                            alignmentLines =
-                                mapOf(
-                                    BlueprintAlignmentLines.LockIcon.Left to lockIconBounds.left,
-                                    BlueprintAlignmentLines.LockIcon.Top to lockIconBounds.top,
-                                    BlueprintAlignmentLines.LockIcon.Right to lockIconBounds.right,
-                                    BlueprintAlignmentLines.LockIcon.Bottom to
-                                        lockIconBounds.bottom,
-                                ),
-                        ) {
-                            placeable.place(0, 0)
-                        }
-                    },
-            ) {
-                Text(
-                    text = "TODO(b/316211368): Lock",
-                    color = Color.White,
-                    modifier = Modifier.align(Alignment.Center),
-                )
-            }
+        if (!keyguardBottomAreaRefactor() && !DeviceEntryUdfpsRefactor.isEnabled) {
+            return
         }
+
+        val context = LocalContext.current
+
+        AndroidView(
+            factory = { context ->
+                val view =
+                    if (DeviceEntryUdfpsRefactor.isEnabled) {
+                        DeviceEntryIconView(context, null).apply {
+                            id = R.id.device_entry_icon_view
+                            DeviceEntryIconViewBinder.bind(
+                                this,
+                                deviceEntryIconViewModel.get(),
+                                deviceEntryForegroundViewModel.get(),
+                                deviceEntryBackgroundViewModel.get(),
+                                falsingManager.get(),
+                                vibratorHelper.get(),
+                            )
+                        }
+                    } else {
+                        // keyguardBottomAreaRefactor()
+                        LockIconView(context, null).apply {
+                            id = R.id.lock_icon_view
+                            lockIconViewController.get().setLockIconView(this)
+                        }
+                    }
+                view
+            },
+            modifier =
+                modifier.element(LockIconElementKey).layout { measurable, _ ->
+                    val lockIconBounds = lockIconBounds(context)
+                    val placeable =
+                        measurable.measure(
+                            Constraints.fixed(
+                                width = lockIconBounds.width,
+                                height = lockIconBounds.height,
+                            )
+                        )
+                    layout(
+                        width = placeable.width,
+                        height = placeable.height,
+                        alignmentLines =
+                            mapOf(
+                                BlueprintAlignmentLines.LockIcon.Left to lockIconBounds.left,
+                                BlueprintAlignmentLines.LockIcon.Top to lockIconBounds.top,
+                                BlueprintAlignmentLines.LockIcon.Right to lockIconBounds.right,
+                                BlueprintAlignmentLines.LockIcon.Bottom to lockIconBounds.bottom,
+                            ),
+                    ) {
+                        placeable.place(0, 0)
+                    }
+                },
+        )
     }
 
     /**
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt
index f135be2..900616f 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt
@@ -16,36 +16,24 @@
 
 package com.android.systemui.keyguard.ui.composable.section
 
-import androidx.compose.foundation.background
-import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
-import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.Color
-import com.android.compose.animation.scene.ElementKey
 import com.android.compose.animation.scene.SceneScope
+import com.android.systemui.notifications.ui.composable.NotificationStack
+import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationsPlaceholderViewModel
 import javax.inject.Inject
 
-class NotificationSection @Inject constructor() {
+class NotificationSection
+@Inject
+constructor(
+    private val viewModel: NotificationsPlaceholderViewModel,
+) {
     @Composable
     fun SceneScope.Notifications(modifier: Modifier = Modifier) {
-        MovableElement(
-            key = NotificationsElementKey,
+        NotificationStack(
+            viewModel = viewModel,
+            isScrimVisible = false,
             modifier = modifier,
-        ) {
-            Box(
-                modifier = Modifier.fillMaxSize().background(Color.Yellow),
-            ) {
-                Text(
-                    text = "TODO(b/316211368): Notifications",
-                    color = Color.White,
-                    modifier = Modifier.align(Alignment.Center),
-                )
-            }
-        }
+        )
     }
 }
-
-private val NotificationsElementKey = ElementKey("Notifications")
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileUser.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/OptionalSectionModule.kt
similarity index 61%
copy from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileUser.kt
copy to packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/OptionalSectionModule.kt
index efc7431..5b7a8e6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileUser.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/OptionalSectionModule.kt
@@ -14,12 +14,16 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.custom.di.bound
+package com.android.systemui.keyguard.ui.composable.section
 
-import javax.inject.Qualifier
+import dagger.BindsOptionalOf
+import dagger.Module
 
-/** User associated with current custom tile binding. */
-@Qualifier
-@MustBeDocumented
-@Retention(AnnotationRetention.RUNTIME)
-annotation class CustomTileUser
+/**
+ * Dagger module for providing placeholders for optional lockscreen scene sections that don't exist
+ * in AOSP but may be provided by OEMs.
+ */
+@Module
+interface OptionalSectionModule {
+    @BindsOptionalOf fun ambientIndicationSection(): AmbientIndicationSection
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/SettingsMenuSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/SettingsMenuSection.kt
new file mode 100644
index 0000000..44b0535
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/SettingsMenuSection.kt
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.composable.section
+
+import android.view.LayoutInflater
+import androidx.compose.foundation.layout.padding
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Rect
+import androidx.compose.ui.layout.onPlaced
+import androidx.compose.ui.layout.positionInParent
+import androidx.compose.ui.res.dimensionResource
+import androidx.compose.ui.unit.toSize
+import androidx.compose.ui.viewinterop.AndroidView
+import androidx.core.view.isVisible
+import com.android.systemui.keyguard.ui.binder.KeyguardSettingsViewBinder
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardLongPressViewModel
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardSettingsMenuViewModel
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.VibratorHelper
+import javax.inject.Inject
+import kotlinx.coroutines.DisposableHandle
+
+class SettingsMenuSection
+@Inject
+constructor(
+    private val viewModel: KeyguardSettingsMenuViewModel,
+    private val longPressViewModel: KeyguardLongPressViewModel,
+    private val vibratorHelper: VibratorHelper,
+    private val activityStarter: ActivityStarter,
+) {
+    @Composable
+    @SuppressWarnings("InflateParams") // null is passed into the inflate call, on purpose.
+    fun SettingsMenu(
+        onPlaced: (Rect?) -> Unit,
+        modifier: Modifier = Modifier,
+    ) {
+        val (disposableHandle, setDisposableHandle) =
+            remember { mutableStateOf<DisposableHandle?>(null) }
+        AndroidView(
+            factory = { context ->
+                LayoutInflater.from(context)
+                    .inflate(
+                        R.layout.keyguard_settings_popup_menu,
+                        null,
+                    )
+                    .apply {
+                        isVisible = false
+                        alpha = 0f
+
+                        setDisposableHandle(
+                            KeyguardSettingsViewBinder.bind(
+                                view = this,
+                                viewModel = viewModel,
+                                longPressViewModel = longPressViewModel,
+                                rootViewModel = null,
+                                vibratorHelper = vibratorHelper,
+                                activityStarter = activityStarter,
+                            )
+                        )
+                    }
+            },
+            onRelease = { disposableHandle?.dispose() },
+            modifier =
+                modifier
+                    .padding(
+                        bottom = dimensionResource(R.dimen.keyguard_affordance_vertical_offset),
+                    )
+                    .padding(
+                        horizontal =
+                            dimensionResource(R.dimen.keyguard_affordance_horizontal_offset),
+                    )
+                    .onPlaced { coordinates ->
+                        onPlaced(
+                            if (!coordinates.size.toSize().isEmpty()) {
+                                Rect(coordinates.positionInParent(), coordinates.size.toSize())
+                            } else {
+                                null
+                            }
+                        )
+                    },
+        )
+    }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/SmartSpaceSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/SmartSpaceSection.kt
index ca03af5..9b71844 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/SmartSpaceSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/SmartSpaceSection.kt
@@ -16,33 +16,181 @@
 
 package com.android.systemui.keyguard.ui.composable.section
 
-import androidx.compose.foundation.background
-import androidx.compose.foundation.layout.Box
+import android.widget.FrameLayout
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.IntrinsicSize
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.material3.Text
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.width
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.res.dimensionResource
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.viewinterop.AndroidView
 import com.android.compose.animation.scene.ElementKey
 import com.android.compose.animation.scene.SceneScope
+import com.android.systemui.keyguard.KeyguardUnlockAnimationController
+import com.android.systemui.keyguard.ui.composable.modifier.burnInAware
+import com.android.systemui.keyguard.ui.composable.modifier.onTopPlacementChanged
+import com.android.systemui.keyguard.ui.viewmodel.AodBurnInViewModel
+import com.android.systemui.keyguard.ui.viewmodel.BurnInParameters
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController
 import javax.inject.Inject
 
-class SmartSpaceSection @Inject constructor() {
+class SmartSpaceSection
+@Inject
+constructor(
+    private val lockscreenSmartspaceController: LockscreenSmartspaceController,
+    private val keyguardUnlockAnimationController: KeyguardUnlockAnimationController,
+    private val keyguardSmartspaceViewModel: KeyguardSmartspaceViewModel,
+    private val aodBurnInViewModel: AodBurnInViewModel,
+) {
     @Composable
-    fun SceneScope.SmartSpace(modifier: Modifier = Modifier) {
-        MovableElement(key = SmartSpaceElementKey, modifier = modifier) {
-            Box(
-                modifier = Modifier.fillMaxWidth().background(Color.Cyan),
-            ) {
-                Text(
-                    text = "TODO(b/316211368): Smart space",
-                    color = Color.White,
-                    modifier = Modifier.align(Alignment.Center),
-                )
+    fun SceneScope.SmartSpace(
+        burnInParams: BurnInParameters,
+        onTopChanged: (top: Float?) -> Unit,
+        modifier: Modifier = Modifier,
+    ) {
+        Column(
+            modifier = modifier.element(SmartSpaceElementKey).onTopPlacementChanged(onTopChanged),
+        ) {
+            if (!keyguardSmartspaceViewModel.isSmartspaceEnabled) {
+                return
             }
+
+            val paddingBelowClockStart = dimensionResource(R.dimen.below_clock_padding_start)
+            val paddingBelowClockEnd = dimensionResource(R.dimen.below_clock_padding_end)
+
+            if (keyguardSmartspaceViewModel.isDateWeatherDecoupled) {
+                Row(
+                    verticalAlignment = Alignment.CenterVertically,
+                    modifier =
+                        Modifier.fillMaxWidth()
+                            // All items will be constrained to be as tall as the shortest item.
+                            .height(IntrinsicSize.Min)
+                            .padding(
+                                start = paddingBelowClockStart,
+                            ),
+                ) {
+                    Date(
+                        modifier =
+                            Modifier.burnInAware(
+                                viewModel = aodBurnInViewModel,
+                                params = burnInParams,
+                            ),
+                    )
+                    Spacer(modifier = Modifier.width(4.dp))
+                    Weather(
+                        modifier =
+                            Modifier.burnInAware(
+                                viewModel = aodBurnInViewModel,
+                                params = burnInParams,
+                            ),
+                    )
+                }
+            }
+
+            Card(
+                modifier =
+                    Modifier.fillMaxWidth()
+                        .padding(
+                            start = paddingBelowClockStart,
+                            end = paddingBelowClockEnd,
+                        )
+                        .burnInAware(
+                            viewModel = aodBurnInViewModel,
+                            params = burnInParams,
+                        ),
+            )
         }
     }
+
+    @Composable
+    private fun Card(
+        modifier: Modifier = Modifier,
+    ) {
+        AndroidView(
+            factory = { context ->
+                FrameLayout(context).apply {
+                    addView(
+                        lockscreenSmartspaceController.buildAndConnectView(this).apply {
+                            layoutParams =
+                                FrameLayout.LayoutParams(
+                                    FrameLayout.LayoutParams.MATCH_PARENT,
+                                    FrameLayout.LayoutParams.WRAP_CONTENT,
+                                )
+
+                            keyguardUnlockAnimationController.lockscreenSmartspace = this
+                        }
+                    )
+                }
+            },
+            onRelease = { keyguardUnlockAnimationController.lockscreenSmartspace = null },
+            modifier = modifier,
+        )
+    }
+
+    @Composable
+    private fun Weather(
+        modifier: Modifier = Modifier,
+    ) {
+        val isVisible by keyguardSmartspaceViewModel.isWeatherVisible.collectAsState()
+        if (!isVisible) {
+            return
+        }
+
+        AndroidView(
+            factory = { context ->
+                FrameLayout(context).apply {
+                    addView(
+                        lockscreenSmartspaceController.buildAndConnectWeatherView(this).apply {
+                            layoutParams =
+                                FrameLayout.LayoutParams(
+                                    FrameLayout.LayoutParams.WRAP_CONTENT,
+                                    FrameLayout.LayoutParams.WRAP_CONTENT,
+                                )
+                        }
+                    )
+                }
+            },
+            modifier = modifier,
+        )
+    }
+
+    @Composable
+    private fun Date(
+        modifier: Modifier = Modifier,
+    ) {
+        val isVisible by keyguardSmartspaceViewModel.isDateVisible.collectAsState()
+        if (!isVisible) {
+            return
+        }
+
+        AndroidView(
+            factory = { context ->
+                FrameLayout(context).apply {
+                    addView(
+                        lockscreenSmartspaceController.buildAndConnectDateView(this).apply {
+                            layoutParams =
+                                FrameLayout.LayoutParams(
+                                    FrameLayout.LayoutParams.WRAP_CONTENT,
+                                    FrameLayout.LayoutParams.WRAP_CONTENT,
+                                )
+                        }
+                    )
+                }
+            },
+            modifier = modifier,
+        )
+    }
 }
 
 private val SmartSpaceElementKey = ElementKey("SmartSpace")
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/StatusBarSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/StatusBarSection.kt
index 6811eb4..ddc12ff 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/StatusBarSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/StatusBarSection.kt
@@ -21,9 +21,11 @@
 import android.view.View
 import android.view.ViewGroup
 import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.unit.dp
 import androidx.compose.ui.viewinterop.AndroidView
 import com.android.compose.animation.scene.ElementKey
 import com.android.compose.animation.scene.SceneScope
@@ -51,37 +53,43 @@
             key = StatusBarElementKey,
             modifier = modifier,
         ) {
-            AndroidView(
-                factory = {
-                    notificationPanelView.get().findViewById<View>(R.id.keyguard_header)?.let {
-                        (it.parent as ViewGroup).removeView(it)
-                    }
-
-                    val provider =
-                        object : ShadeViewStateProvider {
-                            override val lockscreenShadeDragProgress: Float = 0f
-                            override val panelViewExpandedHeight: Float = 0f
-                            override fun shouldHeadsUpBeVisible(): Boolean {
-                                return false
-                            }
+            content {
+                AndroidView(
+                    factory = {
+                        notificationPanelView.get().findViewById<View>(R.id.keyguard_header)?.let {
+                            (it.parent as ViewGroup).removeView(it)
                         }
 
-                    @SuppressLint("InflateParams")
-                    val view =
-                        LayoutInflater.from(context)
-                            .inflate(
-                                R.layout.keyguard_status_bar,
-                                null,
-                                false,
-                            ) as KeyguardStatusBarView
-                    componentFactory.build(view, provider).keyguardStatusBarViewController.init()
-                    view
-                },
-                modifier =
-                    Modifier.fillMaxWidth().height {
-                        Utils.getStatusBarHeaderHeightKeyguard(context)
+                        val provider =
+                            object : ShadeViewStateProvider {
+                                override val lockscreenShadeDragProgress: Float = 0f
+                                override val panelViewExpandedHeight: Float = 0f
+
+                                override fun shouldHeadsUpBeVisible(): Boolean {
+                                    return false
+                                }
+                            }
+
+                        @SuppressLint("InflateParams")
+                        val view =
+                            LayoutInflater.from(context)
+                                .inflate(
+                                    R.layout.keyguard_status_bar,
+                                    null,
+                                    false,
+                                ) as KeyguardStatusBarView
+                        componentFactory
+                            .build(view, provider)
+                            .keyguardStatusBarViewController
+                            .init()
+                        view
                     },
-            )
+                    modifier =
+                        Modifier.fillMaxWidth().padding(horizontal = 16.dp).height {
+                            Utils.getStatusBarHeaderHeightKeyguard(context)
+                        },
+                )
+            }
         }
     }
 }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
index 12f1b30..0eec024 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
@@ -44,7 +44,7 @@
 import com.android.compose.animation.scene.ElementKey
 import com.android.compose.animation.scene.SceneScope
 import com.android.compose.animation.scene.ValueKey
-import com.android.compose.animation.scene.animateSharedFloatAsState
+import com.android.compose.animation.scene.animateElementFloatAsState
 import com.android.systemui.notifications.ui.composable.Notifications.Form
 import com.android.systemui.notifications.ui.composable.Notifications.SharedValues.SharedExpansionValue
 import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationsPlaceholderViewModel
@@ -157,10 +157,10 @@
     modifier: Modifier = Modifier,
 ) {
     val elementKey = Notifications.Elements.NotificationPlaceholder
-    Box(
+    Element(
+        elementKey,
         modifier =
             modifier
-                .element(elementKey)
                 .debugBackground(viewModel)
                 .onSizeChanged { size: IntSize ->
                     debugLog(viewModel) { "STACK onSizeChanged: size=$size" }
@@ -182,19 +182,23 @@
                 }
     ) {
         val animatedExpansion by
-            animateSharedFloatAsState(
+            animateElementFloatAsState(
                 value = if (form == Form.HunFromTop) 0f else 1f,
-                key = SharedExpansionValue,
-                element = elementKey
+                key = SharedExpansionValue
             )
         debugLog(viewModel) { "STACK composed: expansion=$animatedExpansion" }
-        if (viewModel.isPlaceholderTextVisible) {
-            Text(
-                text = "Notifications",
-                style = MaterialTheme.typography.titleLarge,
-                color = MaterialTheme.colorScheme.onSurface,
-                modifier = Modifier.align(Alignment.Center),
-            )
+
+        content {
+            if (viewModel.isPlaceholderTextVisible) {
+                Box(Modifier.fillMaxSize()) {
+                    Text(
+                        text = "Notifications",
+                        style = MaterialTheme.typography.titleLarge,
+                        color = MaterialTheme.colorScheme.onSurface,
+                        modifier = Modifier.align(Alignment.Center),
+                    )
+                }
+            }
         }
     }
 }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt
index f3cde53..9778e53 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt
@@ -40,6 +40,7 @@
 import com.android.systemui.qs.ui.adapter.QSSceneAdapter.State.Expanding
 import com.android.systemui.res.R
 import com.android.systemui.scene.ui.composable.Gone
+import com.android.systemui.scene.ui.composable.Lockscreen
 import com.android.systemui.scene.ui.composable.QuickSettings as QuickSettingsSceneKey
 import com.android.systemui.scene.ui.composable.Shade
 
@@ -77,7 +78,12 @@
                     toScene == Shade -> QSSceneAdapter.State.QQS
                     toScene == QuickSettingsSceneKey -> QSSceneAdapter.State.QS
                     toScene == Gone -> QSSceneAdapter.State.CLOSED
-                    else -> error("Bad transition for QuickSettings: $transitionState")
+                    toScene == Lockscreen -> QSSceneAdapter.State.CLOSED
+                    else ->
+                        error(
+                            "Bad transition for QuickSettings: fromScene=$fromScene," +
+                                " toScene=$toScene"
+                        )
                 }
             }
     }
@@ -98,7 +104,7 @@
         key = QuickSettings.Elements.Content,
         modifier = modifier.fillMaxWidth().defaultMinSize(minHeight = 300.dp)
     ) {
-        QuickSettingsContent(qsSceneAdapter = qsSceneAdapter, contentState)
+        content { QuickSettingsContent(qsSceneAdapter = qsSceneAdapter, contentState) }
     }
 }
 
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
index 4eb9089..c35202c 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
@@ -25,7 +25,6 @@
 import androidx.compose.runtime.DisposableEffect
 import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
-import androidx.compose.runtime.remember
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.ExperimentalComposeUiApi
 import androidx.compose.ui.Modifier
@@ -38,11 +37,11 @@
 import com.android.compose.animation.scene.ObservableTransitionState as SceneTransitionObservableTransitionState
 import com.android.compose.animation.scene.SceneKey as SceneTransitionSceneKey
 import com.android.compose.animation.scene.SceneTransitionLayout
-import com.android.compose.animation.scene.SceneTransitionLayoutState
 import com.android.compose.animation.scene.Swipe
 import com.android.compose.animation.scene.SwipeDirection
 import com.android.compose.animation.scene.UserAction as SceneTransitionUserAction
 import com.android.compose.animation.scene.observableTransitionState
+import com.android.compose.animation.scene.updateSceneTransitionLayoutState
 import com.android.systemui.ribbon.ui.composable.BottomRightCornerRibbon
 import com.android.systemui.scene.shared.model.Direction
 import com.android.systemui.scene.shared.model.Edge
@@ -82,7 +81,12 @@
     val currentScene = checkNotNull(sceneByKey[currentSceneKey])
     val currentDestinations: Map<UserAction, SceneModel> by
         currentScene.destinationScenes.collectAsState()
-    val state = remember { SceneTransitionLayoutState(currentSceneKey.toTransitionSceneKey()) }
+    val state =
+        updateSceneTransitionLayoutState(
+            currentSceneKey.toTransitionSceneKey(),
+            onChangeScene = viewModel::onSceneChanged,
+            transitions = SceneContainerTransitions,
+        )
 
     DisposableEffect(viewModel, state) {
         viewModel.setTransitionState(state.observableTransitionState().map { it.toModel() })
@@ -93,9 +97,6 @@
         modifier = Modifier.fillMaxSize(),
     ) {
         SceneTransitionLayout(
-            currentScene = currentSceneKey.toTransitionSceneKey(),
-            onChangeScene = viewModel::onSceneChanged,
-            transitions = SceneContainerTransitions,
             state = state,
             modifier =
                 modifier
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
index 4bbb78b..e2beaee 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
@@ -50,7 +50,7 @@
 import com.android.compose.animation.scene.ElementKey
 import com.android.compose.animation.scene.SceneScope
 import com.android.compose.animation.scene.ValueKey
-import com.android.compose.animation.scene.animateSharedFloatAsState
+import com.android.compose.animation.scene.animateSceneFloatAsState
 import com.android.compose.windowsizeclass.LocalWindowSizeClass
 import com.android.settingslib.Utils
 import com.android.systemui.battery.BatteryMeterView
@@ -69,7 +69,6 @@
 
 object ShadeHeader {
     object Elements {
-        val FormatPlaceholder = ElementKey("ShadeHeaderFormatPlaceholder")
         val ExpandedContent = ElementKey("ShadeHeaderExpandedContent")
         val CollapsedContent = ElementKey("ShadeHeaderCollapsedContent")
     }
@@ -92,19 +91,19 @@
     statusBarIconController: StatusBarIconController,
     modifier: Modifier = Modifier,
 ) {
-    // TODO(b/298153892): Remove this once animateSharedFloatAsState.element can be null.
-    Spacer(Modifier.element(ShadeHeader.Elements.FormatPlaceholder))
     val formatProgress =
-        animateSharedFloatAsState(
-            0.0f,
-            ShadeHeader.Keys.transitionProgress,
-            ShadeHeader.Elements.FormatPlaceholder
-        )
+        animateSceneFloatAsState(0f, ShadeHeader.Keys.transitionProgress)
+            .unsafeCompositionState(initialValue = 0f)
 
     val cutoutWidth = LocalDisplayCutout.current.width()
     val cutoutLocation = LocalDisplayCutout.current.location
 
-    val useExpandedFormat = formatProgress.value > 0.5f || cutoutLocation != CutoutLocation.CENTER
+    val useExpandedFormat by
+        remember(formatProgress) {
+            derivedStateOf {
+                cutoutLocation != CutoutLocation.CENTER || formatProgress.value > 0.5f
+            }
+        }
 
     // This layout assumes it is globally positioned at (0, 0) and is the
     // same size as the screen.
@@ -217,14 +216,9 @@
     statusBarIconController: StatusBarIconController,
     modifier: Modifier = Modifier,
 ) {
-    // TODO(b/298153892): Remove this once animateSharedFloatAsState.element can be null.
-    Spacer(Modifier.element(ShadeHeader.Elements.FormatPlaceholder))
     val formatProgress =
-        animateSharedFloatAsState(
-            1.0f,
-            ShadeHeader.Keys.transitionProgress,
-            ShadeHeader.Elements.FormatPlaceholder
-        )
+        animateSceneFloatAsState(1f, ShadeHeader.Keys.transitionProgress)
+            .unsafeCompositionState(initialValue = 1f)
     val useExpandedFormat by
         remember(formatProgress) { derivedStateOf { formatProgress.value > 0.5f } }
 
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt
index 2944bd9..b26194f 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt
@@ -17,10 +17,15 @@
 package com.android.compose.animation.scene
 
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.DisposableEffect
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.SideEffect
+import androidx.compose.runtime.Stable
 import androidx.compose.runtime.State
-import androidx.compose.runtime.derivedStateOf
+import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
-import androidx.compose.runtime.snapshots.Snapshot
+import androidx.compose.runtime.snapshotFlow
+import androidx.compose.runtime.snapshots.SnapshotStateMap
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.lerp
 import androidx.compose.ui.unit.Dp
@@ -28,180 +33,263 @@
 import com.android.compose.ui.util.lerp
 
 /**
- * Animate a shared Int value.
+ * A [State] whose [value] is animated.
  *
- * @see SceneScope.animateSharedValueAsState
+ * Important: This animated value should always be ready *after* composition, e.g. during layout,
+ * drawing or inside a LaunchedEffect. If you read [value] during composition, it will probably
+ * throw an exception, for 2 important reasons:
+ * 1. You should never read animated values during composition, because this will probably lead to
+ *    bad performance.
+ * 2. Given that this value depends on the target value in different scenes, its current value
+ *    (depending on the current transition state) can only be computed once the full tree has been
+ *    composed.
+ *
+ * If you don't have the choice and *have to* get the value during composition, for instance because
+ * a Modifier or Composable reading this value does not have a lazy/lambda-based API, then you can
+ * access [unsafeCompositionState] and use a fallback value for the first frame where this animated
+ * value can not be computed yet. Note however that doing so will be bad for performance and might
+ * lead to late-by-one-frame flickers.
+ */
+@Stable
+interface AnimatedState<T> : State<T> {
+    /**
+     * Return a [State] that can be read during composition.
+     *
+     * Important: You should avoid using this as much as possible and instead read [value] during
+     * layout/drawing, otherwise you will probably end up with a few frames that have a value that
+     * is not correctly interpolated.
+     */
+    @Composable fun unsafeCompositionState(initialValue: T): State<T>
+}
+
+/**
+ * Animate a scene Int value.
+ *
+ * @see SceneScope.animateSceneValueAsState
  */
 @Composable
-fun SceneScope.animateSharedIntAsState(
+fun SceneScope.animateSceneIntAsState(
     value: Int,
     key: ValueKey,
-    element: ElementKey?,
     canOverflow: Boolean = true,
-): State<Int> {
-    return animateSharedValueAsState(value, key, element, ::lerp, canOverflow)
+): AnimatedState<Int> {
+    return animateSceneValueAsState(value, key, ::lerp, canOverflow)
 }
 
 /**
- * Animate a shared Int value.
+ * Animate a shared element Int value.
  *
- * @see MovableElementScope.animateSharedValueAsState
+ * @see ElementScope.animateElementValueAsState
  */
 @Composable
-fun MovableElementScope.animateSharedIntAsState(
+fun ElementScope<*>.animateElementIntAsState(
     value: Int,
-    debugName: String,
+    key: ValueKey,
     canOverflow: Boolean = true,
-): State<Int> {
-    return animateSharedValueAsState(value, debugName, ::lerp, canOverflow)
+): AnimatedState<Int> {
+    return animateElementValueAsState(value, key, ::lerp, canOverflow)
 }
 
 /**
- * Animate a shared Float value.
+ * Animate a scene Float value.
  *
- * @see SceneScope.animateSharedValueAsState
+ * @see SceneScope.animateSceneValueAsState
  */
 @Composable
-fun SceneScope.animateSharedFloatAsState(
+fun SceneScope.animateSceneFloatAsState(
     value: Float,
     key: ValueKey,
-    element: ElementKey?,
     canOverflow: Boolean = true,
-): State<Float> {
-    return animateSharedValueAsState(value, key, element, ::lerp, canOverflow)
+): AnimatedState<Float> {
+    return animateSceneValueAsState(value, key, ::lerp, canOverflow)
 }
 
 /**
- * Animate a shared Float value.
+ * Animate a shared element Float value.
  *
- * @see MovableElementScope.animateSharedValueAsState
+ * @see ElementScope.animateElementValueAsState
  */
 @Composable
-fun MovableElementScope.animateSharedFloatAsState(
+fun ElementScope<*>.animateElementFloatAsState(
     value: Float,
-    debugName: String,
+    key: ValueKey,
     canOverflow: Boolean = true,
-): State<Float> {
-    return animateSharedValueAsState(value, debugName, ::lerp, canOverflow)
+): AnimatedState<Float> {
+    return animateElementValueAsState(value, key, ::lerp, canOverflow)
 }
 
 /**
- * Animate a shared Dp value.
+ * Animate a scene Dp value.
  *
- * @see SceneScope.animateSharedValueAsState
+ * @see SceneScope.animateSceneValueAsState
  */
 @Composable
-fun SceneScope.animateSharedDpAsState(
+fun SceneScope.animateSceneDpAsState(
     value: Dp,
     key: ValueKey,
-    element: ElementKey?,
     canOverflow: Boolean = true,
-): State<Dp> {
-    return animateSharedValueAsState(value, key, element, ::lerp, canOverflow)
+): AnimatedState<Dp> {
+    return animateSceneValueAsState(value, key, ::lerp, canOverflow)
 }
 
 /**
- * Animate a shared Dp value.
+ * Animate a shared element Dp value.
  *
- * @see MovableElementScope.animateSharedValueAsState
+ * @see ElementScope.animateElementValueAsState
  */
 @Composable
-fun MovableElementScope.animateSharedDpAsState(
+fun ElementScope<*>.animateElementDpAsState(
     value: Dp,
-    debugName: String,
+    key: ValueKey,
     canOverflow: Boolean = true,
-): State<Dp> {
-    return animateSharedValueAsState(value, debugName, ::lerp, canOverflow)
+): AnimatedState<Dp> {
+    return animateElementValueAsState(value, key, ::lerp, canOverflow)
 }
 
 /**
- * Animate a shared Color value.
+ * Animate a scene Color value.
  *
- * @see SceneScope.animateSharedValueAsState
+ * @see SceneScope.animateSceneValueAsState
  */
 @Composable
-fun SceneScope.animateSharedColorAsState(
+fun SceneScope.animateSceneColorAsState(
     value: Color,
     key: ValueKey,
-    element: ElementKey?,
-): State<Color> {
-    return animateSharedValueAsState(value, key, element, ::lerp, canOverflow = false)
+): AnimatedState<Color> {
+    return animateSceneValueAsState(value, key, ::lerp, canOverflow = false)
 }
 
 /**
- * Animate a shared Color value.
+ * Animate a shared element Color value.
  *
- * @see MovableElementScope.animateSharedValueAsState
+ * @see ElementScope.animateElementValueAsState
  */
 @Composable
-fun MovableElementScope.animateSharedColorAsState(
+fun ElementScope<*>.animateElementColorAsState(
     value: Color,
-    debugName: String,
-): State<Color> {
-    return animateSharedValueAsState(value, debugName, ::lerp, canOverflow = false)
+    key: ValueKey,
+): AnimatedState<Color> {
+    return animateElementValueAsState(value, key, ::lerp, canOverflow = false)
 }
 
 @Composable
 internal fun <T> animateSharedValueAsState(
     layoutImpl: SceneTransitionLayoutImpl,
-    scene: Scene,
-    element: Element?,
+    scene: SceneKey,
+    element: ElementKey?,
     key: ValueKey,
     value: T,
     lerp: (T, T, Float) -> T,
     canOverflow: Boolean,
-): State<T> {
-    val sharedValue =
-        Snapshot.withoutReadObservation {
-            val sharedValues =
-                element?.sceneValues?.getValue(scene.key)?.sharedValues ?: scene.sharedValues
-            sharedValues.getOrPut(key) { Element.SharedValue(key, value) } as Element.SharedValue<T>
-        }
+): AnimatedState<T> {
+    DisposableEffect(layoutImpl, scene, element, key) {
+        // Create the associated maps that hold the current value for each (element, scene) pair.
+        val valueMap = layoutImpl.sharedValues.getOrPut(key) { mutableMapOf() }
+        val sceneToValueMap =
+            valueMap.getOrPut(element) { SnapshotStateMap<SceneKey, Any>() }
+                as SnapshotStateMap<SceneKey, T>
+        sceneToValueMap[scene] = value
 
-    if (value != sharedValue.value) {
-        sharedValue.value = value
+        onDispose {
+            // Remove the value associated to the current scene, and eventually remove the maps if
+            // they are empty.
+            sceneToValueMap.remove(scene)
+
+            if (sceneToValueMap.isEmpty() && valueMap[element] === sceneToValueMap) {
+                valueMap.remove(element)
+
+                if (valueMap.isEmpty() && layoutImpl.sharedValues[key] === valueMap) {
+                    layoutImpl.sharedValues.remove(key)
+                }
+            }
+        }
     }
 
-    return remember(layoutImpl, element, sharedValue, lerp, canOverflow) {
-        derivedStateOf { computeValue(layoutImpl, element, sharedValue, lerp, canOverflow) }
+    // Update the current value. Note that side effects run after disposable effects, so we know
+    // that the associated maps were created at this point.
+    SideEffect { sceneToValueMap<T>(layoutImpl, key, element)[scene] = value }
+
+    return remember(layoutImpl, scene, element, lerp, canOverflow) {
+        object : AnimatedState<T> {
+            override val value: T
+                get() = value(layoutImpl, scene, element, key, lerp, canOverflow)
+
+            @Composable
+            override fun unsafeCompositionState(initialValue: T): State<T> {
+                val state = remember { mutableStateOf(initialValue) }
+
+                val animatedState = this
+                LaunchedEffect(animatedState) {
+                    snapshotFlow { animatedState.value }.collect { state.value = it }
+                }
+
+                return state
+            }
+        }
     }
 }
 
-private fun <T> computeValue(
+private fun <T> sceneToValueMap(
     layoutImpl: SceneTransitionLayoutImpl,
-    element: Element?,
-    sharedValue: Element.SharedValue<T>,
+    key: ValueKey,
+    element: ElementKey?
+): MutableMap<SceneKey, T> {
+    return layoutImpl.sharedValues[key]?.get(element)?.let { it as SnapshotStateMap<SceneKey, T> }
+        ?: error(valueReadTooEarlyMessage(key))
+}
+
+private fun valueReadTooEarlyMessage(key: ValueKey) =
+    "Animated value $key was read before its target values were set. This probably " +
+        "means that you are reading it during composition, which you should not do. See the " +
+        "documentation of AnimatedState for more information."
+
+private fun <T> value(
+    layoutImpl: SceneTransitionLayoutImpl,
+    scene: SceneKey,
+    element: ElementKey?,
+    key: ValueKey,
     lerp: (T, T, Float) -> T,
     canOverflow: Boolean,
 ): T {
-    val transition = layoutImpl.state.currentTransition
-    if (transition == null || !layoutImpl.isTransitionReady(transition)) {
-        return sharedValue.value
-    }
+    return valueOrNull(layoutImpl, scene, element, key, lerp, canOverflow)
+        ?: error(valueReadTooEarlyMessage(key))
+}
 
-    fun sceneValue(scene: SceneKey): Element.SharedValue<T>? {
-        val sharedValues =
-            if (element == null) {
-                layoutImpl.scene(scene).sharedValues
-            } else {
-                element.sceneValues[scene]?.sharedValues
-            }
-                ?: return null
-        val value = sharedValues[sharedValue.key] ?: return null
-        return value as Element.SharedValue<T>
-    }
+private fun <T> valueOrNull(
+    layoutImpl: SceneTransitionLayoutImpl,
+    scene: SceneKey,
+    element: ElementKey?,
+    key: ValueKey,
+    lerp: (T, T, Float) -> T,
+    canOverflow: Boolean,
+): T? {
+    val sceneToValueMap = sceneToValueMap<T>(layoutImpl, key, element)
+    fun sceneValue(scene: SceneKey): T? = sceneToValueMap[scene]
 
-    val fromValue = sceneValue(transition.fromScene)
-    val toValue = sceneValue(transition.toScene)
-    return if (fromValue != null && toValue != null) {
-        val progress =
-            if (canOverflow) transition.progress else transition.progress.coerceIn(0f, 1f)
-        lerp(fromValue.value, toValue.value, progress)
-    } else if (fromValue != null) {
-        fromValue.value
-    } else if (toValue != null) {
-        toValue.value
-    } else {
-        sharedValue.value
+    return when (val transition = layoutImpl.state.transitionState) {
+        is TransitionState.Idle -> sceneValue(transition.currentScene)
+        is TransitionState.Transition -> {
+            // Note: no need to check for transition ready here given that all target values are
+            // defined during composition, we should already have the correct values to interpolate
+            // between here.
+            val fromValue = sceneValue(transition.fromScene)
+            val toValue = sceneValue(transition.toScene)
+            if (fromValue != null && toValue != null) {
+                if (fromValue == toValue) {
+                    // Optimization: avoid reading progress if the values are the same, so we don't
+                    // relayout/redraw for nothing.
+                    fromValue
+                } else {
+                    val progress =
+                        if (canOverflow) transition.progress
+                        else transition.progress.coerceIn(0f, 1f)
+                    lerp(fromValue, toValue, progress)
+                }
+            } else fromValue ?: toValue
+        }
     }
+    // TODO(b/311600838): Remove this. We should not have to fallback to the current scene value,
+    // but we have to because code of removed nodes can still run if they are placed with a graphics
+    // layer.
+    ?: sceneValue(scene)
 }
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateToScene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateToScene.kt
index ba6d00e..7d3b0fb 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateToScene.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateToScene.kt
@@ -28,9 +28,9 @@
  * the currently running transition, if there is one.
  */
 internal fun CoroutineScope.animateToScene(
-    layoutState: SceneTransitionLayoutStateImpl,
+    layoutState: BaseSceneTransitionLayoutState,
     target: SceneKey,
-) {
+): TransitionState.Transition? {
     val transitionState = layoutState.transitionState
     if (transitionState.currentScene == target) {
         // This can happen in 3 different situations, for which there isn't anything else to do:
@@ -41,10 +41,10 @@
         //     a. didn't release their pointer yet.
         //     b. released their pointer such that the swipe gesture was cancelled and the
         //        transition is currently animating back to [target].
-        return
+        return null
     }
 
-    when (transitionState) {
+    return when (transitionState) {
         is TransitionState.Idle -> animate(layoutState, target)
         is TransitionState.Transition -> {
             // A transition is currently running: first check whether `transition.toScene` or
@@ -62,47 +62,43 @@
                     // finish the current transition early to make sure that the current state
                     // change is committed.
                     layoutState.finishTransition(transitionState, transitionState.currentScene)
+                    null
                 } else {
                     // The transition is in progress: start the canned animation at the same
                     // progress as it was in.
                     // TODO(b/290184746): Also take the current velocity into account.
                     animate(layoutState, target, startProgress = progress)
                 }
-
-                return
-            }
-
-            if (transitionState.fromScene == target) {
+            } else if (transitionState.fromScene == target) {
                 // There is a transition from [target] to another scene: simply animate the same
                 // transition progress to `0`.
-
                 check(transitionState.toScene == transitionState.currentScene)
+
                 val progress = transitionState.progress
                 if (progress.absoluteValue < ProgressVisibilityThreshold) {
                     // The transition is at progress ~= 0: no need to animate.We finish the current
                     // transition early to make sure that the current state change is committed.
                     layoutState.finishTransition(transitionState, transitionState.currentScene)
+                    null
                 } else {
                     // TODO(b/290184746): Also take the current velocity into account.
                     animate(layoutState, target, startProgress = progress, reversed = true)
                 }
-
-                return
+            } else {
+                // Generic interruption; the current transition is neither from or to [target].
+                // TODO(b/290930950): Better handle interruptions here.
+                animate(layoutState, target)
             }
-
-            // Generic interruption; the current transition is neither from or to [target].
-            // TODO(b/290930950): Better handle interruptions here.
-            animate(layoutState, target)
         }
     }
 }
 
 private fun CoroutineScope.animate(
-    layoutState: SceneTransitionLayoutStateImpl,
+    layoutState: BaseSceneTransitionLayoutState,
     target: SceneKey,
     startProgress: Float = 0f,
     reversed: Boolean = false,
-) {
+): TransitionState.Transition {
     val fromScene = layoutState.transitionState.currentScene
     val isUserInput =
         (layoutState.transitionState as? TransitionState.Transition)?.isInitiatedByUserInput
@@ -143,10 +139,15 @@
         }
 
     // Animate the progress to its target value.
-    launch {
-        animatable.animateTo(targetProgress, animationSpec)
-        layoutState.finishTransition(transition, target)
-    }
+    launch { animatable.animateTo(targetProgress, animationSpec) }
+        .invokeOnCompletion {
+            // Settle the state to Idle(target). Note that this will do nothing if this transition
+            // was replaced/interrupted by another one, and this also runs if this coroutine is
+            // cancelled, i.e. if [this] coroutine scope is cancelled.
+            layoutState.finishTransition(transition, target)
+        }
+
+    return transition
 }
 
 private class OneOffTransition(
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
index a85d9bf..a910bca 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
@@ -16,19 +16,14 @@
 
 package com.android.compose.animation.scene
 
-import android.graphics.Picture
-import androidx.compose.runtime.Composable
 import androidx.compose.runtime.Stable
 import androidx.compose.runtime.getValue
-import androidx.compose.runtime.movableContentOf
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.setValue
-import androidx.compose.runtime.snapshots.Snapshot
 import androidx.compose.runtime.snapshots.SnapshotStateMap
 import androidx.compose.ui.ExperimentalComposeUiApi
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.geometry.Offset
-import androidx.compose.ui.geometry.isSpecified
 import androidx.compose.ui.geometry.isUnspecified
 import androidx.compose.ui.geometry.lerp
 import androidx.compose.ui.graphics.drawscope.ContentDrawScope
@@ -51,67 +46,21 @@
 /** An element on screen, that can be composed in one or more scenes. */
 @Stable
 internal class Element(val key: ElementKey) {
-    /**
-     * The last values of this element, coming from any scene. Note that this value will be unstable
-     * if this element is present in multiple scenes but the shared element animation is disabled,
-     * given that multiple instances of the element with different states will write to these
-     * values. You should prefer using [TargetValues.lastValues] in the current scene if it is
-     * defined.
-     */
-    val lastSharedValues = Values()
-
-    /** The mapping between a scene and the values/state this element has in that scene, if any. */
-    val sceneValues = SnapshotStateMap<SceneKey, TargetValues>()
-
-    /**
-     * The movable content of this element, if this element is composed using
-     * [SceneScope.MovableElement].
-     */
-    private var _movableContent: (@Composable (@Composable () -> Unit) -> Unit)? = null
-    val movableContent: @Composable (@Composable () -> Unit) -> Unit
-        get() =
-            _movableContent
-                ?: movableContentOf { content: @Composable () -> Unit -> content() }
-                    .also { _movableContent = it }
-
-    /**
-     * The [Picture] to which we save the last drawing commands of this element, if it is movable.
-     * This is necessary because the content of this element might not be composed in the scene it
-     * should currently be drawn.
-     */
-    private var _picture: Picture? = null
-    val picture: Picture
-        get() = _picture ?: Picture().also { _picture = it }
+    /** The mapping between a scene and the state this element has in that scene, if any. */
+    // TODO(b/316901148): Make this a normal map instead once we can make sure that new transitions
+    // are first seen by composition then layout/drawing code. See 316901148#comment2 for details.
+    val sceneStates = SnapshotStateMap<SceneKey, SceneState>()
 
     override fun toString(): String {
         return "Element(key=$key)"
     }
 
-    /** The current values of this element, either in a specific scene or in a shared context. */
-    class Values {
-        /** The offset of the element, relative to the SceneTransitionLayout containing it. */
-        var offset = Offset.Unspecified
-
-        /** The size of this element. */
-        var size = SizeUnspecified
-
-        /** The draw scale of this element. */
-        var drawScale = Scale.Default
-
-        /** The alpha of this element. */
-        var alpha = AlphaUnspecified
-    }
-
-    /** The target values of this element in a given scene. */
+    /** The last and target state of this element in a given scene. */
     @Stable
-    class TargetValues(val scene: SceneKey) {
-        val lastValues = Values()
-
+    class SceneState(val scene: SceneKey) {
         var targetSize by mutableStateOf(SizeUnspecified)
         var targetOffset by mutableStateOf(Offset.Unspecified)
 
-        val sharedValues = SnapshotStateMap<ValueKey, SharedValue<*>>()
-
         /**
          * The attached [ElementNode] a Modifier.element() for a given element and scene. During
          * composition, this set could have 0 to 2 elements. After composition and after all
@@ -120,15 +69,8 @@
         val nodes = mutableSetOf<ElementNode>()
     }
 
-    /** A shared value of this element. */
-    @Stable
-    class SharedValue<T>(val key: ValueKey, initialValue: T) {
-        var value by mutableStateOf(initialValue)
-    }
-
     companion object {
         val SizeUnspecified = IntSize(Int.MAX_VALUE, Int.MAX_VALUE)
-        val AlphaUnspecified = Float.MIN_VALUE
     }
 }
 
@@ -147,27 +89,18 @@
     scene: Scene,
     key: ElementKey,
 ): Modifier {
-    val element: Element
-    val sceneValues: Element.TargetValues
-
-    // Get the element associated to [key] if it was already composed in another scene,
-    // otherwise create it and add it to our Map<ElementKey, Element>. This is done inside a
-    // withoutReadObservation() because there is no need to recompose when that map is mutated.
-    Snapshot.withoutReadObservation {
-        element = layoutImpl.elements[key] ?: Element(key).also { layoutImpl.elements[key] = it }
-        sceneValues =
-            element.sceneValues[scene.key]
-                ?: Element.TargetValues(scene.key).also { element.sceneValues[scene.key] = it }
-    }
-
-    return this.then(ElementModifier(layoutImpl, scene, element, sceneValues))
+    return this.then(ElementModifier(layoutImpl, scene, key))
         // TODO(b/311132415): Move this into ElementNode once we can create a delegate
         // IntermediateLayoutModifierNode.
         .intermediateLayout { measurable, constraints ->
-            val placeable =
-                measure(layoutImpl, scene, element, sceneValues, measurable, constraints)
+            // TODO(b/311132415): No need to fetch the element and sceneState from the map anymore
+            // once this is merged into ElementNode.
+            val element = layoutImpl.elements.getValue(key)
+            val sceneState = element.sceneStates.getValue(scene.key)
+
+            val placeable = measure(layoutImpl, scene, element, sceneState, measurable, constraints)
             layout(placeable.width, placeable.height) {
-                place(layoutImpl, scene, element, sceneValues, placeable, placementScope = this)
+                place(layoutImpl, scene, element, sceneState, placeable, placementScope = this)
             }
         }
         .testTag(key.testTag)
@@ -180,72 +113,89 @@
 private data class ElementModifier(
     private val layoutImpl: SceneTransitionLayoutImpl,
     private val scene: Scene,
-    private val element: Element,
-    private val sceneValues: Element.TargetValues,
+    private val key: ElementKey,
 ) : ModifierNodeElement<ElementNode>() {
-    override fun create(): ElementNode = ElementNode(layoutImpl, scene, element, sceneValues)
+    override fun create(): ElementNode = ElementNode(layoutImpl, scene, key)
 
     override fun update(node: ElementNode) {
-        node.update(layoutImpl, scene, element, sceneValues)
+        node.update(layoutImpl, scene, key)
     }
 }
 
 internal class ElementNode(
     private var layoutImpl: SceneTransitionLayoutImpl,
     private var scene: Scene,
-    private var element: Element,
-    private var sceneValues: Element.TargetValues,
+    private var key: ElementKey,
 ) : Modifier.Node(), DrawModifierNode {
+    private var _element: Element? = null
+    private val element: Element
+        get() = _element!!
+
+    private var _sceneState: Element.SceneState? = null
+    private val sceneState: Element.SceneState
+        get() = _sceneState!!
 
     override fun onAttach() {
         super.onAttach()
-        addNodeToSceneValues()
+        updateElementAndSceneValues()
+        addNodeToSceneState()
     }
 
-    private fun addNodeToSceneValues() {
-        sceneValues.nodes.add(this)
+    private fun updateElementAndSceneValues() {
+        val element =
+            layoutImpl.elements[key] ?: Element(key).also { layoutImpl.elements[key] = it }
+        _element = element
+        _sceneState =
+            element.sceneStates[scene.key]
+                ?: Element.SceneState(scene.key).also { element.sceneStates[scene.key] = it }
+    }
+
+    private fun addNodeToSceneState() {
+        sceneState.nodes.add(this)
 
         coroutineScope.launch {
             // At this point all [CodeLocationNode] have been attached or detached, which means that
-            // [sceneValues.codeLocations] should have exactly 1 element, otherwise this means that
+            // [sceneState.codeLocations] should have exactly 1 element, otherwise this means that
             // this element was composed multiple times in the same scene.
-            val nCodeLocations = sceneValues.nodes.size
-            if (nCodeLocations != 1 || !sceneValues.nodes.contains(this@ElementNode)) {
-                error("${element.key} was composed $nCodeLocations times in ${sceneValues.scene}")
+            val nCodeLocations = sceneState.nodes.size
+            if (nCodeLocations != 1 || !sceneState.nodes.contains(this@ElementNode)) {
+                error("$key was composed $nCodeLocations times in ${sceneState.scene}")
             }
         }
     }
 
     override fun onDetach() {
         super.onDetach()
-        removeNodeFromSceneValues()
-        maybePruneMaps(layoutImpl, element, sceneValues)
+        removeNodeFromSceneState()
+        maybePruneMaps(layoutImpl, element, sceneState)
+
+        _element = null
+        _sceneState = null
     }
 
-    private fun removeNodeFromSceneValues() {
-        sceneValues.nodes.remove(this)
+    private fun removeNodeFromSceneState() {
+        sceneState.nodes.remove(this)
     }
 
     fun update(
         layoutImpl: SceneTransitionLayoutImpl,
         scene: Scene,
-        element: Element,
-        sceneValues: Element.TargetValues,
+        key: ElementKey,
     ) {
         check(layoutImpl == this.layoutImpl && scene == this.scene)
-        removeNodeFromSceneValues()
+        removeNodeFromSceneState()
 
         val prevElement = this.element
-        val prevSceneValues = this.sceneValues
-        this.element = element
-        this.sceneValues = sceneValues
+        val prevSceneState = this.sceneState
+        this.key = key
+        updateElementAndSceneValues()
 
-        addNodeToSceneValues()
-        maybePruneMaps(layoutImpl, prevElement, prevSceneValues)
+        addNodeToSceneState()
+        maybePruneMaps(layoutImpl, prevElement, prevSceneState)
     }
 
     override fun ContentDrawScope.draw() {
-        val drawScale = getDrawScale(layoutImpl, element, scene, sceneValues)
+        val drawScale = getDrawScale(layoutImpl, element, scene)
         if (drawScale == Scale.Default) {
             drawContent()
         } else {
@@ -263,18 +213,16 @@
         private fun maybePruneMaps(
             layoutImpl: SceneTransitionLayoutImpl,
             element: Element,
-            sceneValues: Element.TargetValues,
+            sceneState: Element.SceneState,
         ) {
             // If element is not composed from this scene anymore, remove the scene values. This
             // works because [onAttach] is called before [onDetach], so if an element is moved from
             // the UI tree we will first add the new code location then remove the old one.
-            if (
-                sceneValues.nodes.isEmpty() && element.sceneValues[sceneValues.scene] == sceneValues
-            ) {
-                element.sceneValues.remove(sceneValues.scene)
+            if (sceneState.nodes.isEmpty() && element.sceneStates[sceneState.scene] == sceneState) {
+                element.sceneStates.remove(sceneState.scene)
 
                 // If the element is not composed in any scene, remove it from the elements map.
-                if (element.sceneValues.isEmpty() && layoutImpl.elements[element.key] == element) {
+                if (element.sceneStates.isEmpty() && layoutImpl.elements[element.key] == element) {
                     layoutImpl.elements.remove(element.key)
                 }
             }
@@ -292,9 +240,8 @@
     // Always draw the element if there is no ongoing transition or if the element is not shared.
     if (
         transition == null ||
-            !layoutImpl.isTransitionReady(transition) ||
-            transition.fromScene !in element.sceneValues ||
-            transition.toScene !in element.sceneValues
+            transition.fromScene !in element.sceneStates ||
+            transition.toScene !in element.sceneStates
     ) {
         return true
     }
@@ -310,7 +257,6 @@
         transition,
         scene.key,
         element.key,
-        sharedTransformation,
     )
 }
 
@@ -319,24 +265,21 @@
     transition: TransitionState.Transition,
     scene: SceneKey,
     element: ElementKey,
-    sharedTransformation: SharedElementTransformation?
 ): Boolean {
-    val scenePicker = sharedTransformation?.scenePicker ?: DefaultSharedElementScenePicker
+    val scenePicker = element.scenePicker
     val fromScene = transition.fromScene
     val toScene = transition.toScene
 
     return scenePicker.sceneDuringTransition(
         element = element,
-        fromScene = fromScene,
-        toScene = toScene,
-        progress = transition::progress,
+        transition = transition,
         fromSceneZIndex = layoutImpl.scenes.getValue(fromScene).zIndex,
         toSceneZIndex = layoutImpl.scenes.getValue(toScene).zIndex,
     ) == scene
 }
 
 private fun isSharedElementEnabled(
-    layoutState: SceneTransitionLayoutStateImpl,
+    layoutState: BaseSceneTransitionLayoutState,
     transition: TransitionState.Transition,
     element: ElementKey,
 ): Boolean {
@@ -344,7 +287,7 @@
 }
 
 internal fun sharedElementTransformation(
-    layoutState: SceneTransitionLayoutStateImpl,
+    layoutState: BaseSceneTransitionLayoutState,
     transition: TransitionState.Transition,
     element: ElementKey,
 ): SharedElementTransformation? {
@@ -374,28 +317,19 @@
     layoutImpl: SceneTransitionLayoutImpl,
     element: Element,
     scene: Scene,
-    sceneValues: Element.TargetValues,
 ): Boolean {
     val transition = layoutImpl.state.currentTransition ?: return true
 
-    if (!layoutImpl.isTransitionReady(transition)) {
-        val lastValue =
-            sceneValues.lastValues.alpha.takeIf { it != Element.AlphaUnspecified }
-                ?: element.lastSharedValues.alpha.takeIf { it != Element.AlphaUnspecified } ?: 1f
-
-        return lastValue == 1f
-    }
-
     val fromScene = transition.fromScene
     val toScene = transition.toScene
-    val fromValues = element.sceneValues[fromScene]
-    val toValues = element.sceneValues[toScene]
+    val fromState = element.sceneStates[fromScene]
+    val toState = element.sceneStates[toScene]
 
-    if (fromValues == null && toValues == null) {
+    if (fromState == null && toState == null) {
         error("This should not happen, element $element is neither in $fromScene or $toScene")
     }
 
-    val isSharedElement = fromValues != null && toValues != null
+    val isSharedElement = fromState != null && toState != null
     if (isSharedElement && isSharedElementEnabled(layoutImpl.state, transition, element.key)) {
         return true
     }
@@ -415,7 +349,6 @@
     layoutImpl: SceneTransitionLayoutImpl,
     element: Element,
     scene: Scene,
-    sceneValues: Element.TargetValues,
 ): Float {
     return computeValue(
             layoutImpl,
@@ -425,11 +358,7 @@
             transformation = { it.alpha },
             idleValue = 1f,
             currentValue = { 1f },
-            lastValue = {
-                sceneValues.lastValues.alpha.takeIf { it != Element.AlphaUnspecified }
-                    ?: element.lastSharedValues.alpha.takeIf { it != Element.AlphaUnspecified }
-                        ?: 1f
-            },
+            isSpecified = { true },
             ::lerp,
         )
         .coerceIn(0f, 1f)
@@ -440,15 +369,15 @@
     layoutImpl: SceneTransitionLayoutImpl,
     scene: Scene,
     element: Element,
-    sceneValues: Element.TargetValues,
+    sceneState: Element.SceneState,
     measurable: Measurable,
     constraints: Constraints,
 ): Placeable {
     // Update the size this element has in this scene when idle.
     val targetSizeInScene = lookaheadSize
-    if (targetSizeInScene != sceneValues.targetSize) {
+    if (targetSizeInScene != sceneState.targetSize) {
         // TODO(b/290930950): Better handle when this changes to avoid instant size jumps.
-        sceneValues.targetSize = targetSizeInScene
+        sceneState.targetSize = targetSizeInScene
     }
 
     // Some lambdas called (max once) by computeValue() will need to measure [measurable], in which
@@ -467,34 +396,23 @@
             transformation = { it.size },
             idleValue = lookaheadSize,
             currentValue = { measurable.measure(constraints).also { maybePlaceable = it }.size() },
-            lastValue = {
-                sceneValues.lastValues.size.takeIf { it != Element.SizeUnspecified }
-                    ?: element.lastSharedValues.size.takeIf { it != Element.SizeUnspecified }
-                        ?: measurable.measure(constraints).also { maybePlaceable = it }.size()
-            },
+            isSpecified = { it != Element.SizeUnspecified },
             ::lerp,
         )
 
-    val placeable =
-        maybePlaceable
-            ?: measurable.measure(
-                Constraints.fixed(
-                    targetSize.width.coerceAtLeast(0),
-                    targetSize.height.coerceAtLeast(0),
-                )
+    return maybePlaceable
+        ?: measurable.measure(
+            Constraints.fixed(
+                targetSize.width.coerceAtLeast(0),
+                targetSize.height.coerceAtLeast(0),
             )
-
-    val size = placeable.size()
-    element.lastSharedValues.size = size
-    sceneValues.lastValues.size = size
-    return placeable
+        )
 }
 
 private fun getDrawScale(
     layoutImpl: SceneTransitionLayoutImpl,
     element: Element,
-    scene: Scene,
-    sceneValues: Element.TargetValues
+    scene: Scene
 ): Scale {
     return computeValue(
         layoutImpl,
@@ -504,10 +422,7 @@
         transformation = { it.drawScale },
         idleValue = Scale.Default,
         currentValue = { Scale.Default },
-        lastValue = {
-            sceneValues.lastValues.drawScale.takeIf { it != Scale.Default }
-                ?: element.lastSharedValues.drawScale
-        },
+        isSpecified = { true },
         ::lerp,
     )
 }
@@ -517,7 +432,7 @@
     layoutImpl: SceneTransitionLayoutImpl,
     scene: Scene,
     element: Element,
-    sceneValues: Element.TargetValues,
+    sceneState: Element.SceneState,
     placeable: Placeable,
     placementScope: Placeable.PlacementScope,
 ) {
@@ -526,14 +441,17 @@
         // when idle.
         val coords = coordinates ?: error("Element ${element.key} does not have any coordinates")
         val targetOffsetInScene = lookaheadScopeCoordinates.localLookaheadPositionOf(coords)
-        if (targetOffsetInScene != sceneValues.targetOffset) {
+        if (targetOffsetInScene != sceneState.targetOffset) {
             // TODO(b/290930950): Better handle when this changes to avoid instant offset jumps.
-            sceneValues.targetOffset = targetOffsetInScene
+            sceneState.targetOffset = targetOffsetInScene
+        }
+
+        // No need to place the element in this scene if we don't want to draw it anyways.
+        if (!shouldDrawElement(layoutImpl, scene, element)) {
+            return
         }
 
         val currentOffset = lookaheadScopeCoordinates.localPositionOf(coords, Offset.Zero)
-        val lastSharedValues = element.lastSharedValues
-        val lastValues = sceneValues.lastValues
         val targetOffset =
             computeValue(
                 layoutImpl,
@@ -543,37 +461,19 @@
                 transformation = { it.offset },
                 idleValue = targetOffsetInScene,
                 currentValue = { currentOffset },
-                lastValue = {
-                    lastValues.offset.takeIf { it.isSpecified }
-                        ?: lastSharedValues.offset.takeIf { it.isSpecified } ?: currentOffset
-                },
+                isSpecified = { it != Offset.Unspecified },
                 ::lerp,
             )
 
-        lastSharedValues.offset = targetOffset
-        lastValues.offset = targetOffset
-
-        // No need to place the element in this scene if we don't want to draw it anyways. Note that
-        // it's still important to compute the target offset and update lastValues, otherwise it
-        // will be out of date.
-        if (!shouldDrawElement(layoutImpl, scene, element)) {
-            return
-        }
-
         val offset = (targetOffset - currentOffset).round()
-        if (isElementOpaque(layoutImpl, element, scene, sceneValues)) {
+        if (isElementOpaque(layoutImpl, element, scene)) {
             // TODO(b/291071158): Call placeWithLayer() if offset != IntOffset.Zero and size is not
             // animated once b/305195729 is fixed. Test that drawing is not invalidated in that
             // case.
             placeable.place(offset)
-            lastSharedValues.alpha = 1f
-            lastValues.alpha = 1f
         } else {
             placeable.placeWithLayer(offset) {
-                val alpha = elementAlpha(layoutImpl, element, scene, sceneValues)
-                this.alpha = alpha
-                lastSharedValues.alpha = alpha
-                lastValues.alpha = alpha
+                this.alpha = elementAlpha(layoutImpl, element, scene)
             }
         }
     }
@@ -596,8 +496,6 @@
  *   different than [idleValue] even if the value is not transformed directly because it could be
  *   impacted by the transformations on other elements, like a parent that is being translated or
  *   resized.
- * @param lastValue the last value that was used. This should be equal to [currentValue] if this is
- *   the first time the value is set.
  * @param lerp the linear interpolation function used to interpolate between two values of this
  *   value type.
  */
@@ -605,11 +503,11 @@
     layoutImpl: SceneTransitionLayoutImpl,
     scene: Scene,
     element: Element,
-    sceneValue: (Element.TargetValues) -> T,
+    sceneValue: (Element.SceneState) -> T,
     transformation: (ElementTransformations) -> PropertyTransformation<T>?,
     idleValue: T,
     currentValue: () -> T,
-    lastValue: () -> T,
+    isSpecified: (T) -> Boolean,
     lerp: (T, T, Float) -> T,
 ): T {
     val transition =
@@ -620,30 +518,30 @@
         // layout phase.
         ?: return currentValue()
 
-    // A transition was started but it's not ready yet (not all elements have been composed/laid
-    // out yet). Use the last value that was set, to make sure elements don't unexpectedly jump.
-    if (!layoutImpl.isTransitionReady(transition)) {
-        return lastValue()
-    }
-
     val fromScene = transition.fromScene
     val toScene = transition.toScene
-    val fromValues = element.sceneValues[fromScene]
-    val toValues = element.sceneValues[toScene]
 
-    if (fromValues == null && toValues == null) {
+    val fromState = element.sceneStates[fromScene]
+    val toState = element.sceneStates[toScene]
+
+    if (fromState == null && toState == null) {
         // TODO(b/311600838): Throw an exception instead once layers of disposed elements are not
         // run anymore.
-        return lastValue()
+        return idleValue
     }
 
     // The element is shared: interpolate between the value in fromScene and the value in toScene.
     // TODO(b/290184746): Support non linear shared paths as well as a way to make sure that shared
     // elements follow the finger direction.
-    val isSharedElement = fromValues != null && toValues != null
+    val isSharedElement = fromState != null && toState != null
     if (isSharedElement && isSharedElementEnabled(layoutImpl.state, transition, element.key)) {
-        val start = sceneValue(fromValues!!)
-        val end = sceneValue(toValues!!)
+        val start = sceneValue(fromState!!)
+        val end = sceneValue(toState!!)
+
+        // TODO(b/316901148): Remove checks to isSpecified() once the lookahead pass runs for all
+        // nodes before the intermediate layout pass.
+        if (!isSpecified(start)) return end
+        if (!isSpecified(end)) return start
 
         // Make sure we don't read progress if values are the same and we don't need to interpolate,
         // so we don't invalidate the phase where this is read.
@@ -659,12 +557,12 @@
 
     // Get the transformed value, i.e. the target value at the beginning (for entering elements) or
     // end (for leaving elements) of the transition.
-    val sceneValues =
+    val sceneState =
         checkNotNull(
             when {
-                isSharedElement && scene.key == fromScene -> fromValues
-                isSharedElement -> toValues
-                else -> fromValues ?: toValues
+                isSharedElement && scene.key == fromScene -> fromState
+                isSharedElement -> toState
+                else -> fromState ?: toState
             }
         )
 
@@ -673,7 +571,7 @@
             layoutImpl,
             scene,
             element,
-            sceneValues,
+            sceneState,
             transition,
             idleValue,
         )
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Key.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Key.kt
index 84d3b86..90f46bd 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Key.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Key.kt
@@ -64,10 +64,10 @@
     identity: Any = Object(),
 
     /**
-     * Whether this element is a background and usually drawn below other elements. This should be
-     * set to true to make sure that shared backgrounds are drawn below elements of other scenes.
+     * The [ElementScenePicker] to use when deciding in which scene we should draw shared Elements
+     * or compose MovableElements.
      */
-    val isBackground: Boolean = false,
+    val scenePicker: ElementScenePicker = DefaultElementScenePicker,
 ) : Key(name, identity), ElementMatcher {
     @VisibleForTesting
     // TODO(b/240432457): Make internal once PlatformComposeSceneTransitionLayoutTestsUtils can
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MovableElement.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MovableElement.kt
index 49df2f6..cdc4778 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MovableElement.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MovableElement.kt
@@ -16,27 +16,36 @@
 
 package com.android.compose.animation.scene
 
-import android.util.Log
 import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.BoxScope
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.State
 import androidx.compose.runtime.derivedStateOf
 import androidx.compose.runtime.getValue
+import androidx.compose.runtime.movableContentOf
 import androidx.compose.runtime.remember
-import androidx.compose.runtime.snapshots.Snapshot
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.draw.drawBehind
-import androidx.compose.ui.draw.drawWithCache
-import androidx.compose.ui.graphics.Canvas
-import androidx.compose.ui.graphics.drawscope.draw
-import androidx.compose.ui.graphics.drawscope.drawIntoCanvas
-import androidx.compose.ui.graphics.nativeCanvas
-import androidx.compose.ui.layout.layout
-import androidx.compose.ui.unit.Constraints
+import androidx.compose.ui.layout.Layout
 import androidx.compose.ui.unit.IntSize
 
-private const val TAG = "MovableElement"
+@Composable
+internal fun Element(
+    layoutImpl: SceneTransitionLayoutImpl,
+    scene: Scene,
+    key: ElementKey,
+    modifier: Modifier,
+    content: @Composable ElementScope<ElementContentScope>.() -> Unit,
+) {
+    Box(modifier.element(layoutImpl, scene, key)) {
+        val sceneScope = scene.scope
+        val boxScope = this
+        val elementScope =
+            remember(layoutImpl, key, scene, sceneScope, boxScope) {
+                ElementScopeImpl(layoutImpl, key, scene, sceneScope, boxScope)
+            }
+
+        content(elementScope)
+    }
+}
 
 @Composable
 internal fun MovableElement(
@@ -44,72 +53,113 @@
     scene: Scene,
     key: ElementKey,
     modifier: Modifier,
-    content: @Composable MovableElementScope.() -> Unit,
+    content: @Composable ElementScope<MovableElementContentScope>.() -> Unit,
 ) {
     Box(modifier.element(layoutImpl, scene, key)) {
-        // Get the Element from the map. It will always be the same and we don't want to recompose
-        // every time an element is added/removed from SceneTransitionLayoutImpl.elements, so we
-        // disable read observation during the look-up in that map.
-        val element = Snapshot.withoutReadObservation { layoutImpl.elements.getValue(key) }
-        val movableElementScope =
-            remember(layoutImpl, element, scene) {
-                MovableElementScopeImpl(layoutImpl, element, scene)
+        val sceneScope = scene.scope
+        val boxScope = this
+        val elementScope =
+            remember(layoutImpl, key, scene, sceneScope, boxScope) {
+                MovableElementScopeImpl(layoutImpl, key, scene, sceneScope, boxScope)
             }
 
-        // The [Picture] to which we save the last drawing commands of this element. This is
-        // necessary because the content of this element might not be composed in this scene, in
-        // which case we still need to draw it.
-        val picture = element.picture
+        content(elementScope)
+    }
+}
 
+private abstract class BaseElementScope<ContentScope>(
+    private val layoutImpl: SceneTransitionLayoutImpl,
+    private val element: ElementKey,
+    private val scene: Scene,
+) : ElementScope<ContentScope> {
+    @Composable
+    override fun <T> animateElementValueAsState(
+        value: T,
+        key: ValueKey,
+        lerp: (start: T, stop: T, fraction: Float) -> T,
+        canOverflow: Boolean
+    ): AnimatedState<T> {
+        return animateSharedValueAsState(
+            layoutImpl,
+            scene.key,
+            element,
+            key,
+            value,
+            lerp,
+            canOverflow,
+        )
+    }
+}
+
+private class ElementScopeImpl(
+    layoutImpl: SceneTransitionLayoutImpl,
+    element: ElementKey,
+    scene: Scene,
+    private val sceneScope: SceneScope,
+    private val boxScope: BoxScope,
+) : BaseElementScope<ElementContentScope>(layoutImpl, element, scene) {
+    private val contentScope =
+        object : ElementContentScope, SceneScope by sceneScope, BoxScope by boxScope {}
+
+    @Composable
+    override fun content(content: @Composable ElementContentScope.() -> Unit) {
+        contentScope.content()
+    }
+}
+
+private class MovableElementScopeImpl(
+    private val layoutImpl: SceneTransitionLayoutImpl,
+    private val element: ElementKey,
+    private val scene: Scene,
+    private val sceneScope: BaseSceneScope,
+    private val boxScope: BoxScope,
+) : BaseElementScope<MovableElementContentScope>(layoutImpl, element, scene) {
+    private val contentScope =
+        object : MovableElementContentScope, BaseSceneScope by sceneScope, BoxScope by boxScope {}
+
+    @Composable
+    override fun content(content: @Composable MovableElementContentScope.() -> Unit) {
         // Whether we should compose the movable element here. The scene picker logic to know in
         // which scene we should compose/draw a movable element might depend on the current
         // transition progress, so we put this in a derivedStateOf to prevent many recompositions
         // during the transition.
+        // TODO(b/317026105): Use derivedStateOf only if the scene picker reads the progress in its
+        // logic.
         val shouldComposeMovableElement by
             remember(layoutImpl, scene.key, element) {
                 derivedStateOf { shouldComposeMovableElement(layoutImpl, scene.key, element) }
             }
 
         if (shouldComposeMovableElement) {
-            Box(
-                Modifier.drawWithCache {
-                    val width = size.width.toInt()
-                    val height = size.height.toInt()
-
-                    onDrawWithContent {
-                        // Save the draw commands into [picture] for later to draw the last content
-                        // even when this movable content is not composed.
-                        val pictureCanvas = Canvas(picture.beginRecording(width, height))
-                        draw(this, this.layoutDirection, pictureCanvas, this.size) {
-                            this@onDrawWithContent.drawContent()
+            val movableContent: MovableElementContent =
+                layoutImpl.movableContents[element]
+                    ?: movableContentOf {
+                            contentScope: MovableElementContentScope,
+                            content: @Composable MovableElementContentScope.() -> Unit ->
+                            contentScope.content()
                         }
-                        picture.endRecording()
+                        .also { layoutImpl.movableContents[element] = it }
 
-                        // Draw the content.
-                        drawIntoCanvas { canvas -> canvas.nativeCanvas.drawPicture(picture) }
-                    }
-                }
-            ) {
-                element.movableContent { movableElementScope.content() }
-            }
+            // Important: Don't introduce any parent Box or other layout here, because contentScope
+            // delegates its BoxScope implementation to the Box where this content() function is
+            // called, so it's important that this movableContent is composed directly under that
+            // Box.
+            movableContent(contentScope, content)
         } else {
-            // If we are not composed, we draw the previous drawing commands at the same size as the
-            // movable content when it was composed in this scene.
-            val sceneValues = element.sceneValues.getValue(scene.key)
-
-            Spacer(
-                Modifier.layout { measurable, _ ->
-                        val size =
-                            sceneValues.targetSize.takeIf { it != Element.SizeUnspecified }
-                                ?: IntSize.Zero
-                        val placeable =
-                            measurable.measure(Constraints.fixed(size.width, size.height))
-                        layout(size.width, size.height) { placeable.place(0, 0) }
-                    }
-                    .drawBehind {
-                        drawIntoCanvas { canvas -> canvas.nativeCanvas.drawPicture(picture) }
-                    }
-            )
+            // If we are not composed, we still need to lay out an empty space with the same *target
+            // size* as its movable content, i.e. the same *size when idle*. During transitions,
+            // this size will be used to interpolate the transition size, during the intermediate
+            // layout pass.
+            Layout { _, _ ->
+                // No need to measure or place anything.
+                val size =
+                    placeholderContentSize(
+                        layoutImpl,
+                        scene.key,
+                        layoutImpl.elements.getValue(element),
+                    )
+                layout(size.width, size.height) {}
+            }
         }
     }
 }
@@ -117,85 +167,52 @@
 private fun shouldComposeMovableElement(
     layoutImpl: SceneTransitionLayoutImpl,
     scene: SceneKey,
-    element: Element,
+    element: ElementKey,
 ): Boolean {
     val transition =
         layoutImpl.state.currentTransition
         // If we are idle, there is only one [scene] that is composed so we can compose our
         // movable content here.
         ?: return true
-    val fromScene = transition.fromScene
-    val toScene = transition.toScene
 
-    val fromReady = layoutImpl.isSceneReady(fromScene)
-    val toReady = layoutImpl.isSceneReady(toScene)
-
-    val otherScene =
-        when (scene) {
-            fromScene -> toScene
-            toScene -> fromScene
-            else ->
-                error(
-                    "shouldComposeMovableElement(scene=$scene) called with fromScene=$fromScene " +
-                        "and toScene=$toScene"
-                )
-        }
-
-    val isShared = otherScene in element.sceneValues
-
-    if (isShared && !toReady && !fromReady) {
-        // This should usually not happen given that fromScene should be ready, but let's log a
-        // warning here in case it does so it helps debugging flicker issues caused by this part of
-        // the code.
-        Log.w(
-            TAG,
-            "MovableElement $element might have to be composed for the first time in both " +
-                "fromScene=$fromScene and toScene=$toScene. This will probably lead to a flicker " +
-                "where the size of the element will jump from IntSize.Zero to its actual size " +
-                "during the transition."
-        )
-    }
-
-    // Element is not shared in this transition.
-    if (!isShared) {
-        return true
-    }
-
-    // toScene is not ready (because we are composing it for the first time), so we compose it there
-    // first. This is the most common scenario when starting a transition that has a shared movable
-    // element.
-    if (!toReady) {
-        return scene == toScene
-    }
-
-    // This should usually not happen, but if we are also composing for the first time in fromScene
-    // then we should compose it there only.
-    if (!fromReady) {
-        return scene == fromScene
-    }
-
+    // Always compose movable elements in the scene picked by their scene picker.
     return shouldDrawOrComposeSharedElement(
         layoutImpl,
         transition,
         scene,
-        element.key,
-        sharedElementTransformation(layoutImpl.state, transition, element.key),
+        element,
     )
 }
 
-private class MovableElementScopeImpl(
-    private val layoutImpl: SceneTransitionLayoutImpl,
-    private val element: Element,
-    private val scene: Scene,
-) : MovableElementScope {
-    @Composable
-    override fun <T> animateSharedValueAsState(
-        value: T,
-        debugName: String,
-        lerp: (start: T, stop: T, fraction: Float) -> T,
-        canOverflow: Boolean,
-    ): State<T> {
-        val key = remember { ValueKey(debugName) }
-        return animateSharedValueAsState(layoutImpl, scene, element, key, value, lerp, canOverflow)
+/**
+ * Return the size of the placeholder/space that is composed when the movable content is not
+ * composed in a scene.
+ */
+private fun placeholderContentSize(
+    layoutImpl: SceneTransitionLayoutImpl,
+    scene: SceneKey,
+    element: Element,
+): IntSize {
+    // If the content of the movable element was already composed in this scene before, use that
+    // target size.
+    val targetValueInScene = element.sceneStates.getValue(scene).targetSize
+    if (targetValueInScene != Element.SizeUnspecified) {
+        return targetValueInScene
     }
+
+    // This code is only run during transitions (otherwise the content would be composed and the
+    // placeholder would not), so it's ok to cast the state into a Transition directly.
+    val transition = layoutImpl.state.transitionState as TransitionState.Transition
+
+    // If the content was already composed in the other scene, we use that target size assuming it
+    // doesn't change between scenes.
+    // TODO(b/317026105): Provide a way to give a hint size/content for cases where this is not
+    // true.
+    val otherScene = if (transition.fromScene == scene) transition.toScene else transition.fromScene
+    val targetValueInOtherScene = element.sceneStates[otherScene]?.targetSize
+    if (targetValueInOtherScene != null && targetValueInOtherScene != Element.SizeUnspecified) {
+        return targetValueInOtherScene
+    }
+
+    return IntSize.Zero
 }
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/PunchHole.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/PunchHole.kt
index 560e92b..b346a70 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/PunchHole.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/PunchHole.kt
@@ -16,96 +16,118 @@
 
 package com.android.compose.animation.scene
 
+import androidx.compose.runtime.Stable
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.geometry.Size
-import androidx.compose.ui.geometry.toRect
 import androidx.compose.ui.graphics.BlendMode
 import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.CompositingStrategy
 import androidx.compose.ui.graphics.Outline
-import androidx.compose.ui.graphics.Paint
 import androidx.compose.ui.graphics.RectangleShape
 import androidx.compose.ui.graphics.Shape
 import androidx.compose.ui.graphics.drawOutline
 import androidx.compose.ui.graphics.drawscope.ContentDrawScope
 import androidx.compose.ui.graphics.drawscope.DrawScope
-import androidx.compose.ui.graphics.drawscope.drawIntoCanvas
 import androidx.compose.ui.graphics.drawscope.translate
-import androidx.compose.ui.graphics.withSaveLayer
+import androidx.compose.ui.layout.LayoutCoordinates
+import androidx.compose.ui.layout.Measurable
+import androidx.compose.ui.layout.MeasureResult
+import androidx.compose.ui.layout.MeasureScope
+import androidx.compose.ui.node.DelegatingNode
 import androidx.compose.ui.node.DrawModifierNode
+import androidx.compose.ui.node.GlobalPositionAwareModifierNode
+import androidx.compose.ui.node.LayoutModifierNode
 import androidx.compose.ui.node.ModifierNodeElement
+import androidx.compose.ui.unit.Constraints
 import androidx.compose.ui.unit.LayoutDirection
 import androidx.compose.ui.unit.toSize
 
-internal fun Modifier.punchHole(
-    layoutImpl: SceneTransitionLayoutImpl,
-    element: ElementKey,
-    bounds: ElementKey,
-    shape: Shape,
-): Modifier = this.then(PunchHoleElement(layoutImpl, element, bounds, shape))
+/**
+ * Punch a hole in this node with the given [size], [offset] and [shape].
+ *
+ * Punching a hole in an element will "remove" any pixel drawn by that element in the hole area.
+ * This can be used to make content drawn below an opaque element visible. For example, if we have
+ * [this lockscreen scene](http://shortn/_VYySFnJDhN) drawn below
+ * [this shade scene](http://shortn/_fpxGUk0Rg7) and punch a hole in the latter using the big clock
+ * time bounds and a RoundedCornerShape(10dp), [this](http://shortn/_qt80IvORFj) would be the
+ * result.
+ */
+@Stable
+fun Modifier.punchHole(
+    size: () -> Size,
+    offset: () -> Offset,
+    shape: Shape = RectangleShape,
+): Modifier = this.then(PunchHoleElement(size, offset, shape))
+
+/**
+ * Punch a hole in this node using the bounds of [coords] and the given [shape].
+ *
+ * You can use [androidx.compose.ui.layout.onGloballyPositioned] to get the last coordinates of a
+ * node.
+ */
+@Stable
+fun Modifier.punchHole(
+    coords: () -> LayoutCoordinates?,
+    shape: Shape = RectangleShape,
+): Modifier = this.then(PunchHoleWithBoundsElement(coords, shape))
 
 private data class PunchHoleElement(
-    private val layoutImpl: SceneTransitionLayoutImpl,
-    private val element: ElementKey,
-    private val bounds: ElementKey,
+    private val size: () -> Size,
+    private val offset: () -> Offset,
     private val shape: Shape,
 ) : ModifierNodeElement<PunchHoleNode>() {
-    override fun create(): PunchHoleNode = PunchHoleNode(layoutImpl, element, bounds, shape)
+    override fun create(): PunchHoleNode = PunchHoleNode(size, offset, { shape })
 
     override fun update(node: PunchHoleNode) {
-        node.layoutImpl = layoutImpl
-        node.element = element
-        node.bounds = bounds
-        node.shape = shape
+        node.size = size
+        node.offset = offset
+        node.shape = { shape }
     }
 }
 
 private class PunchHoleNode(
-    var layoutImpl: SceneTransitionLayoutImpl,
-    var element: ElementKey,
-    var bounds: ElementKey,
-    var shape: Shape,
-) : Modifier.Node(), DrawModifierNode {
+    var size: () -> Size,
+    var offset: () -> Offset,
+    var shape: () -> Shape,
+) : Modifier.Node(), DrawModifierNode, LayoutModifierNode {
     private var lastSize: Size = Size.Unspecified
     private var lastLayoutDirection: LayoutDirection = LayoutDirection.Ltr
     private var lastOutline: Outline? = null
 
-    override fun ContentDrawScope.draw() {
-        val bounds = layoutImpl.elements[bounds]
-
-        if (
-            bounds == null ||
-                bounds.lastSharedValues.size == Element.SizeUnspecified ||
-                bounds.lastSharedValues.offset == Offset.Unspecified
-        ) {
-            drawContent()
-            return
-        }
-
-        val element = layoutImpl.elements.getValue(element)
-        drawIntoCanvas { canvas ->
-            canvas.withSaveLayer(size.toRect(), Paint()) {
-                drawContent()
-
-                val offset = bounds.lastSharedValues.offset - element.lastSharedValues.offset
-                translate(offset.x, offset.y) { drawHole(bounds) }
+    override fun MeasureScope.measure(
+        measurable: Measurable,
+        constraints: Constraints
+    ): MeasureResult {
+        return measurable.measure(constraints).run {
+            layout(width, height) {
+                placeWithLayer(0, 0) { compositingStrategy = CompositingStrategy.Offscreen }
             }
         }
     }
 
-    private fun DrawScope.drawHole(bounds: Element) {
-        val boundsSize = bounds.lastSharedValues.size.toSize()
+    override fun ContentDrawScope.draw() {
+        drawContent()
+
+        val holeSize = size()
+        if (holeSize != Size.Zero) {
+            val offset = offset()
+            translate(offset.x, offset.y) { drawHole(holeSize) }
+        }
+    }
+
+    private fun DrawScope.drawHole(size: Size) {
         if (shape == RectangleShape) {
-            drawRect(Color.Black, size = boundsSize, blendMode = BlendMode.DstOut)
+            drawRect(Color.Black, size = size, blendMode = BlendMode.DstOut)
             return
         }
 
         val outline =
-            if (boundsSize == lastSize && layoutDirection == lastLayoutDirection) {
+            if (size == lastSize && layoutDirection == lastLayoutDirection) {
                 lastOutline!!
             } else {
-                val newOutline = shape.createOutline(boundsSize, layoutDirection, this)
-                lastSize = boundsSize
+                val newOutline = shape().createOutline(size, layoutDirection, this)
+                lastSize = size
                 lastLayoutDirection = layoutDirection
                 lastOutline = newOutline
                 newOutline
@@ -118,3 +140,39 @@
         )
     }
 }
+
+private data class PunchHoleWithBoundsElement(
+    private val coords: () -> LayoutCoordinates?,
+    private val shape: Shape,
+) : ModifierNodeElement<PunchHoleWithBoundsNode>() {
+    override fun create(): PunchHoleWithBoundsNode = PunchHoleWithBoundsNode(coords, shape)
+
+    override fun update(node: PunchHoleWithBoundsNode) {
+        node.holeCoords = coords
+        node.shape = shape
+    }
+}
+
+private class PunchHoleWithBoundsNode(
+    var holeCoords: () -> LayoutCoordinates?,
+    var shape: Shape,
+) : DelegatingNode(), DrawModifierNode, GlobalPositionAwareModifierNode {
+    private val delegate = delegate(PunchHoleNode(::holeSize, ::holeOffset, ::shape))
+    private var lastCoords: LayoutCoordinates? = null
+
+    override fun onGloballyPositioned(coordinates: LayoutCoordinates) {
+        this.lastCoords = coordinates
+    }
+
+    override fun ContentDrawScope.draw() = with(delegate) { draw() }
+
+    private fun holeSize(): Size {
+        return holeCoords()?.size?.toSize() ?: Size.Zero
+    }
+
+    private fun holeOffset(): Offset {
+        val holeCoords = holeCoords() ?: return Offset.Zero
+        val lastCoords = lastCoords ?: error("draw() was called before onGloballyPositioned()")
+        return lastCoords.localPositionOf(holeCoords, relativeToSource = Offset.Zero)
+    }
+}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Scene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Scene.kt
index 30e50a9..f67df54 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Scene.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Scene.kt
@@ -20,16 +20,12 @@
 import androidx.compose.foundation.layout.Box
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.Stable
-import androidx.compose.runtime.State
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableFloatStateOf
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.setValue
-import androidx.compose.runtime.snapshots.Snapshot
-import androidx.compose.runtime.snapshots.SnapshotStateMap
 import androidx.compose.ui.ExperimentalComposeUiApi
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.Shape
 import androidx.compose.ui.layout.intermediateLayout
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.unit.IntSize
@@ -45,16 +41,13 @@
     actions: Map<UserAction, SceneKey>,
     zIndex: Float,
 ) {
-    private val scope = SceneScopeImpl(layoutImpl, this)
+    internal val scope = SceneScopeImpl(layoutImpl, this)
 
     var content by mutableStateOf(content)
     var userActions by mutableStateOf(actions)
     var zIndex by mutableFloatStateOf(zIndex)
     var targetSize by mutableStateOf(IntSize.Zero)
 
-    /** The shared values in this scene that are not tied to a specific element. */
-    val sharedValues = SnapshotStateMap<ValueKey, Element.SharedValue<*>>()
-
     @Composable
     @OptIn(ExperimentalComposeUiApi::class)
     fun Content(modifier: Modifier = Modifier) {
@@ -77,7 +70,7 @@
     }
 }
 
-private class SceneScopeImpl(
+internal class SceneScopeImpl(
     private val layoutImpl: SceneTransitionLayoutImpl,
     private val scene: Scene,
 ) : SceneScope {
@@ -87,6 +80,42 @@
         return element(layoutImpl, scene, key)
     }
 
+    @Composable
+    override fun Element(
+        key: ElementKey,
+        modifier: Modifier,
+        content: @Composable (ElementScope<ElementContentScope>.() -> Unit)
+    ) {
+        Element(layoutImpl, scene, key, modifier, content)
+    }
+
+    @Composable
+    override fun MovableElement(
+        key: ElementKey,
+        modifier: Modifier,
+        content: @Composable (ElementScope<MovableElementContentScope>.() -> Unit)
+    ) {
+        MovableElement(layoutImpl, scene, key, modifier, content)
+    }
+
+    @Composable
+    override fun <T> animateSceneValueAsState(
+        value: T,
+        key: ValueKey,
+        lerp: (T, T, Float) -> T,
+        canOverflow: Boolean
+    ): AnimatedState<T> {
+        return animateSharedValueAsState(
+            layoutImpl = layoutImpl,
+            scene = scene.key,
+            element = null,
+            key = key,
+            value = value,
+            lerp = lerp,
+            canOverflow = canOverflow,
+        )
+    }
+
     override fun Modifier.horizontalNestedScrollToScene(
         leftBehavior: NestedScrollBehavior,
         rightBehavior: NestedScrollBehavior,
@@ -109,51 +138,6 @@
             bottomOrRightBehavior = bottomBehavior,
         )
 
-    @Composable
-    override fun <T> animateSharedValueAsState(
-        value: T,
-        key: ValueKey,
-        element: ElementKey?,
-        lerp: (T, T, Float) -> T,
-        canOverflow: Boolean
-    ): State<T> {
-        val element =
-            element?.let { key ->
-                Snapshot.withoutReadObservation {
-                    layoutImpl.elements[key]
-                        ?: error(
-                            "Element $key is not composed. Make sure to call " +
-                                "animateSharedXAsState *after* Modifier.element(key)."
-                        )
-                }
-            }
-
-        return animateSharedValueAsState(
-            layoutImpl,
-            scene,
-            element,
-            key,
-            value,
-            lerp,
-            canOverflow,
-        )
-    }
-
-    @Composable
-    override fun MovableElement(
-        key: ElementKey,
-        modifier: Modifier,
-        content: @Composable MovableElementScope.() -> Unit,
-    ) {
-        MovableElement(layoutImpl, scene, key, modifier, content)
-    }
-
-    override fun Modifier.punchHole(
-        element: ElementKey,
-        bounds: ElementKey,
-        shape: Shape
-    ): Modifier = punchHole(layoutImpl, element, bounds, shape)
-
     override fun Modifier.noResizeDuringTransitions(): Modifier {
         return noResizeDuringTransitions(layoutState = layoutImpl.state)
     }
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt
index 338557d..64388b7 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt
@@ -312,13 +312,16 @@
             // immediately go back B => A.
             if (targetScene != swipeTransition._currentScene) {
                 swipeTransition._currentScene = targetScene
-                layoutImpl.onChangeScene(targetScene.key)
+                with(layoutImpl.state) { coroutineScope.onChangeScene(targetScene.key) }
             }
 
-            animateOffset(
+            swipeTransition.animateOffset(
+                coroutineScope = coroutineScope,
                 initialVelocity = velocity,
                 targetOffset = targetOffset,
-                targetScene = targetScene.key
+                onAnimationCompleted = {
+                    layoutState.finishTransition(swipeTransition, idleScene = targetScene.key)
+                }
             )
         }
 
@@ -410,34 +413,6 @@
         }
     }
 
-    private fun animateOffset(
-        initialVelocity: Float,
-        targetOffset: Float,
-        targetScene: SceneKey,
-    ) {
-        swipeTransition.startOffsetAnimation {
-            coroutineScope.launch {
-                if (!swipeTransition.isAnimatingOffset) {
-                    swipeTransition.offsetAnimatable.snapTo(swipeTransition.dragOffset)
-                }
-                swipeTransition.isAnimatingOffset = true
-
-                swipeTransition.offsetAnimatable.animateTo(
-                    targetOffset,
-                    // TODO(b/290184746): Make this spring spec configurable.
-                    spring(
-                        stiffness = Spring.StiffnessMediumLow,
-                        visibilityThreshold = OffsetVisibilityThreshold
-                    ),
-                    initialVelocity = initialVelocity,
-                )
-
-                swipeTransition.finishOffsetAnimation()
-                layoutState.finishTransition(swipeTransition, targetScene)
-            }
-        }
-    }
-
     internal class SwipeTransition(
         val _fromScene: Scene,
         val _toScene: Scene,
@@ -479,12 +454,14 @@
         private var offsetAnimationJob: Job? = null
 
         /** Ends any previous [offsetAnimationJob] and runs the new [job]. */
-        fun startOffsetAnimation(job: () -> Job) {
+        private fun startOffsetAnimation(job: () -> Job) {
             cancelOffsetAnimation()
             offsetAnimationJob = job()
         }
 
         /** Cancel any ongoing offset animation. */
+        // TODO(b/317063114) This should be a suspended function to avoid multiple jobs running at
+        // the same time.
         fun cancelOffsetAnimation() {
             offsetAnimationJob?.cancel()
             finishOffsetAnimation()
@@ -496,6 +473,43 @@
                 dragOffset = offsetAnimatable.value
             }
         }
+
+        // TODO(b/290184746): Make this spring spec configurable.
+        private val animationSpec =
+            spring(
+                stiffness = Spring.StiffnessMediumLow,
+                visibilityThreshold = OffsetVisibilityThreshold
+            )
+
+        fun animateOffset(
+            // TODO(b/317063114) The CoroutineScope should be removed.
+            coroutineScope: CoroutineScope,
+            initialVelocity: Float,
+            targetOffset: Float,
+            onAnimationCompleted: () -> Unit,
+        ) {
+            startOffsetAnimation {
+                coroutineScope.launch {
+                    animateOffset(targetOffset, initialVelocity)
+                    onAnimationCompleted()
+                }
+            }
+        }
+
+        private suspend fun animateOffset(targetOffset: Float, initialVelocity: Float) {
+            if (!isAnimatingOffset) {
+                offsetAnimatable.snapTo(dragOffset)
+            }
+            isAnimatingOffset = true
+
+            offsetAnimatable.animateTo(
+                targetValue = targetOffset,
+                animationSpec = animationSpec,
+                initialVelocity = initialVelocity,
+            )
+
+            finishOffsetAnimation()
+        }
     }
 
     companion object {
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
index 5eb339e..80f8c1c 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
@@ -19,17 +19,48 @@
 import androidx.annotation.FloatRange
 import androidx.compose.foundation.gestures.Orientation
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.SideEffect
 import androidx.compose.runtime.Stable
-import androidx.compose.runtime.State
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.Shape
 import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
 import androidx.compose.ui.platform.LocalDensity
-import kotlinx.coroutines.channels.Channel
+
+/**
+ * [SceneTransitionLayout] is a container that automatically animates its content whenever its state
+ * changes.
+ *
+ * Note: You should use [androidx.compose.animation.AnimatedContent] instead of
+ * [SceneTransitionLayout] if it fits your need. Use [SceneTransitionLayout] over AnimatedContent if
+ * you need support for swipe gestures, shared elements or transitions defined declaratively outside
+ * UI code.
+ *
+ * @param state the state of this layout.
+ * @param edgeDetector the edge detector used to detect which edge a swipe is started from, if any.
+ * @param transitionInterceptionThreshold used during a scene transition. For the scene to be
+ *   intercepted, the progress value must be above the threshold, and below (1 - threshold).
+ * @param scenes the configuration of the different scenes of this layout.
+ * @see updateSceneTransitionLayoutState
+ */
+@Composable
+fun SceneTransitionLayout(
+    state: SceneTransitionLayoutState,
+    modifier: Modifier = Modifier,
+    edgeDetector: EdgeDetector = DefaultEdgeDetector,
+    @FloatRange(from = 0.0, to = 0.5) transitionInterceptionThreshold: Float = 0f,
+    scenes: SceneTransitionLayoutScope.() -> Unit,
+) {
+    SceneTransitionLayoutForTesting(
+        state,
+        modifier,
+        edgeDetector,
+        transitionInterceptionThreshold,
+        onLayoutImpl = null,
+        scenes,
+    )
+}
 
 /**
  * [SceneTransitionLayout] is a container that automatically animates its content whenever
@@ -45,7 +76,6 @@
  *   This is called when the user commits a transition to a new scene because of a [UserAction], for
  *   instance by triggering back navigation or by swiping to a new scene.
  * @param transitions the definition of the transitions used to animate a change of scene.
- * @param state the observable state of this layout.
  * @param edgeDetector the edge detector used to detect which edge a swipe is started from, if any.
  * @param transitionInterceptionThreshold used during a scene transition. For the scene to be
  *   intercepted, the progress value must be above the threshold, and below (1 - threshold).
@@ -57,20 +87,16 @@
     onChangeScene: (SceneKey) -> Unit,
     transitions: SceneTransitions,
     modifier: Modifier = Modifier,
-    state: SceneTransitionLayoutState = remember { SceneTransitionLayoutState(currentScene) },
     edgeDetector: EdgeDetector = DefaultEdgeDetector,
     @FloatRange(from = 0.0, to = 0.5) transitionInterceptionThreshold: Float = 0f,
     scenes: SceneTransitionLayoutScope.() -> Unit,
 ) {
-    SceneTransitionLayoutForTesting(
-        currentScene,
-        onChangeScene,
-        modifier,
-        transitions,
+    val state = updateSceneTransitionLayoutState(currentScene, onChangeScene, transitions)
+    SceneTransitionLayout(
         state,
+        modifier,
         edgeDetector,
         transitionInterceptionThreshold,
-        onLayoutImpl = null,
         scenes,
     )
 }
@@ -98,9 +124,9 @@
  */
 @DslMarker annotation class ElementDsl
 
-@ElementDsl
 @Stable
-interface SceneScope {
+@ElementDsl
+interface BaseSceneScope {
     /** The state of the [SceneTransitionLayout] in which this scene is contained. */
     val layoutState: SceneTransitionLayoutState
 
@@ -111,21 +137,74 @@
      * that the element can be transformed and animated when the scene transitions in or out.
      *
      * Additionally, this [key] will be used to detect elements that are shared between scenes to
-     * automatically interpolate their size, offset and [shared values][animateSharedValueAsState].
+     * automatically interpolate their size and offset. If you need to animate shared element values
+     * (i.e. values associated to this element that change depending on which scene it is composed
+     * in), use [Element] instead.
      *
      * Note that shared elements tagged using this function will be duplicated in each scene they
      * are part of, so any **internal** state (e.g. state created using `remember {
      * mutableStateOf(...) }`) will be lost. If you need to preserve internal state, you should use
      * [MovableElement] instead.
      *
+     * @see Element
      * @see MovableElement
-     *
-     * TODO(b/291566282): Migrate this to the new Modifier Node API and remove the @Composable
-     *   constraint.
      */
     fun Modifier.element(key: ElementKey): Modifier
 
     /**
+     * Create an element identified by [key].
+     *
+     * Similar to [element], this creates an element that will be automatically shared when present
+     * in multiple scenes and that can be transformed during transitions, the same way that
+     * [element] does.
+     *
+     * The only difference with [element] is that the provided [ElementScope] allows you to
+     * [animate element values][ElementScope.animateElementValueAsState] or specify its
+     * [movable content][Element.movableContent] that will be "moved" and composed only once during
+     * transitions (as opposed to [element] that duplicates shared elements) so that any internal
+     * state is preserved during and after the transition.
+     *
+     * @see element
+     * @see MovableElement
+     */
+    @Composable
+    fun Element(
+        key: ElementKey,
+        modifier: Modifier,
+
+        // TODO(b/317026105): As discussed in http://shortn/_gJVdltF8Si, remove the @Composable
+        // scope here to make sure that callers specify the content in ElementScope.content {} or
+        // ElementScope.movableContent {}.
+        content: @Composable ElementScope<ElementContentScope>.() -> Unit,
+    )
+
+    /**
+     * Create a *movable* element identified by [key].
+     *
+     * Similar to [Element], this creates an element that will be automatically shared when present
+     * in multiple scenes and that can be transformed during transitions, and you can also use the
+     * provided [ElementScope] to [animate element values][ElementScope.animateElementValueAsState].
+     *
+     * The important difference with [element] and [Element] is that this element
+     * [content][ElementScope.content] will be "moved" and composed only once during transitions, as
+     * opposed to [element] and [Element] that duplicates shared elements, so that any internal
+     * state is preserved during and after the transition.
+     *
+     * @see element
+     * @see Element
+     */
+    @Composable
+    fun MovableElement(
+        key: ElementKey,
+        modifier: Modifier,
+
+        // TODO(b/317026105): As discussed in http://shortn/_gJVdltF8Si, remove the @Composable
+        // scope here to make sure that callers specify the content in ElementScope.content {} or
+        // ElementScope.movableContent {}.
+        content: @Composable ElementScope<MovableElementContentScope>.() -> Unit,
+    )
+
+    /**
      * Adds a [NestedScrollConnection] to intercept scroll events not handled by the scrollable
      * component.
      *
@@ -150,82 +229,102 @@
     ): Modifier
 
     /**
-     * Create a *movable* element identified by [key].
-     *
-     * This creates an element that will be automatically shared when present in multiple scenes and
-     * that can be transformed during transitions, the same way that [element] does. The major
-     * difference with [element] is that elements created with [MovableElement] will be "moved" and
-     * composed only once during transitions (as opposed to [element] that duplicates shared
-     * elements) so that any internal state is preserved during and after the transition.
-     *
-     * @see element
-     */
-    @Composable
-    fun MovableElement(
-        key: ElementKey,
-        modifier: Modifier,
-        content: @Composable MovableElementScope.() -> Unit,
-    )
-
-    /**
-     * Animate some value of a shared element.
-     *
-     * @param value the value of this shared value in the current scene.
-     * @param key the key of this shared value.
-     * @param element the element associated with this value. If `null`, this value will be
-     *   associated at the scene level, which means that [key] should be used maximum once in the
-     *   same scene.
-     * @param lerp the *linear* interpolation function that should be used to interpolate between
-     *   two different values. Note that it has to be linear because the [fraction] passed to this
-     *   interpolator is already interpolated.
-     * @param canOverflow whether this value can overflow past the values it is interpolated
-     *   between, for instance because the transition is animated using a bouncy spring.
-     * @see animateSharedIntAsState
-     * @see animateSharedFloatAsState
-     * @see animateSharedDpAsState
-     * @see animateSharedColorAsState
-     */
-    @Composable
-    fun <T> animateSharedValueAsState(
-        value: T,
-        key: ValueKey,
-        element: ElementKey?,
-        lerp: (start: T, stop: T, fraction: Float) -> T,
-        canOverflow: Boolean,
-    ): State<T>
-
-    /**
-     * Punch a hole in this [element] using the bounds of [bounds] in [scene] and the given [shape].
-     *
-     * Punching a hole in an element will "remove" any pixel drawn by that element in the hole area.
-     * This can be used to make content drawn below an opaque element visible. For example, if we
-     * have [this lockscreen scene](http://shortn/_VYySFnJDhN) drawn below
-     * [this shade scene](http://shortn/_fpxGUk0Rg7) and punch a hole in the latter using the big
-     * clock time bounds and a RoundedCornerShape(10dp), [this](http://shortn/_qt80IvORFj) would be
-     * the result.
-     */
-    fun Modifier.punchHole(element: ElementKey, bounds: ElementKey, shape: Shape): Modifier
-
-    /**
      * Don't resize during transitions. This can for instance be used to make sure that scrollable
      * lists keep a constant size during transitions even if its elements are growing/shrinking.
      */
     fun Modifier.noResizeDuringTransitions(): Modifier
 }
 
-// TODO(b/291053742): Add animateSharedValueAsState(targetValue) without any ValueKey and ElementKey
-// arguments to allow sharing values inside a movable element.
+@Stable
 @ElementDsl
-interface MovableElementScope {
+interface SceneScope : BaseSceneScope {
+    /**
+     * Animate some value at the scene level.
+     *
+     * @param value the value of this shared value in the current scene.
+     * @param key the key of this shared value.
+     * @param lerp the *linear* interpolation function that should be used to interpolate between
+     *   two different values. Note that it has to be linear because the [fraction] passed to this
+     *   interpolator is already interpolated.
+     * @param canOverflow whether this value can overflow past the values it is interpolated
+     *   between, for instance because the transition is animated using a bouncy spring.
+     * @see animateSceneIntAsState
+     * @see animateSceneFloatAsState
+     * @see animateSceneDpAsState
+     * @see animateSceneColorAsState
+     */
     @Composable
-    fun <T> animateSharedValueAsState(
+    fun <T> animateSceneValueAsState(
         value: T,
-        debugName: String,
+        key: ValueKey,
         lerp: (start: T, stop: T, fraction: Float) -> T,
         canOverflow: Boolean,
-    ): State<T>
+    ): AnimatedState<T>
 }
 
+@Stable
+@ElementDsl
+interface ElementScope<ContentScope> {
+    /**
+     * Animate some value associated to this element.
+     *
+     * @param value the value of this shared value in the current scene.
+     * @param key the key of this shared value.
+     * @param lerp the *linear* interpolation function that should be used to interpolate between
+     *   two different values. Note that it has to be linear because the [fraction] passed to this
+     *   interpolator is already interpolated.
+     * @param canOverflow whether this value can overflow past the values it is interpolated
+     *   between, for instance because the transition is animated using a bouncy spring.
+     * @see animateElementIntAsState
+     * @see animateElementFloatAsState
+     * @see animateElementDpAsState
+     * @see animateElementColorAsState
+     */
+    @Composable
+    fun <T> animateElementValueAsState(
+        value: T,
+        key: ValueKey,
+        lerp: (start: T, stop: T, fraction: Float) -> T,
+        canOverflow: Boolean,
+    ): AnimatedState<T>
+
+    /**
+     * The content of this element.
+     *
+     * Important: This must be called exactly once, after all calls to [animateElementValueAsState].
+     */
+    @Composable fun content(content: @Composable ContentScope.() -> Unit)
+}
+
+/**
+ * The exact same scope as [androidx.compose.foundation.layout.BoxScope].
+ *
+ * We can't reuse BoxScope directly because of the @LayoutScopeMarker annotation on it, which would
+ * prevent us from calling Modifier.element() and other methods of [SceneScope] inside any Box {} in
+ * the [content][ElementScope.content] of a [SceneScope.Element] or a [SceneScope.MovableElement].
+ */
+@Stable
+@ElementDsl
+interface ElementBoxScope {
+    /** @see [androidx.compose.foundation.layout.BoxScope.align]. */
+    @Stable fun Modifier.align(alignment: Alignment): Modifier
+
+    /** @see [androidx.compose.foundation.layout.BoxScope.matchParentSize]. */
+    @Stable fun Modifier.matchParentSize(): Modifier
+}
+
+/** The scope for "normal" (not movable) elements. */
+@Stable @ElementDsl interface ElementContentScope : SceneScope, ElementBoxScope
+
+/**
+ * The scope for the content of movable elements.
+ *
+ * Note that it extends [BaseSceneScope] and not [SceneScope] because movable elements should not
+ * call [SceneScope.animateSceneValueAsState], given that their content is not composed in all
+ * scenes.
+ */
+@Stable @ElementDsl interface MovableElementContentScope : BaseSceneScope, ElementBoxScope
+
 /** An action performed by the user. */
 sealed interface UserAction
 
@@ -261,11 +360,8 @@
  */
 @Composable
 internal fun SceneTransitionLayoutForTesting(
-    currentScene: SceneKey,
-    onChangeScene: (SceneKey) -> Unit,
+    state: SceneTransitionLayoutState,
     modifier: Modifier = Modifier,
-    transitions: SceneTransitions = transitions {},
-    state: SceneTransitionLayoutState = remember { SceneTransitionLayoutState(currentScene) },
     edgeDetector: EdgeDetector = DefaultEdgeDetector,
     transitionInterceptionThreshold: Float = 0f,
     onLayoutImpl: ((SceneTransitionLayoutImpl) -> Unit)? = null,
@@ -275,8 +371,7 @@
     val coroutineScope = rememberCoroutineScope()
     val layoutImpl = remember {
         SceneTransitionLayoutImpl(
-                state = state as SceneTransitionLayoutStateImpl,
-                onChangeScene = onChangeScene,
+                state = state as BaseSceneTransitionLayoutState,
                 density = density,
                 edgeDetector = edgeDetector,
                 transitionInterceptionThreshold = transitionInterceptionThreshold,
@@ -290,7 +385,6 @@
     // SnapshotStateMap anymore.
     layoutImpl.updateScenes(scenes)
 
-    val targetSceneChannel = remember { Channel<SceneKey>(Channel.CONFLATED) }
     SideEffect {
         if (state != layoutImpl.state) {
             error(
@@ -299,23 +393,8 @@
             )
         }
 
-        layoutImpl.onChangeScene = onChangeScene
-        (state as SceneTransitionLayoutStateImpl).transitions = transitions
         layoutImpl.density = density
         layoutImpl.edgeDetector = edgeDetector
-
-        state.transitions = transitions
-
-        targetSceneChannel.trySend(currentScene)
-    }
-
-    LaunchedEffect(targetSceneChannel) {
-        for (newKey in targetSceneChannel) {
-            // Inspired by AnimateAsState.kt: let's poll the last value to avoid being one frame
-            // late.
-            val newKey = targetSceneChannel.tryReceive().getOrNull() ?: newKey
-            animateToScene(layoutImpl.state, newKey)
-        }
     }
 
     layoutImpl.Content(modifier)
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt
index 45e1a0f..7cc9d26 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt
@@ -20,14 +20,11 @@
 import androidx.compose.foundation.gestures.Orientation
 import androidx.compose.foundation.layout.Box
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.DisposableEffect
-import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.Stable
 import androidx.compose.runtime.key
 import androidx.compose.runtime.snapshots.SnapshotStateMap
 import androidx.compose.ui.ExperimentalComposeUiApi
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.draw.drawWithContent
 import androidx.compose.ui.layout.LookaheadScope
 import androidx.compose.ui.layout.intermediateLayout
 import androidx.compose.ui.unit.Density
@@ -36,15 +33,24 @@
 import com.android.compose.ui.util.lerp
 import kotlinx.coroutines.CoroutineScope
 
+/**
+ * The type for the content of movable elements.
+ *
+ * TODO(b/317972419): Revert back to make this movable content have a single @Composable lambda
+ *   parameter.
+ */
+internal typealias MovableElementContent =
+    @Composable
+    (MovableElementContentScope, @Composable MovableElementContentScope.() -> Unit) -> Unit
+
 @Stable
 internal class SceneTransitionLayoutImpl(
-    internal val state: SceneTransitionLayoutStateImpl,
-    internal var onChangeScene: (SceneKey) -> Unit,
+    internal val state: BaseSceneTransitionLayoutState,
     internal var density: Density,
     internal var edgeDetector: EdgeDetector,
     internal var transitionInterceptionThreshold: Float,
     builder: SceneTransitionLayoutScope.() -> Unit,
-    coroutineScope: CoroutineScope,
+    private val coroutineScope: CoroutineScope,
 ) {
     /**
      * The map of [Scene]s.
@@ -56,18 +62,39 @@
     /**
      * The map of [Element]s.
      *
-     * Note that this map is *mutated* directly during composition, so it is a [SnapshotStateMap] to
-     * make sure that mutations are reverted if composition is cancelled.
+     * Important: [Element]s from this map should never be accessed during composition because the
+     * Elements are added when the associated Modifier.element() node is attached to the Modifier
+     * tree, i.e. after composition.
      */
-    internal val elements = SnapshotStateMap<ElementKey, Element>()
+    internal val elements = mutableMapOf<ElementKey, Element>()
 
     /**
-     * The scenes that are "ready", i.e. they were composed and fully laid-out at least once.
+     * The map of contents of movable elements.
      *
-     * Note that this map is *read* during composition, so it is a [SnapshotStateMap] to make sure
-     * that we recompose when modifications are made to this map.
+     * Note that given that this map is mutated directly during a composition, it has to be a
+     * [SnapshotStateMap] to make sure that mutations are reverted if composition is cancelled.
      */
-    private val readyScenes = SnapshotStateMap<SceneKey, Boolean>()
+    private var _movableContents: SnapshotStateMap<ElementKey, MovableElementContent>? = null
+    val movableContents: SnapshotStateMap<ElementKey, MovableElementContent>
+        get() =
+            _movableContents
+                ?: SnapshotStateMap<ElementKey, MovableElementContent>().also {
+                    _movableContents = it
+                }
+
+    /**
+     * The different values of a shared value keyed by a a [ValueKey] and the different elements and
+     * scenes it is associated to.
+     */
+    private var _sharedValues:
+        MutableMap<ValueKey, MutableMap<ElementKey?, SnapshotStateMap<SceneKey, *>>>? =
+        null
+    internal val sharedValues:
+        MutableMap<ValueKey, MutableMap<ElementKey?, SnapshotStateMap<SceneKey, *>>>
+        get() =
+            _sharedValues
+                ?: mutableMapOf<ValueKey, MutableMap<ElementKey?, SnapshotStateMap<SceneKey, *>>>()
+                    .also { _sharedValues = it }
 
     private val horizontalGestureHandler: SceneGestureHandler
     private val verticalGestureHandler: SceneGestureHandler
@@ -203,49 +230,19 @@
                 // TODO(b/290184746): Make sure that this works with SystemUI once we use
                 // SceneTransitionLayout in Flexiglass.
                 scene(state.transitionState.currentScene).userActions[Back]?.let { backScene ->
-                    BackHandler { onChangeScene(backScene) }
+                    BackHandler { with(state) { coroutineScope.onChangeScene(backScene) } }
                 }
 
                 Box {
                     scenesToCompose.fastForEach { scene ->
                         val key = scene.key
-                        key(key) {
-                            // Mark this scene as ready once it has been composed, laid out and
-                            // drawn the first time. We have to do this in a LaunchedEffect here
-                            // because DisposableEffect runs between composition and layout.
-                            LaunchedEffect(key) { readyScenes[key] = true }
-                            DisposableEffect(key) { onDispose { readyScenes.remove(key) } }
-
-                            scene.Content(
-                                Modifier.drawWithContent {
-                                    if (state.currentTransition == null) {
-                                        drawContent()
-                                    } else {
-                                        // Don't draw scenes that are not ready yet.
-                                        if (readyScenes.containsKey(key)) {
-                                            drawContent()
-                                        }
-                                    }
-                                }
-                            )
-                        }
+                        key(key) { scene.Content() }
                     }
                 }
             }
         }
     }
 
-    /**
-     * Return whether [transition] is ready, i.e. the elements of both scenes of the transition were
-     * laid out at least once.
-     */
-    internal fun isTransitionReady(transition: TransitionState.Transition): Boolean {
-        return readyScenes.containsKey(transition.fromScene) &&
-            readyScenes.containsKey(transition.toScene)
-    }
-
-    internal fun isSceneReady(scene: SceneKey): Boolean = readyScenes.containsKey(scene)
-
     internal fun setScenesTargetSizeForTest(size: IntSize) {
         scenes.values.forEach { it.targetSize = size }
     }
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
index d1ba582..956e326 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
@@ -16,12 +16,23 @@
 
 package com.android.compose.animation.scene
 
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.SideEffect
 import androidx.compose.runtime.Stable
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.channels.Channel
 
-/** The state of a [SceneTransitionLayout]. */
+/**
+ * The state of a [SceneTransitionLayout].
+ *
+ * @see MutableSceneTransitionLayoutState
+ * @see updateSceneTransitionLayoutState
+ */
 @Stable
 sealed interface SceneTransitionLayoutState {
     /**
@@ -36,6 +47,9 @@
     val currentTransition: TransitionState.Transition?
         get() = transitionState as? TransitionState.Transition
 
+    /** The [SceneTransitions] used when animating this state. */
+    val transitions: SceneTransitions
+
     /**
      * Whether we are transitioning. If [from] or [to] is empty, we will also check that they match
      * the scenes we are animating from and/or to.
@@ -46,9 +60,68 @@
     fun isTransitioningBetween(scene: SceneKey, other: SceneKey): Boolean
 }
 
-/** Create a new [SceneTransitionLayoutState] that is currently idle at scene [currentScene]. */
-fun SceneTransitionLayoutState(currentScene: SceneKey): SceneTransitionLayoutState {
-    return SceneTransitionLayoutStateImpl(currentScene, SceneTransitions.Empty)
+/** A [SceneTransitionLayoutState] whose target scene can be imperatively set. */
+sealed interface MutableSceneTransitionLayoutState : SceneTransitionLayoutState {
+    /** The [SceneTransitions] used when animating this state. */
+    override var transitions: SceneTransitions
+
+    /**
+     * Set the target scene of this state to [targetScene].
+     *
+     * If [targetScene] is the same as the [currentScene][TransitionState.currentScene] of
+     * [transitionState], then nothing will happen and this will return `null`. Note that this means
+     * that this will also do nothing if the user is currently swiping from [targetScene] to another
+     * scene, or if we were already animating to [targetScene].
+     *
+     * If [targetScene] is different than the [currentScene][TransitionState.currentScene] of
+     * [transitionState], then this will animate to [targetScene]. The associated
+     * [TransitionState.Transition] will be returned and will be set as the current
+     * [transitionState] of this [MutableSceneTransitionLayoutState].
+     *
+     * Note that because a non-null [TransitionState.Transition] is returned does not mean that the
+     * transition will finish and that we will settle to [targetScene]. The returned transition
+     * might still be interrupted, for instance by another call to [setTargetScene] or by a user
+     * gesture.
+     *
+     * If [this] [CoroutineScope] is cancelled during the transition and that the transition was
+     * still active, then the [transitionState] of this [MutableSceneTransitionLayoutState] will be
+     * set to `TransitionState.Idle(targetScene)`.
+     *
+     * TODO(b/318794193): Add APIs to await() and cancel() any [TransitionState.Transition].
+     */
+    fun setTargetScene(
+        targetScene: SceneKey,
+        coroutineScope: CoroutineScope,
+    ): TransitionState.Transition?
+}
+
+/** Return a [MutableSceneTransitionLayoutState] initially idle at [initialScene]. */
+fun MutableSceneTransitionLayoutState(
+    initialScene: SceneKey,
+    transitions: SceneTransitions = SceneTransitions.Empty,
+): MutableSceneTransitionLayoutState {
+    return MutableSceneTransitionLayoutStateImpl(initialScene, transitions)
+}
+
+/**
+ * Sets up a [SceneTransitionLayoutState] and keeps it synced with [currentScene], [onChangeScene]
+ * and [transitions]. New transitions will automatically be started whenever [currentScene] is
+ * changed.
+ *
+ * @param currentScene the current scene
+ * @param onChangeScene a mutator that should set [currentScene] to the given scene when called.
+ *   This is called when the user commits a transition to a new scene because of a [UserAction], for
+ *   instance by triggering back navigation or by swiping to a new scene.
+ * @param transitions the definition of the transitions used to animate a change of scene.
+ */
+@Composable
+fun updateSceneTransitionLayoutState(
+    currentScene: SceneKey,
+    onChangeScene: (SceneKey) -> Unit,
+    transitions: SceneTransitions = SceneTransitions.Empty,
+): SceneTransitionLayoutState {
+    return remember { HoistedSceneTransitionLayoutScene(currentScene, transitions, onChangeScene) }
+        .apply { update(currentScene, onChangeScene, transitions) }
 }
 
 @Stable
@@ -92,16 +165,28 @@
 
         /** Whether user input is currently driving the transition. */
         abstract val isUserInputOngoing: Boolean
+
+        /**
+         * Whether we are transitioning. If [from] or [to] is empty, we will also check that they
+         * match the scenes we are animating from and/or to.
+         */
+        fun isTransitioning(from: SceneKey? = null, to: SceneKey? = null): Boolean {
+            return (from == null || fromScene == from) && (to == null || toScene == to)
+        }
+
+        /** Whether we are transitioning from [scene] to [other], or from [other] to [scene]. */
+        fun isTransitioningBetween(scene: SceneKey, other: SceneKey): Boolean {
+            return isTransitioning(from = scene, to = other) ||
+                isTransitioning(from = other, to = scene)
+        }
     }
 }
 
-internal class SceneTransitionLayoutStateImpl(
-    initialScene: SceneKey,
-    internal var transitions: SceneTransitions,
-) : SceneTransitionLayoutState {
+internal abstract class BaseSceneTransitionLayoutState(initialScene: SceneKey) :
+    SceneTransitionLayoutState {
     override var transitionState: TransitionState by
         mutableStateOf(TransitionState.Idle(initialScene))
-        private set
+        protected set
 
     /**
      * The current [transformationSpec] associated to [transitionState]. Accessing this value makes
@@ -109,15 +194,22 @@
      */
     internal var transformationSpec: TransformationSpecImpl = TransformationSpec.Empty
 
+    /**
+     * Called when the [current scene][TransitionState.currentScene] should be changed to [scene].
+     *
+     * When this is called, the source of truth for the current scene should be changed so that
+     * [transitionState] will animate and settle to [scene].
+     */
+    internal abstract fun CoroutineScope.onChangeScene(scene: SceneKey)
+
     override fun isTransitioning(from: SceneKey?, to: SceneKey?): Boolean {
         val transition = currentTransition ?: return false
-        return (from == null || transition.fromScene == from) &&
-            (to == null || transition.toScene == to)
+        return transition.isTransitioning(from, to)
     }
 
     override fun isTransitioningBetween(scene: SceneKey, other: SceneKey): Boolean {
-        return isTransitioning(from = scene, to = other) ||
-            isTransitioning(from = other, to = scene)
+        val transition = currentTransition ?: return false
+        return transition.isTransitioningBetween(scene, other)
     }
 
     /** Start a new [transition], instantly interrupting any ongoing transition if there was one. */
@@ -141,3 +233,62 @@
         }
     }
 }
+
+/**
+ * A [SceneTransitionLayout] whose current scene/source of truth is hoisted (its current value comes
+ * from outside).
+ */
+internal class HoistedSceneTransitionLayoutScene(
+    initialScene: SceneKey,
+    override var transitions: SceneTransitions,
+    private var changeScene: (SceneKey) -> Unit,
+) : BaseSceneTransitionLayoutState(initialScene) {
+    private val targetSceneChannel = Channel<SceneKey>(Channel.CONFLATED)
+
+    override fun CoroutineScope.onChangeScene(scene: SceneKey) = changeScene(scene)
+
+    @Composable
+    fun update(
+        currentScene: SceneKey,
+        onChangeScene: (SceneKey) -> Unit,
+        transitions: SceneTransitions,
+    ) {
+        SideEffect {
+            this.changeScene = onChangeScene
+            this.transitions = transitions
+
+            targetSceneChannel.trySend(currentScene)
+        }
+
+        LaunchedEffect(targetSceneChannel) {
+            for (newKey in targetSceneChannel) {
+                // Inspired by AnimateAsState.kt: let's poll the last value to avoid being one frame
+                // late.
+                val newKey = targetSceneChannel.tryReceive().getOrNull() ?: newKey
+                animateToScene(layoutState = this@HoistedSceneTransitionLayoutScene, newKey)
+            }
+        }
+    }
+}
+
+/** A [MutableSceneTransitionLayoutState] that holds the value for the current scene. */
+internal class MutableSceneTransitionLayoutStateImpl(
+    initialScene: SceneKey,
+    override var transitions: SceneTransitions,
+) : MutableSceneTransitionLayoutState, BaseSceneTransitionLayoutState(initialScene) {
+    override fun setTargetScene(
+        targetScene: SceneKey,
+        coroutineScope: CoroutineScope
+    ): TransitionState.Transition? {
+        return with(this) {
+            coroutineScope.animateToScene(
+                layoutState = this@MutableSceneTransitionLayoutStateImpl,
+                target = targetScene,
+            )
+        }
+    }
+
+    override fun CoroutineScope.onChangeScene(scene: SceneKey) {
+        setTargetScene(scene, coroutineScope = this)
+    }
+}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt
index dfa2a9a..dc8505c 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt
@@ -119,14 +119,8 @@
      *
      * @param enabled whether the matched element(s) should actually be shared in this transition.
      *   Defaults to true.
-     * @param scenePicker the [SharedElementScenePicker] to use when deciding in which scene we
-     *   should draw or compose this shared element.
      */
-    fun sharedElement(
-        matcher: ElementMatcher,
-        enabled: Boolean = true,
-        scenePicker: SharedElementScenePicker = DefaultSharedElementScenePicker,
-    )
+    fun sharedElement(matcher: ElementMatcher, enabled: Boolean = true)
 
     /**
      * Adds the transformations in [builder] but in reversed order. This allows you to partially
@@ -136,37 +130,65 @@
     fun reversed(builder: TransitionBuilder.() -> Unit)
 }
 
-interface SharedElementScenePicker {
+/**
+ * An interface to decide where we should draw shared Elements or compose MovableElements.
+ *
+ * @see DefaultElementScenePicker
+ * @see HighestZIndexScenePicker
+ * @see LowestZIndexScenePicker
+ * @see MovableElementScenePicker
+ */
+interface ElementScenePicker {
     /**
      * Return the scene in which [element] should be drawn (when using `Modifier.element(key)`) or
-     * composed (when using `MovableElement(key)`) during the transition from [fromScene] to
-     * [toScene].
+     * composed (when using `MovableElement(key)`) during the given [transition].
+     *
+     * Important: For [MovableElements][SceneScope.MovableElement], this scene picker will *always*
+     * be used during transitions to decide whether we should compose that element in a given scene
+     * or not. Therefore, you should make sure that the returned [SceneKey] contains the movable
+     * element, otherwise that element will not be composed in any scene during the transition.
      */
     fun sceneDuringTransition(
         element: ElementKey,
-        fromScene: SceneKey,
-        toScene: SceneKey,
-        progress: () -> Float,
+        transition: TransitionState.Transition,
         fromSceneZIndex: Float,
         toSceneZIndex: Float,
     ): SceneKey
-}
 
-object DefaultSharedElementScenePicker : SharedElementScenePicker {
-    override fun sceneDuringTransition(
+    /**
+     * Return [transition.fromScene] if it is in [scenes] and [transition.toScene] is not, or return
+     * [transition.toScene] if it is in [scenes] and [transition.fromScene] is not, otherwise throw
+     * an exception (i.e. if neither or both of fromScene and toScene are in [scenes]).
+     *
+     * This function can be useful when computing the scene in which a movable element should be
+     * composed.
+     */
+    fun pickSingleSceneIn(
+        scenes: Set<SceneKey>,
+        transition: TransitionState.Transition,
         element: ElementKey,
-        fromScene: SceneKey,
-        toScene: SceneKey,
-        progress: () -> Float,
-        fromSceneZIndex: Float,
-        toSceneZIndex: Float
     ): SceneKey {
-        // By default shared elements are drawn in the highest scene possible, unless it is a
-        // background.
-        return if (
-            (fromSceneZIndex > toSceneZIndex && !element.isBackground) ||
-                (fromSceneZIndex < toSceneZIndex && element.isBackground)
-        ) {
+        val fromScene = transition.fromScene
+        val toScene = transition.toScene
+        val fromSceneInScenes = scenes.contains(fromScene)
+        val toSceneInScenes = scenes.contains(toScene)
+        if (fromSceneInScenes && toSceneInScenes) {
+            error(
+                "Element $element can be in both $fromScene and $toScene. You should add a " +
+                    "special case for this transition before calling pickSingleSceneIn()."
+            )
+        }
+
+        if (!fromSceneInScenes && !toSceneInScenes) {
+            error(
+                "Element $element can be neither in $fromScene and $toScene. This either means " +
+                    "that you should add one of them in the scenes set passed to " +
+                    "pickSingleSceneIn(), or there is an internal error and this element was " +
+                    "composed when it shouldn't be."
+            )
+        }
+
+        return if (fromSceneInScenes) {
             fromScene
         } else {
             toScene
@@ -174,6 +196,66 @@
     }
 }
 
+/** An [ElementScenePicker] that draws/composes elements in the scene with the highest z-order. */
+object HighestZIndexScenePicker : ElementScenePicker {
+    override fun sceneDuringTransition(
+        element: ElementKey,
+        transition: TransitionState.Transition,
+        fromSceneZIndex: Float,
+        toSceneZIndex: Float
+    ): SceneKey {
+        return if (fromSceneZIndex > toSceneZIndex) {
+            transition.fromScene
+        } else {
+            transition.toScene
+        }
+    }
+}
+
+/** An [ElementScenePicker] that draws/composes elements in the scene with the lowest z-order. */
+object LowestZIndexScenePicker : ElementScenePicker {
+    override fun sceneDuringTransition(
+        element: ElementKey,
+        transition: TransitionState.Transition,
+        fromSceneZIndex: Float,
+        toSceneZIndex: Float
+    ): SceneKey {
+        return if (fromSceneZIndex < toSceneZIndex) {
+            transition.fromScene
+        } else {
+            transition.toScene
+        }
+    }
+}
+
+/**
+ * An [ElementScenePicker] that draws/composes elements in the scene we are transitioning to, iff
+ * that scene is in [scenes].
+ *
+ * This picker can be useful for movable elements whose content size depends on its content (because
+ * it wraps it) in at least one scene. That way, the target size of the MovableElement will be
+ * computed in the scene we are going to and, given that this element was probably already composed
+ * in the scene we are going from before starting the transition, the interpolated size of the
+ * movable element during the transition should be correct.
+ *
+ * The downside of this picker is that the zIndex of the element when going from scene A to scene B
+ * is not the same as when going from scene B to scene A, so it's not usable in situations where
+ * z-ordering during the transition matters.
+ */
+class MovableElementScenePicker(private val scenes: Set<SceneKey>) : ElementScenePicker {
+    override fun sceneDuringTransition(
+        element: ElementKey,
+        transition: TransitionState.Transition,
+        fromSceneZIndex: Float,
+        toSceneZIndex: Float,
+    ): SceneKey {
+        return if (scenes.contains(transition.toScene)) transition.toScene else transition.fromScene
+    }
+}
+
+/** The default [ElementScenePicker]. */
+val DefaultElementScenePicker = HighestZIndexScenePicker
+
 @TransitionDsl
 interface PropertyTransformationBuilder {
     /**
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt
index 7046866..b96f9be 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt
@@ -108,12 +108,8 @@
         range = null
     }
 
-    override fun sharedElement(
-        matcher: ElementMatcher,
-        enabled: Boolean,
-        scenePicker: SharedElementScenePicker,
-    ) {
-        transformations.add(SharedElementTransformation(matcher, enabled, scenePicker))
+    override fun sharedElement(matcher: ElementMatcher, enabled: Boolean) {
+        transformations.add(SharedElementTransformation(matcher, enabled))
     }
 
     override fun timestampRange(
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredSize.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredSize.kt
index 40c814e..124ec29 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredSize.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredSize.kt
@@ -36,12 +36,12 @@
         layoutImpl: SceneTransitionLayoutImpl,
         scene: Scene,
         element: Element,
-        sceneValues: Element.TargetValues,
+        sceneState: Element.SceneState,
         transition: TransitionState.Transition,
         value: IntSize,
     ): IntSize {
         fun anchorSizeIn(scene: SceneKey): IntSize {
-            val size = layoutImpl.elements[anchor]?.sceneValues?.get(scene)?.targetSize
+            val size = layoutImpl.elements[anchor]?.sceneStates?.get(scene)?.targetSize
             return if (size != null && size != Element.SizeUnspecified) {
                 IntSize(
                     width = if (anchorWidth) size.width else value.width,
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredTranslate.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredTranslate.kt
index a1d6319..7aa702b 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredTranslate.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredTranslate.kt
@@ -35,13 +35,13 @@
         layoutImpl: SceneTransitionLayoutImpl,
         scene: Scene,
         element: Element,
-        sceneValues: Element.TargetValues,
+        sceneState: Element.SceneState,
         transition: TransitionState.Transition,
         value: Offset,
     ): Offset {
         val anchor = layoutImpl.elements[anchor] ?: return value
         fun anchorOffsetIn(scene: SceneKey): Offset? {
-            return anchor.sceneValues[scene]?.targetOffset?.takeIf { it.isSpecified }
+            return anchor.sceneStates[scene]?.targetOffset?.takeIf { it.isSpecified }
         }
 
         // [element] will move the same amount as [anchor] does.
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/DrawScale.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/DrawScale.kt
index d1cf8ee..6704a3b 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/DrawScale.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/DrawScale.kt
@@ -39,7 +39,7 @@
         layoutImpl: SceneTransitionLayoutImpl,
         scene: Scene,
         element: Element,
-        sceneValues: Element.TargetValues,
+        sceneState: Element.SceneState,
         transition: TransitionState.Transition,
         value: Scale,
     ): Scale {
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/EdgeTranslate.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/EdgeTranslate.kt
index 70534dd..191a8fb 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/EdgeTranslate.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/EdgeTranslate.kt
@@ -34,12 +34,12 @@
         layoutImpl: SceneTransitionLayoutImpl,
         scene: Scene,
         element: Element,
-        sceneValues: Element.TargetValues,
+        sceneState: Element.SceneState,
         transition: TransitionState.Transition,
         value: Offset
     ): Offset {
         val sceneSize = scene.targetSize
-        val elementSize = sceneValues.targetSize
+        val elementSize = sceneState.targetSize
         if (elementSize == Element.SizeUnspecified) {
             return value
         }
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Fade.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Fade.kt
index 17032dc..41f626e 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Fade.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Fade.kt
@@ -30,7 +30,7 @@
         layoutImpl: SceneTransitionLayoutImpl,
         scene: Scene,
         element: Element,
-        sceneValues: Element.TargetValues,
+        sceneState: Element.SceneState,
         transition: TransitionState.Transition,
         value: Float
     ): Float {
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/ScaleSize.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/ScaleSize.kt
index 233ae59..f5207dc 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/ScaleSize.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/ScaleSize.kt
@@ -37,7 +37,7 @@
         layoutImpl: SceneTransitionLayoutImpl,
         scene: Scene,
         element: Element,
-        sceneValues: Element.TargetValues,
+        sceneState: Element.SceneState,
         transition: TransitionState.Transition,
         value: IntSize,
     ): IntSize {
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Transformation.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Transformation.kt
index 0cd11b9..04254fb 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Transformation.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Transformation.kt
@@ -20,7 +20,6 @@
 import com.android.compose.animation.scene.ElementMatcher
 import com.android.compose.animation.scene.Scene
 import com.android.compose.animation.scene.SceneTransitionLayoutImpl
-import com.android.compose.animation.scene.SharedElementScenePicker
 import com.android.compose.animation.scene.TransitionState
 
 /** A transformation applied to one or more elements during a transition. */
@@ -48,7 +47,6 @@
 internal class SharedElementTransformation(
     override val matcher: ElementMatcher,
     internal val enabled: Boolean,
-    internal val scenePicker: SharedElementScenePicker,
 ) : Transformation
 
 /** A transformation that changes the value of an element property, like its size or offset. */
@@ -62,7 +60,7 @@
         layoutImpl: SceneTransitionLayoutImpl,
         scene: Scene,
         element: Element,
-        sceneValues: Element.TargetValues,
+        sceneState: Element.SceneState,
         transition: TransitionState.Transition,
         value: T,
     ): T
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Translate.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Translate.kt
index 864b937..04d5914 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Translate.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Translate.kt
@@ -35,7 +35,7 @@
         layoutImpl: SceneTransitionLayoutImpl,
         scene: Scene,
         element: Element,
-        sceneValues: Element.TargetValues,
+        sceneState: Element.SceneState,
         transition: TransitionState.Transition,
         value: Offset,
     ): Offset {
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/AnimatedSharedAsStateTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/AnimatedSharedAsStateTest.kt
index 5473186..a116501 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/AnimatedSharedAsStateTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/AnimatedSharedAsStateTest.kt
@@ -18,10 +18,11 @@
 
 import androidx.compose.animation.core.LinearEasing
 import androidx.compose.animation.core.tween
-import androidx.compose.foundation.layout.Box
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.SideEffect
 import androidx.compose.runtime.getValue
+import androidx.compose.runtime.snapshotFlow
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.lerp
@@ -32,6 +33,7 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import com.android.compose.ui.util.lerp
 import com.google.common.truth.Truth.assertThat
+import org.junit.Assert.assertThrows
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -62,17 +64,17 @@
         onCurrentValueChanged: (Values) -> Unit,
     ) {
         val key = TestElements.Foo
-        Box(Modifier.element(key)) {
-            val int by animateSharedIntAsState(targetValues.int, TestValues.Value1, key)
-            val float by animateSharedFloatAsState(targetValues.float, TestValues.Value2, key)
-            val dp by animateSharedDpAsState(targetValues.dp, TestValues.Value3, key)
-            val color by
-                animateSharedColorAsState(targetValues.color, TestValues.Value4, element = null)
+        Element(key, Modifier) {
+            val int by animateElementIntAsState(targetValues.int, key = TestValues.Value1)
+            val float by animateElementFloatAsState(targetValues.float, key = TestValues.Value2)
+            val dp by animateElementDpAsState(targetValues.dp, key = TestValues.Value3)
+            val color by animateElementColorAsState(targetValues.color, key = TestValues.Value4)
 
-            // Make sure we read the values during composition, so that we recompose and call
-            // onCurrentValueChanged() with the latest values.
-            val currentValues = Values(int, float, dp, color)
-            SideEffect { onCurrentValueChanged(currentValues) }
+            content {
+                LaunchedEffect(Unit) {
+                    snapshotFlow { Values(int, float, dp, color) }.collect(onCurrentValueChanged)
+                }
+            }
         }
     }
 
@@ -83,30 +85,34 @@
     ) {
         val key = TestElements.Foo
         MovableElement(key = key, Modifier) {
-            val int by
-                animateSharedIntAsState(targetValues.int, debugName = TestValues.Value1.debugName)
-            val float by
-                animateSharedFloatAsState(
-                    targetValues.float,
-                    debugName = TestValues.Value2.debugName
-                )
-            val dp by
-                animateSharedDpAsState(targetValues.dp, debugName = TestValues.Value3.debugName)
-            val color by
-                animateSharedColorAsState(
-                    targetValues.color,
-                    debugName = TestValues.Value4.debugName
-                )
+            val int by animateElementIntAsState(targetValues.int, key = TestValues.Value1)
+            val float by animateElementFloatAsState(targetValues.float, key = TestValues.Value2)
+            val dp by animateElementDpAsState(targetValues.dp, key = TestValues.Value3)
+            val color by animateElementColorAsState(targetValues.color, key = TestValues.Value4)
 
-            // Make sure we read the values during composition, so that we recompose and call
-            // onCurrentValueChanged() with the latest values.
-            val currentValues = Values(int, float, dp, color)
-            SideEffect { onCurrentValueChanged(currentValues) }
+            LaunchedEffect(Unit) {
+                snapshotFlow { Values(int, float, dp, color) }.collect(onCurrentValueChanged)
+            }
+        }
+    }
+
+    @Composable
+    private fun SceneScope.SceneValues(
+        targetValues: Values,
+        onCurrentValueChanged: (Values) -> Unit,
+    ) {
+        val int by animateSceneIntAsState(targetValues.int, key = TestValues.Value1)
+        val float by animateSceneFloatAsState(targetValues.float, key = TestValues.Value2)
+        val dp by animateSceneDpAsState(targetValues.dp, key = TestValues.Value3)
+        val color by animateSceneColorAsState(targetValues.color, key = TestValues.Value4)
+
+        LaunchedEffect(Unit) {
+            snapshotFlow { Values(int, float, dp, color) }.collect(onCurrentValueChanged)
         }
     }
 
     @Test
-    fun animateSharedValues() {
+    fun animateElementValues() {
         val fromValues = Values(int = 0, float = 0f, dp = 0.dp, color = Color.Red)
         val toValues = Values(int = 100, float = 100f, dp = 100.dp, color = Color.Blue)
 
@@ -194,24 +200,183 @@
             }
 
             at(16) {
-                // Given that we use MovableElement here, animateSharedXAsState is composed only
-                // once, in the highest scene (in this case, in toScene).
-                assertThat(lastValueInFrom).isEqualTo(fromValues)
+                assertThat(lastValueInFrom).isEqualTo(lerp(fromValues, toValues, fraction = 0.25f))
                 assertThat(lastValueInTo).isEqualTo(lerp(fromValues, toValues, fraction = 0.25f))
             }
 
             at(32) {
-                assertThat(lastValueInFrom).isEqualTo(fromValues)
+                assertThat(lastValueInFrom).isEqualTo(lerp(fromValues, toValues, fraction = 0.5f))
                 assertThat(lastValueInTo).isEqualTo(lerp(fromValues, toValues, fraction = 0.5f))
             }
 
             at(48) {
-                assertThat(lastValueInFrom).isEqualTo(fromValues)
+                assertThat(lastValueInFrom).isEqualTo(lerp(fromValues, toValues, fraction = 0.75f))
                 assertThat(lastValueInTo).isEqualTo(lerp(fromValues, toValues, fraction = 0.75f))
             }
 
             after {
+                assertThat(lastValueInFrom).isEqualTo(toValues)
+                assertThat(lastValueInTo).isEqualTo(toValues)
+            }
+        }
+    }
+
+    @Test
+    fun animateSceneValues() {
+        val fromValues = Values(int = 0, float = 0f, dp = 0.dp, color = Color.Red)
+        val toValues = Values(int = 100, float = 100f, dp = 100.dp, color = Color.Blue)
+
+        var lastValueInFrom = fromValues
+        var lastValueInTo = toValues
+
+        rule.testTransition(
+            fromSceneContent = {
+                SceneValues(
+                    targetValues = fromValues,
+                    onCurrentValueChanged = { lastValueInFrom = it }
+                )
+            },
+            toSceneContent = {
+                SceneValues(targetValues = toValues, onCurrentValueChanged = { lastValueInTo = it })
+            },
+            transition = {
+                // The transition lasts 64ms = 4 frames.
+                spec = tween(durationMillis = 16 * 4, easing = LinearEasing)
+            },
+            fromScene = TestScenes.SceneA,
+            toScene = TestScenes.SceneB,
+        ) {
+            before {
                 assertThat(lastValueInFrom).isEqualTo(fromValues)
+
+                // to was not composed yet, so lastValueInTo was not set yet.
+                assertThat(lastValueInTo).isEqualTo(toValues)
+            }
+
+            at(16) {
+                // Given that we use scene values here, animateSceneXAsState is composed in both
+                // scenes and values should be interpolated with the transition fraction.
+                val expectedValues = lerp(fromValues, toValues, fraction = 0.25f)
+                assertThat(lastValueInFrom).isEqualTo(expectedValues)
+                assertThat(lastValueInTo).isEqualTo(expectedValues)
+            }
+
+            at(32) {
+                val expectedValues = lerp(fromValues, toValues, fraction = 0.5f)
+                assertThat(lastValueInFrom).isEqualTo(expectedValues)
+                assertThat(lastValueInTo).isEqualTo(expectedValues)
+            }
+
+            at(48) {
+                val expectedValues = lerp(fromValues, toValues, fraction = 0.75f)
+                assertThat(lastValueInFrom).isEqualTo(expectedValues)
+                assertThat(lastValueInTo).isEqualTo(expectedValues)
+            }
+
+            after {
+                assertThat(lastValueInFrom).isEqualTo(toValues)
+                assertThat(lastValueInTo).isEqualTo(toValues)
+            }
+        }
+    }
+
+    @Test
+    fun readingAnimatedStateValueDuringCompositionThrows() {
+        assertThrows(IllegalStateException::class.java) {
+            rule.testTransition(
+                fromSceneContent = { animateSceneIntAsState(0, TestValues.Value1).value },
+                toSceneContent = {},
+                transition = {},
+            ) {}
+        }
+    }
+
+    @Test
+    fun readingAnimatedStateValueDuringCompositionIsStillPossible() {
+        @Composable
+        fun SceneScope.SceneValuesDuringComposition(
+            targetValues: Values,
+            onCurrentValueChanged: (Values) -> Unit,
+        ) {
+            val int by
+                animateSceneIntAsState(targetValues.int, key = TestValues.Value1)
+                    .unsafeCompositionState(targetValues.int)
+            val float by
+                animateSceneFloatAsState(targetValues.float, key = TestValues.Value2)
+                    .unsafeCompositionState(targetValues.float)
+            val dp by
+                animateSceneDpAsState(targetValues.dp, key = TestValues.Value3)
+                    .unsafeCompositionState(targetValues.dp)
+            val color by
+                animateSceneColorAsState(targetValues.color, key = TestValues.Value4)
+                    .unsafeCompositionState(targetValues.color)
+
+            val values = Values(int, float, dp, color)
+            SideEffect { onCurrentValueChanged(values) }
+        }
+
+        val fromValues = Values(int = 0, float = 0f, dp = 0.dp, color = Color.Red)
+        val toValues = Values(int = 100, float = 100f, dp = 100.dp, color = Color.Blue)
+
+        var lastValueInFrom = fromValues
+        var lastValueInTo = toValues
+
+        rule.testTransition(
+            fromSceneContent = {
+                SceneValuesDuringComposition(
+                    targetValues = fromValues,
+                    onCurrentValueChanged = { lastValueInFrom = it },
+                )
+            },
+            toSceneContent = {
+                SceneValuesDuringComposition(
+                    targetValues = toValues,
+                    onCurrentValueChanged = { lastValueInTo = it },
+                )
+            },
+            transition = {
+                // The transition lasts 64ms = 4 frames.
+                spec = tween(durationMillis = 16 * 4, easing = LinearEasing)
+            },
+        ) {
+            before {
+                assertThat(lastValueInFrom).isEqualTo(fromValues)
+
+                // to was not composed yet, so lastValueInTo was not set yet.
+                assertThat(lastValueInTo).isEqualTo(toValues)
+            }
+
+            at(16) {
+                // Because we are using unsafeCompositionState(), values are one frame behind their
+                // expected progress so at this first frame we are at progress = 0% instead of 25%.
+                val expectedValues = lerp(fromValues, toValues, fraction = 0f)
+                assertThat(lastValueInFrom).isEqualTo(expectedValues)
+                assertThat(lastValueInTo).isEqualTo(expectedValues)
+            }
+
+            at(32) {
+                // One frame behind, so 25% instead of 50%.
+                val expectedValues = lerp(fromValues, toValues, fraction = 0.25f)
+                assertThat(lastValueInFrom).isEqualTo(expectedValues)
+                assertThat(lastValueInTo).isEqualTo(expectedValues)
+            }
+
+            at(48) {
+                // One frame behind, so 50% instead of 75%.
+                val expectedValues = lerp(fromValues, toValues, fraction = 0.5f)
+                assertThat(lastValueInFrom).isEqualTo(expectedValues)
+                assertThat(lastValueInTo).isEqualTo(expectedValues)
+            }
+
+            after {
+                // from should have been last composed at progress = 100% before it is removed from
+                // composition, but given that we are one frame behind the last values are stuck at
+                // 75%.
+                assertThat(lastValueInFrom).isEqualTo(lerp(fromValues, toValues, fraction = 0.75f))
+
+                // The after {} block resumes the clock and will run as many frames as necessary so
+                // that the application is idle, so the toScene settle to the idle state and to the
+                // final values.
                 assertThat(lastValueInTo).isEqualTo(toValues)
             }
         }
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementScenePickerTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementScenePickerTest.kt
new file mode 100644
index 0000000..3b022e8
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementScenePickerTest.kt
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.compose.animation.scene
+
+import androidx.compose.animation.core.LinearEasing
+import androidx.compose.animation.core.tween
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.size
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.assertIsNotDisplayed
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.unit.dp
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class ElementScenePickerTest {
+    @get:Rule val rule = createComposeRule()
+
+    @Test
+    fun highestZIndexPicker() {
+        val key = ElementKey("TestElement", scenePicker = HighestZIndexScenePicker)
+        rule.testTransition(
+            fromSceneContent = { Box(Modifier.element(key).size(10.dp)) },
+            toSceneContent = { Box(Modifier.element(key).size(10.dp)) },
+            transition = { spec = tween(4 * 16, easing = LinearEasing) },
+            fromScene = TestScenes.SceneA,
+            toScene = TestScenes.SceneB,
+        ) {
+            before {
+                onElement(key, TestScenes.SceneA).assertIsDisplayed()
+                onElement(key, TestScenes.SceneB).assertDoesNotExist()
+            }
+            at(32) {
+                // Scene B has the highest index, so the element is placed only there.
+                onElement(key, TestScenes.SceneA).assertExists().assertIsNotDisplayed()
+                onElement(key, TestScenes.SceneB).assertIsDisplayed()
+            }
+            after {
+                onElement(key, TestScenes.SceneA).assertDoesNotExist()
+                onElement(key, TestScenes.SceneB).assertIsDisplayed()
+            }
+        }
+    }
+
+    @Test
+    fun lowestZIndexPicker() {
+        val key = ElementKey("TestElement", scenePicker = LowestZIndexScenePicker)
+        rule.testTransition(
+            fromSceneContent = { Box(Modifier.element(key).size(10.dp)) },
+            toSceneContent = { Box(Modifier.element(key).size(10.dp)) },
+            transition = { spec = tween(4 * 16, easing = LinearEasing) },
+            fromScene = TestScenes.SceneA,
+            toScene = TestScenes.SceneB,
+        ) {
+            before {
+                onElement(key, TestScenes.SceneA).assertIsDisplayed()
+                onElement(key, TestScenes.SceneB).assertDoesNotExist()
+            }
+            at(32) {
+                // Scene A has the lowest index, so the element is placed only there.
+                onElement(key, TestScenes.SceneA).assertIsDisplayed()
+                onElement(key, TestScenes.SceneB).assertExists().assertIsNotDisplayed()
+            }
+            after {
+                onElement(key, TestScenes.SceneA).assertDoesNotExist()
+                onElement(key, TestScenes.SceneB).assertIsDisplayed()
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
index da5a0a0..c0de87a 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
@@ -16,7 +16,6 @@
 
 package com.android.compose.animation.scene
 
-import androidx.compose.animation.core.LinearEasing
 import androidx.compose.animation.core.tween
 import androidx.compose.foundation.ExperimentalFoundationApi
 import androidx.compose.foundation.layout.Box
@@ -35,17 +34,11 @@
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.ExperimentalComposeUiApi
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.layout.intermediateLayout
-import androidx.compose.ui.platform.LocalDensity
 import androidx.compose.ui.test.junit4.createComposeRule
-import androidx.compose.ui.unit.Density
 import androidx.compose.ui.unit.Dp
-import androidx.compose.ui.unit.DpOffset
 import androidx.compose.ui.unit.dp
 import androidx.test.ext.junit.runners.AndroidJUnit4
-import com.android.compose.test.subjects.DpOffsetSubject
-import com.android.compose.test.subjects.assertThat
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.launch
@@ -263,8 +256,11 @@
 
         rule.setContent {
             SceneTransitionLayoutForTesting(
-                currentScene = currentScene,
-                onChangeScene = { currentScene = it },
+                state =
+                    updateSceneTransitionLayoutState(
+                        currentScene = currentScene,
+                        onChangeScene = { currentScene = it }
+                    ),
                 onLayoutImpl = { nullableLayoutImpl = it },
             ) {
                 scene(TestScenes.SceneA) { /* Nothing */}
@@ -306,7 +302,7 @@
 
         assertThat(layoutImpl.elements.keys).containsExactly(key)
         val element = layoutImpl.elements.getValue(key)
-        assertThat(element.sceneValues.keys).containsExactly(TestScenes.SceneB)
+        assertThat(element.sceneStates.keys).containsExactly(TestScenes.SceneB)
 
         // Scene C, state 0: the same element is reused.
         currentScene = TestScenes.SceneC
@@ -315,7 +311,7 @@
 
         assertThat(layoutImpl.elements.keys).containsExactly(key)
         assertThat(layoutImpl.elements.getValue(key)).isSameInstanceAs(element)
-        assertThat(element.sceneValues.keys).containsExactly(TestScenes.SceneC)
+        assertThat(element.sceneStates.keys).containsExactly(TestScenes.SceneC)
 
         // Scene C, state 1: the same element is reused.
         sceneCState = 1
@@ -323,7 +319,7 @@
 
         assertThat(layoutImpl.elements.keys).containsExactly(key)
         assertThat(layoutImpl.elements.getValue(key)).isSameInstanceAs(element)
-        assertThat(element.sceneValues.keys).containsExactly(TestScenes.SceneC)
+        assertThat(element.sceneStates.keys).containsExactly(TestScenes.SceneC)
 
         // Scene D, state 0: the same element is reused.
         currentScene = TestScenes.SceneD
@@ -332,7 +328,7 @@
 
         assertThat(layoutImpl.elements.keys).containsExactly(key)
         assertThat(layoutImpl.elements.getValue(key)).isSameInstanceAs(element)
-        assertThat(element.sceneValues.keys).containsExactly(TestScenes.SceneD)
+        assertThat(element.sceneStates.keys).containsExactly(TestScenes.SceneD)
 
         // Scene D, state 1: the same element is reused.
         sceneDState = 1
@@ -340,13 +336,13 @@
 
         assertThat(layoutImpl.elements.keys).containsExactly(key)
         assertThat(layoutImpl.elements.getValue(key)).isSameInstanceAs(element)
-        assertThat(element.sceneValues.keys).containsExactly(TestScenes.SceneD)
+        assertThat(element.sceneStates.keys).containsExactly(TestScenes.SceneD)
 
         // Scene D, state 2: the element is removed from the map.
         sceneDState = 2
         rule.waitForIdle()
 
-        assertThat(element.sceneValues).isEmpty()
+        assertThat(element.sceneStates).isEmpty()
         assertThat(layoutImpl.elements).isEmpty()
     }
 
@@ -428,8 +424,11 @@
 
         rule.setContent {
             SceneTransitionLayoutForTesting(
-                currentScene = TestScenes.SceneA,
-                onChangeScene = {},
+                state =
+                    updateSceneTransitionLayoutState(
+                        currentScene = TestScenes.SceneA,
+                        onChangeScene = {}
+                    ),
                 onLayoutImpl = { nullableLayoutImpl = it },
             ) {
                 scene(TestScenes.SceneA) { Box(Modifier.element(key)) }
@@ -442,7 +441,7 @@
         // There is only Foo in the elements map.
         assertThat(layoutImpl.elements.keys).containsExactly(TestElements.Foo)
         val fooElement = layoutImpl.elements.getValue(TestElements.Foo)
-        assertThat(fooElement.sceneValues.keys).containsExactly(TestScenes.SceneA)
+        assertThat(fooElement.sceneStates.keys).containsExactly(TestScenes.SceneA)
 
         key = TestElements.Bar
 
@@ -450,8 +449,8 @@
         rule.waitForIdle()
         assertThat(layoutImpl.elements.keys).containsExactly(TestElements.Bar)
         val barElement = layoutImpl.elements.getValue(TestElements.Bar)
-        assertThat(barElement.sceneValues.keys).containsExactly(TestScenes.SceneA)
-        assertThat(fooElement.sceneValues).isEmpty()
+        assertThat(barElement.sceneStates.keys).containsExactly(TestScenes.SceneA)
+        assertThat(fooElement.sceneStates).isEmpty()
     }
 
     @Test
@@ -478,8 +477,11 @@
             scrollScope = rememberCoroutineScope()
 
             SceneTransitionLayoutForTesting(
-                currentScene = TestScenes.SceneA,
-                onChangeScene = {},
+                state =
+                    updateSceneTransitionLayoutState(
+                        currentScene = TestScenes.SceneA,
+                        onChangeScene = {}
+                    ),
                 onLayoutImpl = { nullableLayoutImpl = it },
             ) {
                 scene(TestScenes.SceneA) {
@@ -487,7 +489,7 @@
                     // page should be composed.
                     HorizontalPager(
                         pagerState,
-                        beyondBoundsPageCount = 0,
+                        outOfBoundsPageCount = 0,
                     ) { page ->
                         when (page) {
                             0 -> Box(Modifier.element(TestElements.Foo).fillMaxSize())
@@ -505,7 +507,7 @@
         // There is only Foo in the elements map.
         assertThat(layoutImpl.elements.keys).containsExactly(TestElements.Foo)
         val element = layoutImpl.elements.getValue(TestElements.Foo)
-        val sceneValues = element.sceneValues
+        val sceneValues = element.sceneStates
         assertThat(sceneValues.keys).containsExactly(TestScenes.SceneA)
 
         // Get the ElementModifier node that should be reused later on when coming back to this
@@ -528,7 +530,7 @@
 
         assertThat(layoutImpl.elements.keys).containsExactly(TestElements.Foo)
         val newElement = layoutImpl.elements.getValue(TestElements.Foo)
-        val newSceneValues = newElement.sceneValues
+        val newSceneValues = newElement.sceneStates
         assertThat(newElement).isNotEqualTo(element)
         assertThat(newSceneValues).isNotEqualTo(sceneValues)
         assertThat(newSceneValues.keys).containsExactly(TestScenes.SceneA)
@@ -565,86 +567,4 @@
             after { assertThat(fooCompositions).isEqualTo(1) }
         }
     }
-
-    @Test
-    fun sharedElementOffsetIsUpdatedEvenWhenNotPlaced() {
-        var nullableLayoutImpl: SceneTransitionLayoutImpl? = null
-        var density: Density? = null
-
-        fun layoutImpl() = nullableLayoutImpl ?: error("nullableLayoutImpl was not set")
-
-        fun density() = density ?: error("density was not set")
-
-        fun Offset.toDpOffset() = with(density()) { DpOffset(x.toDp(), y.toDp()) }
-
-        fun foo() = layoutImpl().elements[TestElements.Foo] ?: error("Foo not in elements map")
-
-        fun Element.lastSharedOffset() = lastSharedValues.offset.toDpOffset()
-
-        fun Element.lastOffsetIn(scene: SceneKey) =
-            (sceneValues[scene] ?: error("$scene not in sceneValues map"))
-                .lastValues
-                .offset
-                .toDpOffset()
-
-        rule.testTransition(
-            from = TestScenes.SceneA,
-            to = TestScenes.SceneB,
-            transitionLayout = { currentScene, onChangeScene ->
-                density = LocalDensity.current
-
-                SceneTransitionLayoutForTesting(
-                    currentScene = currentScene,
-                    onChangeScene = onChangeScene,
-                    onLayoutImpl = { nullableLayoutImpl = it },
-                    transitions =
-                        transitions {
-                            from(TestScenes.SceneA, to = TestScenes.SceneB) {
-                                spec = tween(durationMillis = 4 * 16, easing = LinearEasing)
-                            }
-                        }
-                ) {
-                    scene(TestScenes.SceneA) { Box(Modifier.element(TestElements.Foo)) }
-                    scene(TestScenes.SceneB) {
-                        Box(Modifier.offset(x = 40.dp, y = 80.dp).element(TestElements.Foo))
-                    }
-                }
-            }
-        ) {
-            val tolerance = DpOffsetSubject.DefaultTolerance
-
-            before {
-                val expected = DpOffset(0.dp, 0.dp)
-                assertThat(foo().lastSharedOffset()).isWithin(tolerance).of(expected)
-                assertThat(foo().lastOffsetIn(TestScenes.SceneA)).isWithin(tolerance).of(expected)
-            }
-
-            at(16) {
-                val expected = DpOffset(10.dp, 20.dp)
-                assertThat(foo().lastSharedOffset()).isWithin(tolerance).of(expected)
-                assertThat(foo().lastOffsetIn(TestScenes.SceneA)).isWithin(tolerance).of(expected)
-                assertThat(foo().lastOffsetIn(TestScenes.SceneB)).isWithin(tolerance).of(expected)
-            }
-
-            at(32) {
-                val expected = DpOffset(20.dp, 40.dp)
-                assertThat(foo().lastSharedOffset()).isWithin(tolerance).of(expected)
-                assertThat(foo().lastOffsetIn(TestScenes.SceneA)).isWithin(tolerance).of(expected)
-                assertThat(foo().lastOffsetIn(TestScenes.SceneB)).isWithin(tolerance).of(expected)
-            }
-
-            at(48) {
-                val expected = DpOffset(30.dp, 60.dp)
-                assertThat(foo().lastSharedOffset()).isWithin(tolerance).of(expected)
-                assertThat(foo().lastOffsetIn(TestScenes.SceneA)).isWithin(tolerance).of(expected)
-                assertThat(foo().lastOffsetIn(TestScenes.SceneB)).isWithin(tolerance).of(expected)
-            }
-
-            after {
-                val expected = DpOffset(40.dp, 80.dp)
-                assertThat(foo().lastSharedOffset()).isWithin(tolerance).of(expected)
-                assertThat(foo().lastOffsetIn(TestScenes.SceneB)).isWithin(tolerance).of(expected)
-            }
-        }
-    }
 }
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MovableElementScenePickerTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MovableElementScenePickerTest.kt
new file mode 100644
index 0000000..fb46a34
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MovableElementScenePickerTest.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.compose.animation.scene
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class MovableElementScenePickerTest {
+    @Test
+    fun toSceneInScenes() {
+        val picker = MovableElementScenePicker(scenes = setOf(TestScenes.SceneA, TestScenes.SceneB))
+        assertThat(
+                picker.sceneDuringTransition(
+                    TestElements.Foo,
+                    transition(from = TestScenes.SceneA, to = TestScenes.SceneB),
+                    fromSceneZIndex = 0f,
+                    toSceneZIndex = 1f,
+                )
+            )
+            .isEqualTo(TestScenes.SceneB)
+    }
+
+    @Test
+    fun toSceneNotInScenes() {
+        val picker = MovableElementScenePicker(scenes = emptySet())
+        assertThat(
+                picker.sceneDuringTransition(
+                    TestElements.Foo,
+                    transition(from = TestScenes.SceneA, to = TestScenes.SceneB),
+                    fromSceneZIndex = 0f,
+                    toSceneZIndex = 1f,
+                )
+            )
+            .isEqualTo(TestScenes.SceneA)
+    }
+}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MovableElementTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MovableElementTest.kt
index 3cd65cd..35cb691 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MovableElementTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MovableElementTest.kt
@@ -28,19 +28,24 @@
 import androidx.compose.runtime.mutableIntStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.test.assertIsDisplayed
 import androidx.compose.ui.test.assertIsNotDisplayed
+import androidx.compose.ui.test.assertPositionInRootIsEqualTo
 import androidx.compose.ui.test.hasParent
 import androidx.compose.ui.test.hasText
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onAllNodesWithText
+import androidx.compose.ui.test.onNodeWithTag
 import androidx.compose.ui.test.onNodeWithText
 import androidx.compose.ui.test.performClick
 import androidx.compose.ui.unit.dp
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import com.android.compose.test.assertSizeIsEqualTo
 import com.google.common.truth.Truth.assertThat
+import org.junit.Ignore
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -58,7 +63,7 @@
 
     @Composable
     private fun SceneScope.MovableCounter(key: ElementKey, modifier: Modifier) {
-        MovableElement(key, modifier) { Counter() }
+        MovableElement(key, modifier) { content { Counter() } }
     }
 
     @Test
@@ -142,39 +147,37 @@
 
     @Test
     fun movableElementIsMovedAndComposedOnlyOnce() {
-        rule.testTransition(
-            fromSceneContent = { MovableCounter(TestElements.Foo, Modifier.size(50.dp)) },
-            toSceneContent = { MovableCounter(TestElements.Foo, Modifier.size(100.dp)) },
-            transition = {
-                spec = tween(durationMillis = 16 * 4, easing = LinearEasing)
-                sharedElement(
-                    TestElements.Foo,
-                    scenePicker =
-                        object : SharedElementScenePicker {
-                            override fun sceneDuringTransition(
-                                element: ElementKey,
-                                fromScene: SceneKey,
-                                toScene: SceneKey,
-                                progress: () -> Float,
-                                fromSceneZIndex: Float,
-                                toSceneZIndex: Float
-                            ): SceneKey {
-                                assertThat(fromScene).isEqualTo(TestScenes.SceneA)
-                                assertThat(toScene).isEqualTo(TestScenes.SceneB)
-                                assertThat(fromSceneZIndex).isEqualTo(0)
-                                assertThat(toSceneZIndex).isEqualTo(1)
+        val key =
+            ElementKey(
+                "Foo",
+                scenePicker =
+                    object : ElementScenePicker {
+                        override fun sceneDuringTransition(
+                            element: ElementKey,
+                            transition: TransitionState.Transition,
+                            fromSceneZIndex: Float,
+                            toSceneZIndex: Float
+                        ): SceneKey {
+                            assertThat(transition.fromScene).isEqualTo(TestScenes.SceneA)
+                            assertThat(transition.toScene).isEqualTo(TestScenes.SceneB)
+                            assertThat(fromSceneZIndex).isEqualTo(0)
+                            assertThat(toSceneZIndex).isEqualTo(1)
 
-                                // Compose Foo in Scene A if progress < 0.65f, otherwise compose it
-                                // in Scene B.
-                                return if (progress() < 0.65f) {
-                                    TestScenes.SceneA
-                                } else {
-                                    TestScenes.SceneB
-                                }
+                            // Compose Foo in Scene A if progress < 0.65f, otherwise compose it
+                            // in Scene B.
+                            return if (transition.progress < 0.65f) {
+                                TestScenes.SceneA
+                            } else {
+                                TestScenes.SceneB
                             }
                         }
-                )
-            },
+                    }
+            )
+
+        rule.testTransition(
+            fromSceneContent = { MovableCounter(key, Modifier.size(50.dp)) },
+            toSceneContent = { MovableCounter(key, Modifier.size(100.dp)) },
+            transition = { spec = tween(durationMillis = 16 * 4, easing = LinearEasing) },
             fromScene = TestScenes.SceneA,
             toScene = TestScenes.SceneB,
         ) {
@@ -257,4 +260,73 @@
             }
         }
     }
+
+    @Test
+    @Ignore("b/317972419#comment2")
+    fun movableElementContentIsRecomposedIfContentParametersChange() {
+        @Composable
+        fun SceneScope.MovableFoo(text: String, modifier: Modifier = Modifier) {
+            MovableElement(TestElements.Foo, modifier) { content { Text(text) } }
+        }
+
+        rule.testTransition(
+            fromSceneContent = { MovableFoo(text = "fromScene") },
+            toSceneContent = { MovableFoo(text = "toScene") },
+            transition = { spec = tween(durationMillis = 16 * 4, easing = LinearEasing) },
+            fromScene = TestScenes.SceneA,
+            toScene = TestScenes.SceneB,
+        ) {
+            // Before the transition, only fromScene is composed.
+            before {
+                rule.onNodeWithText("fromScene").assertIsDisplayed()
+                rule.onNodeWithText("toScene").assertDoesNotExist()
+            }
+
+            // During the transition, the element is composed in toScene.
+            at(32) {
+                rule.onNodeWithText("fromScene").assertDoesNotExist()
+                rule.onNodeWithText("toScene").assertIsDisplayed()
+            }
+
+            // At the end of the transition, the element is composed in toScene.
+            after {
+                rule.onNodeWithText("fromScene").assertDoesNotExist()
+                rule.onNodeWithText("toScene").assertIsDisplayed()
+            }
+        }
+    }
+
+    @Test
+    fun elementScopeExtendsBoxScope() {
+        rule.setContent {
+            TestSceneScope {
+                Element(TestElements.Foo, Modifier.size(200.dp)) {
+                    content {
+                        Box(Modifier.testTag("bottomEnd").align(Alignment.BottomEnd))
+                        Box(Modifier.testTag("matchParentSize").matchParentSize())
+                    }
+                }
+            }
+        }
+
+        rule.onNodeWithTag("bottomEnd").assertPositionInRootIsEqualTo(200.dp, 200.dp)
+        rule.onNodeWithTag("matchParentSize").assertSizeIsEqualTo(200.dp, 200.dp)
+    }
+
+    @Test
+    fun movableElementScopeExtendsBoxScope() {
+        rule.setContent {
+            TestSceneScope {
+                MovableElement(TestElements.Foo, Modifier.size(200.dp)) {
+                    content {
+                        Box(Modifier.testTag("bottomEnd").align(Alignment.BottomEnd))
+                        Box(Modifier.testTag("matchParentSize").matchParentSize())
+                    }
+                }
+            }
+        }
+
+        rule.onNodeWithTag("bottomEnd").assertPositionInRootIsEqualTo(200.dp, 200.dp)
+        rule.onNodeWithTag("matchParentSize").assertSizeIsEqualTo(200.dp, 200.dp)
+    }
 }
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ObservableTransitionStateTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ObservableTransitionStateTest.kt
index 04b3f8a..0f9b024 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ObservableTransitionStateTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ObservableTransitionStateTest.kt
@@ -32,7 +32,7 @@
 
     @Test
     fun testObservableTransitionState() = runTest {
-        val state = SceneTransitionLayoutState(TestScenes.SceneA)
+        lateinit var state: SceneTransitionLayoutState
 
         // Collect the current observable state into [observableState].
         // TODO(b/290184746): Use collectValues {} once it is extracted into a library that can be
@@ -58,12 +58,14 @@
             from = TestScenes.SceneA,
             to = TestScenes.SceneB,
             transitionLayout = { currentScene, onChangeScene ->
-                SceneTransitionLayout(
-                    currentScene,
-                    onChangeScene,
-                    EmptyTestTransitions,
-                    state = state,
-                ) {
+                state =
+                    updateSceneTransitionLayoutState(
+                        currentScene,
+                        onChangeScene,
+                        EmptyTestTransitions
+                    )
+
+                SceneTransitionLayout(state = state) {
                     scene(TestScenes.SceneA) {}
                     scene(TestScenes.SceneB) {}
                 }
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt
index d9ce519..066a3e4 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt
@@ -18,9 +18,6 @@
 
 import androidx.compose.foundation.gestures.Orientation
 import androidx.compose.material3.Text
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.setValue
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
 import androidx.compose.ui.input.nestedscroll.NestedScrollSource
@@ -53,10 +50,8 @@
     private class TestGestureScope(
         val coroutineScope: MonotonicClockTestScope,
     ) {
-        private var internalCurrentScene: SceneKey by mutableStateOf(SceneA)
-
         private val layoutState =
-            SceneTransitionLayoutStateImpl(internalCurrentScene, EmptyTestTransitions)
+            MutableSceneTransitionLayoutStateImpl(SceneA, EmptyTestTransitions)
 
         val mutableUserActionsA: MutableMap<UserAction, SceneKey> =
             mutableMapOf(Swipe.Up to SceneB, Swipe.Down to SceneC)
@@ -94,7 +89,6 @@
         private val layoutImpl =
             SceneTransitionLayoutImpl(
                     state = layoutState,
-                    onChangeScene = { internalCurrentScene = it },
                     density = Density(1f),
                     edgeDetector = DefaultEdgeDetector,
                     transitionInterceptionThreshold = transitionInterceptionThreshold,
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt
index c5b8d9a..48825fb 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt
@@ -18,7 +18,11 @@
 
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.compose.test.runMonotonicClockTest
 import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.CoroutineStart
+import kotlinx.coroutines.cancel
+import kotlinx.coroutines.launch
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -29,7 +33,7 @@
 
     @Test
     fun isTransitioningTo_idle() {
-        val state = SceneTransitionLayoutState(TestScenes.SceneA)
+        val state = MutableSceneTransitionLayoutStateImpl(TestScenes.SceneA, SceneTransitions.Empty)
 
         assertThat(state.isTransitioning()).isFalse()
         assertThat(state.isTransitioning(from = TestScenes.SceneA)).isFalse()
@@ -40,7 +44,7 @@
 
     @Test
     fun isTransitioningTo_transition() {
-        val state = SceneTransitionLayoutStateImpl(TestScenes.SceneA, SceneTransitions.Empty)
+        val state = MutableSceneTransitionLayoutStateImpl(TestScenes.SceneA, SceneTransitions.Empty)
         state.startTransition(transition(from = TestScenes.SceneA, to = TestScenes.SceneB))
 
         assertThat(state.isTransitioning()).isTrue()
@@ -51,12 +55,55 @@
         assertThat(state.isTransitioning(from = TestScenes.SceneA, to = TestScenes.SceneB)).isTrue()
     }
 
-    private fun transition(from: SceneKey, to: SceneKey): TransitionState.Transition {
-        return object : TransitionState.Transition(from, to) {
-            override val currentScene: SceneKey = from
-            override val progress: Float = 0f
-            override val isInitiatedByUserInput: Boolean = false
-            override val isUserInputOngoing: Boolean = false
-        }
+    @Test
+    fun setTargetScene_idleToSameScene() = runMonotonicClockTest {
+        val state = MutableSceneTransitionLayoutState(TestScenes.SceneA)
+        assertThat(state.setTargetScene(TestScenes.SceneA, coroutineScope = this)).isNull()
+    }
+
+    @Test
+    fun setTargetScene_idleToDifferentScene() = runMonotonicClockTest {
+        val state = MutableSceneTransitionLayoutState(TestScenes.SceneA)
+        val transition = state.setTargetScene(TestScenes.SceneB, coroutineScope = this)
+        assertThat(transition).isNotNull()
+        assertThat(state.transitionState).isEqualTo(transition)
+
+        testScheduler.advanceUntilIdle()
+        assertThat(state.transitionState).isEqualTo(TransitionState.Idle(TestScenes.SceneB))
+    }
+
+    @Test
+    fun setTargetScene_transitionToSameScene() = runMonotonicClockTest {
+        val state = MutableSceneTransitionLayoutState(TestScenes.SceneA)
+        assertThat(state.setTargetScene(TestScenes.SceneB, coroutineScope = this)).isNotNull()
+        assertThat(state.setTargetScene(TestScenes.SceneB, coroutineScope = this)).isNull()
+        testScheduler.advanceUntilIdle()
+        assertThat(state.transitionState).isEqualTo(TransitionState.Idle(TestScenes.SceneB))
+    }
+
+    @Test
+    fun setTargetScene_transitionToDifferentScene() = runMonotonicClockTest {
+        val state = MutableSceneTransitionLayoutState(TestScenes.SceneA)
+        assertThat(state.setTargetScene(TestScenes.SceneB, coroutineScope = this)).isNotNull()
+        assertThat(state.setTargetScene(TestScenes.SceneC, coroutineScope = this)).isNotNull()
+        testScheduler.advanceUntilIdle()
+        assertThat(state.transitionState).isEqualTo(TransitionState.Idle(TestScenes.SceneC))
+    }
+
+    @Test
+    fun setTargetScene_coroutineScopeCancelled() = runMonotonicClockTest {
+        val state = MutableSceneTransitionLayoutState(TestScenes.SceneA)
+
+        lateinit var transition: TransitionState.Transition
+        val job =
+            launch(start = CoroutineStart.UNDISPATCHED) {
+                transition = state.setTargetScene(TestScenes.SceneB, coroutineScope = this)!!
+            }
+        assertThat(state.transitionState).isEqualTo(transition)
+
+        // Cancelling the scope/job still sets the state to Idle(targetScene).
+        job.cancel()
+        testScheduler.advanceUntilIdle()
+        assertThat(state.transitionState).isEqualTo(TransitionState.Idle(TestScenes.SceneB))
     }
 }
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt
index ebbd500..efaea71 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt
@@ -63,7 +63,7 @@
     }
 
     private var currentScene by mutableStateOf(TestScenes.SceneA)
-    private val layoutState = SceneTransitionLayoutState(currentScene)
+    private lateinit var layoutState: SceneTransitionLayoutState
 
     // We use createAndroidComposeRule() here and not createComposeRule() because we need an
     // activity for testBack().
@@ -72,10 +72,14 @@
     /** The content under test. */
     @Composable
     private fun TestContent() {
+        layoutState =
+            updateSceneTransitionLayoutState(
+                currentScene,
+                { currentScene = it },
+                EmptyTestTransitions
+            )
+
         SceneTransitionLayout(
-            currentScene,
-            { currentScene = it },
-            EmptyTestTransitions,
             state = layoutState,
             modifier = Modifier.size(LayoutSize),
         ) {
@@ -113,25 +117,21 @@
 
     @Composable
     private fun SceneScope.SharedFoo(size: Dp, childOffset: Dp, modifier: Modifier = Modifier) {
-        Box(
-            modifier
-                .size(size)
-                .background(Color.Red)
-                .element(TestElements.Foo)
-                .testTag(TestElements.Foo.debugName)
-        ) {
+        Element(TestElements.Foo, modifier.size(size).background(Color.Red)) {
             // Offset the single child of Foo by some animated shared offset.
-            val offset by animateSharedDpAsState(childOffset, TestValues.Value1, TestElements.Foo)
+            val offset by animateElementDpAsState(childOffset, TestValues.Value1)
 
-            Box(
-                Modifier.offset {
-                        val pxOffset = offset.roundToPx()
-                        IntOffset(pxOffset, pxOffset)
-                    }
-                    .size(30.dp)
-                    .background(Color.Blue)
-                    .testTag(TestElements.Bar.debugName)
-            )
+            content {
+                Box(
+                    Modifier.offset {
+                            val pxOffset = offset.roundToPx()
+                            IntOffset(pxOffset, pxOffset)
+                        }
+                        .size(30.dp)
+                        .background(Color.Blue)
+                        .testTag(TestElements.Bar.debugName)
+                )
+            }
         }
     }
 
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt
index 58d853e..1ec3c8b 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt
@@ -20,9 +20,6 @@
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.size
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.platform.LocalViewConfiguration
@@ -58,18 +55,15 @@
             get() = Offset(0f, (LayoutHeight / 2).toPx())
     }
 
-    private var currentScene by mutableStateOf(TestScenes.SceneA)
-    private val layoutState = SceneTransitionLayoutState(currentScene)
-
     @get:Rule val rule = createComposeRule()
 
+    private fun layoutState(initialScene: SceneKey = TestScenes.SceneA) =
+        MutableSceneTransitionLayoutState(initialScene, EmptyTestTransitions)
+
     /** The content under test. */
     @Composable
-    private fun TestContent() {
+    private fun TestContent(layoutState: SceneTransitionLayoutState) {
         SceneTransitionLayout(
-            currentScene,
-            { currentScene = it },
-            EmptyTestTransitions,
             state = layoutState,
             modifier = Modifier.size(LayoutWidth, LayoutHeight).testTag(TestElements.Foo.debugName),
         ) {
@@ -109,9 +103,11 @@
         // The draggable touch slop, i.e. the min px distance a touch pointer must move before it is
         // detected as a drag event.
         var touchSlop = 0f
+
+        val layoutState = layoutState()
         rule.setContent {
             touchSlop = LocalViewConfiguration.current.touchSlop
-            TestContent()
+            TestContent(layoutState)
         }
 
         assertThat(layoutState.transitionState).isInstanceOf(TransitionState.Idle::class.java)
@@ -195,9 +191,10 @@
         // The draggable touch slop, i.e. the min px distance a touch pointer must move before it is
         // detected as a drag event.
         var touchSlop = 0f
+        val layoutState = layoutState()
         rule.setContent {
             touchSlop = LocalViewConfiguration.current.touchSlop
-            TestContent()
+            TestContent(layoutState)
         }
 
         assertThat(layoutState.transitionState).isInstanceOf(TransitionState.Idle::class.java)
@@ -260,14 +257,14 @@
     @Test
     fun multiPointerSwipe() {
         // Start at scene C.
-        currentScene = TestScenes.SceneC
+        val layoutState = layoutState(TestScenes.SceneC)
 
         // The draggable touch slop, i.e. the min px distance a touch pointer must move before it is
         // detected as a drag event.
         var touchSlop = 0f
         rule.setContent {
             touchSlop = LocalViewConfiguration.current.touchSlop
-            TestContent()
+            TestContent(layoutState)
         }
 
         assertThat(layoutState.transitionState).isInstanceOf(TransitionState.Idle::class.java)
@@ -299,14 +296,14 @@
     @Test
     fun defaultEdgeSwipe() {
         // Start at scene C.
-        currentScene = TestScenes.SceneC
+        val layoutState = layoutState(TestScenes.SceneC)
 
         // The draggable touch slop, i.e. the min px distance a touch pointer must move before it is
         // detected as a drag event.
         var touchSlop = 0f
         rule.setContent {
             touchSlop = LocalViewConfiguration.current.touchSlop
-            TestContent()
+            TestContent(layoutState)
         }
 
         assertThat(layoutState.transitionState).isInstanceOf(TransitionState.Idle::class.java)
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/RunMonotonicClockTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/RunMonotonicClockTest.kt
index cb122dc..fbcd5b2 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/RunMonotonicClockTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/RunMonotonicClockTest.kt
@@ -13,7 +13,7 @@
  *
  * The [TestCoroutineScheduler] is passed to provide the functionality to wait for idle.
  */
-@ExperimentalTestApi
+@OptIn(ExperimentalTestApi::class)
 fun runMonotonicClockTest(block: suspend MonotonicClockTestScope.() -> Unit) = runTest {
     // We need a CoroutineScope (like a TestScope) to create a TestMonotonicFrameClock.
     withContext(TestMonotonicFrameClock(this)) {
diff --git a/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/Transition.kt b/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/Transition.kt
new file mode 100644
index 0000000..238b21e1
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/Transition.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.compose.animation.scene
+
+/** A utility to easily create a [TransitionState.Transition] in tests. */
+fun transition(
+    from: SceneKey,
+    to: SceneKey,
+    progress: () -> Float = { 0f },
+    isInitiatedByUserInput: Boolean = false,
+    isUserInputOngoing: Boolean = false,
+): TransitionState.Transition {
+    return object : TransitionState.Transition(from, to) {
+        override val currentScene: SceneKey = from
+        override val progress: Float = progress()
+        override val isInitiatedByUserInput: Boolean = isInitiatedByUserInput
+        override val isUserInputOngoing: Boolean = isUserInputOngoing
+    }
+}
diff --git a/packages/SystemUI/customization/Android.bp b/packages/SystemUI/customization/Android.bp
index 927fd8e..1d18496 100644
--- a/packages/SystemUI/customization/Android.bp
+++ b/packages/SystemUI/customization/Android.bp
@@ -30,8 +30,8 @@
         "src/**/*.aidl",
     ],
     static_libs: [
+        "PlatformAnimationLib",
         "PluginCoreLib",
-        "SystemUIAnimationLib",
         "SystemUIPluginLib",
         "SystemUIUnfoldLib",
         "androidx.dynamicanimation_dynamicanimation",
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
index 41bde52..3dfe65a 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
@@ -22,6 +22,7 @@
 import android.os.UserHandle
 import android.provider.Settings
 import androidx.annotation.OpenForTesting
+import com.android.systemui.log.LogBuffer
 import com.android.systemui.log.core.LogLevel
 import com.android.systemui.log.core.LogcatOnlyMessageBuffer
 import com.android.systemui.log.core.Logger
@@ -120,8 +121,9 @@
             override fun onPluginAttached(
                 manager: PluginLifecycleManager<ClockProviderPlugin>
             ): Boolean {
-                manager.isDebug = !keepAllLoaded
-
+                manager.setLogFunc({ tag, msg ->
+                    (clockBuffers?.infraMessageBuffer as LogBuffer?)?.log(tag, LogLevel.DEBUG, msg)
+                })
                 if (keepAllLoaded) {
                     // Always load new plugins if requested
                     return true
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/notifications/data/repository/NotificationSettingsRepository.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/notifications/data/repository/NotificationSettingsRepository.kt
index 26da1f0..4b21105 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/notifications/data/repository/NotificationSettingsRepository.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/notifications/data/repository/NotificationSettingsRepository.kt
@@ -47,7 +47,7 @@
 
     suspend fun setShowNotificationsOnLockscreenEnabled(enabled: Boolean) {
         withContext(backgroundDispatcher) {
-            secureSettingsRepository.set(
+            secureSettingsRepository.setInt(
                 name = Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS,
                 value = if (enabled) 1 else 0,
             )
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/settings/data/repository/SecureSettingsRepository.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/settings/data/repository/SecureSettingsRepository.kt
index 7ef16a8..754d5dc 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/settings/data/repository/SecureSettingsRepository.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/settings/data/repository/SecureSettingsRepository.kt
@@ -37,15 +37,17 @@
     ): Flow<Int>
 
     /** Updates the value of the setting with the given name. */
-    suspend fun set(
+    suspend fun setInt(
         name: String,
         value: Int,
     )
 
-    suspend fun get(
+    suspend fun getInt(
         name: String,
         defaultValue: Int = 0,
     ): Int
+
+    suspend fun getString(name: String): String?
 }
 
 class SecureSettingsRepositoryImpl(
@@ -80,7 +82,7 @@
             .flowOn(backgroundDispatcher)
     }
 
-    override suspend fun set(name: String, value: Int) {
+    override suspend fun setInt(name: String, value: Int) {
         withContext(backgroundDispatcher) {
             Settings.Secure.putInt(
                 contentResolver,
@@ -90,7 +92,7 @@
         }
     }
 
-    override suspend fun get(name: String, defaultValue: Int): Int {
+    override suspend fun getInt(name: String, defaultValue: Int): Int {
         return withContext(backgroundDispatcher) {
             Settings.Secure.getInt(
                 contentResolver,
@@ -99,4 +101,13 @@
             )
         }
     }
+
+    override suspend fun getString(name: String): String? {
+        return withContext(backgroundDispatcher) {
+            Settings.Secure.getString(
+                contentResolver,
+                name,
+            )
+        }
+    }
 }
diff --git a/packages/SystemUI/customization/tests/utils/src/com/android/systemui/shared/settings/data/repository/FakeSecureSettingsRepository.kt b/packages/SystemUI/customization/tests/utils/src/com/android/systemui/shared/settings/data/repository/FakeSecureSettingsRepository.kt
index 1c86a07..37b9792 100644
--- a/packages/SystemUI/customization/tests/utils/src/com/android/systemui/shared/settings/data/repository/FakeSecureSettingsRepository.kt
+++ b/packages/SystemUI/customization/tests/utils/src/com/android/systemui/shared/settings/data/repository/FakeSecureSettingsRepository.kt
@@ -28,11 +28,15 @@
         return settings.map { it.getOrDefault(name, defaultValue.toString()) }.map { it.toInt() }
     }
 
-    override suspend fun set(name: String, value: Int) {
+    override suspend fun setInt(name: String, value: Int) {
         settings.value = settings.value.toMutableMap().apply { this[name] = value.toString() }
     }
 
-    override suspend fun get(name: String, defaultValue: Int): Int {
+    override suspend fun getInt(name: String, defaultValue: Int): Int {
         return settings.value[name]?.toInt() ?: defaultValue
     }
+
+    override suspend fun getString(name: String): String? {
+        return settings.value[name]
+    }
 }
diff --git a/packages/SystemUI/docs/imgs/ribbon.png b/packages/SystemUI/docs/imgs/ribbon.png
new file mode 100644
index 0000000..9f57652
--- /dev/null
+++ b/packages/SystemUI/docs/imgs/ribbon.png
Binary files differ
diff --git a/packages/SystemUI/docs/scene.md b/packages/SystemUI/docs/scene.md
new file mode 100644
index 0000000..3e4a1b4
--- /dev/null
+++ b/packages/SystemUI/docs/scene.md
@@ -0,0 +1,297 @@
+# The Scene Framework
+
+Known internally as "Flexiglass", this framework defines a graph where each node
+is a "scene" and each edge between the scenes is a transition. The scenes are
+the main components of System UI, on phones these are: the lockscreen, bouncer,
+shade, and quick settings panels/views/screens). Each scene is a standalone
+experience.
+
+The **main goal** of the framework is to increase code health by applying
+[Separation of concerns](https://en.wikipedia.org/wiki/Separation_of_concerns)
+over several dimensions:
+
+1.  Each scene is a standalone piece of UI; their code doesn't need to concern
+    itself with either transition animations or anything in other scenes. This
+    frees the developer to be able to focus only on the content of the UI for
+    that scene.
+2.  Transition definitions (which scene leads to which other scene following
+    which user action) are pulled out and separated from the content of the UI.
+3.  Transition animations (the effects that happen alongside the gradual change
+    from one scene to another) are also pulled out and separated from the
+    content of the UI.
+
+In addition to the above, some of the **secondary goals** are: 4. Make
+**customization easier**: by separating scenes to standalone pieces, it becomes
+possible for variant owners and OEMs to exclude or replace certain scenes or to
+add brand-new scenes. 5. **Enable modularization**: by separating scenes to
+standalone pieces, it becomes possible to break down System UI into smaller
+codebases, each one of which could be built on its own. Note: this isn't part of
+the scene framework itself but is something that can be done more easily once
+the scene framework is in place.
+
+## Terminology
+
+*   **Scene** a collection of UI elements in a layout that, together, make up a
+    "screen" or "page" that is as large as the container. Scenes can be
+    navigated between / transition to/from. To learn more, please see
+    [this section](#Defining-a-scene).
+*   **Element** (or "UI element") a single unit of UI within a scene. One scene
+    can arrange multiple elements within a layout structure.
+*   **Transition** the gradual switching from one scene to another scene. There
+    are two kinds: [user-driven](Scene-navigation) and
+    [automatic](Automatic-scene-transitions) scene transitions.
+*   **Transition animation** the set of UI effects that occurs while/during a
+    transition. These can apply to the entire scene or to specific elements in
+    the scene. To learn more, please see
+    [this section](#Scene-transition-animations).
+*   **Scene container** (or just "container") the root piece of UI (typically a
+    `@Composable` function) that sets up all the scenes, their transitions, etc.
+    To learn more, please see [this section](#Scene-container).
+*   **Container configuration** (or just "configuration") the collection of
+    scenes and some added information about the desired behaviour of a
+    container. To learn more, please see
+    [this section](#Scene-container-configuration).
+
+## Enabling the framework
+
+As of the end of 2023, the scene framework is under development; as such, it is
+disabled by default. For those who are interested in a preview, please follow
+the instructions below to turn it on.
+
+NOTE: in case these instructions become stale and don't actually enable the
+framework, please make sure `SceneContainerFlag.isEnabled` in the
+[`SceneContainerFlags.kt`](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt)
+file evalutes to `true`.
+
+1.  Set **`SCENE_CONTAINER_ENABLED`** to `true` in the
+    [`Flags.kt`](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/packages/SystemUI/src/com/android/systemui/flags/Flags.kt)
+    file
+2.  Set the **`migrate_keyguard_status_bar_view`** classic flag to `true` by
+    running: `console $ adb shell statusbar cmd migrate_keyguard_status_bar_view
+    true`
+3.  Set a collection of **aconfig flags** to `true` by running the following
+    commands: `console $ adb shell device_config put systemui
+    com.android.systemui.scene_container true $ adb shell device_config put
+    systemui com.android.systemui.keyguard_bottom_area_refactor true $ adb shell
+    device_config put systemui
+    com.android.systemui.keyguard_shade_migration_nssl true $ adb shell
+    device_config put systemui com.android.systemui.media_in_scene_container
+    true`
+4.  **Restart** System UI by issuing the following command: `console $ adb shell
+    am crash com.android.systemui`
+5.  **Verify** that the scene framework was turned on. There are two ways to do
+    this:
+
+    *(a)* look for the sash/ribbon UI at the bottom-right corner of the display:
+    ![ribbon](imgs/ribbon.png)
+
+    NOTE: this will be removed proper to the actual release of the framework.
+
+    *(b)* Turn on logging and look for the logging statements in `logcat`:
+    ```console
+
+    # Turn on logging from the framework:
+
+    $ adb shell cmd statusbar echo -b SceneFramework:verbose
+
+# Look for the log statements from the framework:
+
+$ adb logcat -v time SceneFramework:* *:S ```
+
+To **disable** the framework, simply turn off the main aconfig flag: `console $
+adb shell device_config put systemui com.android.systemui.scene_container false`
+
+## Defining a scene
+
+Each scene is defined as an implementation of the
+[`ComposableScene`](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/ComposableScene.kt)
+interface, which has three parts: 1. The `key` property returns the
+[`SceneKey`](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneKey.kt)
+that uniquely identifies that scene 2. The `destinationScenes` `Flow` returns
+the (potentially ever-changing) set of navigation edges to other scenes, based
+on user-actions, which is how the navigation graph is defined (see
+[the Scene navigation](#Scene-navigation) section for more) 3. The `Content`
+function which uses
+[Jetpack Compose](https://developer.android.com/jetpack/compose) to declare of
+the UI itself. This is the UI "at rest", e.g. once there is no transition
+between any two scenes. The Scene Framework has other ways to define how the
+content of your UI changes with and throughout a transition to learn more please
+see the [Scene transition animations](#Scene-transition-animations) section
+
+For example: ```kotlin @SysUISingleton class YourScene @Inject constructor( //
+your dependencies here ) : ComposableScene { override val key =
+SceneKey.YourScene
+
+```
+override val destinationScenes: StateFlow<Map<UserAction, SceneModel>> =
+    MutableStateFlow<Map<UserAction, SceneModel>>(
+        mapOf(
+            // This is where scene navigation is defined, more on that below.
+        )
+    ).asStateFlow()
+
+@Composable
+override fun SceneScope.Content(
+    modifier: Modifier,
+) {
+    // This is where the UI is defined using Jetpack Compose.
+}
+```
+
+} ```
+
+### Injecting scenes
+
+Scenes are injected into the Dagger dependency graph from the
+[`SceneModule`](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/ui/composable/SceneModule.kt;l=35-50;drc=564f233d5b597aedf06961c76e582464eebe8ba6).
+
+## Scene navigation
+
+As seen above, each scene is responsible for providing an observable `Flow` of a
+`Map` that connects `UserAction` (for example: swipe down, swipe up, back
+button/gesture, etc.) keys to `SceneModel` destinations. This is how the scene
+navigation graph is defined.
+
+NOTE: this controls *only* user-input based navigation. To learn about the other
+type of scene navigation, please see the
+[Automatic scene transitions](#Automatic-scene-transitions) section.
+
+Because this is a `Flow`, scene implemetations should feel free to emit new
+values over time. For example, the `Lockscreen` scene ties the "swipe up" user
+action to go to the `Bouncer` scene if the device is still locked or to go to
+the `Gone` scene if the device is unlocked, allowing the user to dismiss the
+lockscreen UI when not locked.
+
+## Scene transition animations
+
+The Scene Framework separates transition animations from content UI declaration
+by placing the definition of the former in a different location. This way,
+there's no longer a need to contaminate the content UI declaration with
+animation logic, a practice that becomes unscalable over time.
+
+Under the hood, the Scene Framework uses
+[`SceneTransitionLayout`](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitionLayout.kt),
+a `@Composable` function designed with scene graph and transitions in mind. In
+fact, the Scene Framework is merely a shallow wrapper around
+`SceneTransitionLayout`.
+
+The `SceneTransitionLayout` API requires the transitions to be passed-in
+separately from the scenes themselves. In System UI, the transitions can be
+found in
+[`SceneContainerTransitions`](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt).
+As you can see, each possible scene-to-scene transition has its own builder,
+here's one example:
+
+```kotlin
+fun TransitionBuilder.lockscreenToShadeTransition() {
+    spec = tween(durationMillis = 500)
+
+    punchHole(Shade.Elements.QuickSettings, bounds = Shade.Elements.Scrim, Shade.Shapes.Scrim)
+    translate(Shade.Elements.Scrim, Edge.Top, startsOutsideLayoutBounds = false)
+    fractionRange(end = 0.5f) {
+        fade(Shade.Elements.ScrimBackground)
+        translate(
+            QuickSettings.Elements.CollapsedGrid,
+            Edge.Top,
+            startsOutsideLayoutBounds = false,
+        )
+    }
+    fractionRange(start = 0.5f) { fade(Notifications.Elements.Notifications) }
+}
+```
+
+Going through the example code: * The `spec` is the animation that should be
+invoked, in the example above, we use a `tween` animation with a duration of 500
+milliseconds * Then there's a series of function calls: `punchHole` applies a
+clip mask to the `Scrim` element in the destination scene (in this case it's the
+`Shade` scene) which has the position and size determined by the `bounds`
+parameter and the shape passed into the `shape` parameter. This lets the
+`Lockscreen` scene render "through" the `Shade` scene * The `translate` call
+shifts the `Scrim` element to/from the `Top` edge of the scene container * The
+first `fractionRange` wrapper tells the system to apply its contained functions
+only during the first half of the transition. Inside of it, we see a `fade` of
+the `ScrimBackground` element and a `translate` o the `CollpasedGrid` element
+to/from the `Top` edge * The second `fractionRange` only starts at the second
+half of the transition (e.g. when the previous one ends) and applies a `fade` on
+the `Notifications` element
+
+You can find the actual documentation for this API
+[here](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/TransitionDsl.kt).
+
+### Tagging elements
+
+As demonstrated above, elements within a scene can be addressed from transition
+defintions. In order to "tag" an element with a specific `ElementKey`, the
+[`element` modifier](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitionLayout.kt)
+must be used on the composable that declared that element's UI:
+
+```kotlin
+Text(
+    text = "Some text",
+    modifier = Modifier.element(MyElements.SomeText),
+)
+```
+
+In addition to the ability to refer to a tagged element in transition
+definitions, if the same `ElementKey` is used for one element in the current
+scene and another element in the destination scene, the element is considered to
+be a **shared element**. As such, the framework automatically translates and
+scales the bounds of the shared element from its current bounds in the source
+scene to its final bounds in the destination scene.
+
+## Scene container
+
+To set up a scene framework instance, a scene container must be declared. This
+is the root of an entire scene graph that puts together the scenes, their
+transitions, and the configuration. The container is then added to a parent
+`@Composable` or `View` so it can be displayed.
+
+The default scene container in System UI is defined in the
+[`SceneContainer.kt` file](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt).
+
+### Scene container configuration
+
+The `SceneContainer` function is passed a few parameters including a view-model
+and a set of scenes. The exact details of what gets passed in depends on the
+[`SceneContainerConfig` object](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneContainerConfig.kt)
+which is injected into the Dagger dependency graph
+[here](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneContainerConfigModule.kt).
+
+## Automatic scene transitions
+
+The scene framework supports the ability for scenes to change automatically
+based on device state or events other than direct user input. For example: when
+the device is locked, there's an automatic scene transition to the `Lockscreen`
+scene.
+
+This logic is contained within the
+[`SceneContainerStartable`](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt)
+class.
+
+## Side-effects
+
+Similarly to [the above](#Automatic-scene-transitions), the
+`SceneContainerStartable` also handles side-effects by updating other parts of
+the System UI codebase whenever internal scene framework state changes. As an
+example: the visibility of the `View` that contains our
+[scene container](#Scene-container) is updated every time there's a transition
+to or from the `Gone` scene.
+
+## Observing scene transition state
+
+There are a couple of ways to observe the transition state:
+
+1.  [Easiest] using the `SceneScope` of the scene container, simply use the
+    `animateSharedXAsState` API, the full list is
+    [here](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/AnimateSharedAsState.kt).
+2.  [Harder] if outside the `SceneScope` of the scene container, observe
+    [`SceneInteractor.transitionState`](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt;l=88;drc=af57d5e49431c6728e7cf192bada88e0541ebf0c).
+
+## Dependency Injection
+
+The entire framework is provided into the Dagger dependency graph from the
+top-level Dagger module at
+[`SceneContainerFrameworkModule`](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt)
+this puts together the scenes from `SceneModule`, the configuration from
+`SceneContainerConfigModule`, and the startable from
+`SceneContainerStartableModule`.
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
index 3fbcf6d..1519021 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
@@ -24,6 +24,7 @@
 import androidx.test.filters.SmallTest
 import com.android.internal.util.LatencyTracker
 import com.android.internal.widget.LockPatternUtils
+import com.android.systemui.Flags as AconfigFlags
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.classifier.FalsingCollector
 import com.android.systemui.flags.FakeFeatureFlags
@@ -90,8 +91,8 @@
             .thenReturn(mock(ImageView::class.java))
         `when`(keyguardPasswordView.resources).thenReturn(context.resources)
         val fakeFeatureFlags = FakeFeatureFlags()
-        fakeFeatureFlags.set(Flags.REVAMPED_BOUNCER_MESSAGES, true)
         fakeFeatureFlags.set(Flags.LOCKSCREEN_ENABLE_LANDSCAPE, false)
+        mSetFlagsRule.enableFlags(AconfigFlags.FLAG_REVAMPED_BOUNCER_MESSAGES)
         keyguardPasswordViewController =
             KeyguardPasswordViewController(
                 keyguardPasswordView,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
index 74c9225..e2bdc49 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
@@ -24,6 +24,7 @@
 import androidx.test.filters.SmallTest
 import com.android.internal.util.LatencyTracker
 import com.android.internal.widget.LockPatternUtils
+import com.android.systemui.Flags as AConfigFlags
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.classifier.FalsingCollector
 import com.android.systemui.classifier.FalsingCollectorFake
@@ -75,8 +76,7 @@
     private lateinit var mKeyguardMessageAreaControllerFactory:
         KeyguardMessageAreaController.Factory
 
-    @Mock
-    private lateinit var mSelectedUserInteractor: SelectedUserInteractor
+    @Mock private lateinit var mSelectedUserInteractor: SelectedUserInteractor
 
     @Mock
     private lateinit var mKeyguardMessageAreaController:
@@ -95,7 +95,6 @@
         whenever(mKeyguardMessageAreaControllerFactory.create(any()))
             .thenReturn(mKeyguardMessageAreaController)
         fakeFeatureFlags = FakeFeatureFlags()
-        fakeFeatureFlags.set(Flags.REVAMPED_BOUNCER_MESSAGES, false)
         fakeFeatureFlags.set(Flags.LOCKSCREEN_ENABLE_LANDSCAPE, false)
         mKeyguardPatternView =
             View.inflate(mContext, R.layout.keyguard_pattern_view, null) as KeyguardPatternView
@@ -166,7 +165,7 @@
 
     @Test
     fun withFeatureFlagOn_oldMessage_isHidden() {
-        fakeFeatureFlags.set(Flags.REVAMPED_BOUNCER_MESSAGES, true)
+        mSetFlagsRule.enableFlags(AConfigFlags.FLAG_REVAMPED_BOUNCER_MESSAGES)
 
         mKeyguardPatternViewController.onViewAttached()
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
index d41c249..e893169 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
@@ -35,7 +35,6 @@
 import com.android.systemui.classifier.FalsingCollector;
 import com.android.systemui.classifier.FalsingCollectorFake;
 import com.android.systemui.flags.FakeFeatureFlags;
-import com.android.systemui.flags.Flags;
 import com.android.systemui.res.R;
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
 
@@ -103,8 +102,7 @@
 
         when(mPinBasedInputView.getResources()).thenReturn(getContext().getResources());
         FakeFeatureFlags featureFlags = new FakeFeatureFlags();
-        featureFlags.set(Flags.REVAMPED_BOUNCER_MESSAGES, true);
-
+        mSetFlagsRule.enableFlags(com.android.systemui.Flags.FLAG_REVAMPED_BOUNCER_MESSAGES);
         mKeyguardPinViewController = new KeyguardPinBasedInputViewController(mPinBasedInputView,
                 mKeyguardUpdateMonitor, mSecurityMode, mLockPatternUtils, mKeyguardSecurityCallback,
                 mKeyguardMessageAreaControllerFactory, mLatencyTracker, mLiftToactivateListener,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
index f170135..02d30c5e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
@@ -36,6 +36,7 @@
 import com.android.internal.widget.LockPatternUtils
 import com.android.keyguard.KeyguardSecurityContainer.UserSwitcherViewMode.UserSwitcherCallback
 import com.android.keyguard.KeyguardSecurityModel.SecurityMode
+import com.android.systemui.Flags as AConfigFlags
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.biometrics.FaceAuthAccessibilityDelegate
 import com.android.systemui.biometrics.SideFpsController
@@ -44,10 +45,10 @@
 import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants
 import com.android.systemui.classifier.FalsingA11yDelegate
 import com.android.systemui.classifier.FalsingCollector
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
 import com.android.systemui.flags.FakeFeatureFlags
 import com.android.systemui.flags.Flags
-import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
 import com.android.systemui.log.SessionTracker
@@ -139,7 +140,7 @@
     @Mock private lateinit var viewMediatorCallback: ViewMediatorCallback
     @Mock private lateinit var audioManager: AudioManager
     @Mock private lateinit var mSelectedUserInteractor: SelectedUserInteractor
-    @Mock private lateinit var faceAuthInteractor: KeyguardFaceAuthInteractor
+    @Mock private lateinit var faceAuthInteractor: DeviceEntryFaceAuthInteractor
     @Mock private lateinit var faceAuthAccessibilityDelegate: FaceAuthAccessibilityDelegate
     @Mock private lateinit var deviceProvisionedController: DeviceProvisionedController
     @Mock private lateinit var postureController: DevicePostureController
@@ -196,12 +197,11 @@
         whenever(deviceProvisionedController.isUserSetup(anyInt())).thenReturn(true)
 
         featureFlags = FakeFeatureFlags()
-        featureFlags.set(Flags.REVAMPED_BOUNCER_MESSAGES, true)
-        featureFlags.set(Flags.BOUNCER_USER_SWITCHER, false)
         featureFlags.set(Flags.KEYGUARD_WM_STATE_REFACTOR, false)
         featureFlags.set(Flags.REFACTOR_KEYGUARD_DISMISS_INTENT, false)
         featureFlags.set(Flags.LOCKSCREEN_ENABLE_LANDSCAPE, false)
 
+        mSetFlagsRule.enableFlags(AConfigFlags.FLAG_REVAMPED_BOUNCER_MESSAGES)
         keyguardPasswordViewController =
             KeyguardPasswordViewController(
                 keyguardPasswordView,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt
index 84d73543..f775175 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt
@@ -24,10 +24,10 @@
 import androidx.test.filters.SmallTest
 import com.android.internal.util.LatencyTracker
 import com.android.internal.widget.LockPatternUtils
+import com.android.systemui.Flags
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.classifier.FalsingCollector
 import com.android.systemui.flags.FakeFeatureFlags
-import com.android.systemui.flags.Flags
 import com.android.systemui.res.R
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor
 import com.android.systemui.util.mockito.any
@@ -81,8 +81,8 @@
             LayoutInflater.from(context).inflate(R.layout.keyguard_sim_pin_view, null)
                 as KeyguardSimPinView
         val fakeFeatureFlags = FakeFeatureFlags()
-        fakeFeatureFlags.set(Flags.REVAMPED_BOUNCER_MESSAGES, true)
 
+        mSetFlagsRule.enableFlags(Flags.FLAG_REVAMPED_BOUNCER_MESSAGES)
         underTest =
             KeyguardSimPinViewController(
                 simPinView,
@@ -100,9 +100,12 @@
                 mSelectedUserInteractor
             )
         underTest.init()
+        underTest.onViewAttached()
         underTest.onResume(0)
         verify(keyguardUpdateMonitor)
             .registerCallback(updateMonitorCallbackArgumentCaptor.capture())
+        reset(keyguardMessageAreaController)
+        reset(keyguardUpdateMonitor)
     }
 
     @Test
@@ -110,25 +113,24 @@
         underTest.onViewAttached()
         verify(keyguardMessageAreaController)
             .setMessage(context.resources.getString(R.string.keyguard_enter_your_pin), false)
-    }
-
-    @Test
-    fun onViewDetached() {
-        underTest.onViewDetached()
-    }
-
-    @Test
-    fun onResume() {
-        reset(keyguardUpdateMonitor)
-        underTest.onResume(KeyguardSecurityView.VIEW_REVEALED)
         verify(keyguardUpdateMonitor)
             .registerCallback(any(KeyguardUpdateMonitorCallback::class.java))
     }
 
     @Test
+    fun onViewDetached() {
+        underTest.onViewDetached()
+        verify(keyguardUpdateMonitor).removeCallback(any(KeyguardUpdateMonitorCallback::class.java))
+    }
+
+    @Test
+    fun onResume() {
+        underTest.onResume(KeyguardSecurityView.VIEW_REVEALED)
+    }
+
+    @Test
     fun onPause() {
         underTest.onPause()
-        verify(keyguardUpdateMonitor).removeCallback(any(KeyguardUpdateMonitorCallback::class.java))
     }
 
     @Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt
index 7b1f302..45a60199 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt
@@ -24,10 +24,10 @@
 import androidx.test.filters.SmallTest
 import com.android.internal.util.LatencyTracker
 import com.android.internal.widget.LockPatternUtils
+import com.android.systemui.Flags
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.classifier.FalsingCollector
 import com.android.systemui.flags.FakeFeatureFlags
-import com.android.systemui.flags.Flags
 import com.android.systemui.res.R
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor
 import com.android.systemui.util.mockito.any
@@ -75,8 +75,7 @@
             LayoutInflater.from(context).inflate(R.layout.keyguard_sim_puk_view, null)
                 as KeyguardSimPukView
         val fakeFeatureFlags = FakeFeatureFlags()
-        fakeFeatureFlags.set(Flags.REVAMPED_BOUNCER_MESSAGES, true)
-
+        mSetFlagsRule.enableFlags(Flags.FLAG_REVAMPED_BOUNCER_MESSAGES)
         underTest =
             KeyguardSimPukViewController(
                 simPukView,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/SysuiTestCaseSelfTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/SysuiTestCaseSelfTest.kt
new file mode 100644
index 0000000..be6bb9c
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/SysuiTestCaseSelfTest.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.google.common.truth.Truth
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class SysuiTestCaseSelfTest : SysuiTestCase() {
+    private val contextBeforeSetup = context
+
+    // cf b/311612168
+    @Test
+    fun captureCorrectContextBeforeSetupRuns() {
+        Truth.assertThat(contextBeforeSetup).isEqualTo(context)
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt
index c961be9..b9759cc 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt
@@ -57,6 +57,7 @@
     @Mock private lateinit var lockPatternUtils: LockPatternUtils
     @Mock private lateinit var getSecurityMode: Function<Int, KeyguardSecurityModel.SecurityMode>
     @Mock private lateinit var tableLogger: TableLogBuffer
+    @Mock private lateinit var devicePolicyManager: DevicePolicyManager
 
     private val testUtils = SceneTestUtils(this)
     private val testScope = testUtils.testScope
@@ -87,6 +88,7 @@
                 getSecurityMode = getSecurityMode,
                 userRepository = userRepository,
                 lockPatternUtils = lockPatternUtils,
+                devicePolicyManager = devicePolicyManager,
                 broadcastDispatcher = fakeBroadcastDispatcher,
                 mobileConnectionsRepository = mobileConnectionsRepository,
             )
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt
index c113b37..10c16bd 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt
@@ -19,6 +19,7 @@
 import android.app.admin.DevicePolicyManager
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
+import com.android.internal.widget.LockPatternUtils
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository
 import com.android.systemui.authentication.shared.model.AuthenticationMethodModel.None
@@ -26,6 +27,7 @@
 import com.android.systemui.authentication.shared.model.AuthenticationMethodModel.Pattern
 import com.android.systemui.authentication.shared.model.AuthenticationMethodModel.Pin
 import com.android.systemui.authentication.shared.model.AuthenticationPatternCoordinate
+import com.android.systemui.authentication.shared.model.AuthenticationWipeModel
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.scene.SceneTestUtils
 import com.google.common.truth.Truth.assertThat
@@ -404,6 +406,55 @@
         }
 
     @Test
+    fun upcomingWipe() =
+        testScope.runTest {
+            val upcomingWipe by collectLastValue(underTest.upcomingWipe)
+            utils.authenticationRepository.setAuthenticationMethod(Pin)
+            val correctPin = FakeAuthenticationRepository.DEFAULT_PIN
+            val wrongPin = FakeAuthenticationRepository.DEFAULT_PIN.map { it + 1 }
+
+            underTest.authenticate(correctPin)
+            assertThat(upcomingWipe).isNull()
+
+            var expectedFailedAttempts = 0
+            var remainingFailedAttempts =
+                utils.authenticationRepository.getMaxFailedUnlockAttemptsForWipe()
+            assertThat(remainingFailedAttempts)
+                .isGreaterThan(LockPatternUtils.FAILED_ATTEMPTS_BEFORE_WIPE_GRACE)
+
+            // Make many wrong attempts, until wipe is triggered:
+            repeat(remainingFailedAttempts) { attemptIndex ->
+                underTest.authenticate(wrongPin)
+                expectedFailedAttempts++
+                remainingFailedAttempts--
+                if (underTest.lockoutEndTimestamp != null) {
+                    // If there's a lockout, wait it out:
+                    advanceTimeBy(FakeAuthenticationRepository.LOCKOUT_DURATION_SECONDS.seconds)
+                }
+
+                if (attemptIndex < LockPatternUtils.FAILED_ATTEMPTS_BEFORE_WIPE_GRACE) {
+                    // No risk of wipe.
+                    assertThat(upcomingWipe).isNull()
+                } else {
+                    // Wipe grace period started; Make additional wrong attempts, confirm the
+                    // warning is shown each time:
+                    assertThat(upcomingWipe)
+                        .isEqualTo(
+                            AuthenticationWipeModel(
+                                wipeTarget = AuthenticationWipeModel.WipeTarget.WholeDevice,
+                                failedAttempts = expectedFailedAttempts,
+                                remainingAttempts = remainingFailedAttempts
+                            )
+                        )
+                }
+            }
+
+            // Unlock successfully, no more risk of upcoming wipe:
+            assertSucceeded(underTest.authenticate(correctPin))
+            assertThat(upcomingWipe).isNull()
+        }
+
+    @Test
     fun hintedPinLength_withoutAutoConfirm_isNull() =
         testScope.runTest {
             val hintedPinLength by collectLastValue(underTest.hintedPinLength)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthControllerTest.java
index da97a12..343280d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthControllerTest.java
@@ -801,6 +801,20 @@
     }
 
     @Test
+    public void testOnBiometricPromptDismissedCallback_hideAuthenticationDialog() {
+        // GIVEN a callback is registered
+        AuthController.Callback callback = mock(AuthController.Callback.class);
+        mAuthController.addCallback(callback);
+
+        // WHEN dialog is shown and then dismissed
+        showDialog(new int[]{1} /* sensorIds */, false /* credentialAllowed */);
+        mAuthController.hideAuthenticationDialog(mAuthController.mCurrentDialog.getRequestId());
+
+        // THEN callback should be received
+        verify(callback).onBiometricPromptDismissed();
+    }
+
+    @Test
     public void testSubscribesToLogContext() {
         mAuthController.setBiometricContextListener(mContextListener);
         verify(mLogContextInteractor).addBiometricContextListener(same(mContextListener));
@@ -923,14 +937,14 @@
         doReturn(500).when(mResources)
                 .getDimensionPixelSize(eq(com.android.systemui.res.R.dimen
                         .physical_fingerprint_sensor_center_screen_location_y));
-        mAuthController.onConfigurationChanged(null /* newConfig */);
+        mAuthController.onConfigChanged(null /* newConfig */);
 
         final Point firstFpLocation = mAuthController.getFingerprintSensorLocation();
 
         doReturn(1000).when(mResources)
                 .getDimensionPixelSize(eq(com.android.systemui.res.R.dimen
                         .physical_fingerprint_sensor_center_screen_location_y));
-        mAuthController.onConfigurationChanged(null /* newConfig */);
+        mAuthController.onConfigChanged(null /* newConfig */);
 
         assertNotSame(firstFpLocation, mAuthController.getFingerprintSensorLocation());
     }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index e5da1f8..cec2d74 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -85,10 +85,10 @@
 import com.android.systemui.biometrics.ui.viewmodel.DeviceEntryUdfpsTouchOverlayViewModel;
 import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.keyguard.ScreenLifecycle;
-import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor;
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
 import com.android.systemui.log.SessionTracker;
 import com.android.systemui.plugins.FalsingManager;
@@ -336,7 +336,7 @@
                 mSessionTracker,
                 mAlternateBouncerInteractor,
                 mInputManager,
-                mock(KeyguardFaceAuthInteractor.class),
+                mock(DeviceEntryFaceAuthInteractor.class),
                 mUdfpsKeyguardAccessibilityDelegate,
                 mSelectedUserInteractor,
                 mFpsUnlockTracker,
@@ -496,7 +496,8 @@
         final float[] scaleFactor = new float[]{1f, displayHeight[1] / (float) displayHeight[0]};
         final int[] rotation = new int[]{Surface.ROTATION_0, Surface.ROTATION_90};
         final UdfpsOverlayParams oldParams = new UdfpsOverlayParams(sensorBounds[0],
-                sensorBounds[0], displayWidth[0], displayHeight[0], scaleFactor[0], rotation[0]);
+                sensorBounds[0], displayWidth[0], displayHeight[0], scaleFactor[0], rotation[0],
+                FingerprintSensorProperties.TYPE_UDFPS_OPTICAL);
 
         for (int i1 = 0; i1 <= 1; ++i1) {
             for (int i2 = 0; i2 <= 1; ++i2) {
@@ -505,7 +506,8 @@
                         for (int i5 = 0; i5 <= 1; ++i5) {
                             final UdfpsOverlayParams newParams = new UdfpsOverlayParams(
                                     sensorBounds[i1], sensorBounds[i1], displayWidth[i2],
-                                    displayHeight[i3], scaleFactor[i4], rotation[i5]);
+                                    displayHeight[i3], scaleFactor[i4], rotation[i5],
+                                    FingerprintSensorProperties.TYPE_UDFPS_OPTICAL);
 
                             if (newParams.equals(oldParams)) {
                                 continue;
@@ -549,7 +551,7 @@
         // Initialize the overlay.
         mUdfpsController.updateOverlayParams(mOpticalProps,
                 new UdfpsOverlayParams(sensorBounds, sensorBounds, displayWidth, displayHeight,
-                        scaleFactor, rotation));
+                        scaleFactor, rotation, FingerprintSensorProperties.TYPE_UDFPS_OPTICAL));
 
         // Show the overlay.
         mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
@@ -560,7 +562,7 @@
         // Update overlay with the same parameters.
         mUdfpsController.updateOverlayParams(mOpticalProps,
                 new UdfpsOverlayParams(sensorBounds, sensorBounds, displayWidth, displayHeight,
-                        scaleFactor, rotation));
+                        scaleFactor, rotation, FingerprintSensorProperties.TYPE_UDFPS_OPTICAL));
         mFgExecutor.runAllReady();
 
         // Ensure the overlay was not recreated.
@@ -642,7 +644,8 @@
         // Test ROTATION_0
         mUdfpsController.updateOverlayParams(testParams.sensorProps,
                 new UdfpsOverlayParams(sensorBounds, sensorBounds, displayWidth, displayHeight,
-                        scaleFactor, Surface.ROTATION_0));
+                        scaleFactor, Surface.ROTATION_0,
+                        FingerprintSensorProperties.TYPE_UDFPS_OPTICAL));
         MotionEvent event = obtainMotionEvent(ACTION_DOWN, displayWidth, displayHeight, touchMinor,
                 touchMajor);
         mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event);
@@ -657,7 +660,8 @@
         reset(mFingerprintManager);
         mUdfpsController.updateOverlayParams(testParams.sensorProps,
                 new UdfpsOverlayParams(sensorBounds, sensorBounds, displayWidth, displayHeight,
-                        scaleFactor, Surface.ROTATION_90));
+                        scaleFactor, Surface.ROTATION_90,
+                        FingerprintSensorProperties.TYPE_UDFPS_OPTICAL));
         event = obtainMotionEvent(ACTION_DOWN, displayHeight, 0, touchMinor, touchMajor);
         mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event);
         mBiometricExecutor.runAllReady();
@@ -671,7 +675,8 @@
         reset(mFingerprintManager);
         mUdfpsController.updateOverlayParams(testParams.sensorProps,
                 new UdfpsOverlayParams(sensorBounds, sensorBounds, displayWidth, displayHeight,
-                        scaleFactor, Surface.ROTATION_270));
+                        scaleFactor, Surface.ROTATION_270,
+                        FingerprintSensorProperties.TYPE_UDFPS_OPTICAL));
         event = obtainMotionEvent(ACTION_DOWN, 0, displayWidth, touchMinor, touchMajor);
         mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event);
         mBiometricExecutor.runAllReady();
@@ -685,7 +690,8 @@
         reset(mFingerprintManager);
         mUdfpsController.updateOverlayParams(testParams.sensorProps,
                 new UdfpsOverlayParams(sensorBounds, sensorBounds, displayWidth, displayHeight,
-                        scaleFactor, Surface.ROTATION_180));
+                        scaleFactor, Surface.ROTATION_180,
+                        FingerprintSensorProperties.TYPE_UDFPS_OPTICAL));
         // ROTATION_180 is not supported. It should be treated like ROTATION_0.
         event = obtainMotionEvent(ACTION_DOWN, displayWidth, displayHeight, touchMinor, touchMajor);
         mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event);
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt
index 1f8854f..335ac9d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt
@@ -32,11 +32,11 @@
 import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants
 import com.android.systemui.bouncer.ui.BouncerView
 import com.android.systemui.classifier.FalsingCollector
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
 import com.android.systemui.keyguard.DismissCallbackRegistry
 import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
 import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.data.repository.FakeTrustRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.TransitionState
@@ -111,7 +111,7 @@
                 FakeTrustRepository(),
                 testScope.backgroundScope,
                 mSelectedUserInteractor,
-                mock(KeyguardFaceAuthInteractor::class.java),
+                mock(DeviceEntryFaceAuthInteractor::class.java),
             )
         mAlternateBouncerInteractor =
             AlternateBouncerInteractor(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractorTest.kt
similarity index 83%
rename from packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractorTest.kt
index 67d3a20..8d6d052 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractorTest.kt
@@ -24,6 +24,7 @@
 import android.view.WindowInsets
 import android.view.WindowManager
 import android.view.WindowMetrics
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.biometrics.FingerprintInteractiveToAuthProvider
@@ -36,15 +37,24 @@
 import com.android.systemui.biometrics.shared.model.FingerprintSensorType
 import com.android.systemui.biometrics.shared.model.SensorStrength
 import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.dump.logcatLogBuffer
+import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING
+import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
+import com.android.systemui.keyguard.shared.model.KeyguardState.OFF
+import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.kosmos.testScope
 import com.android.systemui.log.SideFpsLogger
+import com.android.systemui.log.logcatLogBuffer
 import com.android.systemui.res.R
+import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth.assertThat
 import java.util.Optional
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.test.StandardTestDispatcher
 import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
@@ -52,7 +62,6 @@
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 import org.mockito.Mock
 import org.mockito.Mockito.mock
 import org.mockito.Mockito.spy
@@ -60,11 +69,12 @@
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 class SideFpsSensorInteractorTest : SysuiTestCase() {
+    private val kosmos = testKosmos()
 
     @JvmField @Rule var mockitoRule = MockitoJUnit.rule()
-    private val testScope = TestScope(StandardTestDispatcher())
+    private val testScope = kosmos.testScope
 
     private val fingerprintRepository = FakeFingerprintPropertyRepository()
 
@@ -94,6 +104,7 @@
         contextDisplayInfo.uniqueId = "current-display"
         whenever(fingerprintInteractiveToAuthProvider.enabledForCurrentUser)
             .thenReturn(isRestToUnlockEnabled)
+        overrideResource(R.bool.config_restToUnlockSupported, true)
         underTest =
             SideFpsSensorInteractor(
                 mContext,
@@ -101,6 +112,7 @@
                 windowManager,
                 displayStateInteractor,
                 Optional.of(fingerprintInteractiveToAuthProvider),
+                kosmos.keyguardTransitionInteractor,
                 SideFpsLogger(logcatLogBuffer("SfpsLogger"))
             )
     }
@@ -129,11 +141,62 @@
             assertThat(isAvailable).isFalse()
         }
 
+    private suspend fun sendTransition(from: KeyguardState, to: KeyguardState) {
+        kosmos.fakeKeyguardTransitionRepository.sendTransitionSteps(
+            listOf(
+                TransitionStep(
+                    from = from,
+                    to = to,
+                    transitionState = TransitionState.STARTED,
+                ),
+                TransitionStep(
+                    from = from,
+                    to = to,
+                    transitionState = TransitionState.FINISHED,
+                    value = 1.0f
+                )
+            ),
+            testScope
+        )
+    }
+
     @Test
-    fun authenticationDurationIsAvailableWhenSFPSSensorIsAvailable() =
+    fun authenticationDurationIsLongerIfScreenIsOff() =
         testScope.runTest {
-            assertThat(underTest.authenticationDuration)
-                .isEqualTo(context.resources.getInteger(R.integer.config_restToUnlockDuration))
+            val authenticationDuration by collectLastValue(underTest.authenticationDuration)
+            val longDuration =
+                context.resources.getInteger(R.integer.config_restToUnlockDurationScreenOff)
+            sendTransition(LOCKSCREEN, OFF)
+
+            runCurrent()
+            assertThat(authenticationDuration).isEqualTo(longDuration)
+        }
+
+    @Test
+    fun authenticationDurationIsLongerIfScreenIsDozing() =
+        testScope.runTest {
+            val authenticationDuration by collectLastValue(underTest.authenticationDuration)
+            val longDuration =
+                context.resources.getInteger(R.integer.config_restToUnlockDurationScreenOff)
+            sendTransition(LOCKSCREEN, DOZING)
+            runCurrent()
+            assertThat(authenticationDuration).isEqualTo(longDuration)
+        }
+
+    @Test
+    fun authenticationDurationIsShorterIfScreenIsNotDozingOrOff() =
+        testScope.runTest {
+            val authenticationDuration by collectLastValue(underTest.authenticationDuration)
+            val shortDuration =
+                context.resources.getInteger(R.integer.config_restToUnlockDurationDefault)
+            val allOtherKeyguardStates = KeyguardState.entries.filter { it != OFF && it != DOZING }
+
+            allOtherKeyguardStates.forEach { destinationState ->
+                sendTransition(OFF, destinationState)
+
+                runCurrent()
+                assertThat(authenticationDuration).isEqualTo(shortDuration)
+            }
         }
 
     @Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
index 99c1874..93ba6a4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
@@ -25,7 +25,7 @@
 import com.android.systemui.authentication.shared.model.AuthenticationPatternCoordinate
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.coroutines.collectValues
-import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
 import com.android.systemui.res.R
 import com.android.systemui.scene.SceneTestUtils
 import com.google.common.truth.Truth.assertThat
@@ -46,7 +46,7 @@
 @RunWith(AndroidJUnit4::class)
 class BouncerInteractorTest : SysuiTestCase() {
 
-    @Mock private lateinit var keyguardFaceAuthInteractor: KeyguardFaceAuthInteractor
+    @Mock private lateinit var mDeviceEntryFaceAuthInteractor: DeviceEntryFaceAuthInteractor
 
     private val utils = SceneTestUtils(this)
     private val testScope = utils.testScope
@@ -67,7 +67,7 @@
         underTest =
             utils.bouncerInteractor(
                 authenticationInteractor = authenticationInteractor,
-                keyguardFaceAuthInteractor = keyguardFaceAuthInteractor,
+                deviceEntryFaceAuthInteractor = mDeviceEntryFaceAuthInteractor,
             )
     }
 
@@ -306,7 +306,7 @@
     fun intentionalUserInputEvent_notifiesFaceAuthInteractor() =
         testScope.runTest {
             underTest.onIntentionalUserInput()
-            verify(keyguardFaceAuthInteractor).onPrimaryBouncerUserInput()
+            verify(mDeviceEntryFaceAuthInteractor).onPrimaryBouncerUserInput()
         }
 
     companion object {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt
index bdf5041..c8560c3 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt
@@ -26,9 +26,9 @@
 import com.android.systemui.bouncer.ui.BouncerView
 import com.android.systemui.classifier.FalsingCollector
 import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
 import com.android.systemui.keyguard.DismissCallbackRegistry
 import com.android.systemui.keyguard.data.repository.TrustRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor
 import com.android.systemui.statusbar.policy.KeyguardStateController
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor
 import com.android.systemui.utils.os.FakeHandler
@@ -54,7 +54,7 @@
     @Mock private lateinit var dismissCallbackRegistry: DismissCallbackRegistry
     @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
     @Mock private lateinit var mSelectedUserInteractor: SelectedUserInteractor
-    @Mock private lateinit var faceAuthInteractor: KeyguardFaceAuthInteractor
+    @Mock private lateinit var faceAuthInteractor: DeviceEntryFaceAuthInteractor
     private val mainHandler = FakeHandler(Looper.getMainLooper())
     private lateinit var underTest: PrimaryBouncerInteractor
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt
index 4be9b0a..47bbe6f4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt
@@ -21,6 +21,11 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository
 import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel.None
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel.Password
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel.Pattern
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel.Pin
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel.Sim
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.flags.Flags
 import com.android.systemui.scene.SceneTestUtils
@@ -47,7 +52,6 @@
     private val utils = SceneTestUtils(this)
     private val testScope = utils.testScope
     private val authenticationInteractor = utils.authenticationInteractor()
-    private val actionButtonInteractor = utils.bouncerActionButtonInteractor()
     private val bouncerInteractor =
         utils.bouncerInteractor(
             authenticationInteractor = authenticationInteractor,
@@ -56,7 +60,6 @@
         utils.bouncerViewModel(
             bouncerInteractor = bouncerInteractor,
             authenticationInteractor = authenticationInteractor,
-            actionButtonInteractor = actionButtonInteractor,
         )
 
     @Test
@@ -136,7 +139,7 @@
     fun message() =
         testScope.runTest {
             val message by collectLastValue(underTest.message)
-            utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
+            utils.authenticationRepository.setAuthenticationMethod(Pin)
             assertThat(message?.isUpdateAnimated).isTrue()
 
             repeat(FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_LOCKOUT) {
@@ -154,7 +157,7 @@
         testScope.runTest {
             val authMethodViewModel by collectLastValue(underTest.authMethodViewModel)
             val message by collectLastValue(underTest.message)
-            utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
+            utils.authenticationRepository.setAuthenticationMethod(Pin)
             assertThat(utils.authenticationRepository.lockoutEndTimestamp).isNull()
             assertThat(authMethodViewModel?.lockoutMessageId).isNotNull()
 
@@ -189,7 +192,7 @@
                         authViewModel?.isInputEnabled ?: emptyFlow()
                     }
                 )
-            utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
+            utils.authenticationRepository.setAuthenticationMethod(Pin)
             assertThat(isInputEnabled).isTrue()
 
             repeat(FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_LOCKOUT) {
@@ -203,21 +206,22 @@
         }
 
     @Test
-    fun dialogMessage() =
+    fun dialogViewModel() =
         testScope.runTest {
             val authMethodViewModel by collectLastValue(underTest.authMethodViewModel)
-            val dialogMessage by collectLastValue(underTest.dialogMessage)
-            utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
+            val dialogViewModel by collectLastValue(underTest.dialogViewModel)
+            utils.authenticationRepository.setAuthenticationMethod(Pin)
             assertThat(authMethodViewModel?.lockoutMessageId).isNotNull()
 
             repeat(FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_LOCKOUT) {
-                assertThat(dialogMessage).isNull()
+                assertThat(dialogViewModel).isNull()
                 bouncerInteractor.authenticate(WRONG_PIN)
             }
-            assertThat(dialogMessage).isNotEmpty()
+            assertThat(dialogViewModel).isNotNull()
+            assertThat(dialogViewModel?.text).isNotEmpty()
 
-            underTest.onDialogDismissed()
-            assertThat(dialogMessage).isNull()
+            dialogViewModel?.onDismiss?.invoke()
+            assertThat(dialogViewModel).isNull()
         }
 
     @Test
@@ -225,20 +229,16 @@
         testScope.runTest {
             val isSideBySideSupported by collectLastValue(underTest.isSideBySideSupported)
             utils.featureFlags.set(Flags.FULL_SCREEN_USER_SWITCHER, true)
-            utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
+            utils.authenticationRepository.setAuthenticationMethod(Pin)
             assertThat(isSideBySideSupported).isTrue()
-            utils.authenticationRepository.setAuthenticationMethod(
-                AuthenticationMethodModel.Password
-            )
+            utils.authenticationRepository.setAuthenticationMethod(Password)
             assertThat(isSideBySideSupported).isTrue()
 
             utils.featureFlags.set(Flags.FULL_SCREEN_USER_SWITCHER, false)
-            utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
+            utils.authenticationRepository.setAuthenticationMethod(Pin)
             assertThat(isSideBySideSupported).isTrue()
 
-            utils.authenticationRepository.setAuthenticationMethod(
-                AuthenticationMethodModel.Password
-            )
+            utils.authenticationRepository.setAuthenticationMethod(Password)
             assertThat(isSideBySideSupported).isFalse()
         }
 
@@ -246,27 +246,17 @@
     fun isFoldSplitRequired() =
         testScope.runTest {
             val isFoldSplitRequired by collectLastValue(underTest.isFoldSplitRequired)
-            utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
+            utils.authenticationRepository.setAuthenticationMethod(Pin)
             assertThat(isFoldSplitRequired).isTrue()
-            utils.authenticationRepository.setAuthenticationMethod(
-                AuthenticationMethodModel.Password
-            )
+            utils.authenticationRepository.setAuthenticationMethod(Password)
             assertThat(isFoldSplitRequired).isFalse()
 
-            utils.authenticationRepository.setAuthenticationMethod(
-                AuthenticationMethodModel.Pattern
-            )
+            utils.authenticationRepository.setAuthenticationMethod(Pattern)
             assertThat(isFoldSplitRequired).isTrue()
         }
 
     private fun authMethodsToTest(): List<AuthenticationMethodModel> {
-        return listOf(
-            AuthenticationMethodModel.None,
-            AuthenticationMethodModel.Pin,
-            AuthenticationMethodModel.Password,
-            AuthenticationMethodModel.Pattern,
-            AuthenticationMethodModel.Sim,
-        )
+        return listOf(None, Pin, Password, Pattern, Sim)
     }
 
     private fun assertTryAgainMessage(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt
index a3bf3f4..a0c2acc 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt
@@ -30,9 +30,9 @@
 import com.android.systemui.classifier.FalsingCollector
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.coroutines.collectValues
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
 import com.android.systemui.keyguard.DismissCallbackRegistry
 import com.android.systemui.keyguard.data.repository.TrustRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor
 import com.android.systemui.shared.Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
 import com.android.systemui.statusbar.policy.KeyguardStateController
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor
@@ -63,7 +63,7 @@
     @Mock private lateinit var dismissCallbackRegistry: DismissCallbackRegistry
     @Mock private lateinit var mSelectedUserInteractor: SelectedUserInteractor
     @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
-    @Mock private lateinit var faceAuthInteractor: KeyguardFaceAuthInteractor
+    @Mock private lateinit var faceAuthInteractor: DeviceEntryFaceAuthInteractor
 
     lateinit var bouncerInteractor: PrimaryBouncerInteractor
     private val mainHandler = FakeHandler(Looper.getMainLooper())
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt
index ed76099..83d1938 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt
@@ -303,7 +303,7 @@
     fun onDragEnd_whenPatternTooShort() =
         testScope.runTest {
             val message by collectLastValue(bouncerViewModel.message)
-            val dialogMessage by collectLastValue(bouncerViewModel.dialogMessage)
+            val dialogViewModel by collectLastValue(bouncerViewModel.dialogViewModel)
             lockDeviceAndOpenPatternBouncer()
 
             // Enter a pattern that's too short more than enough times that would normally trigger
@@ -326,7 +326,7 @@
                 underTest.onDragEnd()
 
                 assertWithMessage("Attempt #$attempt").that(message?.text).isEqualTo(WRONG_PATTERN)
-                assertWithMessage("Attempt #$attempt").that(dialogMessage).isNull()
+                assertWithMessage("Attempt #$attempt").that(dialogViewModel).isNull()
             }
         }
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/common/data/repository/PackageChangeRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/common/data/repository/PackageChangeRepositoryTest.kt
new file mode 100644
index 0000000..6380ace
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/common/data/repository/PackageChangeRepositoryTest.kt
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.common.data.repository
+
+import android.content.Context
+import android.content.pm.PackageManager
+import android.os.Handler
+import android.os.UserHandle
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.data.shared.model.PackageChangeModel
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.coroutines.collectValues
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.log.logcatLogBuffer
+import com.android.systemui.testKosmos
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class PackageChangeRepositoryTest : SysuiTestCase() {
+
+    private val kosmos = testKosmos()
+
+    @Mock private lateinit var context: Context
+    @Mock private lateinit var packageManager: PackageManager
+    @Mock private lateinit var handler: Handler
+
+    private lateinit var repository: PackageChangeRepository
+    private lateinit var updateMonitor: PackageUpdateMonitor
+
+    @Before
+    fun setUp() =
+        with(kosmos) {
+            MockitoAnnotations.initMocks(this@PackageChangeRepositoryTest)
+            whenever(context.packageManager).thenReturn(packageManager)
+
+            repository = PackageChangeRepositoryImpl { user ->
+                updateMonitor =
+                    PackageUpdateMonitor(
+                        user = user,
+                        bgDispatcher = testDispatcher,
+                        scope = applicationCoroutineScope,
+                        context = context,
+                        bgHandler = handler,
+                        logger = PackageUpdateLogger(logcatLogBuffer())
+                    )
+                updateMonitor
+            }
+        }
+
+    @Test
+    fun packageUninstalled() =
+        with(kosmos) {
+            testScope.runTest {
+                val packageChange by collectLastValue(repository.packageChanged(USER_100))
+                assertThat(packageChange).isNull()
+
+                updateMonitor.onPackageRemoved(
+                    packageName = TEST_PACKAGE,
+                    uid = UserHandle.getUid(/* userId = */ 100, /* appId = */ 10)
+                )
+
+                assertThat(packageChange).isInstanceOf(PackageChangeModel.Uninstalled::class.java)
+                assertThat(packageChange?.packageName).isEqualTo(TEST_PACKAGE)
+            }
+        }
+
+    @Test
+    fun packageUpdateStarted() =
+        with(kosmos) {
+            testScope.runTest {
+                val packageChange by collectLastValue(repository.packageChanged(USER_100))
+                assertThat(packageChange).isNull()
+
+                updateMonitor.onPackageUpdateStarted(
+                    packageName = TEST_PACKAGE,
+                    uid = UserHandle.getUid(/* userId = */ 100, /* appId = */ 10)
+                )
+
+                assertThat(packageChange).isInstanceOf(PackageChangeModel.UpdateStarted::class.java)
+                assertThat(packageChange?.packageName).isEqualTo(TEST_PACKAGE)
+            }
+        }
+
+    @Test
+    fun packageUpdateFinished() =
+        with(kosmos) {
+            testScope.runTest {
+                val packageChange by collectLastValue(repository.packageChanged(USER_100))
+                assertThat(packageChange).isNull()
+
+                updateMonitor.onPackageUpdateFinished(
+                    packageName = TEST_PACKAGE,
+                    uid = UserHandle.getUid(/* userId = */ 100, /* appId = */ 10)
+                )
+
+                assertThat(packageChange)
+                    .isInstanceOf(PackageChangeModel.UpdateFinished::class.java)
+                assertThat(packageChange?.packageName).isEqualTo(TEST_PACKAGE)
+            }
+        }
+
+    @Test
+    fun packageInstalled() =
+        with(kosmos) {
+            testScope.runTest {
+                val packageChange by collectLastValue(repository.packageChanged(UserHandle.ALL))
+                assertThat(packageChange).isNull()
+
+                updateMonitor.onPackageAdded(
+                    packageName = TEST_PACKAGE,
+                    uid = UserHandle.getUid(/* userId = */ 100, /* appId = */ 10)
+                )
+
+                assertThat(packageChange).isInstanceOf(PackageChangeModel.Installed::class.java)
+                assertThat(packageChange?.packageName).isEqualTo(TEST_PACKAGE)
+            }
+        }
+
+    @Test
+    fun packageIsChanged() =
+        with(kosmos) {
+            testScope.runTest {
+                val packageChange by collectLastValue(repository.packageChanged(USER_100))
+                assertThat(packageChange).isNull()
+
+                updateMonitor.onPackageChanged(
+                    packageName = TEST_PACKAGE,
+                    uid = UserHandle.getUid(/* userId = */ 100, /* appId = */ 10),
+                    components = emptyArray()
+                )
+
+                assertThat(packageChange).isInstanceOf(PackageChangeModel.Changed::class.java)
+                assertThat(packageChange?.packageName).isEqualTo(TEST_PACKAGE)
+            }
+        }
+
+    @Test
+    fun filtersOutUpdatesFromOtherUsers() =
+        with(kosmos) {
+            testScope.runTest {
+                val packageChange by collectLastValue(repository.packageChanged(USER_100))
+                assertThat(packageChange).isNull()
+
+                updateMonitor.onPackageUpdateFinished(
+                    packageName = TEST_PACKAGE,
+                    uid = UserHandle.getUid(/* userId = */ 101, /* appId = */ 10)
+                )
+
+                updateMonitor.onPackageAdded(
+                    packageName = TEST_PACKAGE,
+                    uid = UserHandle.getUid(/* userId = */ 99, /* appId = */ 10)
+                )
+
+                assertThat(packageChange).isNull()
+            }
+        }
+
+    @Test
+    fun listenToUpdatesFromAllUsers() =
+        with(kosmos) {
+            testScope.runTest {
+                val packageChanges by collectValues(repository.packageChanged(UserHandle.ALL))
+                assertThat(packageChanges).isEmpty()
+
+                updateMonitor.onPackageUpdateFinished(
+                    packageName = TEST_PACKAGE,
+                    uid = UserHandle.getUid(/* userId = */ 101, /* appId = */ 10)
+                )
+
+                updateMonitor.onPackageAdded(
+                    packageName = TEST_PACKAGE,
+                    uid = UserHandle.getUid(/* userId = */ 99, /* appId = */ 10)
+                )
+
+                assertThat(packageChanges).hasSize(2)
+            }
+        }
+
+    private companion object {
+        val USER_100 = UserHandle.of(100)
+        const val TEST_PACKAGE = "pkg.test"
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/common/data/repository/PackageUpdateMonitorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/common/data/repository/PackageUpdateMonitorTest.kt
new file mode 100644
index 0000000..d610925
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/common/data/repository/PackageUpdateMonitorTest.kt
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.common.data.repository
+
+import android.content.Context
+import android.content.pm.PackageManager
+import android.os.Handler
+import android.os.UserHandle
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.data.shared.model.PackageChangeModel
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.coroutines.collectValues
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.log.logcatLogBuffer
+import com.android.systemui.testKosmos
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@ExperimentalCoroutinesApi
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class PackageUpdateMonitorTest : SysuiTestCase() {
+    private val kosmos = testKosmos()
+
+    @Mock private lateinit var context: Context
+    @Mock private lateinit var packageManager: PackageManager
+    @Mock private lateinit var handler: Handler
+
+    private lateinit var monitor: PackageUpdateMonitor
+
+    @Before
+    fun setUp() =
+        with(kosmos) {
+            MockitoAnnotations.initMocks(this@PackageUpdateMonitorTest)
+            whenever(context.packageManager).thenReturn(packageManager)
+
+            monitor =
+                PackageUpdateMonitor(
+                    user = USER_100,
+                    bgDispatcher = testDispatcher,
+                    bgHandler = handler,
+                    context = context,
+                    scope = applicationCoroutineScope,
+                    logger = PackageUpdateLogger(logcatLogBuffer())
+                )
+        }
+
+    @Test
+    fun becomesActiveWhenFlowCollected() =
+        with(kosmos) {
+            testScope.runTest {
+                assertThat(monitor.isActive).isFalse()
+                val job = monitor.packageChanged.launchIn(this)
+                runCurrent()
+                assertThat(monitor.isActive).isTrue()
+                job.cancel()
+                runCurrent()
+                assertThat(monitor.isActive).isFalse()
+            }
+        }
+
+    @Test
+    fun packageAdded() =
+        with(kosmos) {
+            testScope.runTest {
+                val packageChange by collectLastValue(monitor.packageChanged)
+                assertThat(packageChange).isNull()
+
+                monitor.onPackageAdded(TEST_PACKAGE, 123)
+
+                assertThat(packageChange)
+                    .isEqualTo(
+                        PackageChangeModel.Installed(packageName = TEST_PACKAGE, packageUid = 123)
+                    )
+            }
+        }
+
+    @Test
+    fun packageRemoved() =
+        with(kosmos) {
+            testScope.runTest {
+                val packageChange by collectLastValue(monitor.packageChanged)
+                assertThat(packageChange).isNull()
+
+                monitor.onPackageRemoved(TEST_PACKAGE, 123)
+
+                assertThat(packageChange)
+                    .isEqualTo(
+                        PackageChangeModel.Uninstalled(packageName = TEST_PACKAGE, packageUid = 123)
+                    )
+            }
+        }
+
+    @Test
+    fun packageChanged() =
+        with(kosmos) {
+            testScope.runTest {
+                val packageChange by collectLastValue(monitor.packageChanged)
+                assertThat(packageChange).isNull()
+
+                monitor.onPackageChanged(TEST_PACKAGE, 123, emptyArray())
+
+                assertThat(packageChange)
+                    .isEqualTo(
+                        PackageChangeModel.Changed(packageName = TEST_PACKAGE, packageUid = 123)
+                    )
+            }
+        }
+
+    @Test
+    fun packageUpdateStarted() =
+        with(kosmos) {
+            testScope.runTest {
+                val packageChange by collectLastValue(monitor.packageChanged)
+                assertThat(packageChange).isNull()
+
+                monitor.onPackageUpdateStarted(TEST_PACKAGE, 123)
+
+                assertThat(packageChange)
+                    .isEqualTo(
+                        PackageChangeModel.UpdateStarted(
+                            packageName = TEST_PACKAGE,
+                            packageUid = 123
+                        )
+                    )
+            }
+        }
+
+    @Test
+    fun packageUpdateFinished() =
+        with(kosmos) {
+            testScope.runTest {
+                val packageChange by collectLastValue(monitor.packageChanged)
+                assertThat(packageChange).isNull()
+
+                monitor.onPackageUpdateFinished(TEST_PACKAGE, 123)
+
+                assertThat(packageChange)
+                    .isEqualTo(
+                        PackageChangeModel.UpdateFinished(
+                            packageName = TEST_PACKAGE,
+                            packageUid = 123
+                        )
+                    )
+            }
+        }
+
+    @Test
+    fun handlesBackflow() =
+        with(kosmos) {
+            testScope.runTest {
+                val latch = MutableSharedFlow<Unit>()
+                val packageChanges by collectValues(monitor.packageChanged.onEach { latch.first() })
+                assertThat(packageChanges).isEmpty()
+
+                monitor.onPackageAdded(TEST_PACKAGE, 123)
+                monitor.onPackageUpdateStarted(TEST_PACKAGE, 123)
+                monitor.onPackageUpdateFinished(TEST_PACKAGE, 123)
+
+                assertThat(packageChanges).isEmpty()
+                latch.emit(Unit)
+                assertThat(packageChanges).hasSize(1)
+                latch.emit(Unit)
+                assertThat(packageChanges).hasSize(2)
+                latch.emit(Unit)
+                assertThat(packageChanges)
+                    .containsExactly(
+                        PackageChangeModel.Installed(TEST_PACKAGE, 123),
+                        PackageChangeModel.UpdateStarted(TEST_PACKAGE, 123),
+                        PackageChangeModel.UpdateFinished(TEST_PACKAGE, 123),
+                    )
+                    .inOrder()
+            }
+        }
+
+    companion object {
+        private val USER_100 = UserHandle.of(100)
+        private const val TEST_PACKAGE = "pkg.test"
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/data/repository/CommunalMediaRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalMediaRepositoryImplTest.kt
similarity index 69%
rename from packages/SystemUI/tests/src/com/android/systemui/communal/data/repository/CommunalMediaRepositoryImplTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalMediaRepositoryImplTest.kt
index 455f986..92b75cb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/communal/data/repository/CommunalMediaRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalMediaRepositoryImplTest.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -25,13 +25,13 @@
 import com.android.systemui.util.mockito.KotlinArgumentCaptor
 import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth.assertThat
-import kotlin.test.Test
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.StandardTestDispatcher
 import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
+import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mock
 import org.mockito.Mockito.verify
@@ -59,37 +59,43 @@
     }
 
     @Test
-    fun mediaPlaying_defaultsToFalse() =
+    fun hasAnyMediaOrRecommendation_defaultsToFalse() =
         testScope.runTest {
             mediaRepository = CommunalMediaRepositoryImpl(mediaDataManager)
 
-            val isMediaPlaying = collectLastValue(mediaRepository.mediaPlaying)
+            val mediaModel = collectLastValue(mediaRepository.mediaModel)
             runCurrent()
-            assertThat(isMediaPlaying()).isFalse()
+            assertThat(mediaModel()?.hasAnyMediaOrRecommendation).isFalse()
         }
 
     @Test
-    fun mediaPlaying_emitsInitialValue() =
-        testScope.runTest {
-            // Start with media available.
-            whenever(mediaDataManager.hasAnyMediaOrRecommendation()).thenReturn(true)
-
-            mediaRepository = CommunalMediaRepositoryImpl(mediaDataManager)
-
-            val isMediaPlaying = collectLastValue(mediaRepository.mediaPlaying)
-            runCurrent()
-            assertThat(isMediaPlaying()).isTrue()
-        }
-
-    @Test
-    fun mediaPlaying_updatesWhenMediaDataLoaded() =
+    fun mediaModel_updatesWhenMediaDataLoaded() =
         testScope.runTest {
             mediaRepository = CommunalMediaRepositoryImpl(mediaDataManager)
 
+            // Listener is added
+            verify(mediaDataManager).addListener(mediaDataListenerCaptor.capture())
+
             // Initial value is false.
-            var isMediaPlaying = collectLastValue(mediaRepository.mediaPlaying)
+            val mediaModel = collectLastValue(mediaRepository.mediaModel)
             runCurrent()
-            assertThat(isMediaPlaying()).isFalse()
+            assertThat(mediaModel()?.hasAnyMediaOrRecommendation).isFalse()
+
+            // Change to media available and notify the listener.
+            whenever(mediaDataManager.hasAnyMediaOrRecommendation()).thenReturn(true)
+            whenever(mediaData.createdTimestampMillis).thenReturn(1234L)
+            mediaDataListenerCaptor.value.onMediaDataLoaded("key", null, mediaData)
+            runCurrent()
+
+            // Media active now returns true.
+            assertThat(mediaModel()?.hasAnyMediaOrRecommendation).isTrue()
+            assertThat(mediaModel()?.createdTimestampMillis).isEqualTo(1234L)
+        }
+
+    @Test
+    fun mediaModel_updatesWhenMediaDataRemoved() =
+        testScope.runTest {
+            mediaRepository = CommunalMediaRepositoryImpl(mediaDataManager)
 
             // Listener is added
             verify(mediaDataManager).addListener(mediaDataListenerCaptor.capture())
@@ -97,36 +103,18 @@
             // Change to media available and notify the listener.
             whenever(mediaDataManager.hasAnyMediaOrRecommendation()).thenReturn(true)
             mediaDataListenerCaptor.value.onMediaDataLoaded("key", null, mediaData)
-
-            // mediaPlaying now returns true.
-            isMediaPlaying = collectLastValue(mediaRepository.mediaPlaying)
             runCurrent()
-            assertThat(isMediaPlaying()).isTrue()
-        }
 
-    @Test
-    fun mediaPlaying_updatesWhenMediaDataRemoved() =
-        testScope.runTest {
-            // Start with media available.
-            whenever(mediaDataManager.hasAnyMediaOrRecommendation()).thenReturn(true)
-
-            mediaRepository = CommunalMediaRepositoryImpl(mediaDataManager)
-
-            // Initial value is true.
-            var isMediaPlaying = collectLastValue(mediaRepository.mediaPlaying)
-            runCurrent()
-            assertThat(isMediaPlaying()).isTrue()
-
-            // Listener is added.
-            verify(mediaDataManager).addListener(mediaDataListenerCaptor.capture())
+            // Media active now returns true.
+            val mediaModel = collectLastValue(mediaRepository.mediaModel)
+            assertThat(mediaModel()?.hasAnyMediaOrRecommendation).isTrue()
 
             // Change to media unavailable and notify the listener.
             whenever(mediaDataManager.hasAnyMediaOrRecommendation()).thenReturn(false)
-            mediaDataListenerCaptor.value.onMediaDataLoaded("key", null, mediaData)
-
-            // mediaPlaying now returns false.
-            isMediaPlaying = collectLastValue(mediaRepository.mediaPlaying)
+            mediaDataListenerCaptor.value.onMediaDataRemoved("key")
             runCurrent()
-            assertThat(isMediaPlaying()).isFalse()
+
+            // Media active now returns false.
+            assertThat(mediaModel()?.hasAnyMediaOrRecommendation).isFalse()
         }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt
index ddaa488..4079f12 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt
@@ -19,14 +19,14 @@
 import android.appwidget.AppWidgetHost
 import android.appwidget.AppWidgetManager
 import android.appwidget.AppWidgetProviderInfo
-import android.content.BroadcastReceiver
 import android.content.ComponentName
+import android.content.Intent
+import android.content.Intent.ACTION_USER_UNLOCKED
 import android.os.UserHandle
 import android.os.UserManager
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.broadcast.BroadcastDispatcher
 import com.android.systemui.communal.data.db.CommunalItemRank
 import com.android.systemui.communal.data.db.CommunalWidgetDao
 import com.android.systemui.communal.data.db.CommunalWidgetItem
@@ -38,14 +38,12 @@
 import com.android.systemui.res.R
 import com.android.systemui.settings.UserTracker
 import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.kotlinArgumentCaptor
-import com.android.systemui.util.mockito.nullable
 import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth.assertThat
+import java.util.Optional
 import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.collect
 import kotlinx.coroutines.flow.flowOf
-import kotlinx.coroutines.launch
+import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.test.StandardTestDispatcher
 import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.runCurrent
@@ -54,8 +52,8 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mock
-import org.mockito.Mockito
 import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.never
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
 
@@ -63,12 +61,12 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class CommunalWidgetRepositoryImplTest : SysuiTestCase() {
+    @Mock private lateinit var appWidgetManagerOptional: Optional<AppWidgetManager>
+
     @Mock private lateinit var appWidgetManager: AppWidgetManager
 
     @Mock private lateinit var appWidgetHost: AppWidgetHost
 
-    @Mock private lateinit var broadcastDispatcher: BroadcastDispatcher
-
     @Mock private lateinit var userManager: UserManager
 
     @Mock private lateinit var userHandle: UserHandle
@@ -113,6 +111,8 @@
         whenever(stopwatchProviderInfo.loadLabel(any())).thenReturn("Stopwatch")
         whenever(userTracker.userHandle).thenReturn(userHandle)
         whenever(communalWidgetDao.getWidgets()).thenReturn(flowOf(emptyMap()))
+        whenever(appWidgetManagerOptional.isPresent).thenReturn(true)
+        whenever(appWidgetManagerOptional.get()).thenReturn(appWidgetManager)
     }
 
     @Test
@@ -120,10 +120,10 @@
         testScope.runTest {
             communalEnabled(false)
             val repository = initCommunalWidgetRepository()
-            collectLastValue(repository.communalWidgets)()
+            repository.communalWidgets.launchIn(backgroundScope)
             runCurrent()
 
-            verify(communalWidgetDao, Mockito.never()).getWidgets()
+            verify(communalWidgetDao, never()).getWidgets()
         }
 
     @Test
@@ -131,10 +131,10 @@
         testScope.runTest {
             userUnlocked(false)
             val repository = initCommunalWidgetRepository()
-            collectLastValue(repository.communalWidgets)()
+            repository.communalWidgets.launchIn(backgroundScope)
             runCurrent()
 
-            verify(communalWidgetDao, Mockito.never()).getWidgets()
+            verify(communalWidgetDao, never()).getWidgets()
         }
 
     @Test
@@ -142,8 +142,7 @@
         testScope.runTest {
             userUnlocked(false)
             val repository = initCommunalWidgetRepository()
-            val communalWidgets = collectLastValue(repository.communalWidgets)
-            communalWidgets()
+            val communalWidgets by collectLastValue(repository.communalWidgets)
             runCurrent()
             val communalItemRankEntry = CommunalItemRank(uid = 1L, rank = 1)
             val communalWidgetItemEntry = CommunalWidgetItem(uid = 1L, 1, "pk_name/cls_name", 1L)
@@ -153,11 +152,14 @@
 
             userUnlocked(true)
             installedProviders(listOf(stopwatchProviderInfo))
-            broadcastReceiverUpdate()
+            fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
+                context,
+                Intent(ACTION_USER_UNLOCKED)
+            )
             runCurrent()
 
             verify(communalWidgetDao).getWidgets()
-            assertThat(communalWidgets())
+            assertThat(communalWidgets)
                 .containsExactly(
                     CommunalWidgetContentModel(
                         appWidgetId = communalWidgetItemEntry.widgetId,
@@ -177,9 +179,10 @@
             val provider = ComponentName("pkg_name", "cls_name")
             val id = 1
             val priority = 1
+            whenever(communalWidgetHost.requiresConfiguration(id)).thenReturn(true)
             whenever(communalWidgetHost.allocateIdAndBindWidget(any<ComponentName>()))
                 .thenReturn(id)
-            repository.addWidget(provider, priority)
+            repository.addWidget(provider, priority) { true }
             runCurrent()
 
             verify(communalWidgetHost).allocateIdAndBindWidget(provider)
@@ -187,6 +190,71 @@
         }
 
     @Test
+    fun addWidget_configurationFails_doNotAddWidgetToDb() =
+        testScope.runTest {
+            userUnlocked(true)
+            val repository = initCommunalWidgetRepository()
+            runCurrent()
+
+            val provider = ComponentName("pkg_name", "cls_name")
+            val id = 1
+            val priority = 1
+            whenever(communalWidgetHost.requiresConfiguration(id)).thenReturn(true)
+            whenever(communalWidgetHost.allocateIdAndBindWidget(provider)).thenReturn(id)
+            repository.addWidget(provider, priority) { false }
+            runCurrent()
+
+            verify(communalWidgetHost).allocateIdAndBindWidget(provider)
+            verify(communalWidgetDao, never()).addWidget(id, provider, priority)
+            verify(appWidgetHost).deleteAppWidgetId(id)
+        }
+
+    @Test
+    fun addWidget_configurationThrowsError_doNotAddWidgetToDb() =
+        testScope.runTest {
+            userUnlocked(true)
+            val repository = initCommunalWidgetRepository()
+            runCurrent()
+
+            val provider = ComponentName("pkg_name", "cls_name")
+            val id = 1
+            val priority = 1
+            whenever(communalWidgetHost.requiresConfiguration(id)).thenReturn(true)
+            whenever(communalWidgetHost.allocateIdAndBindWidget(provider)).thenReturn(id)
+            repository.addWidget(provider, priority) { throw IllegalStateException("some error") }
+            runCurrent()
+
+            verify(communalWidgetHost).allocateIdAndBindWidget(provider)
+            verify(communalWidgetDao, never()).addWidget(id, provider, priority)
+            verify(appWidgetHost).deleteAppWidgetId(id)
+        }
+
+    @Test
+    fun addWidget_configurationNotRequired_doesNotConfigure_addWidgetToDb() =
+        testScope.runTest {
+            userUnlocked(true)
+            val repository = initCommunalWidgetRepository()
+            runCurrent()
+
+            val provider = ComponentName("pkg_name", "cls_name")
+            val id = 1
+            val priority = 1
+            whenever(communalWidgetHost.requiresConfiguration(id)).thenReturn(false)
+            whenever(communalWidgetHost.allocateIdAndBindWidget(any<ComponentName>()))
+                .thenReturn(id)
+            var configured = false
+            repository.addWidget(provider, priority) {
+                configured = true
+                true
+            }
+            runCurrent()
+
+            verify(communalWidgetHost).allocateIdAndBindWidget(provider)
+            verify(communalWidgetDao).addWidget(id, provider, priority)
+            assertThat(configured).isFalse()
+        }
+
+    @Test
     fun deleteWidget_removeWidgetId_andDeleteFromDb() =
         testScope.runTest {
             userUnlocked(true)
@@ -220,17 +288,9 @@
         testScope.runTest {
             communalEnabled(false)
             val repository = initCommunalWidgetRepository()
-            collectLastValue(repository.communalWidgets)()
-            verifyBroadcastReceiverNeverRegistered()
-        }
-
-    @Test
-    fun broadcastReceiver_featureEnabledAndUserUnlocked_doNotRegisterBroadcastReceiver() =
-        testScope.runTest {
-            userUnlocked(true)
-            val repository = initCommunalWidgetRepository()
-            collectLastValue(repository.communalWidgets)()
-            verifyBroadcastReceiverNeverRegistered()
+            repository.communalWidgets.launchIn(backgroundScope)
+            runCurrent()
+            assertThat(fakeBroadcastDispatcher.numReceiversRegistered).isEqualTo(0)
         }
 
     @Test
@@ -238,24 +298,9 @@
         testScope.runTest {
             userUnlocked(false)
             val repository = initCommunalWidgetRepository()
-            collectLastValue(repository.communalWidgets)()
-            verifyBroadcastReceiverRegistered()
-        }
-
-    @Test
-    fun broadcastReceiver_whenFlowFinishes_unregisterBroadcastReceiver() =
-        testScope.runTest {
-            userUnlocked(false)
-            val repository = initCommunalWidgetRepository()
-
-            val job = launch { repository.communalWidgets.collect() }
+            repository.communalWidgets.launchIn(backgroundScope)
             runCurrent()
-            val receiver = broadcastReceiverUpdate()
-
-            job.cancel()
-            runCurrent()
-
-            verify(broadcastDispatcher).unregisterReceiver(receiver)
+            assertThat(fakeBroadcastDispatcher.numReceiversRegistered).isEqualTo(1)
         }
 
     @Test
@@ -263,12 +308,16 @@
         testScope.runTest {
             userUnlocked(false)
             val repository = initCommunalWidgetRepository()
-            collectLastValue(repository.communalWidgets)()
-            verify(appWidgetHost, Mockito.never()).startListening()
+            repository.communalWidgets.launchIn(backgroundScope)
+            runCurrent()
+            verify(appWidgetHost, never()).startListening()
 
             userUnlocked(true)
-            broadcastReceiverUpdate()
-            collectLastValue(repository.communalWidgets)()
+            fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
+                context,
+                Intent(ACTION_USER_UNLOCKED)
+            )
+            runCurrent()
 
             verify(appWidgetHost).startListening()
         }
@@ -278,29 +327,36 @@
         testScope.runTest {
             userUnlocked(false)
             val repository = initCommunalWidgetRepository()
-            collectLastValue(repository.communalWidgets)()
+            repository.communalWidgets.launchIn(backgroundScope)
+            runCurrent()
 
             userUnlocked(true)
-            broadcastReceiverUpdate()
-            collectLastValue(repository.communalWidgets)()
+            fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
+                context,
+                Intent(ACTION_USER_UNLOCKED)
+            )
+            runCurrent()
 
             verify(appWidgetHost).startListening()
-            verify(appWidgetHost, Mockito.never()).stopListening()
+            verify(appWidgetHost, never()).stopListening()
 
             userUnlocked(false)
-            broadcastReceiverUpdate()
-            collectLastValue(repository.communalWidgets)()
+            fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
+                context,
+                Intent(ACTION_USER_UNLOCKED)
+            )
+            runCurrent()
 
             verify(appWidgetHost).stopListening()
         }
 
     private fun initCommunalWidgetRepository(): CommunalWidgetRepositoryImpl {
         return CommunalWidgetRepositoryImpl(
-            appWidgetManager,
+            appWidgetManagerOptional,
             appWidgetHost,
             testScope.backgroundScope,
             testDispatcher,
-            broadcastDispatcher,
+            fakeBroadcastDispatcher,
             communalRepository,
             communalWidgetHost,
             communalWidgetDao,
@@ -310,45 +366,6 @@
         )
     }
 
-    private fun verifyBroadcastReceiverRegistered() {
-        verify(broadcastDispatcher)
-            .registerReceiver(
-                any(),
-                any(),
-                nullable(),
-                nullable(),
-                anyInt(),
-                nullable(),
-            )
-    }
-
-    private fun verifyBroadcastReceiverNeverRegistered() {
-        verify(broadcastDispatcher, Mockito.never())
-            .registerReceiver(
-                any(),
-                any(),
-                nullable(),
-                nullable(),
-                anyInt(),
-                nullable(),
-            )
-    }
-
-    private fun broadcastReceiverUpdate(): BroadcastReceiver {
-        val broadcastReceiverCaptor = kotlinArgumentCaptor<BroadcastReceiver>()
-        verify(broadcastDispatcher)
-            .registerReceiver(
-                broadcastReceiverCaptor.capture(),
-                any(),
-                nullable(),
-                nullable(),
-                anyInt(),
-                nullable(),
-            )
-        broadcastReceiverCaptor.value.onReceive(null, null)
-        return broadcastReceiverCaptor.value
-    }
-
     private fun communalEnabled(enabled: Boolean) {
         communalRepository.setIsCommunalEnabled(enabled)
     }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
index 1f8e29a..744b65f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
@@ -28,6 +28,7 @@
 import com.android.systemui.communal.data.repository.FakeCommunalTutorialRepository
 import com.android.systemui.communal.data.repository.FakeCommunalWidgetRepository
 import com.android.systemui.communal.domain.model.CommunalContentModel
+import com.android.systemui.communal.shared.model.CommunalContentSize
 import com.android.systemui.communal.shared.model.CommunalSceneKey
 import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
 import com.android.systemui.communal.widgets.EditWidgetsActivityStarter
@@ -147,25 +148,128 @@
             whenever(target1.smartspaceTargetId).thenReturn("target1")
             whenever(target1.featureType).thenReturn(SmartspaceTarget.FEATURE_WEATHER)
             whenever(target1.remoteViews).thenReturn(mock(RemoteViews::class.java))
+            whenever(target1.creationTimeMillis).thenReturn(0L)
 
             // Does not have RemoteViews
             val target2 = mock(SmartspaceTarget::class.java)
-            whenever(target1.smartspaceTargetId).thenReturn("target2")
-            whenever(target1.featureType).thenReturn(SmartspaceTarget.FEATURE_TIMER)
-            whenever(target1.remoteViews).thenReturn(null)
+            whenever(target2.smartspaceTargetId).thenReturn("target2")
+            whenever(target2.featureType).thenReturn(SmartspaceTarget.FEATURE_TIMER)
+            whenever(target2.remoteViews).thenReturn(null)
+            whenever(target2.creationTimeMillis).thenReturn(0L)
 
             // Timer and has RemoteViews
             val target3 = mock(SmartspaceTarget::class.java)
-            whenever(target1.smartspaceTargetId).thenReturn("target3")
-            whenever(target1.featureType).thenReturn(SmartspaceTarget.FEATURE_TIMER)
-            whenever(target1.remoteViews).thenReturn(mock(RemoteViews::class.java))
+            whenever(target3.smartspaceTargetId).thenReturn("target3")
+            whenever(target3.featureType).thenReturn(SmartspaceTarget.FEATURE_TIMER)
+            whenever(target3.remoteViews).thenReturn(mock(RemoteViews::class.java))
+            whenever(target3.creationTimeMillis).thenReturn(0L)
 
             val targets = listOf(target1, target2, target3)
             smartspaceRepository.setCommunalSmartspaceTargets(targets)
 
-            val smartspaceContent by collectLastValue(underTest.smartspaceContent)
+            val smartspaceContent by collectLastValue(underTest.ongoingContent)
             assertThat(smartspaceContent?.size).isEqualTo(1)
-            assertThat(smartspaceContent?.get(0)?.key).isEqualTo("smartspace_target3")
+            assertThat(smartspaceContent?.get(0)?.key)
+                .isEqualTo(CommunalContentModel.KEY.smartspace("target3"))
+        }
+
+    @Test
+    fun smartspaceDynamicSizing_oneCard_fullSize() =
+        testSmartspaceDynamicSizing(
+            totalTargets = 1,
+            expectedSizes =
+                listOf(
+                    CommunalContentSize.FULL,
+                )
+        )
+
+    @Test
+    fun smartspace_dynamicSizing_twoCards_halfSize() =
+        testSmartspaceDynamicSizing(
+            totalTargets = 2,
+            expectedSizes =
+                listOf(
+                    CommunalContentSize.HALF,
+                    CommunalContentSize.HALF,
+                )
+        )
+
+    @Test
+    fun smartspace_dynamicSizing_threeCards_thirdSize() =
+        testSmartspaceDynamicSizing(
+            totalTargets = 3,
+            expectedSizes =
+                listOf(
+                    CommunalContentSize.THIRD,
+                    CommunalContentSize.THIRD,
+                    CommunalContentSize.THIRD,
+                )
+        )
+
+    @Test
+    fun smartspace_dynamicSizing_fourCards_oneFullAndThreeThirdSize() =
+        testSmartspaceDynamicSizing(
+            totalTargets = 4,
+            expectedSizes =
+                listOf(
+                    CommunalContentSize.FULL,
+                    CommunalContentSize.THIRD,
+                    CommunalContentSize.THIRD,
+                    CommunalContentSize.THIRD,
+                )
+        )
+
+    @Test
+    fun smartspace_dynamicSizing_fiveCards_twoHalfAndThreeThirdSize() =
+        testSmartspaceDynamicSizing(
+            totalTargets = 5,
+            expectedSizes =
+                listOf(
+                    CommunalContentSize.HALF,
+                    CommunalContentSize.HALF,
+                    CommunalContentSize.THIRD,
+                    CommunalContentSize.THIRD,
+                    CommunalContentSize.THIRD,
+                )
+        )
+
+    @Test
+    fun smartspace_dynamicSizing_sixCards_allThirdSize() =
+        testSmartspaceDynamicSizing(
+            totalTargets = 6,
+            expectedSizes =
+                listOf(
+                    CommunalContentSize.THIRD,
+                    CommunalContentSize.THIRD,
+                    CommunalContentSize.THIRD,
+                    CommunalContentSize.THIRD,
+                    CommunalContentSize.THIRD,
+                    CommunalContentSize.THIRD,
+                )
+        )
+
+    private fun testSmartspaceDynamicSizing(
+        totalTargets: Int,
+        expectedSizes: List<CommunalContentSize>,
+    ) =
+        testScope.runTest {
+            // Keyguard showing, and tutorial completed.
+            keyguardRepository.setKeyguardShowing(true)
+            keyguardRepository.setKeyguardOccluded(false)
+            tutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
+
+            val targets = mutableListOf<SmartspaceTarget>()
+            for (index in 0 until totalTargets) {
+                targets.add(smartspaceTimer(index.toString()))
+            }
+
+            smartspaceRepository.setCommunalSmartspaceTargets(targets)
+
+            val smartspaceContent by collectLastValue(underTest.ongoingContent)
+            assertThat(smartspaceContent?.size).isEqualTo(totalTargets)
+            for (index in 0 until totalTargets) {
+                assertThat(smartspaceContent?.get(index)?.size).isEqualTo(expectedSizes[index])
+            }
         }
 
     @Test
@@ -175,13 +279,77 @@
             tutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
 
             // Media is playing.
-            mediaRepository.mediaPlaying.value = true
+            mediaRepository.mediaActive()
 
-            val umoContent by collectLastValue(underTest.umoContent)
+            val umoContent by collectLastValue(underTest.ongoingContent)
 
             assertThat(umoContent?.size).isEqualTo(1)
             assertThat(umoContent?.get(0)).isInstanceOf(CommunalContentModel.Umo::class.java)
-            assertThat(umoContent?.get(0)?.key).isEqualTo(CommunalContentModel.UMO_KEY)
+            assertThat(umoContent?.get(0)?.key).isEqualTo(CommunalContentModel.KEY.umo())
+        }
+
+    @Test
+    fun ongoing_shouldOrderAndSizeByTimestamp() =
+        testScope.runTest {
+            // Keyguard showing, and tutorial completed.
+            keyguardRepository.setKeyguardShowing(true)
+            keyguardRepository.setKeyguardOccluded(false)
+            tutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
+
+            // Timer1 started
+            val timer1 = smartspaceTimer("timer1", timestamp = 1L)
+            smartspaceRepository.setCommunalSmartspaceTargets(listOf(timer1))
+
+            // Umo started
+            mediaRepository.mediaActive(timestamp = 2L)
+
+            // Timer2 started
+            val timer2 = smartspaceTimer("timer2", timestamp = 3L)
+            smartspaceRepository.setCommunalSmartspaceTargets(listOf(timer1, timer2))
+
+            // Timer3 started
+            val timer3 = smartspaceTimer("timer3", timestamp = 4L)
+            smartspaceRepository.setCommunalSmartspaceTargets(listOf(timer1, timer2, timer3))
+
+            val ongoingContent by collectLastValue(underTest.ongoingContent)
+            assertThat(ongoingContent?.size).isEqualTo(4)
+            assertThat(ongoingContent?.get(0)?.key)
+                .isEqualTo(CommunalContentModel.KEY.smartspace("timer3"))
+            assertThat(ongoingContent?.get(0)?.size).isEqualTo(CommunalContentSize.FULL)
+            assertThat(ongoingContent?.get(1)?.key)
+                .isEqualTo(CommunalContentModel.KEY.smartspace("timer2"))
+            assertThat(ongoingContent?.get(1)?.size).isEqualTo(CommunalContentSize.THIRD)
+            assertThat(ongoingContent?.get(2)?.key).isEqualTo(CommunalContentModel.KEY.umo())
+            assertThat(ongoingContent?.get(2)?.size).isEqualTo(CommunalContentSize.THIRD)
+            assertThat(ongoingContent?.get(3)?.key)
+                .isEqualTo(CommunalContentModel.KEY.smartspace("timer1"))
+            assertThat(ongoingContent?.get(3)?.size).isEqualTo(CommunalContentSize.THIRD)
+        }
+
+    @Test
+    fun cta_visibilityTrue_shows() =
+        testScope.runTest {
+            tutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
+            communalRepository.setCtaTileInViewModeVisibility(true)
+
+            val ctaTileContent by collectLastValue(underTest.ctaTileContent)
+
+            assertThat(ctaTileContent?.size).isEqualTo(1)
+            assertThat(ctaTileContent?.get(0))
+                .isInstanceOf(CommunalContentModel.CtaTileInViewMode::class.java)
+            assertThat(ctaTileContent?.get(0)?.key)
+                .isEqualTo(CommunalContentModel.KEY.CTA_TILE_IN_VIEW_MODE_KEY)
+        }
+
+    @Test
+    fun ctaTile_visibilityFalse_doesNotShow() =
+        testScope.runTest {
+            tutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
+            communalRepository.setCtaTileInViewModeVisibility(false)
+
+            val ctaTileContent by collectLastValue(underTest.ctaTileContent)
+
+            assertThat(ctaTileContent).isEmpty()
         }
 
     @Test
@@ -230,4 +398,13 @@
             underTest.showWidgetEditor()
             verify(editWidgetsActivityStarter).startActivity()
         }
+
+    private fun smartspaceTimer(id: String, timestamp: Long = 0L): SmartspaceTarget {
+        val timer = mock(SmartspaceTarget::class.java)
+        whenever(timer.smartspaceTargetId).thenReturn(id)
+        whenever(timer.featureType).thenReturn(SmartspaceTarget.FEATURE_TIMER)
+        whenever(timer.remoteViews).thenReturn(mock(RemoteViews::class.java))
+        whenever(timer.creationTimeMillis).thenReturn(timestamp)
+        return timer
+    }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/log/CommunalLoggerStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/log/CommunalLoggerStartableTest.kt
new file mode 100644
index 0000000..721fc49
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/log/CommunalLoggerStartableTest.kt
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.log
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.internal.logging.UiEventLogger
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.communal.domain.interactor.CommunalInteractor
+import com.android.systemui.communal.domain.interactor.CommunalInteractorFactory
+import com.android.systemui.communal.shared.log.CommunalUiEvent
+import com.android.systemui.communal.shared.model.CommunalSceneKey
+import com.android.systemui.communal.shared.model.ObservableCommunalTransitionState
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.emptyFlow
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.any
+import org.mockito.Mockito.clearInvocations
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@OptIn(ExperimentalCoroutinesApi::class)
+@RunWith(AndroidJUnit4::class)
+class CommunalLoggerStartableTest : SysuiTestCase() {
+    @Mock private lateinit var uiEventLogger: UiEventLogger
+
+    private lateinit var testScope: TestScope
+    private lateinit var communalInteractor: CommunalInteractor
+    private lateinit var underTest: CommunalLoggerStartable
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+
+        val withDeps = CommunalInteractorFactory.create()
+        testScope = withDeps.testScope
+        communalInteractor = withDeps.communalInteractor
+
+        underTest =
+            CommunalLoggerStartable(
+                testScope.backgroundScope,
+                communalInteractor,
+                uiEventLogger,
+            )
+        underTest.start()
+    }
+
+    @Test
+    fun transitionStateLogging_enterCommunalHub() =
+        testScope.runTest {
+            // Transition state is default (non-communal)
+            val transitionState =
+                MutableStateFlow<ObservableCommunalTransitionState>(idle(CommunalSceneKey.DEFAULT))
+            communalInteractor.setTransitionState(transitionState)
+            runCurrent()
+
+            // Verify nothing is logged from the default state
+            verify(uiEventLogger, never()).log(any())
+
+            // Start transition to communal
+            transitionState.value = transition(to = CommunalSceneKey.Communal)
+            runCurrent()
+
+            // Verify UiEvent logged
+            verify(uiEventLogger).log(CommunalUiEvent.COMMUNAL_HUB_SWIPE_TO_ENTER_START)
+
+            // Finish transition to communal
+            transitionState.value = idle(CommunalSceneKey.Communal)
+            runCurrent()
+
+            // Verify UiEvent logged
+            verify(uiEventLogger).log(CommunalUiEvent.COMMUNAL_HUB_SWIPE_TO_ENTER_FINISH)
+            verify(uiEventLogger).log(CommunalUiEvent.COMMUNAL_HUB_SHOWN)
+        }
+
+    @Test
+    fun transitionStateLogging_enterCommunalHub_canceled() =
+        testScope.runTest {
+            // Transition state is default (non-communal)
+            val transitionState =
+                MutableStateFlow<ObservableCommunalTransitionState>(idle(CommunalSceneKey.DEFAULT))
+            communalInteractor.setTransitionState(transitionState)
+            runCurrent()
+
+            // Verify nothing is logged from the default state
+            verify(uiEventLogger, never()).log(any())
+
+            // Start transition to communal
+            transitionState.value = transition(to = CommunalSceneKey.Communal)
+            runCurrent()
+
+            // Verify UiEvent logged
+            verify(uiEventLogger).log(CommunalUiEvent.COMMUNAL_HUB_SWIPE_TO_ENTER_START)
+
+            // Cancel the transition
+            transitionState.value = idle(CommunalSceneKey.DEFAULT)
+            runCurrent()
+
+            // Verify UiEvent logged
+            verify(uiEventLogger).log(CommunalUiEvent.COMMUNAL_HUB_SWIPE_TO_ENTER_CANCEL)
+
+            // Verify neither SHOWN nor GONE is logged
+            verify(uiEventLogger, never()).log(CommunalUiEvent.COMMUNAL_HUB_SHOWN)
+            verify(uiEventLogger, never()).log(CommunalUiEvent.COMMUNAL_HUB_GONE)
+        }
+
+    @Test
+    fun transitionStateLogging_exitCommunalHub() =
+        testScope.runTest {
+            // Transition state is communal
+            val transitionState =
+                MutableStateFlow<ObservableCommunalTransitionState>(idle(CommunalSceneKey.Communal))
+            communalInteractor.setTransitionState(transitionState)
+            runCurrent()
+
+            // Verify SHOWN is logged when it's the default state
+            verify(uiEventLogger).log(CommunalUiEvent.COMMUNAL_HUB_SHOWN)
+
+            // Start transition from communal
+            transitionState.value = transition(from = CommunalSceneKey.Communal)
+            runCurrent()
+
+            // Verify UiEvent logged
+            verify(uiEventLogger).log(CommunalUiEvent.COMMUNAL_HUB_SWIPE_TO_EXIT_START)
+
+            // Finish transition to communal
+            transitionState.value = idle(CommunalSceneKey.DEFAULT)
+            runCurrent()
+
+            // Verify UiEvent logged
+            verify(uiEventLogger).log(CommunalUiEvent.COMMUNAL_HUB_SWIPE_TO_EXIT_FINISH)
+            verify(uiEventLogger).log(CommunalUiEvent.COMMUNAL_HUB_GONE)
+        }
+
+    @Test
+    fun transitionStateLogging_exitCommunalHub_canceled() =
+        testScope.runTest {
+            // Transition state is communal
+            val transitionState =
+                MutableStateFlow<ObservableCommunalTransitionState>(idle(CommunalSceneKey.Communal))
+            communalInteractor.setTransitionState(transitionState)
+            runCurrent()
+
+            // Clear the initial SHOWN event from the logger
+            clearInvocations(uiEventLogger)
+
+            // Start transition from communal
+            transitionState.value = transition(from = CommunalSceneKey.Communal)
+            runCurrent()
+
+            // Verify UiEvent logged
+            verify(uiEventLogger).log(CommunalUiEvent.COMMUNAL_HUB_SWIPE_TO_EXIT_START)
+
+            // Cancel the transition
+            transitionState.value = idle(CommunalSceneKey.Communal)
+            runCurrent()
+
+            // Verify UiEvent logged
+            verify(uiEventLogger).log(CommunalUiEvent.COMMUNAL_HUB_SWIPE_TO_EXIT_CANCEL)
+
+            // Verify neither SHOWN nor GONE is logged
+            verify(uiEventLogger, never()).log(CommunalUiEvent.COMMUNAL_HUB_SHOWN)
+            verify(uiEventLogger, never()).log(CommunalUiEvent.COMMUNAL_HUB_GONE)
+        }
+
+    private fun transition(
+        from: CommunalSceneKey = CommunalSceneKey.DEFAULT,
+        to: CommunalSceneKey = CommunalSceneKey.DEFAULT,
+    ): ObservableCommunalTransitionState.Transition {
+        return ObservableCommunalTransitionState.Transition(
+            fromScene = from,
+            toScene = to,
+            progress = emptyFlow(),
+            isInitiatedByUserInput = true,
+            isUserInputOngoing = emptyFlow(),
+        )
+    }
+
+    private fun idle(sceneKey: CommunalSceneKey): ObservableCommunalTransitionState.Idle {
+        return ObservableCommunalTransitionState.Idle(sceneKey)
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt
index 314dfdf..54510a8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt
@@ -16,12 +16,16 @@
 
 package com.android.systemui.communal.view.viewmodel
 
+import android.app.Activity.RESULT_CANCELED
+import android.app.Activity.RESULT_OK
 import android.app.smartspace.SmartspaceTarget
-import android.os.PowerManager
+import android.appwidget.AppWidgetHost
+import android.content.ComponentName
 import android.provider.Settings
 import android.widget.RemoteViews
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
+import com.android.internal.logging.UiEventLogger
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.communal.data.repository.FakeCommunalMediaRepository
 import com.android.systemui.communal.data.repository.FakeCommunalRepository
@@ -29,34 +33,39 @@
 import com.android.systemui.communal.data.repository.FakeCommunalWidgetRepository
 import com.android.systemui.communal.domain.interactor.CommunalInteractorFactory
 import com.android.systemui.communal.domain.model.CommunalContentModel
+import com.android.systemui.communal.shared.log.CommunalUiEvent
 import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
 import com.android.systemui.communal.ui.viewmodel.CommunalEditModeViewModel
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.kosmos.testScope
 import com.android.systemui.media.controls.ui.MediaHost
-import com.android.systemui.shade.ShadeViewController
 import com.android.systemui.smartspace.data.repository.FakeSmartspaceRepository
+import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth.assertThat
-import javax.inject.Provider
-import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mock
 import org.mockito.Mockito
+import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
 
+@OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class CommunalEditModeViewModelTest : SysuiTestCase() {
     @Mock private lateinit var mediaHost: MediaHost
-    @Mock private lateinit var shadeViewController: ShadeViewController
-    @Mock private lateinit var powerManager: PowerManager
+    @Mock private lateinit var appWidgetHost: AppWidgetHost
+    @Mock private lateinit var uiEventLogger: UiEventLogger
 
-    private lateinit var testScope: TestScope
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
 
     private lateinit var keyguardRepository: FakeKeyguardRepository
     private lateinit var communalRepository: FakeCommunalRepository
@@ -71,9 +80,7 @@
     fun setUp() {
         MockitoAnnotations.initMocks(this)
 
-        testScope = TestScope()
-
-        val withDeps = CommunalInteractorFactory.create()
+        val withDeps = CommunalInteractorFactory.create(testScope)
         keyguardRepository = withDeps.keyguardRepository
         communalRepository = withDeps.communalRepository
         tutorialRepository = withDeps.tutorialRepository
@@ -84,14 +91,14 @@
         underTest =
             CommunalEditModeViewModel(
                 withDeps.communalInteractor,
-                Provider { shadeViewController },
-                powerManager,
+                appWidgetHost,
                 mediaHost,
+                uiEventLogger,
             )
     }
 
     @Test
-    fun communalContent_onlyWidgetsAreShownInEditMode() =
+    fun communalContent_onlyWidgetsAndCtaTileAreShownInEditMode() =
         testScope.runTest {
             tutorialRepository.setTutorialSettingState(Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED)
 
@@ -119,15 +126,97 @@
             smartspaceRepository.setCommunalSmartspaceTargets(listOf(target))
 
             // Media playing.
-            mediaRepository.mediaPlaying.value = true
+            mediaRepository.mediaActive()
 
             val communalContent by collectLastValue(underTest.communalContent)
 
-            // Only Widgets are shown.
-            assertThat(communalContent?.size).isEqualTo(2)
+            // Only Widgets and CTA tile are shown.
+            assertThat(communalContent?.size).isEqualTo(3)
             assertThat(communalContent?.get(0))
                 .isInstanceOf(CommunalContentModel.Widget::class.java)
             assertThat(communalContent?.get(1))
                 .isInstanceOf(CommunalContentModel.Widget::class.java)
+            assertThat(communalContent?.get(2))
+                .isInstanceOf(CommunalContentModel.CtaTileInEditMode::class.java)
         }
+
+    @Test
+    fun interactionHandlerIgnoresClicks() {
+        val interactionHandler = underTest.getInteractionHandler()
+        assertThat(
+                interactionHandler.onInteraction(
+                    /* view = */ mock(),
+                    /* pendingIntent = */ mock(),
+                    /* response = */ mock()
+                )
+            )
+            .isEqualTo(false)
+    }
+
+    @Test
+    fun addingWidgetTriggersConfiguration() =
+        testScope.runTest {
+            val provider = ComponentName("pkg.test", "testWidget")
+            val widgetToConfigure by collectLastValue(underTest.widgetsToConfigure)
+            assertThat(widgetToConfigure).isNull()
+            underTest.onAddWidget(componentName = provider, priority = 0)
+            assertThat(widgetToConfigure).isEqualTo(1)
+        }
+
+    @Test
+    fun settingResultOkAddsWidget() =
+        testScope.runTest {
+            val provider = ComponentName("pkg.test", "testWidget")
+            val widgetAdded by collectLastValue(widgetRepository.widgetAdded)
+            assertThat(widgetAdded).isNull()
+            underTest.onAddWidget(componentName = provider, priority = 0)
+            assertThat(widgetAdded).isNull()
+            underTest.setConfigurationResult(RESULT_OK)
+            assertThat(widgetAdded).isEqualTo(1)
+        }
+
+    @Test
+    fun settingResultCancelledDoesNotAddWidget() =
+        testScope.runTest {
+            val provider = ComponentName("pkg.test", "testWidget")
+            val widgetAdded by collectLastValue(widgetRepository.widgetAdded)
+            assertThat(widgetAdded).isNull()
+            underTest.onAddWidget(componentName = provider, priority = 0)
+            assertThat(widgetAdded).isNull()
+            underTest.setConfigurationResult(RESULT_CANCELED)
+            assertThat(widgetAdded).isNull()
+        }
+
+    @Test(expected = IllegalStateException::class)
+    fun settingResultBeforeWidgetAddedThrowsException() {
+        underTest.setConfigurationResult(RESULT_OK)
+    }
+
+    @Test(expected = IllegalStateException::class)
+    fun addingWidgetWhileConfigurationActiveFails() =
+        testScope.runTest {
+            val providerOne = ComponentName("pkg.test", "testWidget")
+            underTest.onAddWidget(componentName = providerOne, priority = 0)
+            runCurrent()
+            val providerTwo = ComponentName("pkg.test", "testWidget2")
+            underTest.onAddWidget(componentName = providerTwo, priority = 0)
+        }
+
+    @Test
+    fun reorderWidget_uiEventLogging_start() {
+        underTest.onReorderWidgetStart()
+        verify(uiEventLogger).log(CommunalUiEvent.COMMUNAL_HUB_REORDER_WIDGET_START)
+    }
+
+    @Test
+    fun reorderWidget_uiEventLogging_end() {
+        underTest.onReorderWidgetEnd()
+        verify(uiEventLogger).log(CommunalUiEvent.COMMUNAL_HUB_REORDER_WIDGET_FINISH)
+    }
+
+    @Test
+    fun reorderWidget_uiEventLogging_cancel() {
+        underTest.onReorderWidgetCancel()
+        verify(uiEventLogger).log(CommunalUiEvent.COMMUNAL_HUB_REORDER_WIDGET_CANCEL)
+    }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
index 8a71168..a776062 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
@@ -17,7 +17,6 @@
 package com.android.systemui.communal.view.viewmodel
 
 import android.app.smartspace.SmartspaceTarget
-import android.os.PowerManager
 import android.provider.Settings
 import android.widget.RemoteViews
 import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -31,16 +30,18 @@
 import com.android.systemui.communal.domain.model.CommunalContentModel
 import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
 import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
+import com.android.systemui.communal.ui.viewmodel.CommunalViewModel.Companion.POPUP_AUTO_HIDE_TIMEOUT_MS
+import com.android.systemui.communal.widgets.WidgetInteractionHandler
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
 import com.android.systemui.media.controls.ui.MediaHost
-import com.android.systemui.shade.ShadeViewController
 import com.android.systemui.smartspace.data.repository.FakeSmartspaceRepository
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth.assertThat
-import javax.inject.Provider
+import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.advanceTimeBy
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
 import org.junit.Test
@@ -49,12 +50,11 @@
 import org.mockito.Mockito
 import org.mockito.MockitoAnnotations
 
+@OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class CommunalViewModelTest : SysuiTestCase() {
     @Mock private lateinit var mediaHost: MediaHost
-    @Mock private lateinit var shadeViewController: ShadeViewController
-    @Mock private lateinit var powerManager: PowerManager
 
     private lateinit var testScope: TestScope
 
@@ -83,10 +83,10 @@
 
         underTest =
             CommunalViewModel(
+                testScope,
                 withDeps.communalInteractor,
+                WidgetInteractionHandler(mock()),
                 withDeps.tutorialInteractor,
-                Provider { shadeViewController },
-                powerManager,
                 mediaHost,
             )
     }
@@ -110,7 +110,7 @@
         }
 
     @Test
-    fun ordering_smartspaceBeforeUmoBeforeWidgets() =
+    fun ordering_smartspaceBeforeUmoBeforeWidgetsBeforeCtaTile() =
         testScope.runTest {
             tutorialRepository.setTutorialSettingState(Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED)
 
@@ -138,12 +138,15 @@
             smartspaceRepository.setCommunalSmartspaceTargets(listOf(target))
 
             // Media playing.
-            mediaRepository.mediaPlaying.value = true
+            mediaRepository.mediaActive()
+
+            // CTA Tile not dismissed.
+            communalRepository.setCtaTileInViewModeVisibility(true)
 
             val communalContent by collectLastValue(underTest.communalContent)
 
-            // Order is smart space, then UMO, then widget content.
-            assertThat(communalContent?.size).isEqualTo(4)
+            // Order is smart space, then UMO, widget content and cta tile.
+            assertThat(communalContent?.size).isEqualTo(5)
             assertThat(communalContent?.get(0))
                 .isInstanceOf(CommunalContentModel.Smartspace::class.java)
             assertThat(communalContent?.get(1)).isInstanceOf(CommunalContentModel.Umo::class.java)
@@ -151,5 +154,47 @@
                 .isInstanceOf(CommunalContentModel.Widget::class.java)
             assertThat(communalContent?.get(3))
                 .isInstanceOf(CommunalContentModel.Widget::class.java)
+            assertThat(communalContent?.get(4))
+                .isInstanceOf(CommunalContentModel.CtaTileInViewMode::class.java)
+        }
+
+    @Test
+    fun dismissCta_hidesCtaTileAndShowsPopup_thenHidesPopupAfterTimeout() =
+        testScope.runTest {
+            tutorialRepository.setTutorialSettingState(Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED)
+            communalRepository.setCtaTileInViewModeVisibility(true)
+
+            val communalContent by collectLastValue(underTest.communalContent)
+            val isPopupOnDismissCtaShowing by collectLastValue(underTest.isPopupOnDismissCtaShowing)
+
+            assertThat(communalContent?.size).isEqualTo(1)
+            assertThat(communalContent?.get(0))
+                .isInstanceOf(CommunalContentModel.CtaTileInViewMode::class.java)
+
+            underTest.onDismissCtaTile()
+
+            // hide CTA tile and show the popup
+            assertThat(communalContent).isEmpty()
+            assertThat(isPopupOnDismissCtaShowing).isEqualTo(true)
+
+            // hide popup after time elapsed
+            advanceTimeBy(POPUP_AUTO_HIDE_TIMEOUT_MS)
+            assertThat(isPopupOnDismissCtaShowing).isEqualTo(false)
+        }
+
+    @Test
+    fun popup_onDismiss_hidesImmediately() =
+        testScope.runTest {
+            tutorialRepository.setTutorialSettingState(Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED)
+            communalRepository.setCtaTileInViewModeVisibility(true)
+
+            val isPopupOnDismissCtaShowing by collectLastValue(underTest.isPopupOnDismissCtaShowing)
+
+            underTest.onDismissCtaTile()
+            assertThat(isPopupOnDismissCtaShowing).isEqualTo(true)
+
+            // dismiss the popup directly
+            underTest.onHidePopupAfterDismissCta()
+            assertThat(isPopupOnDismissCtaShowing).isEqualTo(false)
         }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
similarity index 96%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
index d6d5b23..6a14220 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.keyguard.data.repository
+package com.android.systemui.deviceentry.data.repository
 
 import android.app.StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP
 import android.app.StatusBarManager.SESSION_KEYGUARD
@@ -37,10 +37,6 @@
 import androidx.test.filters.SmallTest
 import com.android.internal.logging.InstanceId.fakeInstanceId
 import com.android.internal.logging.UiEventLogger
-import com.android.keyguard.FaceAuthUiEvent
-import com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN
-import com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_NOTIFICATION_PANEL_CLICKED
-import com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER
 import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.biometrics.data.repository.FakeDisplayStateRepository
@@ -53,26 +49,37 @@
 import com.android.systemui.coroutines.FlowValue
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.coroutines.collectValues
+import com.android.systemui.deviceentry.shared.FaceAuthUiEvent
+import com.android.systemui.deviceentry.shared.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN
+import com.android.systemui.deviceentry.shared.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_NOTIFICATION_PANEL_CLICKED
+import com.android.systemui.deviceentry.shared.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER
+import com.android.systemui.deviceentry.shared.model.ErrorFaceAuthenticationStatus
+import com.android.systemui.deviceentry.shared.model.FaceAuthenticationStatus
+import com.android.systemui.deviceentry.shared.model.FaceDetectionStatus
+import com.android.systemui.deviceentry.shared.model.HelpFaceAuthenticationStatus
+import com.android.systemui.deviceentry.shared.model.SuccessFaceAuthenticationStatus
 import com.android.systemui.display.data.repository.FakeDisplayRepository
 import com.android.systemui.display.data.repository.display
 import com.android.systemui.dump.DumpManager
-import com.android.systemui.dump.logcatLogBuffer
 import com.android.systemui.flags.FakeFeatureFlags
 import com.android.systemui.flags.Flags.KEYGUARD_WM_STATE_REFACTOR
+import com.android.systemui.keyguard.data.repository.BiometricType
+import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
+import com.android.systemui.keyguard.data.repository.FakeCommandQueue
+import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.data.repository.FakeTrustRepository
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
-import com.android.systemui.keyguard.shared.model.ErrorFaceAuthenticationStatus
-import com.android.systemui.keyguard.shared.model.FaceAuthenticationStatus
-import com.android.systemui.keyguard.shared.model.FaceDetectionStatus
-import com.android.systemui.keyguard.shared.model.HelpFaceAuthenticationStatus
 import com.android.systemui.keyguard.shared.model.KeyguardState
-import com.android.systemui.keyguard.shared.model.SuccessFaceAuthenticationStatus
 import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.keyguard.shared.model.TransitionStep
 import com.android.systemui.log.FaceAuthenticationLogger
 import com.android.systemui.log.SessionTracker
+import com.android.systemui.log.logcatLogBuffer
 import com.android.systemui.log.table.TableLogBuffer
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.power.data.repository.FakePowerRepository
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java
index 3d1efa5..5827671 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java
@@ -293,8 +293,8 @@
     }
 
     /**
-     * Verifies that swiping up when the lock pattern is not secure does not consume the scroll
-     * gesture or expand.
+     * Verifies that swiping up when the lock pattern is not secure dismissed dream and consumes
+     * the gesture.
      */
     @Test
     public void testSwipeUp_keyguardNotSecure_doesNotExpand() {
@@ -314,11 +314,44 @@
 
         reset(mScrimController);
 
-        // Scroll gesture is not consumed.
+        // Scroll gesture is consumed.
         assertThat(gestureListener.onScroll(event1, event2, 0, distanceY))
-                .isFalse();
+                .isTrue();
         // We should not expand since the keyguard is not secure
         verify(mScrimController, never()).expand(any());
+        // Since we are swiping up, we should wake from dreams.
+        verify(mCentralSurfaces).awakenDreams();
+    }
+
+    /**
+     * Verifies that swiping down when the lock pattern is not secure does not dismiss the dream.
+     */
+    @Test
+    public void testSwipeDown_keyguardNotSecure_doesNotExpand() {
+        when(mLockPatternUtils.isSecure(CURRENT_USER_INFO.id)).thenReturn(false);
+        mTouchHandler.onSessionStart(mTouchSession);
+        ArgumentCaptor<GestureDetector.OnGestureListener> gestureListenerCaptor =
+                ArgumentCaptor.forClass(GestureDetector.OnGestureListener.class);
+        verify(mTouchSession).registerGestureListener(gestureListenerCaptor.capture());
+
+        final OnGestureListener gestureListener = gestureListenerCaptor.getValue();
+
+        final float distanceY = SCREEN_HEIGHT_PX * 0.3f;
+        // Swiping down near the bottom of the screen where the touch initiation region is.
+        final MotionEvent event1 = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE,
+                0, SCREEN_HEIGHT_PX - distanceY, 0);
+        final MotionEvent event2 = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE,
+                0, SCREEN_HEIGHT_PX, 0);
+
+        reset(mScrimController);
+
+        // Scroll gesture is not consumed.
+        assertThat(gestureListener.onScroll(event1, event2, 0, distanceY))
+                .isTrue();
+        // We should not expand since the keyguard is not secure
+        verify(mScrimController, never()).expand(any());
+        // Since we are swiping down, we should not dismiss the dream.
+        verify(mCentralSurfaces, never()).awakenDreams();
     }
 
     private void verifyScroll(float percent, Direction direction,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest.kt
similarity index 96%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest.kt
index 477f455..0329794 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -12,20 +12,19 @@
  * WITHOUT WARRANTIES OR CONDITIONS OF 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.app.Activity
 import androidx.test.filters.SmallTest
-import com.android.systemui.res.R
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.controls.ControlsServiceInfo
 import com.android.systemui.controls.controller.ControlsController
 import com.android.systemui.controls.dagger.ControlsComponent
 import com.android.systemui.controls.management.ControlsListingController
 import com.android.systemui.controls.ui.ControlsUiController
+import com.android.systemui.res.R
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth.assertThat
@@ -37,17 +36,17 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.Parameterized
-import org.junit.runners.Parameterized.Parameter
-import org.junit.runners.Parameterized.Parameters
 import org.mockito.ArgumentCaptor
 import org.mockito.Captor
 import org.mockito.Mock
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
+import platform.test.runner.parameterized.Parameter
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
 
 @SmallTest
-@RunWith(Parameterized::class)
+@RunWith(ParameterizedAndroidJunit4::class)
 class HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest : SysuiTestCase() {
 
     companion object {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
index 6c4bb37..c4ebbdc 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
@@ -24,6 +24,7 @@
 import com.android.keyguard.KeyguardUpdateMonitorCallback
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.biometrics.AuthController
+import com.android.systemui.biometrics.data.repository.FakeFacePropertyRepository
 import com.android.systemui.common.shared.model.Position
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.doze.DozeMachine
@@ -71,6 +72,7 @@
     private val testDispatcher = StandardTestDispatcher()
     private val testScope = TestScope(testDispatcher)
     private lateinit var systemClock: FakeSystemClock
+    private lateinit var facePropertyRepository: FakeFacePropertyRepository
 
     private lateinit var underTest: KeyguardRepositoryImpl
 
@@ -78,6 +80,7 @@
     fun setUp() {
         MockitoAnnotations.initMocks(this)
         systemClock = FakeSystemClock()
+        facePropertyRepository = FakeFacePropertyRepository()
         underTest =
             KeyguardRepositoryImpl(
                 statusBarStateController,
@@ -89,6 +92,7 @@
                 mainDispatcher,
                 testScope.backgroundScope,
                 systemClock,
+                facePropertyRepository,
             )
     }
 
@@ -482,10 +486,7 @@
         testScope.runTest {
             val values = mutableListOf<Point?>()
             val job = underTest.faceSensorLocation.onEach(values::add).launchIn(this)
-
-            val captor = argumentCaptor<AuthController.Callback>()
             runCurrent()
-            verify(authController).addCallback(captor.capture())
 
             // An initial, null value should be initially emitted so that flows combined with this
             // one
@@ -500,8 +501,7 @@
                     Point(250, 250),
                 )
                 .onEach {
-                    whenever(authController.faceSensorLocation).thenReturn(it)
-                    captor.value.onFaceSensorLocationChanged()
+                    facePropertyRepository.setSensorLocation(it)
                     runCurrent()
                 }
                 .also { dispatchedSensorLocations ->
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt
index b483085..9bccf4e2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt
@@ -82,6 +82,7 @@
                 keyguardTransitionInteractor,
                 fakeLightRevealScrimRepository,
                 testScope.backgroundScope,
+                mock(),
                 mock()
             )
     }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodAlphaViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodAlphaViewModelTest.kt
new file mode 100644
index 0000000..83782e2
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodAlphaViewModelTest.kt
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
+import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class AodAlphaViewModelTest : SysuiTestCase() {
+
+    @Mock
+    private lateinit var occludedToLockscreenTransitionViewModel:
+        OccludedToLockscreenTransitionViewModel
+
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+    private val keyguardRepository = kosmos.fakeKeyguardRepository
+    private val keyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository
+    private val occludedToLockscreenAlpha = MutableStateFlow(0f)
+
+    private lateinit var underTest: AodAlphaViewModel
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+        whenever(occludedToLockscreenTransitionViewModel.lockscreenAlpha)
+            .thenReturn(occludedToLockscreenAlpha)
+        kosmos.occludedToLockscreenTransitionViewModel = occludedToLockscreenTransitionViewModel
+
+        underTest = kosmos.aodAlphaViewModel
+    }
+
+    @Test
+    fun alpha() =
+        testScope.runTest {
+            val alpha by collectLastValue(underTest.alpha)
+
+            keyguardTransitionRepository.sendTransitionSteps(
+                from = KeyguardState.OFF,
+                to = KeyguardState.LOCKSCREEN,
+                testScope = testScope,
+            )
+
+            keyguardRepository.setKeyguardAlpha(0.1f)
+            assertThat(alpha).isEqualTo(0.1f)
+            keyguardRepository.setKeyguardAlpha(0.5f)
+            assertThat(alpha).isEqualTo(0.5f)
+            keyguardRepository.setKeyguardAlpha(0.2f)
+            assertThat(alpha).isEqualTo(0.2f)
+            keyguardRepository.setKeyguardAlpha(0f)
+            assertThat(alpha).isEqualTo(0f)
+            occludedToLockscreenAlpha.value = 0.8f
+            assertThat(alpha).isEqualTo(0.8f)
+        }
+
+    @Test
+    fun alpha_whenGone_equalsZero() =
+        testScope.runTest {
+            val alpha by collectLastValue(underTest.alpha)
+
+            keyguardTransitionRepository.sendTransitionSteps(
+                from = KeyguardState.LOCKSCREEN,
+                to = KeyguardState.GONE,
+                testScope = testScope,
+            )
+
+            keyguardRepository.setKeyguardAlpha(0.1f)
+            assertThat(alpha).isEqualTo(0f)
+            keyguardRepository.setKeyguardAlpha(0.5f)
+            assertThat(alpha).isEqualTo(0f)
+            keyguardRepository.setKeyguardAlpha(1f)
+            assertThat(alpha).isEqualTo(0f)
+        }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelTest.kt
new file mode 100644
index 0000000..0543bc2
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelTest.kt
@@ -0,0 +1,290 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.domain.interactor.BurnInInteractor
+import com.android.systemui.keyguard.domain.interactor.burnInInteractor
+import com.android.systemui.keyguard.shared.model.BurnInModel
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.plugins.clocks.ClockController
+import com.android.systemui.testKosmos
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.emptyFlow
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Answers
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class AodBurnInViewModelTest : SysuiTestCase() {
+
+    @Mock private lateinit var burnInInteractor: BurnInInteractor
+    @Mock private lateinit var goneToAodTransitionViewModel: GoneToAodTransitionViewModel
+    @Mock(answer = Answers.RETURNS_DEEP_STUBS) private lateinit var clockController: ClockController
+
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+    private val keyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository
+    private lateinit var underTest: AodBurnInViewModel
+
+    private var burnInParameters =
+        BurnInParameters(
+            clockControllerProvider = { clockController },
+        )
+    private val burnInFlow = MutableStateFlow(BurnInModel())
+    private val enterFromTopAnimationAlpha = MutableStateFlow(0f)
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+        whenever(burnInInteractor.keyguardBurnIn).thenReturn(burnInFlow)
+        kosmos.burnInInteractor = burnInInteractor
+        whenever(goneToAodTransitionViewModel.enterFromTopAnimationAlpha)
+            .thenReturn(enterFromTopAnimationAlpha)
+        whenever(goneToAodTransitionViewModel.enterFromTopTranslationY(anyInt()))
+            .thenReturn(emptyFlow())
+        kosmos.goneToAodTransitionViewModel = goneToAodTransitionViewModel
+
+        underTest = kosmos.aodBurnInViewModel
+    }
+
+    @Test
+    fun translationY_initializedToZero() =
+        testScope.runTest {
+            val translationY by collectLastValue(underTest.translationY(burnInParameters))
+            assertThat(translationY).isEqualTo(0)
+        }
+
+    @Test
+    fun translationAndScale_whenNotDozing() =
+        testScope.runTest {
+            val translationX by collectLastValue(underTest.translationX(burnInParameters))
+            val translationY by collectLastValue(underTest.translationY(burnInParameters))
+            val scale by collectLastValue(underTest.scale(burnInParameters))
+
+            // Set to not dozing (on lockscreen)
+            keyguardTransitionRepository.sendTransitionStep(
+                TransitionStep(
+                    from = KeyguardState.AOD,
+                    to = KeyguardState.LOCKSCREEN,
+                    value = 1f,
+                    transitionState = TransitionState.FINISHED
+                ),
+                validateStep = false,
+            )
+
+            // Trigger a change to the burn-in model
+            burnInFlow.value =
+                BurnInModel(
+                    translationX = 20,
+                    translationY = 30,
+                    scale = 0.5f,
+                )
+
+            assertThat(translationX).isEqualTo(0)
+            assertThat(translationY).isEqualTo(0)
+            assertThat(scale)
+                .isEqualTo(
+                    BurnInScaleViewModel(
+                        scale = 1f,
+                        scaleClockOnly = true,
+                    )
+                )
+        }
+
+    @Test
+    fun translationAndScale_whenFullyDozing() =
+        testScope.runTest {
+            burnInParameters = burnInParameters.copy(statusViewTop = 100)
+            val translationX by collectLastValue(underTest.translationX(burnInParameters))
+            val translationY by collectLastValue(underTest.translationY(burnInParameters))
+            val scale by collectLastValue(underTest.scale(burnInParameters))
+
+            // Set to dozing (on AOD)
+            keyguardTransitionRepository.sendTransitionStep(
+                TransitionStep(
+                    from = KeyguardState.GONE,
+                    to = KeyguardState.AOD,
+                    value = 1f,
+                    transitionState = TransitionState.FINISHED
+                ),
+                validateStep = false,
+            )
+            // Trigger a change to the burn-in model
+            burnInFlow.value =
+                BurnInModel(
+                    translationX = 20,
+                    translationY = 30,
+                    scale = 0.5f,
+                )
+
+            assertThat(translationX).isEqualTo(20)
+            assertThat(translationY).isEqualTo(30)
+            assertThat(scale)
+                .isEqualTo(
+                    BurnInScaleViewModel(
+                        scale = 0.5f,
+                        scaleClockOnly = true,
+                    )
+                )
+
+            // Set to the beginning of GONE->AOD transition
+            keyguardTransitionRepository.sendTransitionStep(
+                TransitionStep(
+                    from = KeyguardState.GONE,
+                    to = KeyguardState.AOD,
+                    value = 0f,
+                    transitionState = TransitionState.STARTED
+                ),
+                validateStep = false,
+            )
+            assertThat(translationX).isEqualTo(0)
+            assertThat(translationY).isEqualTo(0)
+            assertThat(scale)
+                .isEqualTo(
+                    BurnInScaleViewModel(
+                        scale = 1f,
+                        scaleClockOnly = true,
+                    )
+                )
+        }
+
+    @Test
+    fun translationAndScale_whenFullyDozing_staysOutOfTopInset() =
+        testScope.runTest {
+            burnInParameters =
+                burnInParameters.copy(
+                    statusViewTop = 100,
+                    topInset = 80,
+                )
+            val translationX by collectLastValue(underTest.translationX(burnInParameters))
+            val translationY by collectLastValue(underTest.translationY(burnInParameters))
+            val scale by collectLastValue(underTest.scale(burnInParameters))
+
+            // Set to dozing (on AOD)
+            keyguardTransitionRepository.sendTransitionStep(
+                TransitionStep(
+                    from = KeyguardState.GONE,
+                    to = KeyguardState.AOD,
+                    value = 1f,
+                    transitionState = TransitionState.FINISHED
+                ),
+                validateStep = false,
+            )
+
+            // Trigger a change to the burn-in model
+            burnInFlow.value =
+                BurnInModel(
+                    translationX = 20,
+                    translationY = -30,
+                    scale = 0.5f,
+                )
+            assertThat(translationX).isEqualTo(20)
+            // -20 instead of -30, due to inset of 80
+            assertThat(translationY).isEqualTo(-20)
+            assertThat(scale)
+                .isEqualTo(
+                    BurnInScaleViewModel(
+                        scale = 0.5f,
+                        scaleClockOnly = true,
+                    )
+                )
+
+            // Set to the beginning of GONE->AOD transition
+            keyguardTransitionRepository.sendTransitionStep(
+                TransitionStep(
+                    from = KeyguardState.GONE,
+                    to = KeyguardState.AOD,
+                    value = 0f,
+                    transitionState = TransitionState.STARTED
+                ),
+                validateStep = false,
+            )
+            assertThat(translationX).isEqualTo(0)
+            assertThat(translationY).isEqualTo(0)
+            assertThat(scale)
+                .isEqualTo(
+                    BurnInScaleViewModel(
+                        scale = 1f,
+                        scaleClockOnly = true,
+                    )
+                )
+        }
+
+    @Test
+    fun translationAndScale_useScaleOnly() =
+        testScope.runTest {
+            whenever(clockController.config.useAlternateSmartspaceAODTransition).thenReturn(true)
+
+            val translationX by collectLastValue(underTest.translationX(burnInParameters))
+            val translationY by collectLastValue(underTest.translationY(burnInParameters))
+            val scale by collectLastValue(underTest.scale(burnInParameters))
+
+            // Set to dozing (on AOD)
+            keyguardTransitionRepository.sendTransitionStep(
+                TransitionStep(
+                    from = KeyguardState.GONE,
+                    to = KeyguardState.AOD,
+                    value = 1f,
+                    transitionState = TransitionState.FINISHED
+                ),
+                validateStep = false,
+            )
+
+            // Trigger a change to the burn-in model
+            burnInFlow.value =
+                BurnInModel(
+                    translationX = 20,
+                    translationY = 30,
+                    scale = 0.5f,
+                )
+
+            assertThat(translationX).isEqualTo(0)
+            assertThat(translationY).isEqualTo(0)
+            assertThat(scale).isEqualTo(BurnInScaleViewModel(scale = 0.5f, scaleClockOnly = false))
+        }
+
+    @Test
+    fun alpha() =
+        testScope.runTest {
+            val alpha by collectLastValue(underTest.alpha)
+
+            enterFromTopAnimationAlpha.value = 0.2f
+            assertThat(alpha).isEqualTo(0.2f)
+
+            enterFromTopAnimationAlpha.value = 1f
+            assertThat(alpha).isEqualTo(1f)
+        }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt
index 53bca48..f1690daf 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt
@@ -55,6 +55,28 @@
     private val underTest = kosmos.dreamingToLockscreenTransitionViewModel
 
     @Test
+    fun shortcutsAlpha_bothShortcutsReceiveLastValue() =
+        testScope.runTest {
+            val valuesLeft by collectValues(underTest.shortcutsAlpha)
+            val valuesRight by collectValues(underTest.shortcutsAlpha)
+
+            keyguardTransitionRepository.sendTransitionSteps(
+                listOf(
+                    step(0f, TransitionState.STARTED),
+                    step(0.3f),
+                    step(0.5f),
+                    step(0.6f),
+                    step(0.8f),
+                    step(1f),
+                ),
+                testScope,
+            )
+
+            assertThat(valuesLeft.last()).isEqualTo(1f)
+            assertThat(valuesRight.last()).isEqualTo(1f)
+        }
+
+    @Test
     fun dreamOverlayTranslationY() =
         testScope.runTest {
             val pixels = 100
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelTest.kt
index 3c07034..f763a67 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelTest.kt
@@ -59,7 +59,7 @@
                 testScope,
             )
 
-            // Only three values should be present, since the dream overlay runs for a small
+            // Only five values should be present, since the dream overlay runs for a small
             // fraction of the overall animation time
             assertThat(values.size).isEqualTo(5)
             values.forEach { assertThat(it).isIn(Range.closed(0f, 1f)) }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
new file mode 100644
index 0000000..5b88ebe6
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import android.view.View
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.Flags as AConfigFlags
+import com.android.systemui.Flags.FLAG_NEW_AOD_TRANSITION
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
+import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.statusbar.notification.stack.domain.interactor.notificationsKeyguardInteractor
+import com.android.systemui.statusbar.phone.dozeParameters
+import com.android.systemui.statusbar.phone.screenOffAnimationController
+import com.android.systemui.testKosmos
+import com.android.systemui.util.mockito.whenever
+import com.android.systemui.util.ui.isAnimating
+import com.android.systemui.util.ui.stopAnimating
+import com.android.systemui.util.ui.value
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class KeyguardRootViewModelTest : SysuiTestCase() {
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+    private val keyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository
+    private val screenOffAnimationController = kosmos.screenOffAnimationController
+    private val deviceEntryRepository = kosmos.fakeDeviceEntryRepository
+    private val notificationsKeyguardInteractor = kosmos.notificationsKeyguardInteractor
+    private val dozeParameters = kosmos.dozeParameters
+    private val underTest = kosmos.keyguardRootViewModel
+
+    @Before
+    fun setUp() {
+        mSetFlagsRule.enableFlags(AConfigFlags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR)
+        mSetFlagsRule.enableFlags(FLAG_NEW_AOD_TRANSITION)
+        mSetFlagsRule.disableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
+    }
+
+    @Test
+    fun burnInLayerVisibility() =
+        testScope.runTest {
+            val burnInLayerVisibility by collectLastValue(underTest.burnInLayerVisibility)
+
+            keyguardTransitionRepository.sendTransitionStep(
+                TransitionStep(
+                    from = KeyguardState.LOCKSCREEN,
+                    to = KeyguardState.AOD,
+                    value = 0f,
+                    transitionState = TransitionState.STARTED
+                ),
+                validateStep = false,
+            )
+            assertThat(burnInLayerVisibility).isEqualTo(View.VISIBLE)
+        }
+
+    @Test
+    fun iconContainer_isNotVisible_notOnKeyguard_dontShowAodIconsWhenShade() =
+        testScope.runTest {
+            val isVisible by collectLastValue(underTest.isNotifIconContainerVisible)
+            runCurrent()
+            keyguardTransitionRepository.sendTransitionSteps(
+                from = KeyguardState.OFF,
+                to = KeyguardState.GONE,
+                testScope,
+            )
+            whenever(screenOffAnimationController.shouldShowAodIconsWhenShade()).thenReturn(false)
+            runCurrent()
+
+            assertThat(isVisible?.value).isFalse()
+            assertThat(isVisible?.isAnimating).isFalse()
+        }
+
+    @Test
+    fun iconContainer_isVisible_bypassEnabled() =
+        testScope.runTest {
+            val isVisible by collectLastValue(underTest.isNotifIconContainerVisible)
+            runCurrent()
+            deviceEntryRepository.setBypassEnabled(true)
+            runCurrent()
+
+            assertThat(isVisible?.value).isTrue()
+        }
+
+    @Test
+    fun iconContainer_isNotVisible_pulseExpanding_notBypassing() =
+        testScope.runTest {
+            val isVisible by collectLastValue(underTest.isNotifIconContainerVisible)
+            runCurrent()
+            notificationsKeyguardInteractor.setPulseExpanding(true)
+            deviceEntryRepository.setBypassEnabled(false)
+            runCurrent()
+
+            assertThat(isVisible?.value).isEqualTo(false)
+        }
+
+    @Test
+    fun iconContainer_isVisible_notifsFullyHidden_bypassEnabled() =
+        testScope.runTest {
+            val isVisible by collectLastValue(underTest.isNotifIconContainerVisible)
+            runCurrent()
+            notificationsKeyguardInteractor.setPulseExpanding(false)
+            deviceEntryRepository.setBypassEnabled(true)
+            notificationsKeyguardInteractor.setNotificationsFullyHidden(true)
+            runCurrent()
+
+            assertThat(isVisible?.value).isTrue()
+            assertThat(isVisible?.isAnimating).isTrue()
+        }
+
+    @Test
+    fun iconContainer_isVisible_notifsFullyHidden_bypassDisabled_aodDisabled() =
+        testScope.runTest {
+            val isVisible by collectLastValue(underTest.isNotifIconContainerVisible)
+            runCurrent()
+            notificationsKeyguardInteractor.setPulseExpanding(false)
+            deviceEntryRepository.setBypassEnabled(false)
+            whenever(dozeParameters.alwaysOn).thenReturn(false)
+            notificationsKeyguardInteractor.setNotificationsFullyHidden(true)
+            runCurrent()
+
+            assertThat(isVisible?.value).isTrue()
+            assertThat(isVisible?.isAnimating).isFalse()
+        }
+
+    @Test
+    fun iconContainer_isVisible_notifsFullyHidden_bypassDisabled_displayNeedsBlanking() =
+        testScope.runTest {
+            val isVisible by collectLastValue(underTest.isNotifIconContainerVisible)
+            runCurrent()
+            notificationsKeyguardInteractor.setPulseExpanding(false)
+            deviceEntryRepository.setBypassEnabled(false)
+            whenever(dozeParameters.alwaysOn).thenReturn(true)
+            whenever(dozeParameters.displayNeedsBlanking).thenReturn(true)
+            notificationsKeyguardInteractor.setNotificationsFullyHidden(true)
+            runCurrent()
+
+            assertThat(isVisible?.value).isTrue()
+            assertThat(isVisible?.isAnimating).isFalse()
+        }
+
+    @Test
+    fun iconContainer_isVisible_notifsFullyHidden_bypassDisabled() =
+        testScope.runTest {
+            val isVisible by collectLastValue(underTest.isNotifIconContainerVisible)
+            runCurrent()
+            notificationsKeyguardInteractor.setPulseExpanding(false)
+            deviceEntryRepository.setBypassEnabled(false)
+            whenever(dozeParameters.alwaysOn).thenReturn(true)
+            whenever(dozeParameters.displayNeedsBlanking).thenReturn(false)
+            notificationsKeyguardInteractor.setNotificationsFullyHidden(true)
+            runCurrent()
+
+            assertThat(isVisible?.value).isTrue()
+            assertThat(isVisible?.isAnimating).isTrue()
+        }
+
+    @Test
+    fun isIconContainerVisible_stopAnimation() =
+        testScope.runTest {
+            val isVisible by collectLastValue(underTest.isNotifIconContainerVisible)
+            runCurrent()
+            notificationsKeyguardInteractor.setPulseExpanding(false)
+            deviceEntryRepository.setBypassEnabled(false)
+            whenever(dozeParameters.alwaysOn).thenReturn(true)
+            whenever(dozeParameters.displayNeedsBlanking).thenReturn(false)
+            notificationsKeyguardInteractor.setNotificationsFullyHidden(true)
+            runCurrent()
+
+            assertThat(isVisible?.isAnimating).isEqualTo(true)
+            isVisible?.stopAnimating()
+            runCurrent()
+
+            assertThat(isVisible?.isAnimating).isEqualTo(false)
+        }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
index d07836d..74d309c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
 package com.android.systemui.keyguard.ui.viewmodel
 
 import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -26,6 +28,7 @@
 import com.android.systemui.scene.shared.model.SceneModel
 import com.android.systemui.util.mockito.mock
 import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.runTest
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -94,7 +97,6 @@
                 KeyguardLongPressViewModel(
                     interactor = mock(),
                 ),
-            keyguardRoot = utils.keyguardRootViewModel(),
             notifications = utils.notificationsPlaceholderViewModel(),
         )
     }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt
index a346e8b..74025fd 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt
@@ -73,7 +73,7 @@
                 testScope = testScope,
             )
 
-            // Only three values should be present, since the dream overlay runs for a small
+            // Only five values should be present, since the dream overlay runs for a small
             // fraction of the overall animation time
             assertThat(values.size).isEqualTo(5)
             values.forEach { assertThat(it).isIn(Range.closed(0f, 1f)) }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt
index 274bde1..6fcb0c1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt
@@ -74,7 +74,7 @@
                     ),
                 testScope = testScope,
             )
-            // Only 3 values should be present, since the dream overlay runs for a small fraction
+            // Only 5 values should be present, since the dream overlay runs for a small fraction
             // of the overall animation time
             assertThat(values.size).isEqualTo(5)
             values.forEach { assertThat(it).isIn(Range.closed(0f, 1f)) }
@@ -99,7 +99,7 @@
                     ),
                 testScope = testScope,
             )
-            assertThat(values.size).isEqualTo(5)
+            assertThat(values.size).isEqualTo(4)
             values.forEach { assertThat(it).isIn(Range.closed(0f, 100f)) }
         }
 
@@ -121,11 +121,11 @@
                     ),
                 testScope = testScope,
             )
-            assertThat(values.size).isEqualTo(4)
+            assertThat(values.size).isEqualTo(3)
             values.forEach { assertThat(it).isIn(Range.closed(0f, 100f)) }
 
             // Cancel will reset the translation
-            assertThat(values[3]).isEqualTo(0)
+            assertThat(values[2]).isEqualTo(0)
         }
 
     @Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt
index d419d4a..639114c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt
@@ -33,6 +33,7 @@
 import com.google.common.collect.Range
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -54,6 +55,7 @@
     fun lockscreenFadeIn() =
         testScope.runTest {
             val values by collectValues(underTest.lockscreenAlpha)
+            runCurrent()
 
             keyguardTransitionRepository.sendTransitionSteps(
                 listOf(
@@ -83,6 +85,7 @@
                 100
             )
             val values by collectValues(underTest.lockscreenTranslationY)
+            runCurrent()
 
             keyguardTransitionRepository.sendTransitionSteps(
                 listOf(
@@ -107,6 +110,7 @@
                 100
             )
             val values by collectValues(underTest.lockscreenTranslationY)
+            runCurrent()
 
             keyguardTransitionRepository.sendTransitionStep(step(0.5f, TransitionState.CANCELED))
 
@@ -117,6 +121,7 @@
     fun deviceEntryParentViewFadeIn() =
         testScope.runTest {
             val values by collectValues(underTest.deviceEntryParentViewAlpha)
+            runCurrent()
 
             keyguardTransitionRepository.sendTransitionSteps(
                 listOf(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt
index f027bc8..30b87bb 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt
@@ -33,6 +33,7 @@
 import com.android.systemui.util.mockito.whenever
 import com.google.common.collect.Range
 import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
 import org.junit.Test
@@ -65,6 +66,7 @@
     fun bouncerAlpha() =
         testScope.runTest {
             val values by collectValues(underTest.bouncerAlpha)
+            runCurrent()
 
             keyguardTransitionRepository.sendTransitionSteps(
                 listOf(
@@ -83,6 +85,7 @@
     fun bouncerAlpha_runDimissFromKeyguard() =
         testScope.runTest {
             val values by collectValues(underTest.bouncerAlpha)
+            runCurrent()
 
             whenever(primaryBouncerInteractor.willRunDismissFromKeyguard()).thenReturn(true)
 
@@ -95,7 +98,7 @@
                 testScope,
             )
 
-            assertThat(values.size).isEqualTo(3)
+            assertThat(values.size).isEqualTo(2)
             values.forEach { assertThat(it).isEqualTo(0f) }
         }
 
@@ -103,6 +106,7 @@
     fun lockscreenAlpha() =
         testScope.runTest {
             val values by collectValues(underTest.lockscreenAlpha)
+            runCurrent()
 
             keyguardTransitionRepository.sendTransitionStep(step(0f, TransitionState.STARTED))
             keyguardTransitionRepository.sendTransitionStep(step(1f))
@@ -115,6 +119,7 @@
     fun lockscreenAlpha_runDimissFromKeyguard() =
         testScope.runTest {
             val values by collectValues(underTest.lockscreenAlpha)
+            runCurrent()
 
             sysuiStatusBarStateController.setLeaveOpenOnKeyguardHide(true)
 
@@ -129,6 +134,7 @@
     fun lockscreenAlpha_leaveShadeOpen() =
         testScope.runTest {
             val values by collectValues(underTest.lockscreenAlpha)
+            runCurrent()
 
             sysuiStatusBarStateController.setLeaveOpenOnKeyguardHide(true)
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerTest.kt
index bd1c310..c104977 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerTest.kt
@@ -16,32 +16,45 @@
 
 package com.android.systemui.qs.tiles.base.actions
 
+import android.app.PendingIntent
+import android.content.ComponentName
 import android.content.Intent
+import android.content.pm.ActivityInfo
+import android.content.pm.PackageManager
+import android.content.pm.PackageManager.ResolveInfoFlags
+import android.content.pm.ResolveInfo
+import android.os.UserHandle
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.util.mockito.argThat
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.ArgumentMatcher
 import org.mockito.Mock
 import org.mockito.Mockito.any
 import org.mockito.Mockito.eq
+import org.mockito.Mockito.never
 import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
 import org.mockito.MockitoAnnotations
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class QSTileIntentUserInputHandlerTest : SysuiTestCase() {
-
-    @Mock private lateinit var activityStarted: ActivityStarter
+    @Mock private lateinit var packageManager: PackageManager
+    @Mock private lateinit var activityStarter: ActivityStarter
 
     lateinit var underTest: QSTileIntentUserInputHandler
 
     @Before
     fun setup() {
         MockitoAnnotations.initMocks(this)
-        underTest = QSTileIntentUserInputHandlerImpl(activityStarted)
+        underTest = QSTileIntentUserInputHandlerImpl(activityStarter, packageManager, user)
     }
 
     @Test
@@ -50,6 +63,103 @@
 
         underTest.handle(null, intent)
 
-        verify(activityStarted).postStartActivityDismissingKeyguard(eq(intent), eq(0), any())
+        verify(activityStarter).postStartActivityDismissingKeyguard(eq(intent), eq(0), any())
+    }
+
+    @Test
+    fun testPassesActivityPendingIntentToStarterAsPendingIntent() {
+        val pendingIntent = mock<PendingIntent> { whenever(isActivity).thenReturn(true) }
+
+        underTest.handle(null, pendingIntent, true)
+
+        verify(activityStarter).postStartActivityDismissingKeyguard(eq(pendingIntent), any())
+    }
+
+    @Test
+    fun testPassesActivityPendingIntentToStarterAsPendingIntentWhenNotRequestingActivityStart() {
+        val pendingIntent = mock<PendingIntent> { whenever(isActivity).thenReturn(true) }
+
+        underTest.handle(null, pendingIntent, false)
+
+        verify(activityStarter).postStartActivityDismissingKeyguard(eq(pendingIntent), any())
+    }
+
+    @Test
+    fun testPassNonActivityPendingIntentAndRequestStartingActivity_findsIntentAndStarts() {
+        val pendingIntent =
+            mock<PendingIntent> {
+                whenever(isActivity).thenReturn(false)
+                whenever(creatorPackage).thenReturn(ORIGINAL_PACKAGE)
+            }
+        setUpQueryResult(listOf(createActivityInfo(testResolvedComponent, exported = true)))
+
+        underTest.handle(null, pendingIntent, true)
+
+        val expectedIntent =
+            Intent(Intent.ACTION_MAIN)
+                .addCategory(Intent.CATEGORY_LAUNCHER)
+                .setPackage(null)
+                .addFlags(
+                    Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
+                )
+                .setComponent(testResolvedComponent)
+
+        verify(activityStarter)
+            .postStartActivityDismissingKeyguard(
+                argThat(IntentMatcher(expectedIntent)),
+                eq(0),
+                any()
+            )
+    }
+
+    @Test
+    fun testPassNonActivityPendingIntentAndDoNotRequestStartingActivity_doesNotStartActivity() {
+        val pendingIntent = mock<PendingIntent> { whenever(isActivity).thenReturn(false) }
+
+        underTest.handle(null, pendingIntent, false)
+
+        verify(activityStarter, never())
+            .postStartActivityDismissingKeyguard(any(Intent::class.java), eq(0), any())
+    }
+
+    private fun createActivityInfo(
+        componentName: ComponentName,
+        exported: Boolean = false,
+    ): ActivityInfo {
+        return ActivityInfo().apply {
+            packageName = componentName.packageName
+            name = componentName.className
+            this.exported = exported
+        }
+    }
+
+    private fun setUpQueryResult(infos: List<ActivityInfo>) {
+        `when`(
+                packageManager.queryIntentActivitiesAsUser(
+                    any(Intent::class.java),
+                    any(ResolveInfoFlags::class.java),
+                    eq(user.identifier)
+                )
+            )
+            .thenReturn(infos.map { ResolveInfo().apply { activityInfo = it } })
+    }
+
+    private class IntentMatcher(intent: Intent) : ArgumentMatcher<Intent> {
+        private val expectedIntent = intent
+        override fun matches(argument: Intent?): Boolean {
+            return argument?.action.equals(expectedIntent.action) &&
+                argument?.`package`.equals(expectedIntent.`package`) &&
+                argument?.component?.equals(expectedIntent.component)!! &&
+                argument?.categories?.equals(expectedIntent.categories)!! &&
+                argument?.flags?.equals(expectedIntent.flags)!!
+        }
+    }
+
+    companion object {
+        private const val ORIGINAL_PACKAGE = "original_pkg"
+        private const val TEST_PACKAGE = "test_pkg"
+        private const val TEST_COMPONENT_CLASS_NAME = "test_component_class_name"
+        private val testResolvedComponent = ComponentName(TEST_PACKAGE, TEST_COMPONENT_CLASS_NAME)
+        private val user = UserHandle.of(0)
     }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt
index 92c2d74..4a39ba2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt
@@ -21,9 +21,9 @@
 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.dump.LogcatEchoTrackerAlways
 import com.android.systemui.log.LogBuffer
 import com.android.systemui.log.LogBufferFactory
+import com.android.systemui.log.LogcatEchoTrackerAlways
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.qs.pipeline.shared.TileSpec
 import com.android.systemui.qs.tiles.viewmodel.QSTileState
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapperTest.kt
index 00405d0..c2ce392 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapperTest.kt
@@ -29,27 +29,37 @@
 import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
 import com.android.systemui.qs.tiles.viewmodel.QSTileState
 import com.android.systemui.res.R
+import com.android.systemui.util.time.FakeSystemClock
 import java.time.Instant
 import java.time.LocalDateTime
 import java.util.TimeZone
+import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class AlarmTileMapperTest : SysuiTestCase() {
+    private val oneMinute = 60000L
     private val kosmos = Kosmos()
     private val alarmTileConfig = kosmos.qsAlarmTileConfig
+    private val fakeClock = FakeSystemClock()
     // Using lazy (versus =) to make sure we override the right context -- see b/311612168
     private val mapper by lazy {
         AlarmTileMapper(
             context.orCreateTestableResources
                 .apply { addOverride(R.drawable.ic_alarm, TestStubDrawable()) }
                 .resources,
-            context.theme
+            context.theme,
+            fakeClock
         )
     }
 
+    @Before
+    fun setup() {
+        fakeClock.setCurrentTimeMillis(0) // same time both in test & map()
+    }
+
     @Test
     fun notAlarmSet() {
         val inputModel = AlarmTileModel.NoAlarmSet
@@ -66,7 +76,7 @@
 
     @Test
     fun nextAlarmSet24HourFormat() {
-        val triggerTime = 1L
+        val triggerTime = fakeClock.currentTimeMillis() + oneMinute
         val inputModel =
             AlarmTileModel.NextAlarmSet(true, AlarmManager.AlarmClockInfo(triggerTime, null))
 
@@ -85,7 +95,7 @@
 
     @Test
     fun nextAlarmSet12HourFormat() {
-        val triggerTime = 1L
+        val triggerTime = fakeClock.currentTimeMillis() + oneMinute
         val inputModel =
             AlarmTileModel.NextAlarmSet(false, AlarmManager.AlarmClockInfo(triggerTime, null))
 
@@ -102,6 +112,66 @@
         QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
     }
 
+    @Test
+    fun nextAlarmSetMoreThanAWeekLater_mapsSecondaryLabelToDisplayDateOnly() {
+        val oneWeekAndOneMinute = 7 * 24 * 60 * 60 * 1000L + oneMinute
+        val triggerTime = fakeClock.currentTimeMillis() + oneWeekAndOneMinute
+        val inputModel =
+            AlarmTileModel.NextAlarmSet(false, AlarmManager.AlarmClockInfo(triggerTime, null))
+
+        val outputState = mapper.map(alarmTileConfig, inputModel)
+
+        val localDateTime =
+            LocalDateTime.ofInstant(
+                Instant.ofEpochMilli(triggerTime),
+                TimeZone.getDefault().toZoneId()
+            )
+        val expectedSecondaryLabel = AlarmTileMapper.formatterDateOnly.format(localDateTime)
+        val expectedState =
+            createAlarmTileState(QSTileState.ActivationState.ACTIVE, expectedSecondaryLabel)
+        QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
+    }
+
+    @Test
+    fun nextAlarmSetOneMinuteLessThanAWeekLater_mapsSecondaryLabelToDisplayTime() {
+        val oneWeekMinusOneMinute = 7 * 24 * 60 * 60 * 1000L - oneMinute
+        val triggerTime = fakeClock.currentTimeMillis() + oneWeekMinusOneMinute
+        val inputModel =
+            AlarmTileModel.NextAlarmSet(false, AlarmManager.AlarmClockInfo(triggerTime, null))
+
+        val outputState = mapper.map(alarmTileConfig, inputModel)
+
+        val localDateTime =
+            LocalDateTime.ofInstant(
+                Instant.ofEpochMilli(triggerTime),
+                TimeZone.getDefault().toZoneId()
+            )
+        val expectedSecondaryLabel = AlarmTileMapper.formatter12Hour.format(localDateTime)
+        val expectedState =
+            createAlarmTileState(QSTileState.ActivationState.ACTIVE, expectedSecondaryLabel)
+        QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
+    }
+
+    @Test
+    fun nextAlarmSetExactlyAWeekLater_mapsSecondaryLabelToDisplayDateOnly() {
+        val oneWeek = 7 * 24 * 60 * 60 * 1000L
+        val triggerTime = fakeClock.currentTimeMillis() + oneWeek
+        val inputModel =
+            AlarmTileModel.NextAlarmSet(false, AlarmManager.AlarmClockInfo(triggerTime, null))
+
+        val outputState = mapper.map(alarmTileConfig, inputModel)
+
+        val localDateTime =
+            LocalDateTime.ofInstant(
+                Instant.ofEpochMilli(triggerTime),
+                TimeZone.getDefault().toZoneId()
+            )
+        val expectedSecondaryLabel = AlarmTileMapper.formatterDateOnly.format(localDateTime)
+        val expectedState =
+            createAlarmTileState(QSTileState.ActivationState.ACTIVE, expectedSecondaryLabel)
+        QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
+    }
+
     private fun createAlarmTileState(
         activationState: QSTileState.ActivationState,
         secondaryLabel: String
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/alarm/domain/interactor/AlarmTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/alarm/domain/interactor/AlarmTileUserActionInteractorTest.kt
index e44c849..be2da17 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/alarm/domain/interactor/AlarmTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/alarm/domain/interactor/AlarmTileUserActionInteractorTest.kt
@@ -18,42 +18,25 @@
 
 import android.app.AlarmManager.AlarmClockInfo
 import android.app.PendingIntent
-import android.content.Intent
 import android.provider.AlarmClock
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerSubject
 import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx.click
 import com.android.systemui.qs.tiles.impl.alarm.domain.model.AlarmTileModel
-import com.android.systemui.util.mockito.capture
-import com.android.systemui.util.mockito.eq
 import com.android.systemui.util.mockito.mock
-import com.android.systemui.util.mockito.nullable
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.test.runTest
-import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.mockito.ArgumentCaptor
-import org.mockito.Mockito.verify
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class AlarmTileUserActionInteractorTest : SysuiTestCase() {
-    private lateinit var activityStarter: ActivityStarter
-    private lateinit var intentCaptor: ArgumentCaptor<Intent>
-    private lateinit var pendingIntentCaptor: ArgumentCaptor<PendingIntent>
-
-    lateinit var underTest: AlarmTileUserActionInteractor
-
-    @Before
-    fun setup() {
-        activityStarter = mock<ActivityStarter>()
-        intentCaptor = ArgumentCaptor.forClass(Intent::class.java)
-        pendingIntentCaptor = ArgumentCaptor.forClass(PendingIntent::class.java)
-        underTest = AlarmTileUserActionInteractor(activityStarter)
-    }
+    private val inputHandler = FakeQSTileIntentUserInputHandler()
+    private val underTest = AlarmTileUserActionInteractor(inputHandler)
 
     @Test
     fun handleClickWithDefaultIntent() = runTest {
@@ -62,21 +45,21 @@
 
         underTest.handleInput(click(inputModel))
 
-        verify(activityStarter)
-            .postStartActivityDismissingKeyguard(capture(intentCaptor), eq(0), nullable())
-        assertThat(intentCaptor.value.action).isEqualTo(AlarmClock.ACTION_SHOW_ALARMS)
+        QSTileIntentUserInputHandlerSubject.assertThat(inputHandler).handledOneIntentInput {
+            assertThat(it.intent.action).isEqualTo(AlarmClock.ACTION_SHOW_ALARMS)
+        }
     }
 
     @Test
     fun handleClickWithPendingIntent() = runTest {
-        val expectedIntent: PendingIntent = mock<PendingIntent>()
+        val expectedIntent = mock<PendingIntent>()
         val alarmInfo = AlarmClockInfo(1L, expectedIntent)
         val inputModel = AlarmTileModel.NextAlarmSet(true, alarmInfo)
 
         underTest.handleInput(click(inputModel))
 
-        verify(activityStarter)
-            .postStartActivityDismissingKeyguard(capture(pendingIntentCaptor), nullable())
-        assertThat(pendingIntentCaptor.value).isEqualTo(expectedIntent)
+        QSTileIntentUserInputHandlerSubject.assertThat(inputHandler).handledOnePendingIntentInput {
+            assertThat(it.pendingIntent).isEqualTo(expectedIntent)
+        }
     }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileInteractorTest.kt
index 90779cb..20653ca 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileInteractorTest.kt
@@ -55,6 +55,7 @@
     private val underTest: CustomTileInteractor =
         with(kosmos) {
             CustomTileInteractor(
+                tileSpec,
                 customTileDefaultsRepository,
                 customTileRepository,
                 testScope.backgroundScope,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
index 224903f..530d127d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
@@ -153,7 +153,6 @@
                 KeyguardLongPressViewModel(
                     interactor = mock(),
                 ),
-            keyguardRoot = utils.keyguardRootViewModel(),
             notifications = utils.notificationsPlaceholderViewModel(),
         )
 
@@ -266,7 +265,8 @@
                 powerInteractor = powerInteractor,
                 bouncerInteractor = bouncerInteractor,
                 simBouncerInteractor = dagger.Lazy { utils.simBouncerInteractor },
-                authenticationInteractor = dagger.Lazy { utils.authenticationInteractor() }
+                authenticationInteractor = dagger.Lazy { utils.authenticationInteractor() },
+                windowController = mock(),
             )
         startable.start()
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
index 2e4986d..dd22976 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
@@ -37,6 +37,7 @@
 import com.android.systemui.scene.shared.model.ObservableTransitionState
 import com.android.systemui.scene.shared.model.SceneKey
 import com.android.systemui.scene.shared.model.SceneModel
+import com.android.systemui.statusbar.NotificationShadeWindowController
 import com.android.systemui.util.mockito.mock
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -46,18 +47,24 @@
 import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
+import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyBoolean
+import org.mockito.Mock
 import org.mockito.Mockito.clearInvocations
 import org.mockito.Mockito.never
 import org.mockito.Mockito.times
 import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 @EnableFlags(AconfigFlags.FLAG_SCENE_CONTAINER)
 class SceneContainerStartableTest : SysuiTestCase() {
 
+    @Mock private lateinit var windowController: NotificationShadeWindowController
+
     private val utils = SceneTestUtils(this)
     private val testScope = utils.testScope
     private val sceneInteractor = utils.sceneInteractor()
@@ -77,22 +84,30 @@
     private val falsingCollector: FalsingCollector = mock()
     private val powerInteractor = PowerInteractorFactory.create().powerInteractor
 
-    private val underTest =
-        SceneContainerStartable(
-            applicationScope = testScope.backgroundScope,
-            sceneInteractor = sceneInteractor,
-            deviceEntryInteractor = deviceEntryInteractor,
-            keyguardInteractor = keyguardInteractor,
-            flags = sceneContainerFlags,
-            sysUiState = sysUiState,
-            displayId = Display.DEFAULT_DISPLAY,
-            sceneLogger = mock(),
-            falsingCollector = falsingCollector,
-            powerInteractor = powerInteractor,
-            bouncerInteractor = bouncerInteractor,
-            simBouncerInteractor = dagger.Lazy { utils.simBouncerInteractor },
-            authenticationInteractor = dagger.Lazy { authenticationInteractor },
-        )
+    private lateinit var underTest: SceneContainerStartable
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+
+        underTest =
+            SceneContainerStartable(
+                applicationScope = testScope.backgroundScope,
+                sceneInteractor = sceneInteractor,
+                deviceEntryInteractor = deviceEntryInteractor,
+                keyguardInteractor = keyguardInteractor,
+                flags = sceneContainerFlags,
+                sysUiState = sysUiState,
+                displayId = Display.DEFAULT_DISPLAY,
+                sceneLogger = mock(),
+                falsingCollector = falsingCollector,
+                powerInteractor = powerInteractor,
+                bouncerInteractor = bouncerInteractor,
+                simBouncerInteractor = dagger.Lazy { utils.simBouncerInteractor },
+                authenticationInteractor = dagger.Lazy { authenticationInteractor },
+                windowController = windowController,
+            )
+    }
 
     @Test
     fun hydrateVisibility() =
@@ -655,6 +670,58 @@
             assertThat(currentSceneKey).isEqualTo(SceneKey.Gone)
         }
 
+    @Test
+    fun hydrateWindowFocus() =
+        testScope.runTest {
+            val currentDesiredSceneKey by
+                collectLastValue(sceneInteractor.desiredScene.map { it.key })
+            val transitionStateFlow =
+                prepareState(
+                    isDeviceUnlocked = true,
+                    initialSceneKey = SceneKey.Gone,
+                )
+            assertThat(currentDesiredSceneKey).isEqualTo(SceneKey.Gone)
+            verify(windowController, never()).setNotificationShadeFocusable(anyBoolean())
+
+            underTest.start()
+            runCurrent()
+            verify(windowController, times(1)).setNotificationShadeFocusable(false)
+
+            sceneInteractor.changeScene(SceneModel(SceneKey.Shade), "reason")
+            transitionStateFlow.value =
+                ObservableTransitionState.Transition(
+                    fromScene = SceneKey.Gone,
+                    toScene = SceneKey.Shade,
+                    progress = flowOf(0.5f),
+                    isInitiatedByUserInput = false,
+                    isUserInputOngoing = flowOf(false),
+                )
+            runCurrent()
+            verify(windowController, times(1)).setNotificationShadeFocusable(false)
+
+            sceneInteractor.onSceneChanged(SceneModel(SceneKey.Shade), "reason")
+            transitionStateFlow.value = ObservableTransitionState.Idle(SceneKey.Shade)
+            runCurrent()
+            verify(windowController, times(1)).setNotificationShadeFocusable(true)
+
+            sceneInteractor.changeScene(SceneModel(SceneKey.Gone), "reason")
+            transitionStateFlow.value =
+                ObservableTransitionState.Transition(
+                    fromScene = SceneKey.Shade,
+                    toScene = SceneKey.Gone,
+                    progress = flowOf(0.5f),
+                    isInitiatedByUserInput = false,
+                    isUserInputOngoing = flowOf(false),
+                )
+            runCurrent()
+            verify(windowController, times(1)).setNotificationShadeFocusable(true)
+
+            sceneInteractor.onSceneChanged(SceneModel(SceneKey.Gone), "reason")
+            transitionStateFlow.value = ObservableTransitionState.Idle(SceneKey.Gone)
+            runCurrent()
+            verify(windowController, times(2)).setNotificationShadeFocusable(false)
+        }
+
     private fun TestScope.prepareState(
         isDeviceUnlocked: Boolean = false,
         isBypassEnabled: Boolean = false,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerMainThreadTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerMainThreadTest.java
similarity index 97%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerMainThreadTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerMainThreadTest.java
index 01fe40f..d0e05fa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerMainThreadTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerMainThreadTest.java
@@ -42,19 +42,15 @@
 import android.content.pm.UserInfo;
 import android.database.ContentObserver;
 import android.net.Uri;
-import android.os.Handler;
-import android.os.Looper;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.Settings;
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
 import android.util.SparseArray;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.widget.LockPatternUtils;
-import com.android.systemui.Dependency;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.dump.DumpManager;
@@ -86,8 +82,7 @@
 import java.util.concurrent.Executor;
 
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper
+@RunWith(AndroidJUnit4.class)
 public class NotificationLockscreenUserManagerMainThreadTest extends SysuiTestCase {
     @Mock
     private NotificationPresenter mPresenter;
@@ -128,6 +123,7 @@
     private NotificationEntry mSecondaryUserNotif;
     private NotificationEntry mWorkProfileNotif;
     private final FakeFeatureFlagsClassic mFakeFeatureFlags = new FakeFeatureFlagsClassic();
+    private Executor mMainExecutor = Runnable::run; // Direct executor
     private Executor mBackgroundExecutor = Runnable::run; // Direct executor
 
     @Before
@@ -156,8 +152,6 @@
                 mSecondaryUser));
         when(mUserManager.getProfilesIncludingCommunal(mSecondaryUser.id)).thenReturn(
                 Lists.newArrayList(mSecondaryUser, mCommunalUser));
-        mDependency.injectTestDependency(Dependency.MAIN_HANDLER,
-                Handler.createAsync(Looper.myLooper()));
 
         Notification notifWithPrivateVisibility = new Notification();
         notifWithPrivateVisibility.visibility = Notification.VISIBILITY_PRIVATE;
@@ -378,28 +372,24 @@
 
         // first call explicitly sets user 0 to not public; notifies
         mLockscreenUserManager.updatePublicMode();
-        TestableLooper.get(this).processAllMessages();
         assertFalse(mLockscreenUserManager.isLockscreenPublicMode(0));
         verify(listener).onNotificationStateChanged();
         clearInvocations(listener);
 
         // calling again has no changes; does not notify
         mLockscreenUserManager.updatePublicMode();
-        TestableLooper.get(this).processAllMessages();
         assertFalse(mLockscreenUserManager.isLockscreenPublicMode(0));
         verify(listener, never()).onNotificationStateChanged();
 
         // Calling again with keyguard now showing makes user 0 public; notifies
         when(mKeyguardStateController.isShowing()).thenReturn(true);
         mLockscreenUserManager.updatePublicMode();
-        TestableLooper.get(this).processAllMessages();
         assertTrue(mLockscreenUserManager.isLockscreenPublicMode(0));
         verify(listener).onNotificationStateChanged();
         clearInvocations(listener);
 
         // calling again has no changes; does not notify
         mLockscreenUserManager.updatePublicMode();
-        TestableLooper.get(this).processAllMessages();
         assertTrue(mLockscreenUserManager.isLockscreenPublicMode(0));
         verify(listener, never()).onNotificationStateChanged();
     }
@@ -595,8 +585,7 @@
                     (() -> mOverviewProxyService),
                     NotificationLockscreenUserManagerMainThreadTest.this.mKeyguardManager,
                     mStatusBarStateController,
-                    Handler.createAsync(Looper.myLooper()),
-                    Handler.createAsync(Looper.myLooper()),
+                    mMainExecutor,
                     mBackgroundExecutor,
                     mDeviceProvisionedController,
                     mKeyguardStateController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
similarity index 91%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
index 42c7375..bcc0710 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
@@ -19,17 +19,21 @@
 import static android.app.Notification.VISIBILITY_PRIVATE;
 import static android.app.NotificationManager.IMPORTANCE_HIGH;
 import static android.app.NotificationManager.VISIBILITY_NO_OVERRIDE;
+import static android.app.StatusBarManager.ACTION_KEYGUARD_PRIVATE_NOTIFICATIONS_CHANGED;
+import static android.app.StatusBarManager.EXTRA_KM_PRIVATE_NOTIFS_ALLOWED;
 import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED;
 import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS;
 import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS;
+import static android.app.Flags.FLAG_KEYGUARD_PRIVATE_NOTIFICATIONS;
 import static android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE;
 import static android.os.UserHandle.USER_ALL;
 import static android.provider.Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS;
 import static android.provider.Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS;
-import static com.android.systemui.util.concurrency.MockExecutorHandlerKt.mockExecutorHandler;
+
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
+
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
@@ -59,13 +63,11 @@
 import android.platform.test.annotations.EnableFlags;
 import android.platform.test.flag.junit.FlagsParameterization;
 import android.provider.Settings;
-import android.testing.TestableLooper;
 import android.util.SparseArray;
 
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.widget.LockPatternUtils;
-import com.android.systemui.Dependency;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.dump.DumpManager;
@@ -85,6 +87,7 @@
 import com.android.systemui.util.concurrency.FakeExecutor;
 import com.android.systemui.util.settings.FakeSettings;
 import com.android.systemui.util.time.FakeSystemClock;
+
 import com.google.android.collect.Lists;
 
 import org.junit.After;
@@ -106,12 +109,13 @@
 
 @SmallTest
 @RunWith(ParameterizedAndroidJunit4.class)
-@TestableLooper.RunWithLooper
 public class NotificationLockscreenUserManagerTest extends SysuiTestCase {
 
     @Parameters(name = "{0}")
     public static List<FlagsParameterization> getParams() {
-        return FlagsParameterization.allCombinationsOf(FLAG_ALLOW_PRIVATE_PROFILE);
+        return FlagsParameterization.allCombinationsOf(
+                FLAG_ALLOW_PRIVATE_PROFILE,
+                FLAG_KEYGUARD_PRIVATE_NOTIFICATIONS);
     }
 
     public NotificationLockscreenUserManagerTest(FlagsParameterization flags) {
@@ -192,8 +196,6 @@
                 mSecondaryUser));
         when(mUserManager.getProfilesIncludingCommunal(mSecondaryUser.id)).thenReturn(
                 Lists.newArrayList(mSecondaryUser, mCommunalUser));
-        mDependency.injectTestDependency(Dependency.MAIN_HANDLER,
-                mockExecutorHandler(mMainExecutor));
 
         Notification notifWithPrivateVisibility = new Notification();
         notifWithPrivateVisibility.visibility = VISIBILITY_PRIVATE;
@@ -245,6 +247,19 @@
     }
 
     @Test
+    @EnableFlags(FLAG_KEYGUARD_PRIVATE_NOTIFICATIONS)
+    public void testInit() {
+        when(mKeyguardManager.getPrivateNotificationsAllowed()).thenReturn(false);
+        mLockscreenUserManager = new TestNotificationLockscreenUserManager(mContext);
+        mLockscreenUserManager.setUpWithPresenter(mPresenter);
+
+        mBackgroundExecutor.runAllReady();
+
+        assertTrue(mLockscreenUserManager.needsRedaction(mCurrentUserNotif));
+        assertTrue(mLockscreenUserManager.needsRedaction(mSecondaryUserNotif));
+    }
+
+    @Test
     public void testGetCurrentProfiles() {
         final SparseArray<UserInfo> expectedCurProfiles = new SparseArray<>();
         expectedCurProfiles.put(mCurrentUser.id, mCurrentUser);
@@ -579,6 +594,40 @@
     }
 
     @Test
+    @EnableFlags(FLAG_KEYGUARD_PRIVATE_NOTIFICATIONS)
+    public void testEarlyUserSwitch() {
+        mLockscreenUserManager =
+                new TestNotificationLockscreenUserManager(mContext);
+        mBackgroundExecutor.runAllReady();
+        mLockscreenUserManager.mUserChangedCallback.onUserChanging(
+                mCurrentUser.id, mContext);
+        // no crash!
+    }
+
+    @Test
+    @EnableFlags(FLAG_KEYGUARD_PRIVATE_NOTIFICATIONS)
+    public void testKeyguardManager_noPrivateNotifications() {
+        Mockito.clearInvocations(mDevicePolicyManager);
+        // User allows notifications
+        mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mCurrentUser.id);
+        changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS);
+
+        BroadcastReceiver.PendingResult pr = new BroadcastReceiver.PendingResult(
+                0, null, null, 0, true, false, null, mCurrentUser.id, 0);
+        mLockscreenUserManager.mAllUsersReceiver.setPendingResult(pr);
+        mLockscreenUserManager.mAllUsersReceiver.onReceive(mContext,
+                new Intent(ACTION_KEYGUARD_PRIVATE_NOTIFICATIONS_CHANGED)
+                        .putExtra(EXTRA_KM_PRIVATE_NOTIFS_ALLOWED, true));
+
+        assertTrue(mLockscreenUserManager.needsRedaction(mCurrentUserNotif));
+        // it's a global field, confirm secondary too
+        assertTrue(mLockscreenUserManager.needsRedaction(mSecondaryUserNotif));
+        assertFalse(mLockscreenUserManager.userAllowsPrivateNotificationsInPublic(mCurrentUser.id));
+        assertFalse(mLockscreenUserManager.userAllowsPrivateNotificationsInPublic(
+                mSecondaryUser.id));
+    }
+
+    @Test
     public void testDevicePolicy_noPrivateNotifications() {
         Mockito.clearInvocations(mDevicePolicyManager);
         // User allows notifications
@@ -699,6 +748,29 @@
     }
 
     @Test
+    @EnableFlags(FLAG_KEYGUARD_PRIVATE_NOTIFICATIONS)
+    public void testShouldShowLockscreenNotifications_keyguardManagerNoPrivateNotifications_show() {
+        // KeyguardManager does not allow notifications
+        when(mKeyguardManager.getPrivateNotificationsAllowed()).thenReturn(false);
+        // User allows notifications
+        mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mCurrentUser.id);
+        changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS);
+        // DevicePolicy allows notifications
+        when(mDevicePolicyManager.getKeyguardDisabledFeatures(null, mCurrentUser.id))
+                .thenReturn(0);
+        BroadcastReceiver.PendingResult pr = new BroadcastReceiver.PendingResult(
+                0, null, null, 0, true, false, null, mCurrentUser.id, 0);
+        mLockscreenUserManager.mKeyguardReceiver.setPendingResult(pr);
+        mLockscreenUserManager.mKeyguardReceiver.onReceive(mContext,
+                new Intent(ACTION_KEYGUARD_PRIVATE_NOTIFICATIONS_CHANGED)
+                        .putExtra(EXTRA_KM_PRIVATE_NOTIFS_ALLOWED, false));
+
+        assertTrue(mLockscreenUserManager.shouldShowLockscreenNotifications());
+        assertTrue(mLockscreenUserManager.userAllowsNotificationsInPublic(mCurrentUser.id));
+    }
+
+    @Test
+    @DisableFlags(FLAG_KEYGUARD_PRIVATE_NOTIFICATIONS)
     public void testShouldShowLockscreenNotifications_keyguardManagerNoPrivateNotifications() {
         // KeyguardManager does not allow notifications
         when(mKeyguardManager.getPrivateNotificationsAllowed()).thenReturn(false);
@@ -718,6 +790,7 @@
     }
 
     @Test
+    @DisableFlags(FLAG_KEYGUARD_PRIVATE_NOTIFICATIONS)
     public void testUserAllowsNotificationsInPublic_keyguardManagerNoPrivateNotifications() {
         // DevicePolicy allows notifications
         when(mDevicePolicyManager.getKeyguardDisabledFeatures(null, mCurrentUser.id))
@@ -873,8 +946,7 @@
                     (() -> mOverviewProxyService),
                     NotificationLockscreenUserManagerTest.this.mKeyguardManager,
                     mStatusBarStateController,
-                    mockExecutorHandler(mMainExecutor),
-                    mockExecutorHandler(mBackgroundExecutor),
+                    mMainExecutor,
                     mBackgroundExecutor,
                     mDeviceProvisionedController,
                     mKeyguardStateController,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/RemoteInputRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/RemoteInputRepositoryImplTest.kt
new file mode 100644
index 0000000..8a0400d
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/RemoteInputRepositoryImplTest.kt
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
+package com.android.systemui.statusbar.data.repository
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.statusbar.NotificationRemoteInputManager
+import com.android.systemui.util.mockito.withArgCaptor
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class RemoteInputRepositoryImplTest : SysuiTestCase() {
+    @Mock private lateinit var remoteInputManager: NotificationRemoteInputManager
+
+    private lateinit var testScope: TestScope
+    private lateinit var underTest: RemoteInputRepositoryImpl
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+
+        testScope = TestScope()
+        underTest = RemoteInputRepositoryImpl(remoteInputManager)
+    }
+
+    @Test
+    fun isRemoteInputActive_updatesOnChange() =
+        testScope.runTest {
+            val active by collectLastValue(underTest.isRemoteInputActive)
+            runCurrent()
+            assertThat(active).isFalse()
+
+            val callback = withArgCaptor {
+                verify(remoteInputManager).addControllerCallback(capture())
+            }
+
+            callback.onRemoteInputActive(true)
+            runCurrent()
+            assertThat(active).isTrue()
+
+            callback.onRemoteInputActive(false)
+            runCurrent()
+            assertThat(active).isFalse()
+        }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/domain/interactor/RemoteInputInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/domain/interactor/RemoteInputInteractorTest.kt
new file mode 100644
index 0000000..12469dd
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/domain/interactor/RemoteInputInteractorTest.kt
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
+package com.android.systemui.statusbar.domain.interactor
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.statusbar.data.repository.fakeRemoteInputRepository
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class RemoteInputInteractorTest : SysuiTestCase() {
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+    private val fakeRemoteInputRepository = kosmos.fakeRemoteInputRepository
+    private val underTest = kosmos.remoteInputInteractor
+
+    @Test
+    fun isRemoteInputActive_true() =
+        testScope.runTest {
+            val active by collectLastValue(underTest.isRemoteInputActive)
+
+            fakeRemoteInputRepository.isRemoteInputActive.value = true
+            runCurrent()
+
+            assertThat(active).isTrue()
+        }
+
+    @Test
+    fun isRemoteInputActive_false() =
+        testScope.runTest {
+            val active by collectLastValue(underTest.isRemoteInputActive)
+
+            fakeRemoteInputRepository.isRemoteInputActive.value = false
+            runCurrent()
+
+            assertThat(active).isFalse()
+        }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java
similarity index 97%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java
index 53cb8a7..7a78b36 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java
@@ -25,15 +25,14 @@
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
 import static org.mockito.Mockito.when;
 
 import android.graphics.Point;
 import android.os.PowerManager;
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper.RunWithLooper;
 import android.view.View;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.keyguard.KeyguardUpdateMonitor;
@@ -68,7 +67,7 @@
 import java.util.HashSet;
 
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @RunWithLooper(setAsMainLooper = true)
 public class DozeServiceHostTest extends SysuiTestCase {
 
@@ -181,6 +180,7 @@
                         DozeLog.PULSE_REASON_DOCKING,
                         DozeLog.REASON_SENSOR_WAKE_UP_PRESENCE,
                         DozeLog.REASON_SENSOR_QUICK_PICKUP,
+                        DozeLog.PULSE_REASON_FINGERPRINT_ACTIVATED,
                         DozeLog.REASON_SENSOR_TAP));
         HashSet<Integer> reasonsThatDontPulse = new HashSet<>(
                 Arrays.asList(DozeLog.REASON_SENSOR_PICKUP,
@@ -232,7 +232,7 @@
     public void onSlpiTap_doesnt_pass_negative_values() {
         mDozeServiceHost.onSlpiTap(-1, 200);
         mDozeServiceHost.onSlpiTap(100, -2);
-        verifyZeroInteractions(mDozeInteractor);
+        verify(mDozeInteractor, never()).setLastTapToWakePosition(any());
     }
     @Test
     public void dozeTimeTickSentToDozeInteractor() {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/SystemUIDialogTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/SystemUIDialogTest.java
index de767e3..7274c0c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/SystemUIDialogTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/SystemUIDialogTest.java
@@ -21,29 +21,30 @@
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
 
-import static org.mockito.Mockito.atLeast;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
 
 import android.content.BroadcastReceiver;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.res.Configuration;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 import android.testing.TestableLooper.RunWithLooper;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.Dependency;
+import com.android.systemui.Flags;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.animation.DialogLaunchAnimator;
 import com.android.systemui.broadcast.BroadcastDispatcher;
-import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
 import com.android.systemui.model.SysUiState;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
@@ -61,17 +62,17 @@
 public class SystemUIDialogTest extends SysuiTestCase {
 
     @Mock
-    private FeatureFlags mFeatureFlags;
-    @Mock
     private BroadcastDispatcher mBroadcastDispatcher;
     @Mock
     private SystemUIDialog.Delegate mDelegate;
 
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
     @Before
     public void setup() {
         MockitoAnnotations.initMocks(this);
 
-        mDependency.injectTestDependency(FeatureFlags.class, mFeatureFlags);
         mDependency.injectTestDependency(BroadcastDispatcher.class, mBroadcastDispatcher);
     }
 
@@ -110,16 +111,13 @@
     }
 
     @Test
+    @RequiresFlagsEnabled(Flags.FLAG_PREDICTIVE_BACK_ANIMATE_DIALOGS)
     public void usePredictiveBackAnimFlag() {
-        when(mFeatureFlags.isEnabled(Flags.WM_ENABLE_PREDICTIVE_BACK_QS_DIALOG_ANIM))
-                .thenReturn(true);
         final SystemUIDialog dialog = new SystemUIDialog(mContext);
 
         dialog.show();
 
         assertTrue(dialog.isShowing());
-        verify(mFeatureFlags, atLeast(1))
-                .isEnabled(Flags.WM_ENABLE_PREDICTIVE_BACK_QS_DIALOG_ANIM);
 
         dialog.dismiss();
         assertFalse(dialog.isShowing());
@@ -174,7 +172,6 @@
     private SystemUIDialog createDialogWithDelegate() {
         SystemUIDialog.Factory factory = new SystemUIDialog.Factory(
                 getContext(),
-                mFeatureFlags,
                 Dependency.get(SystemUIDialogManager.class),
                 Dependency.get(SysUiState.class),
                 Dependency.get(BroadcastDispatcher.class),
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt
index 75d1869..a9ee405 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt
@@ -68,6 +68,8 @@
 
     override val isSingleCarrier = MutableStateFlow(true)
 
+    override val icons: MutableStateFlow<List<MobileIconInteractor>> = MutableStateFlow(emptyList())
+
     private val _defaultMobileIconMapping = MutableStateFlow(TEST_MAPPING)
     override val defaultMobileIconMapping = _defaultMobileIconMapping
 
@@ -80,8 +82,12 @@
     override val isForceHidden = MutableStateFlow(false)
 
     /** Always returns a new fake interactor */
-    override fun getMobileConnectionInteractorForSubId(subId: Int): MobileIconInteractor {
-        return FakeMobileIconInteractor(tableLogBuffer).also { interactorCache[subId] = it }
+    override fun getMobileConnectionInteractorForSubId(subId: Int): FakeMobileIconInteractor {
+        return FakeMobileIconInteractor(tableLogBuffer).also {
+            interactorCache[subId] = it
+            // Also update the icons
+            icons.value = interactorCache.values.toList()
+        }
     }
 
     /**
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/UninstallActionListener.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/domain/interactor/ComponentsInteractorTest.kt
similarity index 71%
copy from packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/UninstallActionListener.java
copy to packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/domain/interactor/ComponentsInteractorTest.kt
index b8a9355..dbff63f 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/UninstallActionListener.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/domain/interactor/ComponentsInteractorTest.kt
@@ -5,7 +5,7 @@
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
- *      https://www.apache.org/licenses/LICENSE-2.0
+ *      http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
@@ -14,11 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.packageinstaller.v2.ui;
+package com.android.systemui.volume.panel.domain.interactor
 
-public interface UninstallActionListener {
+class ComponentsInteractorTest {
 
-    void onPositiveResponse(boolean keepData);
-
-    void onNegativeResponse();
+    // TODO(b/318080198) Write tests
 }
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/UninstallActionListener.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/ui/viewmodel/DefaultComponentsLayoutManagerTest.kt
similarity index 71%
copy from packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/UninstallActionListener.java
copy to packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/ui/viewmodel/DefaultComponentsLayoutManagerTest.kt
index b8a9355..e5fb942 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/UninstallActionListener.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/ui/viewmodel/DefaultComponentsLayoutManagerTest.kt
@@ -5,7 +5,7 @@
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
- *      https://www.apache.org/licenses/LICENSE-2.0
+ *      http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
@@ -14,11 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.packageinstaller.v2.ui;
+package com.android.systemui.volume.panel.ui.viewmodel
 
-public interface UninstallActionListener {
+class DefaultComponentsLayoutManagerTest {
 
-    void onPositiveResponse(boolean keepData);
-
-    void onNegativeResponse();
+    // TODO(b/318080198) Write tests
 }
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/UninstallActionListener.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModelTest.kt
similarity index 71%
copy from packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/UninstallActionListener.java
copy to packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModelTest.kt
index b8a9355..9795237 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/UninstallActionListener.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModelTest.kt
@@ -5,7 +5,7 @@
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
- *      https://www.apache.org/licenses/LICENSE-2.0
+ *      http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
@@ -14,11 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.packageinstaller.v2.ui;
+package com.android.systemui.volume.panel.ui.viewmodel
 
-public interface UninstallActionListener {
+class VolumePanelViewModelTest {
 
-    void onPositiveResponse(boolean keepData);
-
-    void onNegativeResponse();
+    // TODO(b/318080198) Write tests
 }
diff --git a/packages/SystemUI/plugin/Android.bp b/packages/SystemUI/plugin/Android.bp
index 0537f17..9063a02 100644
--- a/packages/SystemUI/plugin/Android.bp
+++ b/packages/SystemUI/plugin/Android.bp
@@ -46,8 +46,8 @@
     static_libs: [
         "androidx.annotation_annotation",
         "androidx-constraintlayout_constraintlayout",
+        "PlatformAnimationLib",
         "PluginCoreLib",
-        "SystemUIAnimationLib",
         "SystemUICommon",
         "SystemUILogLib",
         "androidx.annotation_annotation",
diff --git a/packages/SystemUI/plugin_core/src/com/android/systemui/plugins/PluginLifecycleManager.java b/packages/SystemUI/plugin_core/src/com/android/systemui/plugins/PluginLifecycleManager.java
index 3e5e8a0..f0ce460 100644
--- a/packages/SystemUI/plugin_core/src/com/android/systemui/plugins/PluginLifecycleManager.java
+++ b/packages/SystemUI/plugin_core/src/com/android/systemui/plugins/PluginLifecycleManager.java
@@ -18,6 +18,8 @@
 
 import android.content.ComponentName;
 
+import java.util.function.BiConsumer;
+
 /**
  * Provides the ability for consumers to control plugin lifecycle.
  *
@@ -33,11 +35,8 @@
     /** Returns the currently loaded plugin instance (if plugin is loaded) */
     T getPlugin();
 
-    /** Returns true if the lifecycle manager should log debug messages */
-    boolean getIsDebug();
-
-    /** Sets whether or not hte lifecycle manager should log debug messages */
-    void setIsDebug(boolean debug);
+    /** Log tag and messages will be sent to the provided Consumer */
+    void setLogFunc(BiConsumer<String, String> logConsumer);
 
     /** returns true if the plugin is currently loaded */
     default boolean isLoaded() {
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml
index 557fbf2..e6122a0 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml
@@ -43,7 +43,7 @@
             android:layout_width="match_parent"
             android:layout_height="wrap_content" />
         <FrameLayout
-            android:id="@+id/status_view_media_container"
+            android:id="@id/status_view_media_container"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:padding="@dimen/qs_media_padding"
diff --git a/packages/SystemUI/res/color/notification_state_color_dark.xml b/packages/SystemUI/res/color/notification_state_color_dark.xml
new file mode 100644
index 0000000..d26cbd5
--- /dev/null
+++ b/packages/SystemUI/res/color/notification_state_color_dark.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2023 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
+    <!-- Pressed state's alpha is set to 0.00 temporarily until this bug is resolved permanently
+    b/313920497 Design intended alpha is 0.15-->
+    <item android:state_pressed="true" android:color="#ffffff" android:alpha="0.00" />
+    <item android:state_hovered="true" android:color="#ffffff" android:alpha="0.11" />
+    <item android:color="@color/transparent" />
+</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/color/notification_overlay_color.xml b/packages/SystemUI/res/color/notification_state_color_default.xml
similarity index 100%
rename from packages/SystemUI/res/color/notification_overlay_color.xml
rename to packages/SystemUI/res/color/notification_state_color_default.xml
diff --git a/packages/SystemUI/res/color/notification_state_color_light.xml b/packages/SystemUI/res/color/notification_state_color_light.xml
new file mode 100644
index 0000000..3e8bcf3
--- /dev/null
+++ b/packages/SystemUI/res/color/notification_state_color_light.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2023 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
+    <!-- Pressed state's alpha is set to 0.00 temporarily until this bug is resolved permanently
+    b/313920497 Design intended alpha is 0.15-->
+    <item android:state_pressed="true" android:color="#000000" android:alpha="0.00" />
+    <item android:state_hovered="true" android:color="#000000" android:alpha="0.11" />
+    <item android:color="@color/transparent" />
+</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/connected_display_dialog_bg.xml b/packages/SystemUI/res/drawable/connected_display_dialog_bg.xml
new file mode 100644
index 0000000..2dce37d
--- /dev/null
+++ b/packages/SystemUI/res/drawable/connected_display_dialog_bg.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2023 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+    <corners
+        android:topLeftRadius="28dp"
+        android:topRightRadius="28dp"/>
+    <solid android:color="?android:attr/colorBackground" />
+</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/notification_material_bg.xml b/packages/SystemUI/res/drawable/notification_material_bg.xml
index 355e75d..3f903ae 100644
--- a/packages/SystemUI/res/drawable/notification_material_bg.xml
+++ b/packages/SystemUI/res/drawable/notification_material_bg.xml
@@ -25,7 +25,7 @@
     </item>
     <item>
         <shape>
-            <solid android:color="@color/notification_overlay_color" />
+            <solid android:color="@color/notification_state_color_default" />
         </shape>
     </item>
 </layer-list>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/stat_sys_no_internet_branded_vpn.xml b/packages/SystemUI/res/drawable/stat_sys_no_internet_branded_vpn.xml
new file mode 100644
index 0000000..2161a62
--- /dev/null
+++ b/packages/SystemUI/res/drawable/stat_sys_no_internet_branded_vpn.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2023, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="17dp"
+    android:height="17dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M12.09,9C11.11,7.5 9.43,6.5 7.5,6.5C4.46,6.5 2,8.96 2,12c0,3.04 2.46,5.5 5.5,5.5c1.93,0 3.61,-1 4.59,-2.5H14v3h4V9H12.09zM18,13hv3h-2v-3h-5.16c-0.43,1.44 -1.76,2.5 -3.34,2.5C5.57,15.5 4,13.93 4,12c0,-1.93 1.57,-3.5 3.5,-3.5c1.58,0 2.9,1.06 3.34,2.5H18V13z"/>
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M7.5,12m-1.5,0a1.5,1.5 0,1 1,3 0a1.5,1.5 0,1 1,-3 0"/>
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M22,10h-2v8h2V10z"/>
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M22,20h-2v2h2V20z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/stat_sys_no_internet_vpn_ic.xml b/packages/SystemUI/res/drawable/stat_sys_no_internet_vpn_ic.xml
new file mode 100644
index 0000000..2161a62
--- /dev/null
+++ b/packages/SystemUI/res/drawable/stat_sys_no_internet_vpn_ic.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2023, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="17dp"
+    android:height="17dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M12.09,9C11.11,7.5 9.43,6.5 7.5,6.5C4.46,6.5 2,8.96 2,12c0,3.04 2.46,5.5 5.5,5.5c1.93,0 3.61,-1 4.59,-2.5H14v3h4V9H12.09zM18,13hv3h-2v-3h-5.16c-0.43,1.44 -1.76,2.5 -3.34,2.5C5.57,15.5 4,13.93 4,12c0,-1.93 1.57,-3.5 3.5,-3.5c1.58,0 2.9,1.06 3.34,2.5H18V13z"/>
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M7.5,12m-1.5,0a1.5,1.5 0,1 1,3 0a1.5,1.5 0,1 1,-3 0"/>
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M22,10h-2v8h2V10z"/>
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M22,20h-2v2h2V20z"/>
+</vector>
diff --git a/packages/SystemUI/res/layout/connected_display_dialog.xml b/packages/SystemUI/res/layout/connected_display_dialog.xml
index 8d7f7eb..a71782b 100644
--- a/packages/SystemUI/res/layout/connected_display_dialog.xml
+++ b/packages/SystemUI/res/layout/connected_display_dialog.xml
@@ -22,7 +22,7 @@
     android:orientation="vertical"
     android:paddingHorizontal="@dimen/dialog_side_padding"
     android:paddingTop="@dimen/dialog_top_padding"
-    android:background="@*android:drawable/bottomsheet_background"
+    android:background="@drawable/connected_display_dialog_bg"
     android:paddingBottom="@dimen/dialog_bottom_padding">
 
     <ImageView
@@ -40,7 +40,7 @@
         android:id="@+id/connected_display_dialog_title"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_marginTop="@dimen/screenrecord_title_margin_top"
+        android:layout_marginTop="16dp"
         android:gravity="center"
         android:text="@string/connected_display_dialog_start_mirroring"
         android:textAppearance="@style/TextAppearance.Dialog.Title" />
@@ -51,13 +51,14 @@
         android:layout_height="wrap_content"
         android:gravity="center"
         android:visibility="gone"
+        android:layout_marginTop="16dp"
         android:text="@string/connected_display_dialog_dual_display_stop_warning"
         android:textAppearance="@style/TextAppearance.Dialog.Body" />
 
     <LinearLayout
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:layout_marginTop="@dimen/screenrecord_buttons_margin_top"
+        android:layout_marginTop="16dp"
         android:orientation="horizontal">
 
         <Button
diff --git a/packages/SystemUI/res/layout/keyguard_bottom_area.xml b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
index 6d7ce06..e602d6c 100644
--- a/packages/SystemUI/res/layout/keyguard_bottom_area.xml
+++ b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
@@ -107,4 +107,13 @@
 
     <include layout="@layout/ambient_indication"
              android:id="@id/ambient_indication_container" />
+
+    <FrameLayout
+        android:id="@+id/smartspace_container"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_gravity="bottom"
+        android:layout_marginBottom="@dimen/ambient_indication_margin_bottom"
+        android:visibility="gone">
+    </FrameLayout>
 </com.android.systemui.statusbar.phone.KeyguardBottomAreaView>
diff --git a/packages/SystemUI/res/layout/keyguard_status_bar.xml b/packages/SystemUI/res/layout/keyguard_status_bar.xml
index fc0bf24..4cb7591 100644
--- a/packages/SystemUI/res/layout/keyguard_status_bar.xml
+++ b/packages/SystemUI/res/layout/keyguard_status_bar.xml
@@ -23,7 +23,6 @@
     android:layout_width="match_parent"
     android:layout_height="@dimen/status_bar_header_height_keyguard"
     android:baselineAligned="false"
-    android:gravity="center_vertical"
     >
 
     <LinearLayout
diff --git a/packages/SystemUI/res/layout/qs_footer_impl.xml b/packages/SystemUI/res/layout/qs_footer_impl.xml
index 7ab44e7..73874a0 100644
--- a/packages/SystemUI/res/layout/qs_footer_impl.xml
+++ b/packages/SystemUI/res/layout/qs_footer_impl.xml
@@ -44,6 +44,8 @@
                 android:ellipsize="marquee"
                 android:focusable="true"
                 android:gravity="center_vertical"
+                android:textDirection="locale"
+                android:textAlignment="viewStart"
                 android:singleLine="true"
                 android:textAppearance="@style/TextAppearance.QS.Status.Build"
                 android:visibility="gone" />
diff --git a/packages/SystemUI/res/layout/remote_input.xml b/packages/SystemUI/res/layout/remote_input.xml
index f4b0a45..84681d34 100644
--- a/packages/SystemUI/res/layout/remote_input.xml
+++ b/packages/SystemUI/res/layout/remote_input.xml
@@ -89,7 +89,6 @@
                 android:textColorHint="@color/remote_input_hint"
                 android:textSize="16sp"
                 android:background="@null"
-                android:maxLines="4"
                 android:ellipsize="start"
                 android:inputType="textShortMessage|textMultiLine|textAutoCorrect|textCapSentences"
                 android:imeOptions="actionSend|flagNoExtractUi|flagNoFullscreen" />
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index fba6d21..db4cca3 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -330,12 +330,17 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Skermopname"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Begin"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Stop"</string>
-    <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
-    <skip />
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"Neem kwessie op"</string>
+    <string name="qs_record_issue_start" msgid="2979831312582567056">"Begin"</string>
+    <string name="qs_record_issue_stop" msgid="3531747965741982657">"Stop"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Watter deel van jou toestelervaring is geraak?"</string>
+    <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Kies soort kwessie"</string>
+    <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Skermopname"</string>
+  <string-array name="qs_record_issue_types">
+    <item msgid="2947988124014085798">"Werkverrigting"</item>
+    <item msgid="1627504621139124393">"Gebruikerkoppelvlak"</item>
+    <item msgid="8309220355268900335">"Battery"</item>
+  </string-array>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Eenhandmodus"</string>
     <string name="quick_settings_contrast_label" msgid="988087460210159123">"Kontras"</string>
     <string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Standaard"</string>
@@ -408,12 +413,9 @@
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Laai tans • Vol oor <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Swiep links om die gemeenskaplike tutoriaal te begin"</string>
     <string name="button_to_open_widget_editor" msgid="5599945944349057600">"Maak die legstukredigeerder oop"</string>
-    <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
-    <skip />
-    <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
-    <skip />
-    <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
-    <skip />
+    <string name="button_to_remove_widget" msgid="3948204829181214098">"Verwyder"</string>
+    <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Voeg legstuk by"</string>
+    <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Klaar"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Wissel gebruiker"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"aftrekkieslys"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alle programme en data in hierdie sessie sal uitgevee word."</string>
@@ -867,7 +869,6 @@
     <string name="auto_saver_title" msgid="6873691178754086596">"Tik om Batterybespaarder te skeduleer"</string>
     <string name="auto_saver_text" msgid="3214960308353838764">"Skakel aan wanneer battery waarskynlik sal leegloop"</string>
     <string name="no_auto_saver_action" msgid="7467924389609773835">"Nee, dankie"</string>
-    <string name="heap_dump_tile_name" msgid="2464189856478823046">"Stort SysUI-hoop"</string>
     <string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"In gebruik"</string>
     <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Programme gebruik tans jou <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
     <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,8 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Stel versteknotasapp in Instellings"</string>
     <string name="install_app" msgid="5066668100199613936">"Installeer app"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Sinkroniseer wedersyds na eksterne skerm?"</string>
-    <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"Enige Dual Screen-aktiwiteit wat tans loop sal gestop word"</string>
+    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
+    <skip />
     <string name="mirror_display" msgid="2515262008898122928">"Sinkroniseer skerm wedersyds"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Maak toe"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Skerm is gekoppel"</string>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index 12276f5..40fddc8 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -330,12 +330,17 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"ዚማያ ቀሚጻ"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"ጀምር"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"አቁም"</string>
-    <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
-    <skip />
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"ቜግርን ቅዳ"</string>
+    <string name="qs_record_issue_start" msgid="2979831312582567056">"ጀምር"</string>
+    <string name="qs_record_issue_stop" msgid="3531747965741982657">"አቁም"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"ዚቔኛው ዹመሣáˆȘያዎ ተሞክሼ ክፍል ተጎዔቷል?"</string>
+    <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"ዚቜግሩን አይነቔ ይምሹጡ"</string>
+    <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"ዚማያ መቅሚጫ"</string>
+  <string-array name="qs_record_issue_types">
+    <item msgid="2947988124014085798">"አፈጻጞም"</item>
+    <item msgid="1627504621139124393">"ዹተጠቃሚ በይነገፅ"</item>
+    <item msgid="8309220355268900335">"ባቔáˆȘ"</item>
+  </string-array>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"ዚአንዔ ኄጅ ሁነታ"</string>
     <string name="quick_settings_contrast_label" msgid="988087460210159123">"ንጜጜር"</string>
     <string name="quick_settings_contrast_standard" msgid="2538227821968061832">"መደበኛ"</string>
@@ -408,12 +413,9 @@
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ኃይል በመሙላቔ ላይ • በ<xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> ውሔጄ ይሞላል"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"ዚጋራ አጋዄ ሄልጠናውን ለመጀመር ወደ ግራ á‹«áŠ•áˆžáˆ«á‰”á‰±áą"</string>
     <string name="button_to_open_widget_editor" msgid="5599945944349057600">"ዚምግቄር አርታዒውን ይክፈቱ"</string>
-    <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
-    <skip />
-    <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
-    <skip />
-    <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
-    <skip />
+    <string name="button_to_remove_widget" msgid="3948204829181214098">"አሔወግዔ"</string>
+    <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"ምግቄር አክል"</string>
+    <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"ተኹናውኗል"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ተጠቃሚ ቀይር"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"á‹ˆá‹°á‰łá‰œ á‰°áŒŽá‰łá‰œ ምናሌ"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"በዚህ ክፍለ-ጊዜ ውሔጄ ያሉ ሁሉም መተግበáˆȘያዎቜ ኄና ውሂቄ ይሰሹዛሉ፱"</string>
@@ -867,7 +869,6 @@
     <string name="auto_saver_title" msgid="6873691178754086596">"áˆˆá‰Łá‰”áˆȘ ቆጣቱ መርሐግቄርን ለማሔያዝ መታ ያዔርጉ"</string>
     <string name="auto_saver_text" msgid="3214960308353838764">"ባቔáˆȘው ዹማለቅ ዕዔሉ ኹፍ ያለ ኹሆነ ያቄሩቔ"</string>
     <string name="no_auto_saver_action" msgid="7467924389609773835">"አይ፣ አመሰግናለሁ"</string>
-    <string name="heap_dump_tile_name" msgid="2464189856478823046">"SysUI Heap አራግፍ"</string>
     <string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"በጄቅም ላይ"</string>
     <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"መተግበáˆȘያዎቜ ዚኄርሔዎን <xliff:g id="TYPES_LIST">%s</xliff:g> ኄዚተጠቀሙ ነው፱"</string>
     <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">"፣ "</string>
@@ -1207,7 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"á‰ á‰…áŠ•á‰„áˆźá‰œ ውሔጄ ነባáˆȘ á‹šáˆ›áˆ”á‰łá‹ˆáˆ»á‹Žá‰œ መተግበáˆȘያን ያቀናቄሩ"</string>
     <string name="install_app" msgid="5066668100199613936">"መተግበáˆȘያን ጫን"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"ወደ ውጫዊ áˆ›áˆłá‹« ይንጾባሹቅ?"</string>
-    <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"በአሁኑ ጊዜ ኄያሄደ ያለው ማንኛውም ዹDual Screen ኄንቅሔቃሎ ይቆማል"</string>
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"ዚውሔጄ áˆ›áˆłá‹«á‹Ž ይንጾባሹቃል፱ ዚፊቔ áˆ›áˆłá‹«á‹Ž ይጠፋል፱"</string>
     <string name="mirror_display" msgid="2515262008898122928">"áˆ›áˆłá‹«áŠ• አንጾባርቅ"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"አሰናቄቔ"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"áˆ›áˆłá‹« ተገናኝቷል"</string>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index ac76ed0..d19c77b 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -330,12 +330,17 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"ŰȘŰłŰŹÙŠÙ„ Ű§Ù„ŰŽŰ§ŰŽŰ©"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"ۚۯۥ"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Ű„ÙŠÙ‚Ű§Ù"</string>
-    <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
-    <skip />
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"ŰȘŰłŰŹÙŠÙ„ Ű§Ù„Ù…ŰŽÙƒÙ„Ű©"</string>
+    <string name="qs_record_issue_start" msgid="2979831312582567056">"ۚۯۥ"</string>
+    <string name="qs_record_issue_stop" msgid="3531747965741982657">"Ű„ÙŠÙ‚Ű§Ù"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Ù…Ű§ هو Ű§Ù„ŰŹŰ§Ù†Űš Ű§Ù„Ű°ÙŠ ŰȘŰŁŰ«Ù‘Ű± في ŰȘۏ۱ۚ۩ ۧ۳ŰȘŰźŰŻŰ§Ù… Ű§Ù„ŰŹÙ‡Ű§ŰČ۟"</string>
+    <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"ۧ۟ŰȘÙŠŰ§Ű± نوŰč Ű§Ù„Ù…ŰŽÙƒÙ„Ű©"</string>
+    <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"ŰȘŰłŰŹÙŠÙ„ Ű§Ù„ŰŽŰ§ŰŽŰ©"</string>
+  <string-array name="qs_record_issue_types">
+    <item msgid="2947988124014085798">"Ű§Ù„ŰŁŰŻŰ§ŰĄ"</item>
+    <item msgid="1627504621139124393">"ÙˆŰ§ŰŹÙ‡Ű© Ű§Ù„Ù…ŰłŰȘŰźŰŻÙ…"</item>
+    <item msgid="8309220355268900335">"Ű§Ù„ŰšŰ·Ű§Ű±ÙŠŰ©"</item>
+  </string-array>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"ÙˆŰ¶Űč \"Ű§Ù„ŰȘŰ”ÙŰ­ ŰšÙŠŰŻ ÙˆŰ§Ű­ŰŻŰ©\""</string>
     <string name="quick_settings_contrast_label" msgid="988087460210159123">"Ű§Ù„ŰȘŰšŰ§ÙŠÙ†"</string>
     <string name="quick_settings_contrast_standard" msgid="2538227821968061832">"ŰčŰ§ŰŻÙŠ"</string>
@@ -408,12 +413,9 @@
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ŰŹŰ§Ű±Ù Ű§Ù„ŰŽŰ­Ù† • ŰłŰȘمŰȘÙ„ŰŠ Ű§Ù„ŰšŰ·Ű§Ű±ÙŠŰ© ŰźÙ„Ű§Ù„ <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Ù…Ű±ÙÙ‘Ű± ŰłŰ±ÙŠŰčÙ‹Ű§ لليمين Ù„ŰšŰŻŰĄ Ű§Ù„ŰŻÙ„ÙŠÙ„ Ű§Ù„ŰȘÙˆŰŹÙŠÙ‡ÙŠ Ű§Ù„ŰčŰ§Ù…."</string>
     <string name="button_to_open_widget_editor" msgid="5599945944349057600">"فŰȘŰ­ Ù…Ű­Ű±ÙÙ‘Ű± Ű§Ù„ŰȘŰ·ŰšÙŠÙ‚Ű§ŰȘ Ű§Ù„Ù…Ű”ŰșÙ‘Ű±Ű©"</string>
-    <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
-    <skip />
-    <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
-    <skip />
-    <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
-    <skip />
+    <string name="button_to_remove_widget" msgid="3948204829181214098">"Ű„ŰČŰ§Ù„Ű©"</string>
+    <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Ű„Ű¶Ű§ÙŰ© ŰȘŰ·ŰšÙŠÙ‚ Ù…Ű”ŰșÙ‘Ű±"</string>
+    <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"ŰȘم"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ŰȘŰšŰŻÙŠÙ„ Ű§Ù„Ù…ŰłŰȘŰźŰŻÙ…"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"Ű§Ù„Ù‚Ű§ŰŠÙ…Ű© Ű§Ù„Ù…Ù†ŰłŰŻÙ„Ű©"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ŰłÙŠŰȘم Ű­Ű°Ù كل Ű§Ù„ŰȘŰ·ŰšÙŠÙ‚Ű§ŰȘ ÙˆŰ§Ù„ŰšÙŠŰ§Ù†Ű§ŰȘ في Ù‡Ű°Ù‡ Ű§Ù„ŰŹÙ„ŰłŰ©."</string>
@@ -465,7 +467,7 @@
     <string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Ù…Ű­Ùˆ ŰŹÙ…ÙŠŰč Ű§Ù„Ű„ŰŽŰčۧ۱ۧŰȘ Ű§Ù„Ű”Ű§Ù…ŰȘŰ©"</string>
     <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"ŰȘم Ű„ÙŠÙ‚Ű§Ù Ű§Ù„Ű„ŰŽŰčۧ۱ۧŰȘ Ù…Ű€Ù‚ŰȘÙ‹Ű§ ÙˆÙÙ‚Ù‹Ű§ Ù„Ű„Űčۯۧۯ \"ŰčŰŻÙ… Ű§Ù„Ű„ŰČŰčۧۏ\""</string>
     <string name="media_projection_action_text" msgid="3634906766918186440">"Ű§Ù„ŰšŰŻŰĄ Ű§Ù„ŰąÙ†"</string>
-    <string name="empty_shade_text" msgid="8935967157319717412">"Ù„ÙŠŰł Ù‡Ù†Ű§Ùƒ ŰŁÙŠ ۧێŰčۧ۱ۧŰȘ"</string>
+    <string name="empty_shade_text" msgid="8935967157319717412">"Ù…Ű§ مِن ۄێŰčۧ۱ۧŰȘ."</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"Ù…Ű§ مِن ۄێŰčۧ۱ۧŰȘ ŰŹŰŻÙŠŰŻŰ©"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Ű§ÙŰȘÙŽŰ­ قفل Ű§Ù„ŰŽŰ§ŰŽŰ© لŰč۱۶ Ű§Ù„Ű„ŰŽŰčۧ۱ۧŰȘ Ű§Ù„ŰŁÙ‚ŰŻÙ…."</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"يŰȘولّى ŰŁŰ­ŰŻ Ű§Ù„ÙˆŰ§Ù„ŰŻÙŠÙ† ۄۯۧ۱۩ Ù‡Ű°Ű§ Ű§Ù„ŰŹÙ‡Ű§ŰČ."</string>
@@ -867,7 +869,6 @@
     <string name="auto_saver_title" msgid="6873691178754086596">"Ű§Ù†Ù‚Ű± Ù„ŰŹŰŻÙˆÙ„Ű© \"ŰȘÙˆÙÙŠŰ± ŰŽŰ­Ù† Ű§Ù„ŰšŰ·Ű§Ű±ÙŠŰ©\"."</string>
     <string name="auto_saver_text" msgid="3214960308353838764">"فŰčِّل Ű§Ù„Ù…ÙŠŰČŰ© ۄ۰ۧ ÙƒŰ§Ù† من Ű§Ù„Ù…Ű±ŰŹŰ­ Ù†ÙŰ§ŰŻ ŰŽŰ­Ù† Ű§Ù„ŰšŰ·Ű§Ű±ÙŠŰ©."</string>
     <string name="no_auto_saver_action" msgid="7467924389609773835">"Ù„Ű§ŰŒ ŰŽÙƒŰ±Ù‹Ű§"</string>
-    <string name="heap_dump_tile_name" msgid="2464189856478823046">"‏ŰȘÙŰ±ÙŠŰș Ű°Ű§ÙƒŰ±Ű© SysUI"</string>
     <string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"Ù‚ÙŠŰŻ Ű§Ù„Ű§ŰłŰȘŰźŰŻŰ§Ù…"</string>
     <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"ŰȘŰłŰȘŰźŰŻÙ… Ű§Ù„ŰȘŰ·ŰšÙŠÙ‚Ű§ŰȘ <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
     <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">"ی "</string>
@@ -1207,8 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"يمكنك ۶ۚ۷ ŰȘŰ·ŰšÙŠÙ‚ ŰȘŰŻÙˆÙŠÙ† Ű§Ù„Ù…Ù„Ű§Ű­ŰžŰ§ŰȘ Ű§Ù„ŰȘÙ„Ù‚Ű§ŰŠÙŠ في \"Ű§Ù„Ű„ŰčۯۧۯۧŰȘ\"."</string>
     <string name="install_app" msgid="5066668100199613936">"ŰȘŰ«ŰšÙŠŰȘ Ű§Ù„ŰȘŰ·ŰšÙŠÙ‚"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"هل ŰȘŰ±ÙŠŰŻ ۚ۫ Ù…Ű­ŰȘوى ŰŹÙ‡Ű§ŰČك Űčلى Ű§Ù„ŰŽŰ§ŰŽŰ© Ű§Ù„ŰźŰ§Ű±ŰŹÙŠŰ©ŰŸ"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (2917631104216376315) -->
-    <skip />
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"ŰłÙŠŰȘم Ű§Ù„Ù†ŰłŰź Ű§Ù„Ù…Ű·Ű§ŰšÙ‚ Ù„Ù…Ű­ŰȘوى Ű§Ù„ŰŽŰ§ŰŽŰ© Ű§Ù„ŰŻŰ§ŰźÙ„ÙŠŰ©ŰŒ ÙˆŰ„ÙŠÙ‚Ű§Ù Ű§Ù„ŰŽŰ§ŰŽŰ© Ű§Ù„ŰŁÙ…Ű§Ù…ÙŠŰ©."</string>
     <string name="mirror_display" msgid="2515262008898122928">"ۚ۫ Ű§Ù„Ù…Ű­ŰȘوى Űčلى Ű§Ù„ŰŽŰ§ŰŽŰ©"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Ű„ŰșÙ„Ű§Ù‚"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"ŰȘم ŰȘÙˆŰ”ÙŠÙ„ Ű§Ù„ŰŽŰ§ŰŽŰ©"</string>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index 21da387..4e78f55 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -330,12 +330,17 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"àŠžà§àŠ•à§à§°à§€àŠš à§°à§‡àŠ•à§°à§àŠĄ àŠ•à§°àŠŸ"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"àŠ†à§°àŠźà§àŠ­ àŠ•à§°àŠ•"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"àŠŹàŠšà§àŠ§ àŠ•à§°àŠ•"</string>
-    <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
-    <skip />
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"à§°à§‡àŠ•à§°à§àŠĄ àŠžàŠźà§àŠȘà§°à§àŠ•à§€àŠŻàŠŒ àŠžàŠźàŠžà§àŠŻàŠŸ"</string>
+    <string name="qs_record_issue_start" msgid="2979831312582567056">"àŠ†à§°àŠźà§àŠ­ àŠ•à§°àŠ•"</string>
+    <string name="qs_record_issue_stop" msgid="3531747965741982657">"àŠŹàŠšà§àŠ§ àŠ•à§°àŠ•"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"àŠ†àŠȘà§‹àŠšàŠŸà§° àŠĄàŠżàŠ­àŠŸàŠ‡àŠšà§° àŠ…àŠ­àŠżàŠœà§àŠžàŠ€àŠŸà§° àŠ•à§‹àŠšàŠŸà§‹ àŠ…àŠ‚àŠ¶ àŠȘà§à§°àŠ­àŠŸà§±àŠżàŠ€ àŠčà§ˆàŠ›àŠżàŠČ?"</string>
+    <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"àŠžàŠźàŠžà§àŠŻàŠŸà§° àŠȘà§à§°àŠ•àŠŸà§° àŠŹàŠŸàŠ›àŠšàŠż àŠ•à§°àŠ•"</string>
+    <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"àŠžà§àŠ•à§à§°à§€àŠš à§°à§‡àŠ•à§°à§àŠĄ"</string>
+  <string-array name="qs_record_issue_types">
+    <item msgid="2947988124014085798">"àŠ•àŠŸà§°à§àŠŻàŠŠàŠ•à§àŠ·àŠ€àŠŸ"</item>
+    <item msgid="1627504621139124393">"àŠŹà§àŠŻà§±àŠčàŠŸà§°àŠ•àŠŸà§°à§€à§° àŠ‡àŠŁà§àŠŸàŠŸà§°àŠ«à§‡’àŠš"</item>
+    <item msgid="8309220355268900335">"àŠŹà§‡àŠŸàŠŸà§°à§€"</item>
+  </string-array>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"àŠàŠ–àŠš àŠčàŠŸàŠ€à§‡à§°à§‡ àŠŹà§àŠŻà§±àŠčàŠŸà§° àŠ•à§°àŠŸ àŠź’àŠĄ"</string>
     <string name="quick_settings_contrast_label" msgid="988087460210159123">"àŠ•àŠšàŠŸà§à§°àŠŸàŠ·à§àŠŸ"</string>
     <string name="quick_settings_contrast_standard" msgid="2538227821968061832">"àŠźàŠŸàŠšàŠ•"</string>
@@ -408,12 +413,9 @@
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • àŠšàŠŸà§°à§àŠœ àŠčৈ àŠ†àŠ›à§‡ • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>àŠ€ àŠžàŠźà§àŠȘà§‚à§°à§àŠŁ àŠč’àŠŹ"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"àŠžàŠźà§àŠȘà§à§°àŠŠàŠŸàŠŻàŠŒ àŠžàŠźà§àŠȘà§°à§àŠ•à§€àŠŻàŠŒ àŠšàŠżà§°à§àŠŠà§‡àŠ¶àŠšàŠŸ àŠ†à§°àŠźà§àŠ­ àŠ•à§°àŠżàŠŹàŠČৈ àŠŹàŠŸàŠ“àŠàŠ«àŠŸàŠČে àŠ›à§‹à§±àŠŸàŠ‡àŠȘ àŠ•à§°àŠ•"</string>
     <string name="button_to_open_widget_editor" msgid="5599945944349057600">"à§±àŠżàŠœà§‡àŠŸ àŠžàŠźà§àŠȘàŠŸàŠŠàŠ•àŠŸà§‹ àŠ–à§‹àŠČàŠ•"</string>
-    <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
-    <skip />
-    <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
-    <skip />
-    <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
-    <skip />
+    <string name="button_to_remove_widget" msgid="3948204829181214098">"àŠ†àŠàŠ€à§°àŠŸàŠ“àŠ•"</string>
+    <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"à§±àŠżàŠœà§‡àŠŸ àŠŻà§‹àŠ— àŠŠàŠżàŠŻàŠŒàŠ•"</string>
+    <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"àŠ•à§°àŠŸ àŠč’àŠČ"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"àŠŹà§àŠŻà§±àŠčàŠŸà§°àŠ•àŠŸà§°à§€ àŠžàŠČàŠšàŠż àŠ•à§°àŠ•"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"àŠȘুàŠČ-àŠĄàŠŸàŠ‰àŠšà§° àŠźà§‡àŠšà§"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"àŠàŠ‡ àŠ›à§‡àŠ¶à§àŠŹàŠšà§° àŠ†àŠŸàŠŸàŠ‡àŠŹà§‹à§° àŠàŠȘ্ àŠ†à§°à§ àŠĄà§‡àŠŸàŠŸ àŠźàŠšàŠŸ àŠč\'àŠŹà„€"</string>
@@ -867,7 +869,6 @@
     <string name="auto_saver_title" msgid="6873691178754086596">"àŠŹà§‡àŠŸàŠŸà§°à§€ àŠžàŠžà§àŠšàŠŻàŠŒàŠ•àŠŸà§°à§€à§° àŠžàŠźàŠŻàŠŒàŠžà§‚àŠšà§€ àŠžàŠ•à§à§°àŠżàŠŻàŠŒ àŠ•à§°àŠżàŠŹàŠČৈ àŠŸàŠżàŠȘàŠ•"</string>
     <string name="auto_saver_text" msgid="3214960308353838764">"àŠŹà§‡àŠŸàŠŸà§°à§€ àŠ¶à§‡àŠ· àŠčà§‹à§±àŠŸà§° àŠžàŠźà§àŠ­àŠŸà§±àŠšàŠŸ àŠ„àŠŸàŠ•àŠżàŠČে àŠ…àŠš àŠ•à§°àŠ•"</string>
     <string name="no_auto_saver_action" msgid="7467924389609773835">"àŠšàŠŸàŠČàŠŸàŠ—à§‡, àŠ§àŠšà§àŠŻàŠŹàŠŸàŠŠ"</string>
-    <string name="heap_dump_tile_name" msgid="2464189856478823046">"SysUI àŠčীàŠȘ àŠĄàŠŸàŠźà§àŠȘ àŠ•à§°àŠ•"</string>
     <string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"àŠŹà§àŠŻà§±àŠčàŠŸà§° àŠčৈ àŠ†àŠ›à§‡"</string>
     <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"àŠàŠȘ্àŠČàŠżàŠ•à§‡àŠ¶à§àŠŹàŠšàŠžàŠźà§‚àŠčে àŠ†àŠȘà§‹àŠšàŠŸà§° <xliff:g id="TYPES_LIST">%s</xliff:g> àŠŹà§àŠŻà§±àŠčàŠŸà§° àŠ•à§°àŠż àŠ†àŠ›à§‡à„€"</string>
     <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"àŠ›à§‡àŠŸàŠżàŠ™àŠ€ àŠŸà§‹àŠ•àŠŸà§° àŠĄàŠżàŠ«’àŠČà§àŠŸ àŠàŠȘ্ àŠ›à§‡àŠŸ àŠ•à§°àŠ•"</string>
     <string name="install_app" msgid="5066668100199613936">"àŠàŠȘ্‌àŠŸà§‹ àŠ‡àŠšàŠ·à§àŠŸàŠČ àŠ•à§°àŠ•"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"àŠŹàŠŸàŠčà§àŠŻàŠżàŠ• àŠĄàŠżàŠ›àŠȘ্àŠČে’àŠČৈ àŠźàŠżà§°’à§° àŠ•à§°àŠżàŠŹàŠšà§‡?"</string>
-    <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"àŠŹà§°à§àŠ€àŠźàŠŸàŠš àŠšàŠČàŠż àŠ„àŠ•àŠŸ àŠŻàŠżàŠ•à§‹àŠšà§‹ àŠŠà§àŠŹà§ˆàŠ€ àŠžà§àŠ•à§à§°à§€àŠšà§° àŠ•àŠŸà§°à§àŠŻàŠ•àŠČàŠŸàŠȘ àŠŹàŠšà§àŠ§ àŠ•à§°àŠŸ àŠč’àŠŹ"</string>
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"àŠ†àŠȘà§‹àŠšàŠŸà§° àŠ‡àŠšàŠŸà§° àŠĄàŠżàŠ›àŠȘ্àŠČে’ àŠȘà§à§°àŠ€àŠżàŠŹàŠżàŠźà§àŠŹàŠżàŠ€ àŠ•à§°àŠŸ àŠč’àŠŹà„€ àŠ†àŠȘà§‹àŠšàŠŸà§° àŠ«à§à§°àŠŁà§àŠŸ àŠĄàŠżàŠ›àŠȘ্àŠČে’ àŠ…àŠ« àŠ•à§°àŠŸ àŠč’àŠŹà„€"</string>
     <string name="mirror_display" msgid="2515262008898122928">"àŠĄàŠżàŠ›àŠȘ্àŠČে’ àŠźàŠżà§°’à§° àŠ•à§°àŠ•"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"àŠ…àŠ—à§à§°àŠŸàŠčà§àŠŻ àŠ•à§°àŠ•"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"àŠĄàŠżàŠ›àŠȘ্àŠČে’ àŠžàŠ‚àŠŻà§‹àŠ— àŠ•à§°àŠŸ àŠčà§ˆàŠ›à§‡"</string>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index ec4d712..bf32c5e 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -330,12 +330,17 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Ekran yazması"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Baßlayın"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Dayandırın"</string>
-    <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
-    <skip />
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"Qeyd problemi"</string>
+    <string name="qs_record_issue_start" msgid="2979831312582567056">"Baßlayın"</string>
+    <string name="qs_record_issue_stop" msgid="3531747965741982657">"Dayandırın"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Cihaz istifadəsinə necə təsir etdi?"</string>
+    <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Problem növü seçin"</string>
+    <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Ekran qeydəalma"</string>
+  <string-array name="qs_record_issue_types">
+    <item msgid="2947988124014085798">"Performans"</item>
+    <item msgid="1627504621139124393">"İstifadəçi interfeysi"</item>
+    <item msgid="8309220355268900335">"Batareya"</item>
+  </string-array>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Birəlli rejim"</string>
     <string name="quick_settings_contrast_label" msgid="988087460210159123">"Kontrast"</string>
     <string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Standart"</string>
@@ -408,12 +413,9 @@
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ƞarj edilir • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> sonra dolacaq"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"İcma təlimatını baßlatmaq üçün sola sürüßdürün"</string>
     <string name="button_to_open_widget_editor" msgid="5599945944349057600">"Vidcet redaktorunu açın"</string>
-    <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
-    <skip />
-    <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
-    <skip />
-    <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
-    <skip />
+    <string name="button_to_remove_widget" msgid="3948204829181214098">"Silin"</string>
+    <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Vidcet əlavə edin"</string>
+    <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Hazırdır"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Switch user"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"aßağı çəkilən menyu"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Bu sessiyada bütün tətbiqlər və data silinəcək."</string>
@@ -867,7 +869,6 @@
     <string name="auto_saver_title" msgid="6873691178754086596">"Batareya Qənaətini planlaßdırmaq üçün klikləyin"</string>
     <string name="auto_saver_text" msgid="3214960308353838764">"Batareya bitmək üzrə olduqda aktiv edin"</string>
     <string name="no_auto_saver_action" msgid="7467924389609773835">"Xeyr, təƟəkkür"</string>
-    <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string>
     <string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"İstifadə olunur"</string>
     <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Tətbiqlər <xliff:g id="TYPES_LIST">%s</xliff:g> istifadə edir."</string>
     <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Ayarlarda defolt qeydlər tətbiqi ayarlayın"</string>
     <string name="install_app" msgid="5066668100199613936">"Tətbiqi quraßdırın"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Xarici displeyə əks etdirilsin?"</string>
-    <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"Davam edən istənilən dual screen fəaliyyəti dayandırılacaq"</string>
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"İç displey əks etdiriləcək. Ön ekran deaktiv ediləcək."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Displeyi əks etdirin"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"İmtina edin"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Displey qoßulub"</string>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index 1320d0a..56c3e45 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -330,12 +330,17 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Snimanje ekrana"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Počnite"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Zaustavite"</string>
-    <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
-    <skip />
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"Evidentirajte problem"</string>
+    <string name="qs_record_issue_start" msgid="2979831312582567056">"Pokreni"</string>
+    <string name="qs_record_issue_stop" msgid="3531747965741982657">"Zaustavi"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Na koji deo doĆŸivljaja na uređaju je ovo uticalo?"</string>
+    <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Izaberite tip problema"</string>
+    <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Snimanje ekrana"</string>
+  <string-array name="qs_record_issue_types">
+    <item msgid="2947988124014085798">"Učinak"</item>
+    <item msgid="1627504621139124393">"Korisnički interfejs"</item>
+    <item msgid="8309220355268900335">"Baterija"</item>
+  </string-array>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"ReĆŸim jednom rukom"</string>
     <string name="quick_settings_contrast_label" msgid="988087460210159123">"Kontrast"</string>
     <string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Standardno"</string>
@@ -408,12 +413,9 @@
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Puni se • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> do kraja punjenja"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Prevucite ulevo da biste započeli zajednički vodič"</string>
     <string name="button_to_open_widget_editor" msgid="5599945944349057600">"Otvori uređivač vidĆŸeta"</string>
-    <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
-    <skip />
-    <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
-    <skip />
-    <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
-    <skip />
+    <string name="button_to_remove_widget" msgid="3948204829181214098">"Ukloni"</string>
+    <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Dodaj vidĆŸet"</string>
+    <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Gotovo"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Zameni korisnika"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"padajući meni"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Sve aplikacije i podaci u ovoj sesiji će biti izbrisani."</string>
@@ -867,7 +869,6 @@
     <string name="auto_saver_title" msgid="6873691178754086596">"Dodirnite da biste napravili raspored za uštedu baterije"</string>
     <string name="auto_saver_text" msgid="3214960308353838764">"Uključite ako će baterija verovatno da se isprazni"</string>
     <string name="no_auto_saver_action" msgid="7467924389609773835">"Ne, hvala"</string>
-    <string name="heap_dump_tile_name" msgid="2464189856478823046">"Izdvoji SysUI mem."</string>
     <string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"U upotrebi"</string>
     <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Aplikacije koriste <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
     <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Podesite podrazumevanu aplikaciju za beleške u Podešavanjima"</string>
     <string name="install_app" msgid="5066668100199613936">"Instaliraj aplikaciju"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Ćœelite li da preslikate na spoljnji ekran?"</string>
-    <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"Biće zaustavljena svaka aktivnost dvojnog ekrana koja je u toku"</string>
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Unutrašnji ekran će se preslikati. Prednji ekran će se isključiti."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Preslikaj ekran"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Odbaci"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Ekran je povezan"</string>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index 570acc8..3bc6269 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -330,12 +330,17 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Запіс эĐșŃ€Đ°ĐœĐ°"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Пачаць"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"ĐĄĐżŃ‹ĐœŃ–Ń†ŃŒ"</string>
-    <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
-    <skip />
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"Запіс ĐżŃ€Đ°Đ±Đ»Đ”ĐŒŃ‹"</string>
+    <string name="qs_record_issue_start" msgid="2979831312582567056">"ĐŸĐ°Ń‡Ń‹ĐœĐ°ĐčцД"</string>
+    <string name="qs_record_issue_stop" msgid="3531747965741982657">"ĐĄĐżŃ‹ĐœŃ–Ń†Đ”ŃŃ"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"З Ń‡Ń‹ĐŒ была Đ·ĐČŃĐ·Đ°ĐœĐ° ĐżŃ€Đ°Đ±Đ»Đ”ĐŒĐ°, яĐșая ĐČĐ°ĐŒ ŃŃƒŃŃ‚Ń€ŃĐ»Đ°ŃŃ?"</string>
+    <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"ВыбДрыцД тып ĐżŃ€Đ°Đ±Đ»Đ”ĐŒŃ‹"</string>
+    <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Запіс эĐșŃ€Đ°ĐœĐ°"</string>
+  <string-array name="qs_record_issue_types">
+    <item msgid="2947988124014085798">"ПраЮуĐșцыĐčĐœĐ°ŃŃ†ŃŒ"</item>
+    <item msgid="1627504621139124393">"ĐšĐ°Ń€Ń‹ŃŃ‚Đ°Đ»ŃŒĐœŃ–Ń†Đșі Ń–ĐœŃ‚ŃŃ€Ń„Đ”Đčс"</item>
+    <item msgid="8309220355268900335">"АĐșŃƒĐŒŃƒĐ»ŃŃ‚Đ°Ń€"</item>
+  </string-array>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Đ ŃĐ¶Ń‹ĐŒ ĐșіраĐČĐ°ĐœĐœŃ Đ°ĐŽĐœĐŸĐč руĐșĐŸĐč"</string>
     <string name="quick_settings_contrast_label" msgid="988087460210159123">"ĐšĐ°ĐœŃ‚Ń€Đ°ŃŃ‚ĐœĐ°ŃŃ†ŃŒ"</string>
     <string name="quick_settings_contrast_standard" msgid="2538227821968061832">"ĐĄŃ‚Đ°ĐœĐŽĐ°Ń€Ń‚ĐœĐ°Ń"</string>
@@ -408,12 +413,9 @@
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ІЎзД зараЎĐșа • ĐŸĐŸŃžĐœŃ‹ зараЎ праз <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"ПраĐČŃĐŽĐ·Ń–Ń†Đ” ĐżĐ°Đ»ŃŒŃ†Đ°ĐŒ па эĐșŃ€Đ°ĐœĐ” ўлДĐČа, Đșаб Đ°Đ·ĐœĐ°Ń‘ĐŒŃ–Ń†Ń†Đ° Đ· ĐŽĐ°ĐżĐ°ĐŒĐŸĐ¶ĐœŃ–ĐșĐ°ĐŒ"</string>
     <string name="button_to_open_widget_editor" msgid="5599945944349057600">"АЎĐșрыць рэЮаĐșтар ĐČŃ–ĐŽĐ¶ŃŃ‚Đ°Ńž"</string>
-    <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
-    <skip />
-    <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
-    <skip />
-    <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
-    <skip />
+    <string name="button_to_remove_widget" msgid="3948204829181214098">"Đ’Ń‹ĐŽĐ°Đ»Ń–Ń†ŃŒ"</string>
+    <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"ДаЮаць ĐČŃ–ĐŽĐ¶ŃŃ‚"</string>
+    <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Đ“Đ°Ń‚ĐŸĐČа"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ĐŸĐ”Ń€Đ°Đčсці Ўа Ń–ĐœŃˆĐ°ĐłĐ° ĐșĐ°Ń€Ń‹ŃŃ‚Đ°Đ»ŃŒĐœŃ–Đșа"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ĐČŃ‹ŃĐŸŃžĐœĐ°Đ” ĐŒĐ”ĐœŃŽ"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"УсД ĐżŃ€Đ°ĐłŃ€Đ°ĐŒŃ‹ і ĐŽĐ°ĐœŃ‹Ń гэтага ŃĐ”Đ°ĐœŃĐ° Đ±ŃƒĐŽŃƒŃ†ŃŒ ĐČŃ‹ĐŽĐ°Đ»Đ”ĐœŃ‹."</string>
@@ -867,7 +869,6 @@
     <string name="auto_saver_title" msgid="6873691178754086596">"ĐĐ°Ń†Ń–ŃĐœŃ–Ń†Đ”, Đșаб уĐșĐ»ŃŽŃ‡Ń‹Ń†ŃŒ Ń€ŃĐ¶Ń‹ĐŒ эĐșĐ°ĐœĐŸĐŒŃ–Ń– Đ·Đ°Ń€Đ°ĐŽŃƒ"</string>
     <string name="auto_saver_text" msgid="3214960308353838764">"ĐŁĐșлючыцД, Đșалі зараЎ аĐșŃƒĐŒŃƒĐ»ŃŃ‚Đ°Ń€Đ° заĐșĐ°ĐœŃ‡ĐČаДцца"</string>
     <string name="no_auto_saver_action" msgid="7467924389609773835">"ĐĐ”, ĐŽĐ·ŃĐșуĐč"</string>
-    <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string>
     <string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"ВыĐșĐ°Ń€Ń‹ŃŃ‚ĐŸŃžĐČаДцца"</string>
     <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"ĐŸŃ€Đ°ĐłŃ€Đ°ĐŒŃ‹ ĐČыĐșĐ°Ń€Ń‹ŃŃ‚ĐŸŃžĐČаюць: <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
     <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,8 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"ЗаЮаĐčцД ў ĐĐ°Đ»Đ°ĐŽĐ°Ń… ŃŃ‚Đ°ĐœĐŽĐ°Ń€Ń‚ĐœŃƒŃŽ ĐżŃ€Đ°ĐłŃ€Đ°ĐŒŃƒ ĐŽĐ»Ń ĐœĐ°Ń‚Đ°Ń‚Đ°Đș"</string>
     <string name="install_app" msgid="5066668100199613936">"ĐŁŃŃ‚Đ°Đ»ŃĐČаць ĐżŃ€Đ°ĐłŃ€Đ°ĐŒŃƒ"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"АЎлюстраĐČаць ĐœĐ° Đ·ĐœĐ”ŃˆĐœŃ–ĐŒ ĐŽŃ‹ŃĐżĐ»ŃŃ–?"</string>
-    <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"Đ€ŃƒĐœĐșцыя Đ°ĐŽĐœĐ°Ń‡Đ°ŃĐŸĐČага ĐČыĐșĐ°Ń€Ń‹ŃŃ‚Đ°ĐœĐœŃ ĐŽĐČух эĐșŃ€Đ°ĐœĐ°Ńž буЎзД ŃĐżŃ‹ĐœĐ”ĐœĐ°, Đșалі ŃĐœĐ° аĐșŃ‚Ń‹ŃžĐœĐ°Ń"</string>
+    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
+    <skip />
     <string name="mirror_display" msgid="2515262008898122928">"АЎлюстраĐČаць ĐŽŃ‹ŃĐżĐ»ŃĐč"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"ЗаĐșрыць"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Đ”Ń‹ŃĐżĐ»ŃĐč паЎĐșĐ»ŃŽŃ‡Đ°ĐœŃ‹"</string>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index e52b793..22d167e 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -330,12 +330,17 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Запос ĐœĐ° Đ”ĐșŃ€Đ°ĐœĐ°"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Старт"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"ĐĄŃ‚ĐŸĐż"</string>
-    <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
-    <skip />
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"ЗапосĐČĐ°ĐœĐ” ĐœĐ° ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒ"</string>
+    <string name="qs_record_issue_start" msgid="2979831312582567056">"ĐĄŃ‚Đ°Ń€Ń‚ĐžŃ€Đ°ĐœĐ”"</string>
+    <string name="qs_record_issue_stop" msgid="3531747965741982657">"ĐĄĐżĐžŃ€Đ°ĐœĐ”"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"ĐĄ ĐșаĐșĐČĐŸ ĐžĐŒĐ°Ń…Ń‚Đ” ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒ?"</string>
+    <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Đ˜Đ·Đ±Đ”Ń€Đ”Ń‚Đ” топ ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒ"</string>
+    <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Запос ĐœĐ° Đ”ĐșŃ€Đ°ĐœĐ°"</string>
+  <string-array name="qs_record_issue_types">
+    <item msgid="2947988124014085798">"ЕфДĐșтоĐČĐœĐŸŃŃ‚"</item>
+    <item msgid="1627504621139124393">"ĐŸĐŸŃ‚Ń€Đ”Đ±ĐžŃ‚Đ”Đ»ŃĐșĐž ĐžĐœŃ‚Đ”Ń€Ń„Đ”Đčс"</item>
+    <item msgid="8309220355268900335">"Đ‘Đ°Ń‚Đ”Ń€ĐžŃ"</item>
+  </string-array>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Đ Đ”Đ¶ĐžĐŒ за Ń€Đ°Đ±ĐŸŃ‚Đ° с Đ”ĐŽĐœĐ° ръĐșа"</string>
     <string name="quick_settings_contrast_label" msgid="988087460210159123">"ĐšĐŸĐœŃ‚Ń€Đ°ŃŃ‚"</string>
     <string name="quick_settings_contrast_standard" msgid="2538227821968061832">"ĐĄŃ‚Đ°ĐœĐŽĐ°Ń€Ń‚Đ”Đœ"</string>
@@ -408,12 +413,9 @@
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ЗарДжЎа сД • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> ĐŽĐŸ ĐżŃŠĐ»ĐœĐŸ Đ·Đ°Ń€Đ”Đ¶ĐŽĐ°ĐœĐ”"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"ĐŸŃ€Đ”ĐșараĐčтД пръст ĐœĐ°Đ»ŃĐČĐŸ, за Ўа стартОратД ĐŸĐ±Ń‰ĐžŃ ŃƒŃ€ĐŸĐș"</string>
     <string name="button_to_open_widget_editor" msgid="5599945944349057600">"ОтĐČĐ°Ń€ŃĐœĐ” ĐœĐ° рДЎаĐșŃ‚ĐŸŃ€Đ° ĐœĐ° ĐżŃ€ĐžŃĐżĐŸŃĐŸĐ±Đ»Đ”ĐœĐžĐ”Ń‚ĐŸ"</string>
-    <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
-    <skip />
-    <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
-    <skip />
-    <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
-    <skip />
+    <string name="button_to_remove_widget" msgid="3948204829181214098">"ĐŸŃ€Đ”ĐŒĐ°Ń…ĐČĐ°ĐœĐ”"</string>
+    <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Đ”ĐŸĐ±Đ°ĐČŃĐœĐ” ĐœĐ° ĐżŃ€ĐžŃĐżĐŸŃĐŸĐ±Đ»Đ”ĐœĐžĐ”"</string>
+    <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Đ“ĐŸŃ‚ĐŸĐČĐŸ"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ĐŸŃ€Đ”ĐČĐșлючĐČĐ°ĐœĐ” ĐŒĐ”Đ¶ĐŽŃƒ ĐżĐŸŃ‚Ń€Đ”Đ±ĐžŃ‚Đ”Đ»ĐžŃ‚Đ”"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ĐżĐ°ĐŽĐ°Ń‰ĐŸ ĐŒĐ”ĐœŃŽ"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ВсочĐșĐž ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžŃ Đž ĐŽĐ°ĐœĐœĐž ĐČ Ń‚Đ°Đ·Đž ŃĐ”ŃĐžŃ щД бъЎат ОзтрОтО."</string>
@@ -867,7 +869,6 @@
     <string name="auto_saver_title" msgid="6873691178754086596">"Đ”ĐŸĐșĐŸŃĐœĐ”Ń‚Đ”, за Ўа аĐșтоĐČОратД аĐČŃ‚ĐŸĐŒĐ°Ń‚ĐžŃ‡ĐœĐžŃ Ń€Đ”Đ¶ĐžĐŒ за запазĐČĐ°ĐœĐ” ĐœĐ° Đ±Đ°Ń‚Đ”Ń€ĐžŃŃ‚Đ°"</string>
     <string name="auto_saver_text" msgid="3214960308353838764">"ВĐșлючĐČĐ°ĐœĐ”, ĐșĐŸĐłĐ°Ń‚ĐŸ Đ” ĐČĐ”Ń€ĐŸŃŃ‚ĐœĐŸ Đ±Đ°Ń‚Đ”Ń€ĐžŃŃ‚Đ° Ўа сД ĐžĐ·Ń‚ĐŸŃ‰Đž"</string>
     <string name="no_auto_saver_action" msgid="7467924389609773835">"ĐĐ”, Đ±Đ»Đ°ĐłĐŸĐŽĐ°Ń€Ń"</string>
-    <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string>
     <string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"Đ˜Đ·ĐżĐŸĐ»Đ·ĐČа сД"</string>
     <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"НяĐșĐŸĐž ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžŃ ĐžĐ·ĐżĐŸĐ»Đ·ĐČат <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
     <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"ЗаЮаĐčтД ŃŃ‚Đ°ĐœĐŽĐ°Ń€Ń‚ĐœĐŸ ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžĐ” за бДлДжĐșĐž ĐŸŃ‚ ĐœĐ°ŃŃ‚Ń€ĐŸĐčĐșОтД"</string>
     <string name="install_app" msgid="5066668100199613936">"Đ˜ĐœŃŃ‚Đ°Đ»ĐžŃ€Đ°ĐœĐ” ĐœĐ° ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžĐ”Ń‚ĐŸ"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Да сД ĐŽŃƒĐ±Đ»ĐžŃ€Đ° лО ĐœĐ° ĐČŃŠĐœŃˆĐœĐžŃ Đ”ĐșŃ€Đ°Đœ?"</string>
-    <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"ВсяĐșа тДĐșуща аĐșтоĐČĐœĐŸŃŃ‚ с Ń„ŃƒĐœĐșцоята Dual Screen щД бъЎД ŃĐżŃ€ŃĐœĐ°"</string>
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"ĐĄŃŠĐŽŃŠŃ€Đ¶Đ°ĐœĐžĐ”Ń‚ĐŸ ĐœĐ° ĐČŃŠŃ‚Ń€Đ”ŃˆĐœĐžŃ ĐČĐž ЎОсплДĐč щД бъЎД ĐŽŃƒĐ±Đ»ĐžŃ€Đ°ĐœĐŸ. ĐŸŃ€Đ”ĐŽĐœĐžŃŃ‚ ĐČĐž ЎОсплДĐč щД бъЎД ОзĐșĐ»ŃŽŃ‡Đ”Đœ."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Đ”ŃƒĐ±Đ»ĐžŃ€Đ°ĐœĐ” ĐœĐ° ĐŽĐžŃĐżĐ»Đ”Ń"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"ОтхĐČŃŠŃ€Đ»ŃĐœĐ”"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"ĐĄĐČŃŠŃ€Đ·Đ°Đœ Đ” Đ”ĐșŃ€Đ°Đœ"</string>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index 5e65830..045af93 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -330,12 +330,17 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"àŠžà§àŠ•à§àŠ°àŠżàŠš àŠ°à§‡àŠ•àŠ°à§àŠĄ"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"àŠ¶à§àŠ°à§ àŠ•àŠ°à§àŠš"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"àŠŹàŠšà§àŠ§ àŠ•àŠ°à§àŠš"</string>
-    <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
-    <skip />
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"àŠ°à§‡àŠ•àŠ°à§àŠĄàŠżàŠ‚àŠŻàŠŒà§‡ àŠžàŠźàŠžà§àŠŻàŠŸ"</string>
+    <string name="qs_record_issue_start" msgid="2979831312582567056">"àŠ¶à§àŠ°à§ àŠ•àŠ°à§àŠš"</string>
+    <string name="qs_record_issue_stop" msgid="3531747965741982657">"àŠŹàŠšà§àŠ§ àŠ•àŠ°à§àŠš"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"àŠĄàŠżàŠ­àŠŸàŠ‡àŠž àŠŹà§àŠŻàŠŹàŠčàŠŸàŠ° àŠ•àŠ°àŠŸàŠ° àŠžàŠźàŠŻàŠŒ àŠ•à§‹àŠ„àŠŸàŠŻàŠŒ àŠ…àŠžà§àŠŹàŠżàŠ§àŠŸ àŠčàŠŻàŠŒà§‡àŠ›àŠżàŠČ?"</string>
+    <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"àŠžàŠźàŠžà§àŠŻàŠŸàŠ° àŠȘà§àŠ°àŠ•àŠŸàŠ° àŠŹà§‡àŠ›à§‡ àŠšàŠżàŠš"</string>
+    <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"àŠžà§àŠ•à§àŠ°àŠżàŠš àŠ°à§‡àŠ•àŠ°à§àŠĄ"</string>
+  <string-array name="qs_record_issue_types">
+    <item msgid="2947988124014085798">"àŠȘàŠŸàŠ°àŠ«àŠ°à§àŠźà§àŠŻàŠŸàŠšà§àŠž"</item>
+    <item msgid="1627504621139124393">"àŠ‡àŠ‰àŠœàŠŸàŠ° àŠ‡àŠšà§àŠŸàŠŸàŠ°àŠ«à§‡àŠž"</item>
+    <item msgid="8309220355268900335">"àŠŹà§àŠŻàŠŸàŠŸàŠŸàŠ°àŠż"</item>
+  </string-array>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"àŠàŠ• àŠčàŠŸàŠ€à§‡ àŠŹà§àŠŻàŠŹàŠčàŠŸàŠ° àŠ•àŠ°àŠŸàŠ° àŠźà§‹àŠĄ"</string>
     <string name="quick_settings_contrast_label" msgid="988087460210159123">"àŠ•àŠšàŠŸà§àŠ°àŠŸàŠžà§àŠŸ"</string>
     <string name="quick_settings_contrast_standard" msgid="2538227821968061832">"àŠžà§àŠŸà§àŠŻàŠŸàŠšà§àŠĄàŠŸàŠ°à§àŠĄ"</string>
@@ -408,12 +413,9 @@
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • àŠšàŠŸàŠ°à§àŠœ àŠčàŠšà§àŠ›à§‡ • àŠȘà§àŠ°à§‹ àŠšàŠŸàŠ°à§àŠœ àŠčàŠ€à§‡ àŠ†àŠ°àŠ“ <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> àŠžàŠźàŠŻàŠŒ àŠČàŠŸàŠ—àŠŹà§‡"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"àŠ•àŠźàŠżàŠ‰àŠšàŠżàŠŸàŠż àŠŸàŠżàŠ‰àŠŸà§‹àŠ°àŠżàŠŻàŠŒàŠŸàŠČ àŠšàŠŸàŠČু àŠ•àŠ°àŠ€à§‡ àŠŹàŠŸàŠàŠŠàŠżàŠ•à§‡ àŠžà§‹àŠŻàŠŒàŠŸàŠ‡àŠȘ àŠ•àŠ°à§àŠš"</string>
     <string name="button_to_open_widget_editor" msgid="5599945944349057600">"àŠ‰àŠ‡àŠœà§‡àŠŸ àŠàŠĄàŠżàŠŸàŠ° àŠ–à§àŠČà§àŠš"</string>
-    <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
-    <skip />
-    <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
-    <skip />
-    <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
-    <skip />
+    <string name="button_to_remove_widget" msgid="3948204829181214098">"àŠžàŠ°àŠŸàŠš"</string>
+    <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"àŠ‰àŠ‡àŠœà§‡àŠŸ àŠŻà§‹àŠ— àŠ•àŠ°à§àŠš"</string>
+    <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"àŠčàŠŻàŠŒà§‡ àŠ—à§‡àŠ›à§‡"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"àŠŹà§àŠŻàŠŹàŠčàŠŸàŠ°àŠ•àŠŸàŠ°à§€ àŠȘàŠŸàŠČà§àŠŸà§‡ àŠŠàŠżàŠš"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"àŠȘুàŠČàŠĄàŠŸàŠ‰àŠš àŠźà§‡àŠšà§"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"àŠàŠ‡ àŠžà§‡àŠ¶àŠšà§‡àŠ° àŠžàŠŹ àŠ…à§àŠŻàŠŸàŠȘ àŠ“ àŠĄà§‡àŠŸàŠŸ àŠźà§àŠ›à§‡ àŠ«à§‡àŠČàŠŸ àŠčàŠŹà§‡à„€"</string>
@@ -867,7 +869,6 @@
     <string name="auto_saver_title" msgid="6873691178754086596">"àŠŹà§àŠŻàŠŸàŠŸàŠŸàŠ°àŠż àŠžà§‡àŠ­àŠŸàŠ° àŠšàŠŸàŠČু àŠčàŠ“àŠŻàŠŒàŠŸàŠ° àŠžàŠźàŠŻàŠŒ àŠžà§‡àŠŸ àŠ•àŠ°àŠ€à§‡ àŠŸà§àŠŻàŠŸàŠȘ àŠ•àŠ°à§àŠš"</string>
     <string name="auto_saver_text" msgid="3214960308353838764">"àŠŹà§àŠŻàŠŸàŠŸàŠŸàŠ°àŠżàŠ° àŠšàŠŸàŠ°à§àŠœ àŠ¶à§‡àŠ· àŠčàŠŻàŠŒà§‡ àŠŻàŠŸàŠ“àŠŻàŠŒàŠŸàŠ° àŠžàŠźà§àŠ­àŠŸàŠŹàŠšàŠŸ àŠŠà§‡àŠ–àŠŸ àŠŠàŠżàŠČে àŠšàŠŸàŠČু àŠ•àŠ°à§àŠš"</string>
     <string name="no_auto_saver_action" msgid="7467924389609773835">"àŠšàŠŸ àŠ„àŠŸàŠ•"</string>
-    <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string>
     <string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"àŠŹà§àŠŻàŠŹàŠčàŠŸàŠ° àŠčàŠšà§àŠ›à§‡"</string>
     <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"àŠ…à§àŠŻàŠŸàŠȘ্àŠČàŠżàŠ•à§‡àŠ¶àŠšàŠ—à§àŠČàŠż àŠ†àŠȘàŠšàŠŸàŠ° <xliff:g id="TYPES_LIST">%s</xliff:g> àŠŹà§àŠŻàŠŹàŠčàŠŸàŠ° àŠ•àŠ°àŠ›à§‡à„€"</string>
     <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,8 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"\'àŠžà§‡àŠŸàŠżàŠ‚àŠž\' àŠ„à§‡àŠ•à§‡ àŠĄàŠżàŠ«àŠČà§àŠŸ àŠšà§‹àŠŸ àŠšà§‡àŠ“àŠŻàŠŒàŠŸàŠ° àŠ…à§àŠŻàŠŸàŠȘ àŠžà§‡àŠŸ àŠ•àŠ°à§àŠš"</string>
     <string name="install_app" msgid="5066668100199613936">"àŠ…à§àŠŻàŠŸàŠȘ àŠ‡àŠšàŠžà§àŠŸàŠČ àŠ•àŠ°à§àŠš"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"àŠàŠ•à§àŠžàŠŸàŠŸàŠ°à§àŠšàŠŸàŠČ àŠĄàŠżàŠžàŠȘ্àŠČà§‡àŠ€à§‡ àŠźàŠżàŠ°àŠ° àŠ•àŠ°àŠŹà§‡àŠš?"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (2917631104216376315) -->
-    <skip />
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"àŠ†àŠȘàŠšàŠŸàŠ° àŠ‡àŠšàŠŸàŠ° àŠĄàŠżàŠžàŠȘ্àŠČে àŠźàŠżàŠ°àŠ° àŠ•àŠ°àŠŸ àŠčàŠŹà§‡à„€ àŠ†àŠȘàŠšàŠŸàŠ° àŠ«à§àŠ°àŠšà§àŠŸ àŠĄàŠżàŠžàŠȘ্àŠČে àŠŹàŠšà§àŠ§ àŠ•àŠ°àŠŸ àŠčàŠŹà§‡à„€"</string>
     <string name="mirror_display" msgid="2515262008898122928">"àŠĄàŠżàŠžàŠȘ্àŠČে àŠŠà§‡àŠ–àŠŸàŠš"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"àŠŹàŠŸàŠ€àŠżàŠČ àŠ•àŠ°à§àŠš"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"àŠĄàŠżàŠžàŠȘ্àŠČে àŠ•àŠŸàŠšà§‡àŠ•à§àŠŸ àŠ•àŠ°àŠŸ àŠ†àŠ›à§‡"</string>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index f320d40..ae962ce 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -330,12 +330,17 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Snimanje ekrana"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Započnite"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Zaustavite"</string>
-    <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
-    <skip />
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"Snimite problem"</string>
+    <string name="qs_record_issue_start" msgid="2979831312582567056">"Pokrenite"</string>
+    <string name="qs_record_issue_stop" msgid="3531747965741982657">"Zaustavite"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Koji dio uređaja je imao problem?"</string>
+    <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Odaberite vrstu problema"</string>
+    <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Snimanje ekrana"</string>
+  <string-array name="qs_record_issue_types">
+    <item msgid="2947988124014085798">"Performanse"</item>
+    <item msgid="1627504621139124393">"Korisnički interfejs"</item>
+    <item msgid="8309220355268900335">"Baterija"</item>
+  </string-array>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Način rada jednom rukom"</string>
     <string name="quick_settings_contrast_label" msgid="988087460210159123">"Kontrast"</string>
     <string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Standardno"</string>
@@ -408,12 +413,9 @@
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Punjenje • Potpuna napunjenost za <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Prevucite ulijevo da pokrenete zajednički vodič"</string>
     <string name="button_to_open_widget_editor" msgid="5599945944349057600">"Otvaranje uređivača vidĆŸeta"</string>
-    <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
-    <skip />
-    <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
-    <skip />
-    <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
-    <skip />
+    <string name="button_to_remove_widget" msgid="3948204829181214098">"Uklanjanje"</string>
+    <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Dodajte vidĆŸet"</string>
+    <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Gotovo"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Zamijeni korisnika"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"padajući meni"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Sve aplikacije i podaci iz ove sesije će se izbrisati."</string>
@@ -867,7 +869,6 @@
     <string name="auto_saver_title" msgid="6873691178754086596">"Dodirnite da zakaĆŸete Uštedu baterije"</string>
     <string name="auto_saver_text" msgid="3214960308353838764">"Uključite ako je vjerovatno da će se baterija istrošiti"</string>
     <string name="no_auto_saver_action" msgid="7467924389609773835">"Ne, hvala"</string>
-    <string name="heap_dump_tile_name" msgid="2464189856478823046">"Izdvoji SysUI mem."</string>
     <string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"U upotrebi"</string>
     <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Aplikacije koriste <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
     <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Postavite zadanu aplikaciju za bilješke u Postavkama"</string>
     <string name="install_app" msgid="5066668100199613936">"Instaliraj aplikaciju"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Preslikati na vanjski ekran?"</string>
-    <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"Bilo koja trenutno pokrenuta aktivnost na dvostrukom ekranu se zaustaviti"</string>
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Unutrašnji ekran će se preslikavati. Prednji ekran će se isključiti."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Preslikaj ekran"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Odbaci"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Ekran je povezan"</string>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index a8e908f..8cf8828 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -330,12 +330,17 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Gravació de pantalla"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Inicia"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Atura"</string>
-    <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
-    <skip />
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"Registra el problema"</string>
+    <string name="qs_record_issue_start" msgid="2979831312582567056">"Inicia"</string>
+    <string name="qs_record_issue_stop" msgid="3531747965741982657">"Atura"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"L\'experiència amb el dispositiu s\'ha vist afectada?"</string>
+    <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Selecciona el tipus de problema"</string>
+    <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Gravació de pantalla"</string>
+  <string-array name="qs_record_issue_types">
+    <item msgid="2947988124014085798">"Rendiment"</item>
+    <item msgid="1627504621139124393">"Interfície d\'usuari"</item>
+    <item msgid="8309220355268900335">"Bateria"</item>
+  </string-array>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Mode d\'una mà"</string>
     <string name="quick_settings_contrast_label" msgid="988087460210159123">"Contrast"</string>
     <string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Estàndard"</string>
@@ -408,12 +413,9 @@
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • S\'està carregant • Es completarà d\'aquí a <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Llisca cap a l\'esquerra per iniciar el tutorial de la comunitat"</string>
     <string name="button_to_open_widget_editor" msgid="5599945944349057600">"Obre l\'editor de widgets"</string>
-    <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
-    <skip />
-    <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
-    <skip />
-    <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
-    <skip />
+    <string name="button_to_remove_widget" msgid="3948204829181214098">"Suprimeix"</string>
+    <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Afegeix un widget"</string>
+    <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Fet"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Canvia d\'usuari"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menú desplegable"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Totes les aplicacions i les dades d\'aquesta sessió se suprimiran."</string>
@@ -867,7 +869,6 @@
     <string name="auto_saver_title" msgid="6873691178754086596">"Toca per programar l\'estalvi de bateria"</string>
     <string name="auto_saver_text" msgid="3214960308353838764">"Activa\'l quan sigui probable que et quedis sense bateria"</string>
     <string name="no_auto_saver_action" msgid="7467924389609773835">"No, gràcies"</string>
-    <string name="heap_dump_tile_name" msgid="2464189856478823046">"Aboca el monticle de SysUI"</string>
     <string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"En ús"</string>
     <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Algunes aplicacions estan fent servir el següent: <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
     <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1206,9 +1207,9 @@
     <string name="assistant_attention_content_description" msgid="4166330881435263596">"S\'ha detectat la presència d\'usuaris"</string>
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Defineix l\'aplicació de notes predeterminada a Configuració"</string>
     <string name="install_app" msgid="5066668100199613936">"Instal·la l\'aplicació"</string>
-    <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Replicar a la pantalla externa?"</string>
-    <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"Qualsevol activitat de pantalla dual que s\'estigui executant en aquests moments s\'aturarà"</string>
-    <string name="mirror_display" msgid="2515262008898122928">"Replica la pantalla"</string>
+    <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Duplicar a la pantalla externa?"</string>
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"La pantalla interior es duplicarà. La pantalla frontal es desactivarà."</string>
+    <string name="mirror_display" msgid="2515262008898122928">"Duplica la pantalla"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Ignora"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Pantalla connectada"</string>
     <string name="privacy_dialog_title" msgid="7839968133469098311">"Micròfon i càmera"</string>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index 79aa029..17084dc 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -330,12 +330,17 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Rekordér obrazovky"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Spustit"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Ukončit"</string>
-    <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
-    <skip />
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"Zaznamenat problém"</string>
+    <string name="qs_record_issue_start" msgid="2979831312582567056">"Spustit"</string>
+    <string name="qs_record_issue_stop" msgid="3531747965741982657">"Ukončit"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Co v zaƙízení bylo ovlivněno?"</string>
+    <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Vyberte druh problém"</string>
+    <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Záznam obrazovky"</string>
+  <string-array name="qs_record_issue_types">
+    <item msgid="2947988124014085798">"Výkon"</item>
+    <item msgid="1627504621139124393">"UĆŸivatelské rozhraní"</item>
+    <item msgid="8309220355268900335">"Baterie"</item>
+  </string-array>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"ReĆŸim jedné ruky"</string>
     <string name="quick_settings_contrast_label" msgid="988087460210159123">"Kontrast"</string>
     <string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Standardní"</string>
@@ -408,12 +413,9 @@
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Nabíjení • Plně nabito za <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Pƙejetím doleva spustíte komunitní výukový program"</string>
     <string name="button_to_open_widget_editor" msgid="5599945944349057600">"Otevƙít editor widgetĆŻ"</string>
-    <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
-    <skip />
-    <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
-    <skip />
-    <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
-    <skip />
+    <string name="button_to_remove_widget" msgid="3948204829181214098">"Odstranit"</string>
+    <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Pƙidat widget"</string>
+    <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Hotovo"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Pƙepnout uĆŸivatele"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"rozbalovací nabídka"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Veškeré aplikace a data v této relaci budou vymazána."</string>
@@ -867,7 +869,6 @@
     <string name="auto_saver_title" msgid="6873691178754086596">"Klepnutím naplánujete aktivování spoƙiče baterie"</string>
     <string name="auto_saver_text" msgid="3214960308353838764">"Zapnout, kdyĆŸ bude pravděpodobné, ĆŸe se vybije baterie"</string>
     <string name="no_auto_saver_action" msgid="7467924389609773835">"Ne, díky"</string>
-    <string name="heap_dump_tile_name" msgid="2464189856478823046">"Výpis haldy SysUI"</string>
     <string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"PouĆŸívá se"</string>
     <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Aplikace vyuĆŸívají tato oprávnění: <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
     <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Výchozí aplikaci pro poznámky nastavíte v Nastavení"</string>
     <string name="install_app" msgid="5066668100199613936">"Nainstalovat aplikaci"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Zrcadlit na externí displej?"</string>
-    <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"BÄ›ĆŸící aktivita Dual Screen bude ukončena"</string>
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Vnitƙní displej bude zrcadlen. Pƙední displej bude vypnutý."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Zrcadlit displej"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Zavƙít"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Displej pƙipojen"</string>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index b418ce8..41abea3 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -200,7 +200,7 @@
     <string name="accessibility_bluetooth_device_settings_see_all" msgid="9111952496905423543">"Klik for at se alle enheder"</string>
     <string name="accessibility_bluetooth_device_settings_pair_new_device" msgid="2435184865793496966">"Klik for at parre en ny enhed"</string>
     <string name="accessibility_battery_unknown" msgid="1807789554617976440">"Batteriniveauet er ukendt."</string>
-    <string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Tilsluttet <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
+    <string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Forbundet med <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
     <string name="accessibility_cast_name" msgid="7344437925388773685">"Forbundet til <xliff:g id="CAST">%s</xliff:g>."</string>
     <string name="accessibility_not_connected" msgid="4061305616351042142">"Ikke tilsluttet."</string>
     <string name="data_connection_roaming" msgid="375650836665414797">"Roaming"</string>
@@ -293,8 +293,8 @@
     <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>
-    <string name="quick_settings_connected_battery_level" msgid="1322075669498906959">"Tilsluttet – batteriniveau <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+    <string name="quick_settings_connected" msgid="3873605509184830379">"Forbundet"</string>
+    <string name="quick_settings_connected_battery_level" msgid="1322075669498906959">"Forbundet – batteriniveau <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="quick_settings_connecting" msgid="2381969772953268809">"Opretter forbindelse…"</string>
     <string name="quick_settings_hotspot_label" msgid="1199196300038363424">"Hotspot"</string>
     <string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"Aktiverer…"</string>
@@ -330,12 +330,17 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Skærmoptagelse"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Start"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Stop"</string>
-    <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
-    <skip />
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"Optag problem"</string>
+    <string name="qs_record_issue_start" msgid="2979831312582567056">"Start"</string>
+    <string name="qs_record_issue_stop" msgid="3531747965741982657">"Stop"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Hvilken del af din enhedsoplevelse blev påvirket?"</string>
+    <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Vælg problemtype"</string>
+    <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Skærmoptagelse"</string>
+  <string-array name="qs_record_issue_types">
+    <item msgid="2947988124014085798">"Ydeevne"</item>
+    <item msgid="1627504621139124393">"Brugerflade"</item>
+    <item msgid="8309220355268900335">"Batteri"</item>
+  </string-array>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Enhåndstilstand"</string>
     <string name="quick_settings_contrast_label" msgid="988087460210159123">"Kontrast"</string>
     <string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Standard"</string>
@@ -408,12 +413,9 @@
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Oplader • Fuldt opladet om <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Stryg mod venstre for at starte den fælles vejledning"</string>
     <string name="button_to_open_widget_editor" msgid="5599945944349057600">"Åbn redigeringsværktøjet til widgets"</string>
-    <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
-    <skip />
-    <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
-    <skip />
-    <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
-    <skip />
+    <string name="button_to_remove_widget" msgid="3948204829181214098">"Fjern"</string>
+    <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Tilføj widget"</string>
+    <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Udfør"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Skift bruger"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"rullemenu"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alle apps og data i denne session slettes."</string>
@@ -867,7 +869,6 @@
     <string name="auto_saver_title" msgid="6873691178754086596">"Tryk for at fastsætte en tidsplan for batterisparefunktionen"</string>
     <string name="auto_saver_text" msgid="3214960308353838764">"Aktivér, når det ser ud til, at batteriet løber tør"</string>
     <string name="no_auto_saver_action" msgid="7467924389609773835">"Nej tak"</string>
-    <string name="heap_dump_tile_name" msgid="2464189856478823046">"Gem SysUI-heap"</string>
     <string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"I brug"</string>
     <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Apps anvender enhedens <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
     <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Angiv standardapp til noter i Indstillinger"</string>
     <string name="install_app" msgid="5066668100199613936">"Installer app"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Vil du spejle til ekstern skærm?"</string>
-    <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"Al aktivitet, der i øjeblikket anvender funktioner til brug af to skærme, stoppes"</string>
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Din indre skærm spejles. Din skærm på forsiden slukkes."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Spejl skærm"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Luk"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Skærmen er tilsluttet"</string>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index a6bc4d4..d95e229 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -253,7 +253,7 @@
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Keine gekoppelten Geräte verfügbar"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Zum Verbinden oder Trennen eines Geräts tippen"</string>
     <string name="pair_new_bluetooth_devices" msgid="4601767620843349645">"Neues Gerät koppeln"</string>
-    <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Alle ansehen"</string>
+    <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Alle anzeigen"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Bluetooth verwenden"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Verbunden"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Gespeichert"</string>
@@ -330,12 +330,17 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Bildschirmaufzeichnung"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Starten"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Beenden"</string>
-    <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
-    <skip />
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"Problem aufnehmen"</string>
+    <string name="qs_record_issue_start" msgid="2979831312582567056">"Aufnahme starten"</string>
+    <string name="qs_record_issue_stop" msgid="3531747965741982657">"Aufnahme beenden"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Welche Bereiche des Geräts waren betroffen?"</string>
+    <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Art des Problems auswählen"</string>
+    <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Bildschirmaufnahme"</string>
+  <string-array name="qs_record_issue_types">
+    <item msgid="2947988124014085798">"Leistung"</item>
+    <item msgid="1627504621139124393">"Benutzeroberfläche"</item>
+    <item msgid="8309220355268900335">"Akku"</item>
+  </string-array>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Einhandmodus"</string>
     <string name="quick_settings_contrast_label" msgid="988087460210159123">"Kontrast"</string>
     <string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Standard"</string>
@@ -408,12 +413,9 @@
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Wird geladen • Voll in <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Wische nach links, um das gemeinsame Tutorial zu starten"</string>
     <string name="button_to_open_widget_editor" msgid="5599945944349057600">"Widget-Editor öffnen"</string>
-    <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
-    <skip />
-    <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
-    <skip />
-    <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
-    <skip />
+    <string name="button_to_remove_widget" msgid="3948204829181214098">"Entfernen"</string>
+    <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Widget hinzufügen"</string>
+    <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Fertig"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Nutzer wechseln"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"Pull-down-Menü"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alle Apps und Daten in dieser Sitzung werden gelöscht."</string>
@@ -867,7 +869,6 @@
     <string name="auto_saver_title" msgid="6873691178754086596">"Tippen zum Planen des Energiesparmodus"</string>
     <string name="auto_saver_text" msgid="3214960308353838764">"Aktivieren, wenn der Akku wahrscheinlich nicht mehr lange hält"</string>
     <string name="no_auto_saver_action" msgid="7467924389609773835">"Nein danke"</string>
-    <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string>
     <string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"In Verwendung"</string>
     <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Apps verwenden gerade Folgendes: <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
     <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Standard-Notizen-App in den Einstellungen einrichten"</string>
     <string name="install_app" msgid="5066668100199613936">"App installieren"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Auf externen Bildschirm spiegeln?"</string>
-    <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"Alle momentan ausgeführten Dual-Screen-Aktivitäten werden angehalten"</string>
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Dein inneres Display wird gespiegelt. Das Frontdisplay wird ausgeschaltet."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Bildschirm spiegeln"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Schließen"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Bildschirm verbunden"</string>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index dd6422b..5848e4f 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -330,12 +330,17 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"ΕγγραφÎź οθόνης"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Έναρξη"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"ΔιακοπÎź"</string>
-    <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
-    <skip />
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"ΕγγραφÎź προβλÎźματος"</string>
+    <string name="qs_record_issue_start" msgid="2979831312582567056">"Έναρξη"</string>
+    <string name="qs_record_issue_stop" msgid="3531747965741982657">"ΔιακοπÎź"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Ποιο κομμÎŹτι της εμπειρÎŻας συσκευÎźς επηρεÎŹστηκε;"</string>
+    <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"ΕπιλογÎź τύπου προβλÎźματος"</string>
+    <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"ΕγγραφÎź οθόνης"</string>
+  <string-array name="qs_record_issue_types">
+    <item msgid="2947988124014085798">"Απόδοση"</item>
+    <item msgid="1627504621139124393">"ΔιεπαφÎź χρÎźστη"</item>
+    <item msgid="8309220355268900335">"ΜπαταρÎŻα"</item>
+  </string-array>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"ΛειτουργÎŻα ενός χεριού"</string>
     <string name="quick_settings_contrast_label" msgid="988087460210159123">"ΑντÎŻθεση"</string>
     <string name="quick_settings_contrast_standard" msgid="2538227821968061832">"ΤυπικÎź"</string>
@@ -408,12 +413,9 @@
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Φόρτιση • ΠλÎźρης φόρτιση σε <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Σύρετε προς τα αριστερÎŹ για να ξεκινÎźσετε τον κοινόχρηστο οδηγό"</string>
     <string name="button_to_open_widget_editor" msgid="5599945944349057600">"Άνοιγμα προγρÎŹμ. επεξεργασÎŻας γραφικών στοιχεÎŻων"</string>
-    <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
-    <skip />
-    <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
-    <skip />
-    <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
-    <skip />
+    <string name="button_to_remove_widget" msgid="3948204829181214098">"ΚατÎŹργηση"</string>
+    <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"ΠροσθÎźκη γραφικού στοιχεÎŻου"</string>
+    <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Τέλος"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ΕναλλαγÎź χρÎźστη"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"αναπτυσσόμενο μενού"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Όλες οι εφαρμογές και τα δεδομένα αυτÎźς της περιόδου σύνδεσης θα διαγραφούν."</string>
@@ -867,7 +869,6 @@
     <string name="auto_saver_title" msgid="6873691178754086596">"ΠατÎźστε για προγραμματισμό της Εξοικονόμησης μπαταρÎŻας"</string>
     <string name="auto_saver_text" msgid="3214960308353838764">"ΕνεργοποÎŻηση όταν υπÎŹρχει σημαντικÎź πιθανότητα εξÎŹντλησης της μπαταρÎŻας"</string>
     <string name="no_auto_saver_action" msgid="7467924389609773835">"Όχι, ευχαριστώ"</string>
-    <string name="heap_dump_tile_name" msgid="2464189856478823046">"Στιγμ. μνÎźμης SysUI"</string>
     <string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"ΧρησιμοποιεÎŻται"</string>
     <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Οι εφαρμογές χρησιμοποιούν τις λειτουργÎŻες <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
     <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"ΟρÎŻστε την προεπιλεγμένη εφαρμογÎź σημειώσεων στις ΡυθμÎŻσεις"</string>
     <string name="install_app" msgid="5066668100199613936">"ΕγκατÎŹσταση εφαρμογÎźς"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Κατοπτρισμός σε εξωτερικÎź οθόνη;"</string>
-    <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"Τυχόν τρέχουσα δραστηριότητα Dual Screen θα διακοπεÎŻ"</string>
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Θα γÎŻνει κατοπτρισμός της εσωτερικÎźς προβολÎźς. Η μπροστινÎź οθόνη θα απενεργοποιηθεÎŻ."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Κατοπτρισμός οθόνης"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"ΠαρÎŹβλεψη"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Η οθόνη εÎŻναι συνδεδεμένη"</string>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index 46cdc0f..870e4dd 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -333,6 +333,14 @@
     <string name="qs_record_issue_label" msgid="8166290137285529059">"Record issue"</string>
     <string name="qs_record_issue_start" msgid="2979831312582567056">"Start"</string>
     <string name="qs_record_issue_stop" msgid="3531747965741982657">"Stop"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"What part of your device experience was affected?"</string>
+    <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Select issue type"</string>
+    <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Screen record"</string>
+  <string-array name="qs_record_issue_types">
+    <item msgid="2947988124014085798">"Performance"</item>
+    <item msgid="1627504621139124393">"User interface"</item>
+    <item msgid="8309220355268900335">"Battery"</item>
+  </string-array>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"One-handed mode"</string>
     <string name="quick_settings_contrast_label" msgid="988087460210159123">"Contrast"</string>
     <string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Standard"</string>
@@ -861,7 +869,6 @@
     <string name="auto_saver_title" msgid="6873691178754086596">"Tap to schedule Battery Saver"</string>
     <string name="auto_saver_text" msgid="3214960308353838764">"Turn on when battery is likely to run out"</string>
     <string name="no_auto_saver_action" msgid="7467924389609773835">"No, thanks"</string>
-    <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string>
     <string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"In use"</string>
     <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Applications are using your <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
     <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1201,7 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Set default notes app in Settings"</string>
     <string name="install_app" msgid="5066668100199613936">"Install app"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Mirror to external display?"</string>
-    <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"Any dual screen activity currently running will be stopped"</string>
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Your inner display will be mirrored. Your front display will be turned off."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Mirror display"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Dismiss"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Display connected"</string>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index 9acd568..f25baf2 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -333,6 +333,14 @@
     <string name="qs_record_issue_label" msgid="8166290137285529059">"Record Issue"</string>
     <string name="qs_record_issue_start" msgid="2979831312582567056">"Start"</string>
     <string name="qs_record_issue_stop" msgid="3531747965741982657">"Stop"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"What part of your device experience was affected?"</string>
+    <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Select issue type"</string>
+    <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Screen record"</string>
+  <string-array name="qs_record_issue_types">
+    <item msgid="2947988124014085798">"Performance"</item>
+    <item msgid="1627504621139124393">"User Interface"</item>
+    <item msgid="8309220355268900335">"Battery"</item>
+  </string-array>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"One-handed mode"</string>
     <string name="quick_settings_contrast_label" msgid="988087460210159123">"Contrast"</string>
     <string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Standard"</string>
@@ -861,7 +869,6 @@
     <string name="auto_saver_title" msgid="6873691178754086596">"Tap to schedule Battery Saver"</string>
     <string name="auto_saver_text" msgid="3214960308353838764">"Turn on when battery is likely to run out"</string>
     <string name="no_auto_saver_action" msgid="7467924389609773835">"No thanks"</string>
-    <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string>
     <string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"In use"</string>
     <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Applications are using your <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
     <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1201,7 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Set default notes app in Settings"</string>
     <string name="install_app" msgid="5066668100199613936">"Install app"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Mirror to external display?"</string>
-    <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"Any dual screen activity currently running will be stopped"</string>
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Your inner display will be mirrored. Your front display will be turned off."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Mirror display"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Dismiss"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Display connected"</string>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index 46cdc0f..870e4dd 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -333,6 +333,14 @@
     <string name="qs_record_issue_label" msgid="8166290137285529059">"Record issue"</string>
     <string name="qs_record_issue_start" msgid="2979831312582567056">"Start"</string>
     <string name="qs_record_issue_stop" msgid="3531747965741982657">"Stop"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"What part of your device experience was affected?"</string>
+    <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Select issue type"</string>
+    <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Screen record"</string>
+  <string-array name="qs_record_issue_types">
+    <item msgid="2947988124014085798">"Performance"</item>
+    <item msgid="1627504621139124393">"User interface"</item>
+    <item msgid="8309220355268900335">"Battery"</item>
+  </string-array>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"One-handed mode"</string>
     <string name="quick_settings_contrast_label" msgid="988087460210159123">"Contrast"</string>
     <string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Standard"</string>
@@ -861,7 +869,6 @@
     <string name="auto_saver_title" msgid="6873691178754086596">"Tap to schedule Battery Saver"</string>
     <string name="auto_saver_text" msgid="3214960308353838764">"Turn on when battery is likely to run out"</string>
     <string name="no_auto_saver_action" msgid="7467924389609773835">"No, thanks"</string>
-    <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string>
     <string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"In use"</string>
     <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Applications are using your <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
     <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1201,7 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Set default notes app in Settings"</string>
     <string name="install_app" msgid="5066668100199613936">"Install app"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Mirror to external display?"</string>
-    <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"Any dual screen activity currently running will be stopped"</string>
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Your inner display will be mirrored. Your front display will be turned off."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Mirror display"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Dismiss"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Display connected"</string>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index 46cdc0f..870e4dd 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -333,6 +333,14 @@
     <string name="qs_record_issue_label" msgid="8166290137285529059">"Record issue"</string>
     <string name="qs_record_issue_start" msgid="2979831312582567056">"Start"</string>
     <string name="qs_record_issue_stop" msgid="3531747965741982657">"Stop"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"What part of your device experience was affected?"</string>
+    <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Select issue type"</string>
+    <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Screen record"</string>
+  <string-array name="qs_record_issue_types">
+    <item msgid="2947988124014085798">"Performance"</item>
+    <item msgid="1627504621139124393">"User interface"</item>
+    <item msgid="8309220355268900335">"Battery"</item>
+  </string-array>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"One-handed mode"</string>
     <string name="quick_settings_contrast_label" msgid="988087460210159123">"Contrast"</string>
     <string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Standard"</string>
@@ -861,7 +869,6 @@
     <string name="auto_saver_title" msgid="6873691178754086596">"Tap to schedule Battery Saver"</string>
     <string name="auto_saver_text" msgid="3214960308353838764">"Turn on when battery is likely to run out"</string>
     <string name="no_auto_saver_action" msgid="7467924389609773835">"No, thanks"</string>
-    <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string>
     <string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"In use"</string>
     <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Applications are using your <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
     <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1201,7 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Set default notes app in Settings"</string>
     <string name="install_app" msgid="5066668100199613936">"Install app"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Mirror to external display?"</string>
-    <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"Any dual screen activity currently running will be stopped"</string>
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Your inner display will be mirrored. Your front display will be turned off."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Mirror display"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Dismiss"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Display connected"</string>
diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml
index 9cd3014..b3ed714 100644
--- a/packages/SystemUI/res/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res/values-en-rXC/strings.xml
@@ -333,6 +333,14 @@
     <string name="qs_record_issue_label" msgid="8166290137285529059">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‎‏‎‏‎‏‎‏‎‎‎‏‏‏‏‏‎‏‏‎‎‏‏‏‎‎‏‏‏‎‏‏‎‏‏‎‏‎‏‏‏‎‎‎‎‎‏‏‎‏‏‏‏‎‎‎‏‏‎Record Issue‎‏‎‎‏‎"</string>
     <string name="qs_record_issue_start" msgid="2979831312582567056">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‎‏‎‏‎‏‏‎‏‎‎‏‏‏‏‏‎‎‏‏‎‎‏‎‏‎‎‏‏‏‏‏‎‏‎‎‎‏‎‎‎‏‏‏‏‎‎‏‎‎‏‎‎‏‎‎‎‎‎Start‎‏‎‎‏‎"</string>
     <string name="qs_record_issue_stop" msgid="3531747965741982657">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‎‏‎‎‎‎‎‎‏‏‎‏‎‎‏‎‏‎‎‎‎‏‎‎‎‎‎‎‎‏‎‏‏‎‎‏‎‎‎‎‏‎‎‏‏‎‎‎‏‏‏‏‎‎‎‎‎‏‎Stop‎‏‎‎‏‎"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‏‏‎‎‏‏‎‏‏‎‎‎‎‎‎‎‏‎‏‏‏‎‏‏‏‎‎‎‏‎‎‎‎‏‏‎‎‎‏‎‏‏‏‎‎‎‎‏‏‏‎‎‎‏‏‎‎‏‎What part of your device experience was affected?‎‏‎‎‏‎"</string>
+    <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‏‏‎‎‎‏‎‎‎‏‏‎‎‎‎‏‏‏‏‎‎‏‎‎‎‎‎‎‏‎‏‎‎‏‎‎‏‎‎‏‏‏‎‏‏‎‎‎‏‎‎‏‎‎‏‎‏‏‎Select issue type‎‏‎‎‏‎"</string>
+    <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‎‎‏‏‎‎‎‎‏‏‏‎‏‎‏‎‎‏‎‎‏‏‎‎‏‎‎‎‎‏‏‏‎‎‎‏‎‎‏‎‎‏‎‎‏‏‏‏‏‏‎‏‎‏‏‎‏‎‎Screen record‎‏‎‎‏‎"</string>
+  <string-array name="qs_record_issue_types">
+    <item msgid="2947988124014085798">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‎‎‏‏‏‎‏‎‎‏‎‏‎‏‏‎‏‏‏‎‎‏‎‏‎‎‎‏‏‎‎‏‎‏‏‏‏‎‏‏‏‎‎‎‎‎‎‏‏‎‏‎‏‎‎‏‏‎‎Performance‎‏‎‎‏‎"</item>
+    <item msgid="1627504621139124393">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‏‎‏‎‎‏‎‏‏‎‎‎‎‎‏‏‏‎‏‏‎‏‎‏‎‎‎‏‏‏‏‏‎‏‎‏‎‎‏‏‎‏‏‏‏‎‎‎‎‎‏‎‏‎‏‎‎‏‎User Interface‎‏‎‎‏‎"</item>
+    <item msgid="8309220355268900335">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‏‏‎‏‎‏‎‎‎‎‎‏‎‎‎‏‏‏‏‏‏‎‎‎‏‏‏‎‎‏‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‏‎‏‏‏‏‎‏‏‏‏‎Battery‎‏‎‎‏‎"</item>
+  </string-array>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‎‏‏‎‎‎‏‎‎‏‎‏‎‎‎‏‎‎‎‏‏‏‎‎‎‏‏‎‏‏‎‏‏‎‏‎‎‏‎‏‏‎‎‎‏‏‏‏‏‏‏‎‏‎‏‏‏‏‎One-handed mode‎‏‎‎‏‎"</string>
     <string name="quick_settings_contrast_label" msgid="988087460210159123">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‎‏‏‎‏‏‎‏‏‎‎‏‏‎‎‏‎‎‎‏‎‎‏‏‏‏‎‏‎‏‏‎‎‎‏‏‎‏‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‎‎‏‏‎Contrast‎‏‎‎‏‎"</string>
     <string name="quick_settings_contrast_standard" msgid="2538227821968061832">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‏‏‎‎‏‏‏‎‎‏‏‎‎‏‏‎‎‎‏‏‎‎‎‎‎‏‏‎‎‎‎‏‏‏‎‏‏‎‏‎‏‏‎‏‏‏‎‏‎‏‏‎‎‎‏‎‎‎‎Standard‎‏‎‎‏‎"</string>
@@ -861,7 +869,6 @@
     <string name="auto_saver_title" msgid="6873691178754086596">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‏‏‎‏‏‎‎‏‎‎‎‏‎‎‎‎‎‏‏‎‏‏‎‏‎‏‎‏‏‎‏‏‎‏‎‏‎‏‏‏‏‏‏‎‎‏‎‏‏‎‏‏‎‎‎‏‎‎‎Tap to schedule Battery Saver‎‏‎‎‏‎"</string>
     <string name="auto_saver_text" msgid="3214960308353838764">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‎‎‏‎‎‏‏‏‎‏‏‏‎‏‎‏‎‏‎‏‎‏‏‏‎‏‏‏‏‎‏‎‏‎‎‏‏‎‏‎‎‏‎‎‏‎‏‏‏‎‏‎‏‎‏‏‎‎‎Turn on when battery is likely to run out‎‏‎‎‏‎"</string>
     <string name="no_auto_saver_action" msgid="7467924389609773835">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‏‏‏‎‏‎‎‎‏‏‎‏‏‎‎‏‎‏‏‎‏‎‏‎‏‎‎‏‏‎‎‏‎‎‎‎‏‎‏‎‏‎‏‎‎‏‏‎‏‏‎‎‎‎‏‎‏‏‎No thanks‎‏‎‎‏‎"</string>
-    <string name="heap_dump_tile_name" msgid="2464189856478823046">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‏‎‎‎‏‏‎‎‏‎‏‎‎‎‏‏‏‏‏‎‎‏‏‏‏‎‎‏‎‏‏‏‏‎‏‎‎‎‏‎‏‎‏‎‎‎‎‎‏‎‏‎‎‎‎‏‏‎‎Dump SysUI Heap‎‏‎‎‏‎"</string>
     <string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‎‏‎‎‏‏‏‎‎‏‎‎‎‏‏‏‎‎‎‎‎‏‎‏‎‏‎‎‏‏‎‎‏‎‎‎‎‏‏‎‏‏‎‏‎‏‏‎‏‎‏‏‎‎‏‏‎‎In use‎‏‎‎‏‎"</string>
     <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‏‏‏‏‎‎‎‎‎‏‏‏‏‏‎‎‏‏‏‏‎‎‎‏‏‏‎‏‎‏‏‏‎‎‎‏‏‏‏‎‎‏‏‎‎‏‏‎‏‎‎‏‎‏‎‎‏‎‎Applications are using your ‎‏‎‎‏‏‎<xliff:g id="TYPES_LIST">%s</xliff:g>‎‏‎‎‏‏‏‎.‎‏‎‎‏‎"</string>
     <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‏‏‏‏‎‎‏‏‎‎‎‏‎‎‏‏‏‎‏‏‏‏‎‎‏‏‏‎‏‎‎‎‎‎‎‎‏‏‏‎‎‏‎‎‎‏‎‎‎‎‏‎‎‏‎‏‏‎, ‎‏‎‎‏‎ "</string>
@@ -1201,7 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‏‏‎‎‎‎‎‏‏‏‏‎‎‎‏‏‏‏‏‎‎‎‏‏‎‏‎‏‎‏‏‏‏‎‏‏‏‏‎‏‏‏‏‎‎‏‏‏‎‏‎‏‎‎‎‎‎‏‎Set default notes app in Settings‎‏‎‎‏‎"</string>
     <string name="install_app" msgid="5066668100199613936">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‏‎‎‏‎‏‎‎‎‎‎‏‏‎‏‎‏‏‏‏‎‎‏‎‏‎‏‏‏‏‎‎‏‏‎‎‏‏‎‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‎‎‎‎‎Install app‎‏‎‎‏‎"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‏‎‏‎‎‏‎‎‎‏‎‏‏‏‎‏‎‏‎‎‏‎‏‎‏‏‏‏‎‏‎‎‎‎‎‎‏‎‎‎‏‏‎‎‏‎‎‎‏‏‏‏‎‎‎‏‏‎‎Mirror to external display?‎‏‎‎‏‎"</string>
-    <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‎‎‎‏‎‎‎‎‎‎‏‏‏‏‏‏‏‎‎‎‎‎‏‏‏‎‎‏‎‏‎‏‎‎‎‏‏‏‏‏‏‏‎‏‏‎Any dual screen activity currently running will be stopped‎‏‎‎‏‎"</string>
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‏‏‏‏‎‏‏‏‏‏‎‎‎‏‎‏‎‎‏‎‏‏‎‏‎‎‎‎‎‏‎‎‎‏‎‏‎‎‏‎‎‏‏‎‏‎‎‎‎‏‏‎‎‏‎‏‏‎Your inner display will be mirrored. Your front display will be turned off.‎‏‎‎‏‎"</string>
     <string name="mirror_display" msgid="2515262008898122928">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‏‎‏‏‏‎‏‎‎‎‎‎‎‎‎‎‎‏‎‏‏‏‏‎‎‎‎‏‏‏‏‏‎‏‎‏‏‎‏‎‎‎‎‏‏‏‎‏‎‎‏‎‏‏‎‎‎‎‎Mirror display‎‏‎‎‏‎"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‎‎‏‏‏‏‎‎‎‎‎‎‎‎‎‏‏‎‏‎‏‏‎‎‎‎‎‏‏‏‏‎‎‏‏‎‎‎‏‎‏‎‎‏‎‎‏‏‏‏‎‏‎‏‎‏‎‎Dismiss‎‏‎‎‏‎"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‎‎‎‏‏‏‎‎‏‏‎‏‏‎‏‏‏‏‏‎‏‎‎‎‏‎‎‎‏‎‏‏‎‏‎‎‎‎‎‎‏‏‎‏‏‏‏‏‎‎‎‎‎‏‏‏‎‏‎Display connected‎‏‎‎‏‎"</string>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 4be14e9..c535560 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -330,12 +330,17 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Grabación de pantalla"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Iniciar"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Detener"</string>
-    <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
-    <skip />
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"Grabar error"</string>
+    <string name="qs_record_issue_start" msgid="2979831312582567056">"Iniciar"</string>
+    <string name="qs_record_issue_stop" msgid="3531747965741982657">"Detener"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"¿Qué parte de tu exp. del disp. se vio afectada?"</string>
+    <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Seleccionar tipo de problema"</string>
+    <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Grabadora de pant."</string>
+  <string-array name="qs_record_issue_types">
+    <item msgid="2947988124014085798">"Rendimiento"</item>
+    <item msgid="1627504621139124393">"Interfaz de usuario"</item>
+    <item msgid="8309220355268900335">"Batería"</item>
+  </string-array>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modo una mano"</string>
     <string name="quick_settings_contrast_label" msgid="988087460210159123">"Contraste"</string>
     <string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Estándar"</string>
@@ -408,12 +413,9 @@
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Cargando • Se completará en <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Desliza el dedo a la izquierda para iniciar el instructivo comunal"</string>
     <string name="button_to_open_widget_editor" msgid="5599945944349057600">"Abrir el editor de widget"</string>
-    <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
-    <skip />
-    <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
-    <skip />
-    <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
-    <skip />
+    <string name="button_to_remove_widget" msgid="3948204829181214098">"Quitar"</string>
+    <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Agregar widget"</string>
+    <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Listo"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Cambiar usuario"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menú expandible"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Se eliminarán las aplicaciones y los datos de esta sesión."</string>
@@ -867,7 +869,6 @@
     <string name="auto_saver_title" msgid="6873691178754086596">"Presiona para programar el Ahorro de batería"</string>
     <string name="auto_saver_text" msgid="3214960308353838764">"Actívalo cuando la batería se esté por acabar"</string>
     <string name="no_auto_saver_action" msgid="7467924389609773835">"No, gracias"</string>
-    <string name="heap_dump_tile_name" msgid="2464189856478823046">"Volcar pila de SysUI"</string>
     <string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"En uso"</string>
     <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Hay aplicaciones que están usando tu <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
     <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Configura la app de notas predeterminada en Configuración"</string>
     <string name="install_app" msgid="5066668100199613936">"Instalar app"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"¿Quieres duplicar en la pantalla externa?"</string>
-    <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"Se detendrá cualquier actividad en la pantalla doble que se esté ejecutando en ese momento."</string>
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Se duplicará la pantalla interior. Se apagará la pantalla frontal."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Duplicar pantalla"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Descartar"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Pantalla conectada"</string>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 5d88d64..1157ff1 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -252,7 +252,7 @@
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"No hay dispositivos vinculados disponibles"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Toca para conectar o desconectar un dispositivo"</string>
-    <string name="pair_new_bluetooth_devices" msgid="4601767620843349645">"Empareja un nuevo dispositivo"</string>
+    <string name="pair_new_bluetooth_devices" msgid="4601767620843349645">"Emparejar un nuevo dispositivo"</string>
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Ver todos"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Usar Bluetooth"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Conectado"</string>
@@ -330,12 +330,17 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Grabar pantalla"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Iniciar"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Detener"</string>
-    <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
-    <skip />
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"Problema de grabación"</string>
+    <string name="qs_record_issue_start" msgid="2979831312582567056">"Iniciar"</string>
+    <string name="qs_record_issue_stop" msgid="3531747965741982657">"Detener"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"¿Qué parte de tu experiencia se ha visto afectada?"</string>
+    <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Selecciona el tipo de problema"</string>
+    <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Grabar pantalla"</string>
+  <string-array name="qs_record_issue_types">
+    <item msgid="2947988124014085798">"Rendimiento"</item>
+    <item msgid="1627504621139124393">"Interfaz de usuario"</item>
+    <item msgid="8309220355268900335">"Batería"</item>
+  </string-array>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modo Una mano"</string>
     <string name="quick_settings_contrast_label" msgid="988087460210159123">"Contraste"</string>
     <string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Estándar"</string>
@@ -408,12 +413,9 @@
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Cargando • Carga completa en <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Desliza hacia la izquierda para iniciar el tutorial de la comunidad"</string>
     <string name="button_to_open_widget_editor" msgid="5599945944349057600">"Abrir editor de widgets"</string>
-    <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
-    <skip />
-    <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
-    <skip />
-    <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
-    <skip />
+    <string name="button_to_remove_widget" msgid="3948204829181214098">"Quitar"</string>
+    <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Añadir widget"</string>
+    <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Hecho"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Cambiar de usuario"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menú desplegable"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Se eliminarán todas las aplicaciones y datos de esta sesión."</string>
@@ -867,7 +869,6 @@
     <string name="auto_saver_title" msgid="6873691178754086596">"Tocar para programar el modo Ahorro de batería"</string>
     <string name="auto_saver_text" msgid="3214960308353838764">"Activar cuando sea probable que se quede sin batería"</string>
     <string name="no_auto_saver_action" msgid="7467924389609773835">"No, gracias"</string>
-    <string name="heap_dump_tile_name" msgid="2464189856478823046">"Volcar montículo de SysUI"</string>
     <string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"En uso"</string>
     <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Hay aplicaciones que usan tu <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
     <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Configura la aplicación de notas predeterminada en Ajustes"</string>
     <string name="install_app" msgid="5066668100199613936">"Instalar aplicación"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"¿Proyectar a pantalla externa?"</string>
-    <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"Cualquier actividad de pantalla dual que se esté ejecutando en estos momentos se detendrá"</string>
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Se proyectará tu pantalla interior. Se apagará tu pantalla frontal."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Proyectar pantalla"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Cerrar"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Pantalla conectada"</string>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index 258bd4d..36b3f1e 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -330,12 +330,17 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Ekraanisalvestus"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Alustage"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Peatage"</string>
-    <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
-    <skip />
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"Probleemi salvestamine"</string>
+    <string name="qs_record_issue_start" msgid="2979831312582567056">"Alusta"</string>
+    <string name="qs_record_issue_stop" msgid="3531747965741982657">"Peata"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Millist seadme kasutuskogemuse osa see mõjutas?"</string>
+    <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Valige probleemi tüüp"</string>
+    <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Ekraanisalvestus"</string>
+  <string-array name="qs_record_issue_types">
+    <item msgid="2947988124014085798">"Toimivus"</item>
+    <item msgid="1627504621139124393">"Kasutajaliides"</item>
+    <item msgid="8309220355268900335">"Aku"</item>
+  </string-array>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"ÜhekäereĆŸiim"</string>
     <string name="quick_settings_contrast_label" msgid="988087460210159123">"Kontrastsus"</string>
     <string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Tavaline"</string>
@@ -408,12 +413,9 @@
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Laadimine • Täis <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> pärast"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Ühise õpetuse käivitamiseks pühkige vasakule"</string>
     <string name="button_to_open_widget_editor" msgid="5599945944349057600">"Vidina redaktori avamine"</string>
-    <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
-    <skip />
-    <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
-    <skip />
-    <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
-    <skip />
+    <string name="button_to_remove_widget" msgid="3948204829181214098">"Eemalda"</string>
+    <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Lisa vidin"</string>
+    <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Valmis"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Kasutaja vahetamine"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"rippmenüü"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Seansi kõik rakendused ja andmed kustutatakse."</string>
@@ -867,7 +869,6 @@
     <string name="auto_saver_title" msgid="6873691178754086596">"Puudutage akusäästja ajastamiseks"</string>
     <string name="auto_saver_text" msgid="3214960308353838764">"Lülitatakse sisse, kui aku hakkab tühjaks saama"</string>
     <string name="no_auto_saver_action" msgid="7467924389609773835">"Tänan, ei"</string>
-    <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string>
     <string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"Kasutusel"</string>
     <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Rakendused kasutavad järgmisi: <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
     <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Määrake seadetes märkmete vaikerakendus."</string>
     <string name="install_app" msgid="5066668100199613936">"Installi rakendus"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Kas peegeldada välisekraanile?"</string>
-    <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"Kõik praegu käimas olevad kahe ekraaniga tegevused peatatakse"</string>
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Teie siseekraani peegeldatakse. Teie esiekraan lülitatakse välja."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Peegelda ekraani"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Loobu"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Kuvar on ühendatud"</string>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index 54290f7..5ea02d1 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -330,12 +330,17 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Pantaila-grabaketa"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Hasi"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Gelditu"</string>
-    <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
-    <skip />
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"Arazo bat dago grabaketarekin"</string>
+    <string name="qs_record_issue_start" msgid="2979831312582567056">"Hasi"</string>
+    <string name="qs_record_issue_stop" msgid="3531747965741982657">"Gelditu"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Gailuaren erabileraren zer alderdiri eragin dio?"</string>
+    <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Hautatu arazo mota"</string>
+    <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Pantaila-grabagailua"</string>
+  <string-array name="qs_record_issue_types">
+    <item msgid="2947988124014085798">"Errendimendua"</item>
+    <item msgid="1627504621139124393">"Erabiltzaile-interfazea"</item>
+    <item msgid="8309220355268900335">"Bateria"</item>
+  </string-array>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Esku bakarreko modua"</string>
     <string name="quick_settings_contrast_label" msgid="988087460210159123">"Kontrastea"</string>
     <string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Arrunta"</string>
@@ -408,12 +413,9 @@
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Kargatzen • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> guztiz kargatu arte"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Tutorial komuna hasteko, pasatu hatza ezkerrera"</string>
     <string name="button_to_open_widget_editor" msgid="5599945944349057600">"Ireki widget-editorea"</string>
-    <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
-    <skip />
-    <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
-    <skip />
-    <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
-    <skip />
+    <string name="button_to_remove_widget" msgid="3948204829181214098">"Kendu"</string>
+    <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Gehitu widget bat"</string>
+    <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Eginda"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Aldatu erabiltzailea"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"zabaldu menua"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Saioko aplikazio eta datu guztiak ezabatuko dira."</string>
@@ -867,7 +869,6 @@
     <string name="auto_saver_title" msgid="6873691178754086596">"Sakatu bateria-aurreztailea noiz aktibatu programatzeko"</string>
     <string name="auto_saver_text" msgid="3214960308353838764">"Aktibatu aurreztailea bateria agortzeko arriskua dagoenean"</string>
     <string name="no_auto_saver_action" msgid="7467924389609773835">"Ez, eskerrik asko"</string>
-    <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string>
     <string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"Erabiltzen ari da"</string>
     <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Aplikazio batzuk <xliff:g id="TYPES_LIST">%s</xliff:g> erabiltzen ari dira."</string>
     <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Ezarri oharren aplikazio lehenetsia ezarpenetan"</string>
     <string name="install_app" msgid="5066668100199613936">"Instalatu aplikazioa"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Kanpoko pantailan islatu nahi duzu?"</string>
-    <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"Une honetan exekutatzen ari diren pantaila bikoitzeko jarduera guztiak geldituko dira"</string>
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Barneko pantaila islatuko da. Aurreko pantaila desaktibatu egingo da."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Islatu pantaila"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Baztertu"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Konektatutako pantaila"</string>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index 0305887..aa77b4b 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -330,12 +330,17 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"۶ۚ۷ Ű”ÙŰ­Ù‡‌Ù†Ù…Ű§ÛŒŰŽ"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"ŰŽŰ±ÙˆŰč"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"مŰȘوقف Ú©Ű±ŰŻÙ†"</string>
-    <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
-    <skip />
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"۶ۚ۷ Ù…ŰŽÚ©Ù„"</string>
+    <string name="qs_record_issue_start" msgid="2979831312582567056">"ŰŽŰ±ÙˆŰč"</string>
+    <string name="qs_record_issue_stop" msgid="3531747965741982657">"ŰȘوقف"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Ú©ŰŻŰ§Ù… ۚ۟ێ ŰȘŰŹŰ±ŰšÙ‡ ۧ۳ŰȘÙŰ§ŰŻÙ‡ ۧŰČ ŰŻŰłŰȘÚŻŰ§Ù‡ ŰȘŰ­ŰȘ‌ŰȘŰŁŰ«ÛŒŰ± Ù‚Ű±Ű§Ű± ÚŻŰ±ÙŰȘ۟"</string>
+    <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Ű§Ù†ŰȘ۟ۧۚ نوŰč Ù…ŰŽÚ©Ù„"</string>
+    <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"۶ۚ۷ Ű”ÙŰ­Ù‡‌Ù†Ù…Ű§ÛŒŰŽ"</string>
+  <string-array name="qs_record_issue_types">
+    <item msgid="2947988124014085798">"ŰčÙ…Ù„Ú©Ű±ŰŻ"</item>
+    <item msgid="1627504621139124393">"ÙˆŰ§ŰłŰ· کۧ۱ۚ۱"</item>
+    <item msgid="8309220355268900335">"ۚۧŰȘŰ±ÛŒ"</item>
+  </string-array>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Ű­Ű§Ù„ŰȘ یک‌ŰŻŰłŰȘی"</string>
     <string name="quick_settings_contrast_label" msgid="988087460210159123">"ŰȘ۶ۧۯ"</string>
     <string name="quick_settings_contrast_standard" msgid="2538227821968061832">"ۧ۳ŰȘŰ§Ù†ŰŻŰ§Ű±ŰŻ"</string>
@@ -408,12 +413,9 @@
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ŰŻŰ±Ű­Ű§Ù„ ێۧ۱ژ ŰŽŰŻÙ† • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> ŰȘۧ ێۧ۱ژ Ú©Ű§Ù…Ù„"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"ŰšŰ±Ű§ÛŒ ŰŽŰ±ÙˆŰč ŰąÙ…ÙˆŰČŰŽ ÚŻŰ§Ù…‌ŰšÙ‡‌ÚŻŰ§Ù… ŰčÙ…ÙˆÙ…ÛŒŰŒ ŰȘÙ†ŰŻ ŰšÙ‡‌Ú†ÙŸ ŰšÚ©ŰŽÛŒŰŻ"</string>
     <string name="button_to_open_widget_editor" msgid="5599945944349057600">"ۚۧŰČ Ú©Ű±ŰŻÙ† ÙˆÛŒŰ±Ű§ÛŒŰŽÚŻŰ± ۧۚŰČۧ۱ک"</string>
-    <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
-    <skip />
-    <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
-    <skip />
-    <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
-    <skip />
+    <string name="button_to_remove_widget" msgid="3948204829181214098">"ۚ۱ۯۧێŰȘن"</string>
+    <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Ű§ÙŰČÙˆŰŻÙ† ۧۚŰČۧ۱ک"</string>
+    <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"ŰȘÙ…Ű§Ù…"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ŰȘŰșÛŒÛŒŰ± کۧ۱ۚ۱"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"منوی ÙŸŰ§ÛŒÛŒÙ†‌ÙŸŰ±"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"همه ŰšŰ±Ù†Ű§Ù…Ù‡‌Ù‡Ű§ و ŰŻŰ§ŰŻÙ‡‌Ù‡Ű§ÛŒ Ű§ÛŒÙ† ŰŹÙ„ŰłÙ‡ Ű­Ű°Ù ŰźÙˆŰ§Ù‡ŰŻ ŰŽŰŻ."</string>
@@ -867,7 +869,6 @@
     <string name="auto_saver_title" msgid="6873691178754086596">"ŰšŰ±Ű§ÛŒ ŰČÙ…Ű§Ù†‌ŰšÙ†ŰŻÛŒ «ŰšÙ‡ÛŒÙ†Ù‡‌۳ۧŰČی ۚۧŰȘŰ±ÛŒ» Ű¶Ű±ŰšÙ‡ ŰšŰČÙ†ÛŒŰŻ"</string>
     <string name="auto_saver_text" msgid="3214960308353838764">"وقŰȘی ۚۧŰȘŰ±ÛŒ Ű±ÙˆŰšÙ‡‌ۧŰȘÙ…Ű§Ù… ۧ۳ŰȘی ŰšÙ‡ÛŒÙ†Ù‡‌۳ۧŰČی ۚۧŰȘŰ±ÛŒ ۱ۧ Ű±ÙˆŰŽÙ† Ú©Ù†ÛŒŰŻ"</string>
     <string name="no_auto_saver_action" msgid="7467924389609773835">"نه مŰȘŰŽÚ©Ű±Ù…"</string>
-    <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string>
     <string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"ۧ۳ŰȘÙŰ§ŰŻÙ‡ ŰŽŰŻÙ‡"</string>
     <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"ŰšŰ±Ù†Ű§Ù…Ù‡‌Ù‡Ű§ ۧŰČ <xliff:g id="TYPES_LIST">%s</xliff:g> ŰŽÙ…Ű§ ۧ۳ŰȘÙŰ§ŰŻÙ‡ می‌‌Ú©Ù†Ù†ŰŻ."</string>
     <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">"ی "</string>
@@ -1206,9 +1207,9 @@
     <string name="assistant_attention_content_description" msgid="4166330881435263596">"Ű­Ű¶ÙˆŰ± کۧ۱ۚ۱ ŰŽÙ†Ű§ŰłŰ§ÛŒÛŒ می‌ŰŽÙˆŰŻ"</string>
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"ŰšŰ±Ù†Ű§Ù…Ù‡ ÙŸÛŒŰŽ‌ÙŰ±Ű¶ ÛŒŰ§ŰŻŰŻŰ§ŰŽŰȘ ۱ۧ ۯ۱ «ŰȘÙ†ŰžÛŒÙ…Ű§ŰȘ» ŰȘÙ†ŰžÛŒÙ… Ú©Ù†ÛŒŰŻ"</string>
     <string name="install_app" msgid="5066668100199613936">"Ù†Ű”Űš ŰšŰ±Ù†Ű§Ù…Ù‡"</string>
-    <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"ۯ۱ Ù†Ù…Ű§ÛŒŰŽÚŻŰ± ŰźŰ§Ű±ŰŹÛŒ ÙŸŰźŰŽ ŰŽÙˆŰŻŰŸ"</string>
-    <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"‏همه فŰčŰ§Ù„ÛŒŰȘ‌Ù‡Ű§ÛŒ Dual Screen که ŰŻŰ±Ű­Ű§Ù„ ۧۏ۱ۧ ۧ۳ŰȘ مŰȘوقف ŰźÙˆŰ§Ù‡ŰŻ ŰŽŰŻ"</string>
-    <string name="mirror_display" msgid="2515262008898122928">"ۚۧŰČŰȘŰ§ŰšŰ§Ù†ŰŻÙ† Ű”ÙŰ­Ù‡‌Ù†Ù…Ű§ÛŒŰŽ"</string>
+    <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Ű±ÙˆÛŒ Ù†Ù…Ű§ÛŒŰŽÚŻŰ± ŰźŰ§Ű±ŰŹÛŒ Ù‚Ű±ÛŒÙ†Ù‡‌۳ۧŰČی ŰŽÙˆŰŻŰŸ"</string>
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Ù†Ù…Ű§ÛŒŰŽÚŻŰ± ŰŻŰ§ŰźÙ„ÛŒ ŰŽÙ…Ű§ Ù‚Ű±ÛŒÙ†Ù‡‌۳ۧŰČی می‌ŰŽÙˆŰŻ. Ù†Ù…Ű§ÛŒŰŽÚŻŰ± ŰŹÙ„Ùˆ ŰźŰ§Ù…ÙˆŰŽ می‌ŰŽÙˆŰŻ."</string>
+    <string name="mirror_display" msgid="2515262008898122928">"Ù‚Ű±ÛŒÙ†Ù‡‌۳ۧŰČی Ù†Ù…Ű§ÛŒŰŽÚŻŰ±"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"ۚ۳ŰȘن"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Ù†Ù…Ű§ÛŒŰŽÚŻŰ± مŰȘŰ”Ù„ ŰŽŰŻ"</string>
     <string name="privacy_dialog_title" msgid="7839968133469098311">"Ù…ÛŒÚ©Ű±ÙˆÙÙˆÙ† و ŰŻÙˆŰ±ŰšÛŒÙ†"</string>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index ae92aaa..882c42c 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -330,12 +330,17 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Näytön tallennus"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Aloita"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Lopeta"</string>
-    <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
-    <skip />
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"Tallenna ongelma"</string>
+    <string name="qs_record_issue_start" msgid="2979831312582567056">"Aloita"</string>
+    <string name="qs_record_issue_stop" msgid="3531747965741982657">"Lopeta"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Mitä osaa käyttökokemuksesta ongelma koski?"</string>
+    <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Valitse ongelman tyyppi"</string>
+    <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Näytön tallentaja"</string>
+  <string-array name="qs_record_issue_types">
+    <item msgid="2947988124014085798">"Suorituskyky"</item>
+    <item msgid="1627504621139124393">"Käyttöliittymä"</item>
+    <item msgid="8309220355268900335">"Akku"</item>
+  </string-array>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Yhden käden moodi"</string>
     <string name="quick_settings_contrast_label" msgid="988087460210159123">"Kontrasti"</string>
     <string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Tavallinen"</string>
@@ -408,12 +413,9 @@
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Latautuu • Täynnä <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> päästä"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Aloita yhteisöesittely pyyhkäisemällä vasemmalle"</string>
     <string name="button_to_open_widget_editor" msgid="5599945944349057600">"Avaa widgetien muokkaaja"</string>
-    <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
-    <skip />
-    <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
-    <skip />
-    <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
-    <skip />
+    <string name="button_to_remove_widget" msgid="3948204829181214098">"Poista"</string>
+    <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Lisää widget"</string>
+    <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Valmis"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Vaihda käyttäjää"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"alasvetovalikko"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Kaikki sovellukset ja tämän istunnon tiedot poistetaan."</string>
@@ -867,7 +869,6 @@
     <string name="auto_saver_title" msgid="6873691178754086596">"Ajoita virransäästö napauttamalla"</string>
     <string name="auto_saver_text" msgid="3214960308353838764">"Ota käyttöön, jos akku todennäköisesti loppuu"</string>
     <string name="no_auto_saver_action" msgid="7467924389609773835">"Ei kiitos"</string>
-    <string name="heap_dump_tile_name" msgid="2464189856478823046">"Luo SysUI-keon vedos"</string>
     <string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"Käytössä"</string>
     <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"<xliff:g id="TYPES_LIST">%s</xliff:g> ovat sovellusten käytössä."</string>
     <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Aseta oletusmuistiinpanosovellus Asetuksista"</string>
     <string name="install_app" msgid="5066668100199613936">"Asenna sovellus"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Peilataanko ulkoiselle näytölle?"</string>
-    <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"Kaikki käynnissä oleva kahden näytön toiminta lopetetaan"</string>
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Sisänäyttö peilataan. Etunäyttö laitetaan pois päältä."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Peilaa näyttö"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Ohita"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Näyttö yhdistetty"</string>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index fc4c22f..8370b01 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -265,7 +265,7 @@
     <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Entrée"</string>
     <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Prothèses auditives"</string>
     <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Activation en cours…"</string>
-    <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Rotation automatique"</string>
+    <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Rotation auto"</string>
     <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Rotation automatique de l\'écran"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"Localisation"</string>
     <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Écran de veille"</string>
@@ -330,12 +330,17 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Enregistrement d\'écran"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Démarrer"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Arrêter"</string>
-    <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
-    <skip />
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"Rapporter le problème"</string>
+    <string name="qs_record_issue_start" msgid="2979831312582567056">"Commencer"</string>
+    <string name="qs_record_issue_stop" msgid="3531747965741982657">"Arrêter"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Quelle composante de l\'appareil a été affectée?"</string>
+    <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Sélectionner un type"</string>
+    <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Enregistrement écran"</string>
+  <string-array name="qs_record_issue_types">
+    <item msgid="2947988124014085798">"Performance"</item>
+    <item msgid="1627504621139124393">"Interface utilisateur"</item>
+    <item msgid="8309220355268900335">"Pile"</item>
+  </string-array>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Mode Une main"</string>
     <string name="quick_settings_contrast_label" msgid="988087460210159123">"Contraste"</string>
     <string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Standard"</string>
@@ -408,12 +413,9 @@
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Recharge en cours… • Se terminera dans <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Balayer l\'écran vers la gauche pour démarrer le tutoriel communautaire"</string>
     <string name="button_to_open_widget_editor" msgid="5599945944349057600">"Ouvrir l\'éditeur de widget"</string>
-    <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
-    <skip />
-    <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
-    <skip />
-    <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
-    <skip />
+    <string name="button_to_remove_widget" msgid="3948204829181214098">"Retirer"</string>
+    <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Ajouter un widget"</string>
+    <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Terminé"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Changer d\'utilisateur"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menu déroulant"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Toutes les applications et les données de cette session seront supprimées."</string>
@@ -867,7 +869,6 @@
     <string name="auto_saver_title" msgid="6873691178754086596">"Toucher pour activer la fonction Économiseur de pile"</string>
     <string name="auto_saver_text" msgid="3214960308353838764">"Activer si la pile est susceptible de s\'épuiser totalement"</string>
     <string name="no_auto_saver_action" msgid="7467924389609773835">"Non merci"</string>
-    <string name="heap_dump_tile_name" msgid="2464189856478823046">"Copier mémoire SysUI"</string>
     <string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"En cours d\'utilisation"</string>
     <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Des applications utilisent votre <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
     <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,8 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Définir l\'application de prise de notes par défaut dans les Paramètres"</string>
     <string name="install_app" msgid="5066668100199613936">"Installer l\'application"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Dupliquer l\'écran sur un moniteur externe?"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (2917631104216376315) -->
-    <skip />
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Votre écran intérieur sera dupliqué. Votre écran frontal sera désactivé."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Dupliquer l\'écran"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Fermer"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Écran connecté"</string>
diff --git a/packages/SystemUI/res/values-fr-rCA/tiles_states_strings.xml b/packages/SystemUI/res/values-fr-rCA/tiles_states_strings.xml
index 8df6211..b969841 100644
--- a/packages/SystemUI/res/values-fr-rCA/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/tiles_states_strings.xml
@@ -147,7 +147,7 @@
     <item msgid="8998632451221157987">"Activé"</item>
   </string-array>
   <string-array name="tile_states_controls">
-    <item msgid="8199009425335668294">"Non disponible"</item>
+    <item msgid="8199009425335668294">"Non disponibles"</item>
     <item msgid="4544919905196727508">"Désactivées"</item>
     <item msgid="3422023746567004609">"Activées"</item>
   </string-array>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 462ee6c..af81a31 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -330,12 +330,17 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Enregistrement de l\'écran"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Démarrer"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Arrêter"</string>
-    <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
-    <skip />
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"Enregistrer le problème"</string>
+    <string name="qs_record_issue_start" msgid="2979831312582567056">"Début"</string>
+    <string name="qs_record_issue_stop" msgid="3531747965741982657">"Arrêter"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Quel problème avez-vous rencontré avec votre appareil ?"</string>
+    <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Sélectionnez un type de problème"</string>
+    <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Enregistrement de l\'écran"</string>
+  <string-array name="qs_record_issue_types">
+    <item msgid="2947988124014085798">"Performances"</item>
+    <item msgid="1627504621139124393">"Interface utilisateur"</item>
+    <item msgid="8309220355268900335">"Batterie"</item>
+  </string-array>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Mode une main"</string>
     <string name="quick_settings_contrast_label" msgid="988087460210159123">"Contraste"</string>
     <string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Standard"</string>
@@ -408,12 +413,9 @@
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Recharge • Temps restant : <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Balayer vers la gauche pour démarrer le tutoriel collectif"</string>
     <string name="button_to_open_widget_editor" msgid="5599945944349057600">"Ouvrir l\'éditeur de widgets"</string>
-    <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
-    <skip />
-    <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
-    <skip />
-    <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
-    <skip />
+    <string name="button_to_remove_widget" msgid="3948204829181214098">"Supprimer"</string>
+    <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Ajouter un widget"</string>
+    <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"OK"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Changer d\'utilisateur"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menu déroulant"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Toutes les applications et les données de cette session seront supprimées."</string>
@@ -867,7 +869,6 @@
     <string name="auto_saver_title" msgid="6873691178754086596">"Appuyez ici pour programmer l\'économiseur de batterie"</string>
     <string name="auto_saver_text" msgid="3214960308353838764">"Activer l\'économiseur de batterie si l\'autonomie restante risque d\'être insuffisante"</string>
     <string name="no_auto_saver_action" msgid="7467924389609773835">"Non, merci"</string>
-    <string name="heap_dump_tile_name" msgid="2464189856478823046">"Copier mémoire SysUI"</string>
     <string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"En cours d\'utilisation"</string>
     <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Des applications utilisent votre <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
     <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Définir une appli de notes par défaut dans les paramètres"</string>
     <string name="install_app" msgid="5066668100199613936">"Installer l\'appli"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Dupliquer sur l\'écran externe ?"</string>
-    <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"Toute activité en cours sur double écran sera interrompue."</string>
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Votre écran intérieur sera dupliqué. Votre écran frontal sera éteint."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Dupliquer l\'écran"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Fermer"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Écran connecté"</string>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index bfbb825..ef0751b 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -330,12 +330,17 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Gravar pantalla"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Iniciar"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Deter"</string>
-    <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
-    <skip />
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"Rexistrar problema"</string>
+    <string name="qs_record_issue_start" msgid="2979831312582567056">"Iniciar"</string>
+    <string name="qs_record_issue_stop" msgid="3531747965741982657">"Deter"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Cal foi o problema na experiencia co dispositivo?"</string>
+    <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Selecciona o tipo de problema"</string>
+    <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Gravación de pant."</string>
+  <string-array name="qs_record_issue_types">
+    <item msgid="2947988124014085798">"Rendemento"</item>
+    <item msgid="1627504621139124393">"Interface de usuario"</item>
+    <item msgid="8309220355268900335">"Batería"</item>
+  </string-array>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modo dunha soa man"</string>
     <string name="quick_settings_contrast_label" msgid="988087460210159123">"Contraste"</string>
     <string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Nivel estándar"</string>
@@ -408,12 +413,9 @@
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Cargando • A carga completarase en <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Pasa o dedo cara á esquerda para iniciar o titorial comunitario"</string>
     <string name="button_to_open_widget_editor" msgid="5599945944349057600">"Abrir o editor de widgets"</string>
-    <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
-    <skip />
-    <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
-    <skip />
-    <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
-    <skip />
+    <string name="button_to_remove_widget" msgid="3948204829181214098">"Quitar"</string>
+    <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Engadir widget"</string>
+    <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Feito"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Cambiar usuario"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menú despregable"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Eliminaranse todas as aplicacións e datos desta sesión."</string>
@@ -867,7 +869,6 @@
     <string name="auto_saver_title" msgid="6873691178754086596">"Tocar para programar a función Aforro de batería"</string>
     <string name="auto_saver_text" msgid="3214960308353838764">"Activa a función se prevés que a batería pode esgotarse"</string>
     <string name="no_auto_saver_action" msgid="7467924389609773835">"Non, grazas"</string>
-    <string name="heap_dump_tile_name" msgid="2464189856478823046">"Baleirado mem. SysUI"</string>
     <string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"En uso"</string>
     <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Hai aplicacións que están utilizando <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
     <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Establece a aplicación de notas predeterminada en Configuración"</string>
     <string name="install_app" msgid="5066668100199613936">"Instalar aplicación"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Queres proxectar contido nunha pantalla externa?"</string>
-    <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"Deterase toda a actividade que se estea executando nunha pantalla dual"</string>
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Proxectarase a pantalla interior. Desactivarase a pantalla frontal."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Proxectar pantalla"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Pechar"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Pantalla conectada"</string>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index 4095f71..d92231c 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -330,12 +330,17 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"àȘžà«àȘ•્àȘ°à«€àȘš àȘ°à«‡àȘ•ૉàȘ°à«àȘĄ"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"àȘ¶àȘ°à«‚ àȘ•àȘ°à«‹"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"àȘ°à«‹àȘ•à«‹"</string>
-    <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
-    <skip />
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"àȘ°à«‡àȘ•à«‹àȘ°à«àȘĄàȘżàȘ‚àȘ—àȘźàȘŸàȘ‚ àȘžàȘźàȘžà«àȘŻàȘŸ"</string>
+    <string name="qs_record_issue_start" msgid="2979831312582567056">"àȘ¶àȘ°à«‚ àȘ•àȘ°à«‹"</string>
+    <string name="qs_record_issue_stop" msgid="3531747965741982657">"àȘ°à«‹àȘ•à«‹"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"àȘĄàȘżàȘ”àȘŸàȘ‡àȘž àȘžàȘ‚àȘŹàȘ‚àȘ§à«€ àȘ€àȘźàȘŸàȘ°àȘŸ àȘ…àȘšà«àȘ­àȘ”àȘšàȘŸ àȘ•àȘŻàȘŸ àȘ­àȘŸàȘ—àȘšà«‡ àȘ…àȘžàȘ° àȘ„àȘˆ àȘčàȘ€à«€?"</string>
+    <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"àȘžàȘźàȘžà«àȘŻàȘŸàȘšà«‹ àȘȘ્àȘ°àȘ•àȘŸàȘ° àȘȘàȘžàȘ‚àȘŠ àȘ•àȘ°à«‹"</string>
+    <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"àȘžà«àȘ•્àȘ°à«€àȘš àȘ°à«‡àȘ•à«‹àȘ°à«àȘĄ àȘ•àȘ°à«‹"</string>
+  <string-array name="qs_record_issue_types">
+    <item msgid="2947988124014085798">"àȘȘàȘ°à«àȘ«à«‹àȘ°à«àȘźàȘšà«àȘž"</item>
+    <item msgid="1627504621139124393">"àȘŻà«‚àȘàȘ° àȘ‡àȘšà«àȘŸàȘ°àȘ«à«‡àȘž"</item>
+    <item msgid="8309220355268900335">"àȘŹà«…àȘŸàȘ°à«€"</item>
+  </string-array>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"àȘàȘ•-àȘčàȘŸàȘ„ે àȘ”àȘŸàȘȘàȘ°à«‹ àȘźà«‹àȘĄ"</string>
     <string name="quick_settings_contrast_label" msgid="988087460210159123">"àȘ•à«‹àȘšà«àȘŸà«àȘ°àȘŸàȘžà«àȘŸ"</string>
     <string name="quick_settings_contrast_standard" msgid="2538227821968061832">"àȘžà«àȘŸà«…àȘšà«àȘĄàȘ°à«àȘĄ"</string>
@@ -408,12 +413,9 @@
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • àȘšàȘŸàȘ°à«àȘœ àȘ„àȘˆ àȘ°àȘč્àȘŻà«àȘ‚ àȘ›à«‡ • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>àȘźàȘŸàȘ‚ àȘȘૂàȘ°à«àȘ‚ àȘšàȘŸàȘ°à«àȘœ àȘ„àȘˆ àȘœàȘ¶à«‡"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"àȘ•ૉàȘźà«àȘŻà«àȘšàȘČ àȘŸà«àȘŻà«‚àȘŸà«‰àȘ°àȘżàȘ…àȘČ àȘ¶àȘ°à«‚ àȘ•àȘ°àȘ”àȘŸ àȘźàȘŸàȘŸà«‡ àȘĄàȘŸàȘŹà«‡ àȘžà«àȘ”àȘŸàȘ‡àȘȘ àȘ•àȘ°à«‹"</string>
     <string name="button_to_open_widget_editor" msgid="5599945944349057600">"àȘ”àȘżàȘœà«‡àȘŸ àȘàȘĄàȘżàȘŸàȘ° àȘ–à«‹àȘČો"</string>
-    <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
-    <skip />
-    <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
-    <skip />
-    <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
-    <skip />
+    <string name="button_to_remove_widget" msgid="3948204829181214098">"àȘ•àȘŸàȘąà«€ àȘšàȘŸàȘ–à«‹"</string>
+    <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"àȘ”àȘżàȘœà«‡àȘŸ àȘ‰àȘźà«‡àȘ°à«‹"</string>
+    <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"àȘ„àȘˆ àȘ—àȘŻà«àȘ‚"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"àȘ”àȘȘàȘ°àȘŸàȘ¶àȘ•àȘ°à«àȘ€àȘŸ àȘžà«àȘ”àȘżàȘš àȘ•àȘ°à«‹"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"àȘȘુàȘČàȘĄàȘŸàȘ‰àȘš àȘźà«‡àȘšà«‚"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"àȘ† àȘžàȘ€à«àȘ°àȘźàȘŸàȘ‚àȘšà«€ àȘ€àȘźàȘŸàȘź àȘàȘȘ àȘ…àȘšà«‡ àȘĄà«‡àȘŸàȘŸ àȘ•àȘŸàȘąà«€ àȘšàȘŸàȘ–àȘ”àȘŸàȘźàȘŸàȘ‚ àȘ†àȘ”àȘ¶à«‡."</string>
@@ -867,7 +869,6 @@
     <string name="auto_saver_title" msgid="6873691178754086596">"àȘŹà«…àȘŸàȘ°à«€ àȘžà«‡àȘ”àȘ° àȘ¶à«‡àȘĄà«àȘŻà«‚àȘČ àȘ•àȘ°àȘ”àȘŸ àȘźàȘŸàȘŸà«‡ àȘŸà«…àȘȘ àȘ•àȘ°à«‹"</string>
     <string name="auto_saver_text" msgid="3214960308353838764">"àȘœà«àȘŻàȘŸàȘ°à«‡ àȘŹà«…àȘŸàȘ°à«€ àȘžàȘ‚àȘ­àȘ”àȘżàȘ€ àȘ°à«‚àȘȘે àȘȘૂàȘ°à«€ àȘ„àȘ”àȘŸàȘźàȘŸàȘ‚ àȘčોàȘŻ àȘ€à«àȘŻàȘŸàȘ°à«‡ àȘŹà«…àȘŸàȘ°à«€ àȘžà«‡àȘ”àȘ° àȘšàȘŸàȘČુ àȘ•àȘ°à«‹"</string>
     <string name="no_auto_saver_action" msgid="7467924389609773835">"àȘšàȘŸ, àȘ†àȘ­àȘŸàȘ°"</string>
-    <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string>
     <string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"àȘ‰àȘȘàȘŻà«‹àȘ—àȘźàȘŸàȘ‚ àȘ›à«‡"</string>
     <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"àȘàȘȘ્àȘČàȘżàȘ•ેàȘ¶àȘš àȘ€àȘźàȘŸàȘ°àȘŸ <xliff:g id="TYPES_LIST">%s</xliff:g>àȘšà«‹ àȘ‰àȘȘàȘŻà«‹àȘ— àȘ•àȘ°à«€ àȘ°àȘčી àȘ›à«‡."</string>
     <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"àȘžà«‡àȘŸàȘżàȘ‚àȘ—àȘźàȘŸàȘ‚ àȘšà«‹àȘ‚àȘ§àȘšà«€ àȘĄàȘżàȘ«à«‰àȘČ્àȘŸ àȘàȘȘ àȘžà«‡àȘŸ àȘ•àȘ°à«‹"</string>
     <string name="install_app" msgid="5066668100199613936">"àȘàȘȘ àȘ‡àȘšà«àȘžà«àȘŸà«‰àȘČ àȘ•àȘ°à«‹"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"àȘ¶à«àȘ‚ àȘŹàȘŸàȘč્àȘŻ àȘĄàȘżàȘžà«àȘȘ્àȘČે àȘȘàȘ° àȘźàȘżàȘ°àȘ° àȘ•àȘ°à«€àȘ?"</string>
-    <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"àȘčàȘŸàȘČàȘźàȘŸàȘ‚ àȘĄà«àȘŻà«‚àȘ…àȘČ àȘžà«àȘ•્àȘ°à«€àȘš àȘȘàȘ° àȘšàȘŸàȘČી àȘ°àȘčેàȘČી àȘ•à«‹àȘˆàȘȘàȘŁ àȘȘ્àȘ°àȘ”ૃàȘ€à«àȘ€àȘż àȘ°à«‹àȘ•àȘ”àȘŸàȘźàȘŸàȘ‚ àȘ†àȘ”àȘ¶à«‡"</string>
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"àȘ€àȘźàȘŸàȘ°à«àȘ‚ àȘ‡àȘšàȘ° àȘĄàȘżàȘžà«àȘȘ્àȘČે àȘźàȘżàȘ°àȘ° àȘ•àȘ°àȘ”àȘŸàȘźàȘŸàȘ‚ àȘ†àȘ”àȘ¶à«‡. àȘ€àȘźàȘŸàȘ°à«àȘ‚ àȘ«à«àȘ°àȘšà«àȘŸ àȘĄàȘżàȘžà«àȘȘ્àȘČે àȘŹàȘ‚àȘ§ àȘ•àȘ°àȘ”àȘŸàȘźàȘŸàȘ‚ àȘ†àȘ”àȘ¶à«‡."</string>
     <string name="mirror_display" msgid="2515262008898122928">"àȘźàȘżàȘ°àȘ° àȘĄàȘżàȘžà«àȘȘ્àȘČે"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"àȘ›à«‹àȘĄà«€ àȘŠà«‹"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Display àȘ•àȘšà«‡àȘ•્àȘŸà«‡àȘĄ àȘ›à«‡"</string>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index 6db1f08..f8c78a9 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -330,12 +330,17 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"à€žà„à€•à„à€°à„€à€š à€°à€żà€•à„‰à€°à„à€Ąà€°"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"à€¶à„à€°à„‚ à€•à€°à„‡à€‚"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"à€°à„‹à€•à„‡à€‚"</string>
-    <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
-    <skip />
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"à€žà€źà€žà„à€Żà€Ÿ à€°à€żà€•à„‰à€°à„à€Ą à€•à€°à„‡à€‚"</string>
+    <string name="qs_record_issue_start" msgid="2979831312582567056">"à€¶à„à€°à„‚ à€•à€°à„‡à€‚"</string>
+    <string name="qs_record_issue_stop" msgid="3531747965741982657">"à€°à„‹à€•à„‡à€‚"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"à€†à€Șà€•à„‡ à€Ąà€żà€”à€Ÿà€‡à€ž à€•à„€ à€•à„Œà€šà€žà„€ à€žà„à€”à€żà€§à€Ÿ à€Șà€° à€…à€žà€° à€Șà€Ąà€Œà€Ÿ à€„à€Ÿ?"</string>
+    <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"à€žà€źà€žà„à€Żà€Ÿ à€•à€Ÿ à€Ÿà€Ÿà€‡à€Ș à€šà„à€šà„‡à€‚"</string>
+    <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"à€žà„à€•à„à€°à„€à€š à€°à€żà€•à„‰à€°à„à€Ąà€°"</string>
+  <string-array name="qs_record_issue_types">
+    <item msgid="2947988124014085798">"à€Șà€°à€«à€Œà„‰à€°à„à€źà„‡à€‚à€ž"</item>
+    <item msgid="1627504621139124393">"à€Żà„‚à€œà€Œà€° à€‡à€‚à€Ÿà€°à€«à€Œà„‡à€ž"</item>
+    <item msgid="8309220355268900335">"à€Źà„ˆà€Ÿà€°à„€"</item>
+  </string-array>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"à€”à€š-à€čà„ˆà€‚à€Ąà„‡à€Ą à€źà„‹à€Ą"</string>
     <string name="quick_settings_contrast_label" msgid="988087460210159123">"à€•à€‚à€Ÿà„à€°à€Ÿà€žà„à€Ÿ"</string>
     <string name="quick_settings_contrast_standard" msgid="2538227821968061832">"à€žà„à€Ÿà„ˆà€‚à€Ąà€°à„à€Ą"</string>
@@ -408,12 +413,9 @@
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • à€šà€Ÿà€°à„à€œ à€čà„‹ à€°à€čà€Ÿ à€čà„ˆ • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> à€źà„‡à€‚ à€Șà„‚à€°à€Ÿ à€šà€Ÿà€°à„à€œ à€čà„‹ à€œà€Ÿà€à€—à€Ÿ"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"à€•à€źà„à€Żà„‚à€šà€żà€Ÿà„€ à€Ÿà„à€Żà„‚à€Ÿà„‹à€°à€żà€Żà€Č à€¶à„à€°à„‚ à€•à€°à€šà„‡ à€•à„‡ à€Čà€żà€, à€Źà€Ÿà€ˆà€‚ à€“à€° à€žà„à€”à€Ÿà€‡à€Ș à€•à€°à„‡à€‚"</string>
     <string name="button_to_open_widget_editor" msgid="5599945944349057600">"à€”à€żà€œà„‡à€Ÿ à€à€Ąà€żà€Ÿà€° à€–à„‹à€Čà„‡à€‚"</string>
-    <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
-    <skip />
-    <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
-    <skip />
-    <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
-    <skip />
+    <string name="button_to_remove_widget" msgid="3948204829181214098">"à€čà€Ÿà€Ÿà€à€‚"</string>
+    <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"à€”à€żà€œà„‡à€Ÿ à€œà„‹à€Ąà€Œà„‡à€‚"</string>
+    <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"à€čà„‹ à€—à€Żà€Ÿ"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"à€‰à€Șà€Żà„‹à€—à€•à€°à„à€€à€Ÿ à€Źà€Šà€Čà„‡à€‚"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"à€Șà„à€Čà€Ąà€Ÿà€‰à€š à€źà„‡à€šà„à€Żà„‚"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"à€‡à€ž à€žà„‡à€¶à€š à€•à„‡ à€žà€­à„€ à€à€Șà„à€Čà€żà€•à„‡à€¶à€š à€”à€° à€Ąà„‡à€Ÿà€Ÿ à€•à„‹ à€čà€Ÿà€Ÿ à€Šà€żà€Żà€Ÿ à€œà€Ÿà€à€—à€Ÿ."</string>
@@ -867,7 +869,6 @@
     <string name="auto_saver_title" msgid="6873691178754086596">"à€Źà„ˆà€Ÿà€°à„€ à€žà„‡à€”à€° à€¶à„‡à€Ąà„à€Żà„‚à€Č à€•à€°à€šà„‡ à€•à„‡ à€Čà€żà€ à€Ÿà„ˆà€Ș à€•à€°à„‡à€‚"</string>
     <string name="auto_saver_text" msgid="3214960308353838764">"à€œà€Ź à€Źà„ˆà€Ÿà€°à„€ à€–à€€à„à€ź à€čà„‹à€šà„‡ à€”à€Ÿà€Čà„€ à€čà„‹ à€€à€Ź \'à€Źà„ˆà€Ÿà€°à„€ à€žà„‡à€”à€°\' à€šà€Ÿà€Čà„‚ à€•à€°à„‡à€‚"</string>
     <string name="no_auto_saver_action" msgid="7467924389609773835">"à€œà„€ à€šà€čà„€à€‚, à€¶à„à€•à„à€°à€żà€Żà€Ÿ"</string>
-    <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string>
     <string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"à€‡à€žà„à€€à„‡à€źà€Ÿà€Č à€•à€żà€Żà€Ÿ à€œà€Ÿ à€°à€čà€Ÿ à€čà„ˆ"</string>
     <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"à€à€Șà„à€Čà€żà€•à„‡à€¶à€š à€†à€Șà€•à„€ <xliff:g id="TYPES_LIST">%s</xliff:g> à€•à€Ÿ à€‡à€žà„à€€à„‡à€źà€Ÿà€Č à€•à€° à€°à€čà„‡ à€čà„ˆà€‚."</string>
     <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"à€žà„‡à€Ÿà€żà€‚à€— à€źà„‡à€‚ à€œà€Ÿà€•à€°, à€šà„‹à€Ÿ à€Čà„‡à€šà„‡ à€•à„€ à€žà„à€”à€żà€§à€Ÿ à€Šà„‡à€šà„‡ à€”à€Ÿà€Čà„‡ à€à€Șà„à€Čà€żà€•à„‡à€¶à€š à€•à„‹ à€Ąà€żà€«à€Œà„‰à€Čà„à€Ÿ à€•à„‡ à€€à„Œà€° à€Șà€° à€žà„‡à€Ÿ à€•à€°à„‡à€‚"</string>
     <string name="install_app" msgid="5066668100199613936">"à€à€Șà„à€Čà€żà€•à„‡à€¶à€š à€‡à€‚à€žà„à€Ÿà„‰à€Č à€•à€°à„‡à€‚"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"à€•à„à€Żà€Ÿ à€†à€Șà€•à„‹ à€•à€żà€žà„€ à€Źà€Ÿà€čà€°à„€ à€Ąà€żà€”à€Ÿà€‡à€ž à€Șà€° à€Ąà€żà€žà€Șà„à€Čà„‡ à€•à€°à€šà€Ÿ à€čà„ˆ?"</string>
-    <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"à€Ąà„à€Żà„‚à€…à€Č à€žà„à€•à„à€°à„€à€š à€Șà€° à€šà€Č à€°à€čà„€ à€—à€€à€żà€”à€żà€§à€ż à€Źà€‚à€Š à€čà„‹ à€œà€Ÿà€à€—à„€"</string>
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"à€†à€Șà€•à„‡ à€«à€Œà„‹à€š à€•à„‡ à€‡à€šà€° à€Ąà€żà€žà€Șà„à€Čà„‡ à€•à„€ à€žà„à€•à„à€°à„€à€š à€¶à„‡à€Żà€° à€•à„€ à€œà€Ÿà€à€—à„€. à€«à€Œà„à€°à€‚à€Ÿ à€Ąà€żà€žà€Șà„à€Čà„‡ à€•à„‹ à€Źà€‚à€Š à€•à€° à€Šà€żà€Żà€Ÿ à€œà€Ÿà€à€—à€Ÿ."</string>
     <string name="mirror_display" msgid="2515262008898122928">"à€Ąà€żà€žà€Șà„à€Čà„‡ à€•à€°à„‡à€‚"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"à€–à€Ÿà€°à€żà€œ à€•à€°à„‡à€‚"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"à€Ąà€żà€žà€Șà„à€Čà„‡ à€•à€šà„‡à€•à„à€Ÿ à€•à€żà€Żà€Ÿ à€—à€Żà€Ÿ"</string>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index d14f9a7..0803aeb 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -330,12 +330,17 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Snimanje zaslona"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Početak"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Zaustavi"</string>
-    <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
-    <skip />
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"ZabiljeĆŸi poteškoću"</string>
+    <string name="qs_record_issue_start" msgid="2979831312582567056">"Početak"</string>
+    <string name="qs_record_issue_stop" msgid="3531747965741982657">"Zaustavi"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Na koji dio doĆŸivljaja na uređaju to utjecalo?"</string>
+    <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Odaberite vrstu problema"</string>
+    <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Snimanje zaslona"</string>
+  <string-array name="qs_record_issue_types">
+    <item msgid="2947988124014085798">"Izvedba"</item>
+    <item msgid="1627504621139124393">"Korisničko sučelje"</item>
+    <item msgid="8309220355268900335">"Baterija"</item>
+  </string-array>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Način rada jednom rukom"</string>
     <string name="quick_settings_contrast_label" msgid="988087460210159123">"Kontrast"</string>
     <string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Standardni"</string>
@@ -408,12 +413,9 @@
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • punjenje • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> do napunjenosti"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Prijeđite prstom ulijevo da biste pokrenuli zajednički vodič"</string>
     <string name="button_to_open_widget_editor" msgid="5599945944349057600">"Otvaranje alata za uređivanje widgeta"</string>
-    <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
-    <skip />
-    <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
-    <skip />
-    <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
-    <skip />
+    <string name="button_to_remove_widget" msgid="3948204829181214098">"Ukloni"</string>
+    <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Dodaj widget"</string>
+    <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Gotovo"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Promjena korisnika"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"padajući izbornik"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Izbrisat će se sve aplikacije i podaci u ovoj sesiji."</string>
@@ -867,7 +869,6 @@
     <string name="auto_saver_title" msgid="6873691178754086596">"Dodirnite za zakazivanje štednje baterije"</string>
     <string name="auto_saver_text" msgid="3214960308353838764">"Uključite kad bi se baterija mogla isprazniti"</string>
     <string name="no_auto_saver_action" msgid="7467924389609773835">"Ne, hvala"</string>
-    <string name="heap_dump_tile_name" msgid="2464189856478823046">"Izdvoji mem. SysUI-a"</string>
     <string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"U upotrebi"</string>
     <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Aplikacije upotrebljavaju <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
     <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Postavite zadanu aplikaciju za bilješke u postavkama"</string>
     <string name="install_app" msgid="5066668100199613936">"Instalacija"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Ćœelite li zrcaliti na vanjski zaslon?"</string>
-    <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"Sva aktivnost dvostrukog zaslona koja je u tijeku će se zaustaviti"</string>
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Unutarnji zaslon bit će zrcaljen. Prednji zaslon bit će isključen."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Zrcaljenje zaslona"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Odbaci"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Zaslon je povezan"</string>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index 1c4e00f..fd9d832 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -330,12 +330,17 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"KépernyƑrögzítés"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Indítás"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Leállítás"</string>
-    <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
-    <skip />
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"Probléma rögzítése"</string>
+    <string name="qs_record_issue_start" msgid="2979831312582567056">"Indítás"</string>
+    <string name="qs_record_issue_stop" msgid="3531747965741982657">"Leállítás"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Az eszközhasználati élmény mely része érintett?"</string>
+    <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Problématípus kiválasztása"</string>
+    <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"KépernyƑrögzítés"</string>
+  <string-array name="qs_record_issue_types">
+    <item msgid="2947988124014085798">"Teljesítmény"</item>
+    <item msgid="1627504621139124393">"KezelƑfelület"</item>
+    <item msgid="8309220355268900335">"Akkumulátor"</item>
+  </string-array>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Egykezes mód"</string>
     <string name="quick_settings_contrast_label" msgid="988087460210159123">"Kontraszt"</string>
     <string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Normál"</string>
@@ -408,12 +413,9 @@
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Töltés • A teljes töltöttségig: <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Csúsztasson gyorsan balra a közösségi útmutató elindításához"</string>
     <string name="button_to_open_widget_editor" msgid="5599945944349057600">"A modulszerkesztƑ megnyitása"</string>
-    <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
-    <skip />
-    <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
-    <skip />
-    <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
-    <skip />
+    <string name="button_to_remove_widget" msgid="3948204829181214098">"Eltávolítás"</string>
+    <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Modul hozzáadása"</string>
+    <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Kész"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Felhasználóváltás"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"lehúzható menü"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"A munkamenetben található összes alkalmazás és adat törlƑdni fog."</string>
@@ -867,7 +869,6 @@
     <string name="auto_saver_title" msgid="6873691178754086596">"Koppintson az akkumulátorkímélƑ mód ütemezéséhez"</string>
     <string name="auto_saver_text" msgid="3214960308353838764">"Kapcsolja be, ha az akkumulátor hamarosan lemerül"</string>
     <string name="no_auto_saver_action" msgid="7467924389609773835">"Nem"</string>
-    <string name="heap_dump_tile_name" msgid="2464189856478823046">"SysUI-memória-kiírás"</string>
     <string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"Használatban"</string>
     <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Több alkalmazás használja a következƑket: <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
     <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Állítson be alapértelmezett jegyzetkészítƑ alkalmazást a Beállításokban"</string>
     <string name="install_app" msgid="5066668100199613936">"Alkalmazás telepítése"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Tükrözi a kijelzƑt a külsƑ képernyƑre?"</string>
-    <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"A jelenleg futó kétképernyƑs tevékenységek le fognak állni"</string>
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"A belsƑ kijelzƑ tükrözve lesz. Az elülsƑ kijelzƑ ki lesz kapcsolva."</string>
     <string name="mirror_display" msgid="2515262008898122928">"KijelzƑ tükrözése"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Elvetés"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"KijelzƑ csatlakoztatva"</string>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index 1cfed6d..a9252e2 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -330,12 +330,17 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Ô·ŐŻÖ€ŐĄŐ¶Ő« ŐżŐ„ŐœŐĄŐŁÖ€ŐžÖ‚ŐŽ"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"ŐŐŻŐœŐ„ŐŹ"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"ԿենգնՄցնՄՏ"</string>
-    <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
-    <skip />
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"ՁեՔնեգրՄՏ"</string>
+    <string name="qs_record_issue_start" msgid="2979831312582567056">"ŐŐŻŐœŐ„ŐŹ"</string>
+    <string name="qs_record_issue_stop" msgid="3531747965741982657">"ԿենգնՄցնՄՏ"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"ŐŐĄÖ€Ö„Ő« վ՞ր ŐŽŐĄŐœŐ« Ő°Ő„Őż Ő§ ŐŻŐĄŐșŐŸŐĄŐź Ő­Ő¶Ő€Ő«Ö€ŐšÖ‰"</string>
+    <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"ÔžŐ¶ŐżÖ€Ő„Ö„ Ő­Ő¶Ő€Ö€Ő« ŐżŐ„ŐœŐĄŐŻŐš"</string>
+    <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Ô·ŐŻÖ€ŐĄŐ¶Ő« ŐżŐ„ŐœŐĄŐŁÖ€ŐžÖ‚ŐŽ"</string>
+  <string-array name="qs_record_issue_types">
+    <item msgid="2947988124014085798">"Ô±Ő·Ő­ŐĄŐżŐĄŐ¶Ö„"</item>
+    <item msgid="1627504621139124393">"Ő„Ő«Ő»Ő„Ö€Ő„Őœ"</item>
+    <item msgid="8309220355268900335">"Մարտկվց"</item>
+  </string-array>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Ő„Ő„ŐŻ Ő±Ő„ŐŒÖ„Ő« ŐŒŐ„ŐȘŐ«ŐŽ"</string>
     <string name="quick_settings_contrast_label" msgid="988087460210159123">"ÔżŐžŐ¶ŐżÖ€ŐĄŐœŐż"</string>
     <string name="quick_settings_contrast_standard" msgid="2538227821968061832">"ŐŐžŐŸŐžÖ€ŐĄŐŻŐĄŐ¶"</string>
@@ -408,12 +413,9 @@
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ÔŒŐ«ÖÖ„ŐĄŐŸŐžÖ€ŐžÖ‚ŐŽ • ՄնեցՄՏ Ő§ <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"ÔčŐ„Ö€Ő©Ő„Ö„ Ő±ŐĄŐ­Ő վւŐČՄց՞ւՔց՚ ŐŁŐžÖ€ŐźŐĄÖ€ŐŻŐ„ŐŹŐžÖ‚ հածար"</string>
     <string name="button_to_open_widget_editor" msgid="5599945944349057600">"ÔČեցՄՏ ŐŸŐ«Ő»Ő„Ő©Ő¶Ő„Ö€Ő« Ő­ŐŽŐąŐĄŐŁÖ€Ő«ŐčŐš"</string>
-    <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
-    <skip />
-    <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
-    <skip />
-    <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
-    <skip />
+    <string name="button_to_remove_widget" msgid="3948204829181214098">"Ő€Ő„ŐŒŐĄÖŐ¶Ő„ŐŹ"</string>
+    <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Ô±ŐŸŐ„ŐŹŐĄÖŐ¶Ő„ŐŹ ŐŸŐ«Ő»Ő„Ő©"</string>
+    <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"ŐŠŐĄŐżÖ€ŐĄŐœŐż Ő§"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Ô±Ő¶Ő»ŐĄŐżŐ„ŐŹ Ö…ŐŁŐżŐŸŐžŐČŐ«Ő¶"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"Ő«Ő»Ő¶ŐžŐČ ŐšŐ¶ŐżÖ€ŐĄÖŐĄŐ¶ŐŻ"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Ô±Ő”Őœ ŐĄŐ·Ő­ŐĄŐżŐĄŐ·Ö€Ő»ŐĄŐ¶Ő« ձվլվր Ő°ŐĄŐŸŐ„ŐŹŐŸŐĄŐźŐ¶Ő„Ö€Ő¶ վւ ŐżŐŸŐ”ŐĄŐŹŐ¶Ő„Ö€Őš ŐŻŐ»Ő¶Ő»ŐŸŐ„Ő¶:"</string>
@@ -867,7 +869,6 @@
     <string name="auto_saver_title" msgid="6873691178754086596">"ՀŐșŐ„Ö„Ő Վերտկ՞ցի ŐżŐ¶ŐżŐ„ŐœŐŽŐĄŐ¶ ŐŒŐ„ŐȘŐ«ŐŽŐš ŐŻŐĄÖ€ŐŁŐĄŐŸŐžÖ€Ő„ŐŹŐžÖ‚ հածար"</string>
     <string name="auto_saver_text" msgid="3214960308353838764">"ՄիեցնՄՏ Ő§Ő¶Ő„Ö€ŐŁŐĄŐ­Ő¶ŐĄŐ”ŐžÖ‚ŐŽŐš, Ő„Ö€Őą Վերտկ՞ցի Տիցք՚ ŐŁÖ€Ő„Ő©Ő„ ŐœŐșŐĄŐŒŐŸŐĄŐź Ő§"</string>
     <string name="no_auto_saver_action" msgid="7467924389609773835">"ՈŐč"</string>
-    <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string>
     <string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"Ő•ŐŁŐżŐĄŐŁŐžÖ€ŐźŐŸŐžÖ‚ŐŽ Ő§"</string>
     <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Ő€ŐĄŐŸŐ„ŐŹŐŸŐĄŐźŐ¶Ő„Ö€Ő¶ օգտագվրռվւծ Ő„Ő¶ Ő±Ő„Ö€ <xliff:g id="TYPES_LIST">%s</xliff:g>:"</string>
     <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,8 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"ÔżŐĄÖ€ŐŁŐĄŐŸŐžÖ€Ő„Ö„ Ő¶Ő·ŐžÖ‚ŐŽŐ¶Ő„Ö€Ő« ŐŻŐĄŐ¶Ő­ŐĄŐ€Ö€ŐŸŐĄŐź Ő°ŐĄŐŸŐ„ŐŹŐŸŐĄŐź ÔżŐĄÖ€ŐŁŐĄŐŸŐžÖ€ŐžÖ‚ŐŽŐ¶Ő„Ö€ŐžÖ‚ŐŽ"</string>
     <string name="install_app" msgid="5066668100199613936">"ŐŐ„ŐČŐĄŐ€Ö€Ő„ŐŹ Ő°ŐĄŐŸŐ„ŐŹŐŸŐĄŐźŐš"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Ő€ŐĄŐ”Ő„ŐŹŐĄŐșŐĄŐżŐłŐ„Ő¶Ő„ŐžŐŹ ŐĄÖ€ŐżŐĄÖ„Ő«Ő¶ Ő§ŐŻÖ€ŐĄŐ¶Ő«Ő¶"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (2917631104216376315) -->
-    <skip />
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Ő†Ő„Ö€Ö„Ő«Ő¶ Ő§ŐŻÖ€ŐĄŐ¶Őš ŐŻŐ°ŐĄŐ”Ő„ŐŹŐĄŐșŐĄŐżŐłŐ„Ő¶ŐŸŐ«Ö‰ Ô±ŐŒŐ»Ö‡Ő« Ő§ŐŻÖ€ŐĄŐ¶Őš ŐŻŐĄŐ¶Ő»ŐĄŐżŐŸŐ«Ö‰"</string>
     <string name="mirror_display" msgid="2515262008898122928">"Ő€ŐĄŐ”Ő„ŐŹŐĄŐșŐĄŐżŐłŐ„Ő¶Ő„ŐŹ Ő§ŐŻÖ€ŐĄŐ¶Őš"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Ő“ŐĄŐŻŐ„ŐŹ"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Ô·ŐŻÖ€ŐĄŐ¶Őš Վիեցե՟ Ő§"</string>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index 65c7c1d..8ef809a 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -330,12 +330,17 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Perekam layar"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Mulai"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Berhenti"</string>
-    <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
-    <skip />
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"Mencatat Masalah"</string>
+    <string name="qs_record_issue_start" msgid="2979831312582567056">"Mulai"</string>
+    <string name="qs_record_issue_stop" msgid="3531747965741982657">"Berhenti"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Bagian pengalaman perangkat mana yang terpengaruh?"</string>
+    <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Pilih jenis masalah"</string>
+    <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Perekaman layar"</string>
+  <string-array name="qs_record_issue_types">
+    <item msgid="2947988124014085798">"Performa"</item>
+    <item msgid="1627504621139124393">"Antarmuka Pengguna"</item>
+    <item msgid="8309220355268900335">"Baterai"</item>
+  </string-array>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Mode satu tangan"</string>
     <string name="quick_settings_contrast_label" msgid="988087460210159123">"Kontras"</string>
     <string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Standar"</string>
@@ -408,12 +413,9 @@
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Mengisi daya • Penuh dalam waktu <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Geser ke kiri untuk memulai tutorial komunal"</string>
     <string name="button_to_open_widget_editor" msgid="5599945944349057600">"Buka editor widget"</string>
-    <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
-    <skip />
-    <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
-    <skip />
-    <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
-    <skip />
+    <string name="button_to_remove_widget" msgid="3948204829181214098">"Hapus"</string>
+    <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Tambahkan widget"</string>
+    <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Selesai"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Beralih pengguna"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menu pulldown"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Semua aplikasi dan data dalam sesi ini akan dihapus."</string>
@@ -867,7 +869,6 @@
     <string name="auto_saver_title" msgid="6873691178754086596">"Ketuk untuk menjadwalkan Penghemat Baterai"</string>
     <string name="auto_saver_text" msgid="3214960308353838764">"Aktifkan jika daya baterai kemungkinan akan habis"</string>
     <string name="no_auto_saver_action" msgid="7467924389609773835">"Tidak, terima kasih"</string>
-    <string name="heap_dump_tile_name" msgid="2464189856478823046">"Hapus Heap SysUI"</string>
     <string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"Sedang digunakan"</string>
     <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Aplikasi menggunakan <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
     <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Setel aplikasi catatan default di Setelan"</string>
     <string name="install_app" msgid="5066668100199613936">"Instal aplikasi"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Cerminkan ke layar eksternal?"</string>
-    <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"Aktivitas dua layar yang sedang berjalan akan dihentikan"</string>
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Layar dalam akan dicerminkan. Layar depan akan dinonaktifkan."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Cerminkan layar"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Tutup"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Layar terhubung"</string>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index 29b149d..cef3285 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -330,12 +330,17 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Skjáupptaka"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Hefja"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Stöðva"</string>
-    <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
-    <skip />
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"Vandamál tengt upptöku"</string>
+    <string name="qs_record_issue_start" msgid="2979831312582567056">"Byrja"</string>
+    <string name="qs_record_issue_stop" msgid="3531747965741982657">"Stöðva"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Hvað í tækjaupplifuninni varð fyrir áhrifum?"</string>
+    <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Veldu gerð vandamáls"</string>
+    <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Skjáupptaka"</string>
+  <string-array name="qs_record_issue_types">
+    <item msgid="2947988124014085798">"Afköst"</item>
+    <item msgid="1627504621139124393">"Notandaviðmót"</item>
+    <item msgid="8309220355268900335">"Rafhlaða"</item>
+  </string-array>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Einhent stilling"</string>
     <string name="quick_settings_contrast_label" msgid="988087460210159123">"Birtuskil"</string>
     <string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Staðlað"</string>
@@ -408,12 +413,9 @@
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Í hleðslu • Full hleðsla eftir <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Strjúktu til vinstri til að hefja samfélagsleiðsögnina"</string>
     <string name="button_to_open_widget_editor" msgid="5599945944349057600">"Opna græjuritilinn"</string>
-    <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
-    <skip />
-    <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
-    <skip />
-    <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
-    <skip />
+    <string name="button_to_remove_widget" msgid="3948204829181214098">"Fjarlægja"</string>
+    <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Bæta græju við"</string>
+    <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Lokið"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Skipta um notanda"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"Fellivalmynd"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Öllum forritum og gögnum í þessari lotu verður eytt."</string>
@@ -867,7 +869,6 @@
     <string name="auto_saver_title" msgid="6873691178754086596">"Pikka til að stilla rafhlöðusparnað"</string>
     <string name="auto_saver_text" msgid="3214960308353838764">"Kveikja þegar rafhlaða er við það að klárast"</string>
     <string name="no_auto_saver_action" msgid="7467924389609773835">"Nei, takk"</string>
-    <string name="heap_dump_tile_name" msgid="2464189856478823046">"Vista SysUI-gögn"</string>
     <string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"Í notkun"</string>
     <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Forrit eru að nota <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
     <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Stilltu sjálfgefið glósuforrit í stillingunum"</string>
     <string name="install_app" msgid="5066668100199613936">"Setja upp forrit"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Spegla yfir á ytri skjá?"</string>
-    <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"Virkni sem kann að vera í gangi á tveimur skjám verður stöðvuð"</string>
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Innri skjárinn þinn verður speglaður. Slökkt verður á framskjánum þínum."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Spegla skjá"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Hunsa"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Skjár tengdur"</string>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 733b0b6..c5079fe 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -330,12 +330,17 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Registrazione dello schermo"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Inizia"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Interrompi"</string>
-    <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
-    <skip />
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"Registra problema"</string>
+    <string name="qs_record_issue_start" msgid="2979831312582567056">"Avvia"</string>
+    <string name="qs_record_issue_stop" msgid="3531747965741982657">"Interrompi"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Quali problemi ha l\'esperienza del dispositivo?"</string>
+    <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Seleziona il tipo di problema"</string>
+    <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Regis. dello schermo"</string>
+  <string-array name="qs_record_issue_types">
+    <item msgid="2947988124014085798">"Prestazioni"</item>
+    <item msgid="1627504621139124393">"Interfaccia utente"</item>
+    <item msgid="8309220355268900335">"Batteria"</item>
+  </string-array>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modalità a una mano"</string>
     <string name="quick_settings_contrast_label" msgid="988087460210159123">"Contrasto"</string>
     <string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Standard"</string>
@@ -408,12 +413,9 @@
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • In carica • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> alla ricarica completa"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Scorri a sinistra per iniziare il tutorial della community"</string>
     <string name="button_to_open_widget_editor" msgid="5599945944349057600">"Apri l\'editor del widget"</string>
-    <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
-    <skip />
-    <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
-    <skip />
-    <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
-    <skip />
+    <string name="button_to_remove_widget" msgid="3948204829181214098">"Rimuovi"</string>
+    <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Aggiungi widget"</string>
+    <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Fine"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Cambio utente"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menu a discesa"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Tutte le app e i dati di questa sessione verranno eliminati."</string>
@@ -867,7 +869,6 @@
     <string name="auto_saver_title" msgid="6873691178754086596">"Tocca per programmare il Risparmio energetico"</string>
     <string name="auto_saver_text" msgid="3214960308353838764">"Attiva questa funzionalità se è probabile che la batteria si scarichi"</string>
     <string name="no_auto_saver_action" msgid="7467924389609773835">"No, grazie"</string>
-    <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump heap SysUI"</string>
     <string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"In uso"</string>
     <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Le app stanno usando <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
     <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Imposta l\'app per le note predefinita nelle Impostazioni"</string>
     <string name="install_app" msgid="5066668100199613936">"Installa app"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Vuoi eseguire il mirroring al display esterno?"</string>
-    <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"Qualsiasi attività in modalità Dual Screen in esecuzione viene interrotta"</string>
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Verrà eseguito il mirroring del tuo display interno. Il tuo display frontale verrà spento."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Esegui il mirroring del display"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Chiudi"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Display collegato"</string>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 7072a32..486b22f 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -330,12 +330,17 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Ś”Ś§ŚœŚ˜ŚȘ Ś”ŚžŚĄŚš"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Ś”ŚȘŚ—ŚœŚ”"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"ŚąŚŠŚ™ŚšŚ”"</string>
-    <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
-    <skip />
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"ŚȘŚ™ŚąŚ•Ś“ Ś”Ś‘ŚąŚ™Ś”"</string>
+    <string name="qs_record_issue_start" msgid="2979831312582567056">"Ś”ŚȘŚ—ŚœŚ”"</string>
+    <string name="qs_record_issue_stop" msgid="3531747965741982657">"ŚąŚŠŚ™ŚšŚ”"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"ڐڙږڔ Ś—ŚœŚ§ ڑڗڕڕڙڙŚȘ Ś”Ś©Ś™ŚžŚ•Ś© کڜښ Ś‘ŚžŚ›Ś©Ś™Śš Ś”Ś•Ś©Ś€Śą?"</string>
+    <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Ś‘Ś—Ś™ŚšŚ” Ś‘ŚĄŚ•Ś’ Ś”Ś‘ŚąŚ™Ś”"</string>
+    <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Ś”Ś§ŚœŚ˜ŚȘ Ś”ŚžŚĄŚš"</string>
+  <string-array name="qs_record_issue_types">
+    <item msgid="2947988124014085798">"Ś‘Ś™ŚŠŚ•ŚąŚ™Ś"</item>
+    <item msgid="1627504621139124393">"ŚžŚžŚ©Ś§ ŚžŚ©ŚȘŚžŚ©"</item>
+    <item msgid="8309220355268900335">"ŚĄŚ•ŚœŚœŚ”"</item>
+  </string-array>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"ŚžŚŠŚ‘ Ś©Ś™ŚžŚ•Ś© ڑڙړ ڐڗŚȘ"</string>
     <string name="quick_settings_contrast_label" msgid="988087460210159123">"ڠڙڒڕړڙڕŚȘ"</string>
     <string name="quick_settings_contrast_standard" msgid="2538227821968061832">"ŚšŚ’Ś™ŚœŚ”"</string>
@@ -408,12 +413,9 @@
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Ś‘Ś˜ŚąŚ™Ś Ś” • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> ŚąŚ“ ŚœŚĄŚ™Ś•Ś"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"ŚŚ€Ś©Śš ŚœŚ”Ś—ŚœŚ™Ś§ Ś©ŚžŚŚœŚ” ڛړڙ ŚœŚ”Ś€ŚąŚ™Śœ ڐŚȘ Ś”ŚžŚ“ŚšŚ™Śš Ś”ŚžŚ©Ś•ŚȘŚŁ"</string>
     <string name="button_to_open_widget_editor" msgid="5599945944349057600">"Ś€ŚȘڙڗڔ کڜ Ś”Ś›ŚœŚ™ ŚœŚąŚšŚ™Ś›ŚȘ ڕڕڙړڒ\'Ś˜Ś™Ś"</string>
-    <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
-    <skip />
-    <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
-    <skip />
-    <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
-    <skip />
+    <string name="button_to_remove_widget" msgid="3948204829181214098">"Ś”ŚĄŚšŚ”"</string>
+    <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Ś”Ś•ŚĄŚ€ŚȘ ڕڕڙړڒ\'ژ"</string>
+    <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"ŚĄŚ™Ś•Ś"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Ś”Ś—ŚœŚ€ŚȘ ŚžŚ©ŚȘŚžŚ©"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ŚȘŚ€ŚšŚ™Ś˜ Ś‘ŚžŚ©Ś™Ś›Ś” ŚœŚžŚ˜Ś”"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Ś›Śœ Ś”ŚŚ€ŚœŚ™Ś§ŚŠŚ™Ś•ŚȘ ڕڔڠŚȘŚ•Ś Ś™Ś Ś‘ŚĄŚ©ŚŸ ڔږڔ Ś™Ś™ŚžŚ—Ś§Ś•."</string>
@@ -867,7 +869,6 @@
     <string name="auto_saver_title" msgid="6873691178754086596">"ڙک ŚœŚ”Ś§Ś™Ś© ڛړڙ ڜŚȘŚ–ŚžŚŸ ڐŚȘ ŚžŚŠŚ‘ Ś”Ś—Ś™ŚĄŚ›Ś•ŚŸ Ś‘ŚĄŚ•ŚœŚœŚ”"</string>
     <string name="auto_saver_text" msgid="3214960308353838764">"ŚžŚ•ŚžŚœŚ„ ŚœŚ”Ś€ŚąŚ™Śœ ڐŚȘ Ś”ŚȘڛڕڠڔ ڛکڙک ŚĄŚ‘Ś™ŚšŚ•ŚȘ ڒڑڕڔڔ Ś©Ś”ŚĄŚ•ŚœŚœŚ” ŚȘŚȘŚšŚ•Ś§ŚŸ"</string>
     <string name="no_auto_saver_action" msgid="7467924389609773835">"ڜڐ ŚȘڕړڔ"</string>
-    <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string>
     <string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"Ś‘Ś©Ś™ŚžŚ•Ś©"</string>
     <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"ŚŚ€ŚœŚ™Ś§ŚŠŚ™Ś•ŚȘ ŚžŚ©ŚȘŚžŚ©Ś•ŚȘ Ś‘<xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
     <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -938,7 +939,7 @@
     <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Ś”ŚąŚ‘ŚšŚ” ŚžŚ”Ś©Ś•ŚœŚ™Ś™Ś Ś•Ś”ŚŠŚ’Ś”"</string>
     <string name="accessibility_floating_button_action_remove_menu" msgid="6730432848162552135">"Ś”ŚĄŚšŚ”"</string>
     <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"Ś”Ś—ŚœŚ€ŚȘ ŚžŚŠŚ‘"</string>
-    <string name="quick_controls_title" msgid="6839108006171302273">"ڀڧړڙ ŚžŚ›Ś©Ś™ŚšŚ™Ś"</string>
+    <string name="quick_controls_title" msgid="6839108006171302273">"ŚœŚ—ŚŠŚ Ś™ ŚžŚ›Ś©Ś™ŚšŚ™Ś"</string>
     <string name="controls_providers_title" msgid="6879775889857085056">"ڙک ŚœŚ‘Ś—Ś•Śš ŚŚ€ŚœŚ™Ś§ŚŠŚ™Ś” ڛړڙ ŚœŚ”Ś•ŚĄŚ™ŚŁ Ś€Ś§Ś“Ś™Ś"</string>
     <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{Ś Ś•ŚĄŚŁ ŚŚžŚŠŚąŚ™ Ś‘Ś§ŚšŚ” ڐڗړ (#).}one{Ś Ś•ŚĄŚ€Ś• # ŚŚžŚŠŚąŚ™ Ś‘Ś§ŚšŚ”.}two{Ś Ś•ŚĄŚ€Ś• # ŚŚžŚŠŚąŚ™ Ś‘Ś§ŚšŚ”.}other{Ś Ś•ŚĄŚ€Ś• # ŚŚžŚŠŚąŚ™ Ś‘Ś§ŚšŚ”.}}"</string>
     <string name="controls_removed" msgid="3731789252222856959">"Ś”Ś•ŚĄŚš"</string>
@@ -1207,7 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"ŚŠŚšŚ™Śš ŚœŚ”Ś’Ś“Ś™Śš ڐŚȘ ŚŚ€ŚœŚ™Ś§ŚŠŚ™Ś™ŚȘ Ś‘ŚšŚ™ŚšŚȘ Ś”ŚžŚ—Ś“Śœ ڜڀŚȘŚ§Ś™Ś Ś‘\'Ś”Ś’Ś“ŚšŚ•ŚȘ\'"</string>
     <string name="install_app" msgid="5066668100199613936">"Ś”ŚȘڧڠŚȘ Ś”ŚŚ€ŚœŚ™Ś§ŚŠŚ™Ś”"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"ŚœŚ©Ś§ŚŁ ŚœŚžŚĄŚš Ś—Ś™ŚŠŚ•Ś Ś™?"</string>
-    <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"Ś›Śœ Ś€ŚąŚ™ŚœŚ•ŚȘ کڜ ŚžŚĄŚš ŚžŚ€Ś•ŚŠŚœ Ś©ŚšŚŠŚ” Ś›ŚšŚ’Śą ŚȘŚ•Ś€ŚĄŚ§"</string>
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Ś”ŚžŚĄŚš Ś”Ś€Ś Ś™ŚžŚ™ کڜښ Ś™Ś©Ś•Ś›Ś€Śœ. Ś”ŚžŚĄŚš Ś”Ś—Ś™ŚŠŚ•Ś Ś™ کڜښ ڙڛڕڑڔ."</string>
     <string name="mirror_display" msgid="2515262008898122928">"ŚȘŚŠŚ•Ś’ŚȘ ŚžŚšŚŚ”"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"ŚĄŚ’Ś™ŚšŚ”"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Ś”ŚžŚĄŚš ŚžŚ—Ś•Ś‘Śš"</string>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index ca68f66..c742f93 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -330,12 +330,17 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"ă‚čクăƒȘăƒŒăƒł ăƒŹă‚łăƒŒăƒ‰"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"開構"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"ćœæ­ą"</string>
-    <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
-    <skip />
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"éŒČéŸłă«é–ąă™ă‚‹ć•éĄŒ"</string>
+    <string name="qs_record_issue_start" msgid="2979831312582567056">"開構"</string>
+    <string name="qs_record_issue_stop" msgid="3531747965741982657">"ćœæ­ą"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"ăƒ‡ăƒă‚€ă‚čăźă©ăźéƒšćˆ†ăŒćœ±éŸżă‚’ć—ă‘ăŸă—ăŸă‹ïŒŸ"</string>
+    <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"ć•éĄŒăźçšźéĄžă‚’éžæŠžă™ă‚‹"</string>
+    <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"ă‚čクăƒȘăƒŒăƒł ăƒŹă‚łăƒŒăƒ‰"</string>
+  <string-array name="qs_record_issue_types">
+    <item msgid="2947988124014085798">"ăƒ‘ăƒ•ă‚©ăƒŒăƒžăƒłă‚č"</item>
+    <item msgid="1627504621139124393">"ăƒŠăƒŒă‚¶ăƒŒ ă‚€ăƒłă‚żăƒŒăƒ•ă‚§ăƒŒă‚č"</item>
+    <item msgid="8309220355268900335">"バッテăƒȘăƒŒ"</item>
+  </string-array>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"ç‰‡æ‰‹ăƒąăƒŒăƒ‰"</string>
     <string name="quick_settings_contrast_label" msgid="988087460210159123">"ă‚łăƒłăƒˆăƒ©ă‚čト"</string>
     <string name="quick_settings_contrast_standard" msgid="2538227821968061832">"æš™æș–"</string>
@@ -408,12 +413,9 @@
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • 慅電䞭 • ăƒ•ăƒ«ć……é›»ăŸă§ <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"淊にă‚čăƒŻă‚€ăƒ—ă™ă‚‹ăšă€ă‚łăƒŸăƒ„ăƒ‹ăƒ†ă‚Ł ăƒăƒ„ăƒŒăƒˆăƒȘă‚ąăƒ«ăŒé–‹ć§‹ă—ăŸă™"</string>
     <string name="button_to_open_widget_editor" msgid="5599945944349057600">"ォィゾェット スディタを開く"</string>
-    <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
-    <skip />
-    <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
-    <skip />
-    <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
-    <skip />
+    <string name="button_to_remove_widget" msgid="3948204829181214098">"扊陀"</string>
+    <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"ă‚Šă‚Łă‚žă‚§ăƒƒăƒˆă‚’èżœćŠ "</string>
+    <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"漌äș†"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ăƒŠăƒŒă‚¶ăƒŒă‚’ćˆ‡ă‚Šæ›żăˆă‚‹"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ăƒ—ăƒ«ăƒ€ă‚Šăƒł ăƒĄăƒ‹ăƒ„ăƒŒ"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ă“ăźă‚»ăƒƒă‚·ăƒ§ăƒłă§ăźă‚ąăƒ—ăƒȘăšăƒ‡ăƒŒă‚żăŻă™ăčăŠć‰Šé™€ă•ă‚ŒăŸă™ă€‚"</string>
@@ -867,7 +869,6 @@
     <string name="auto_saver_title" msgid="6873691178754086596">"タップしどバッテăƒȘăƒŒ ă‚»ăƒŒăƒăƒŒăźă‚čă‚±ă‚žăƒ„ăƒŒăƒ«ă‚’èš­ćźš"</string>
     <string name="auto_saver_text" msgid="3214960308353838764">"é›»æ± ćˆ‡ă‚Œă«ăȘă‚‹ćŻèƒœæ€§ăŒé«˜ăăȘă‚‹ăšæœ‰ćŠčにăȘă‚ŠăŸă™"</string>
     <string name="no_auto_saver_action" msgid="7467924389609773835">"いいえ"</string>
-    <string name="heap_dump_tile_name" msgid="2464189856478823046">"SysUI ăƒ’ăƒŒăƒ—ăźăƒ€ăƒłăƒ—"</string>
     <string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"äœżç”šäž­"</string>
     <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"ケプăƒȘは<xliff:g id="TYPES_LIST">%s</xliff:g>ă‚’äœżç”šă—ăŠă„ăŸă™ă€‚"</string>
     <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">"、 "</string>
@@ -1207,7 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"[èš­ćźš] ă§ăƒ‡ăƒ•ă‚©ăƒ«ăƒˆăźăƒĄăƒąă‚ąăƒ—ăƒȘă‚’èš­ćźšă—ăŠăă ă•ă„"</string>
     <string name="install_app" msgid="5066668100199613936">"ケプăƒȘă‚’ă‚€ăƒłă‚čăƒˆăƒŒăƒ«"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"ć€–éƒšăƒ‡ă‚Łă‚čăƒ—ăƒŹă‚€ă«ăƒŸăƒ©ăƒŒăƒȘăƒłă‚°ă—ăŸă™ă‹ïŒŸ"</string>
-    <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"çŸćœšćźŸèĄŒäž­ăźăƒ‡ăƒ„ă‚ąăƒ« ă‚čクăƒȘăƒŒăƒłăźă‚ąă‚Żăƒ†ă‚Łăƒ“ăƒ†ă‚ŁăŻćœæ­ąă•ă‚ŒăŸă™"</string>
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"ă‚€ăƒłăƒŠăƒŒ ディă‚čăƒ—ăƒŹă‚€ăŒăƒŸăƒ©ăƒŒăƒȘăƒłă‚°ă•ă‚ŒăŸă™ă€‚ăƒ•ăƒ­ăƒłăƒˆ ディă‚čăƒ—ăƒŹă‚€ăŻ OFF にăȘă‚ŠăŸă™ă€‚"</string>
     <string name="mirror_display" msgid="2515262008898122928">"ディă‚čăƒ—ăƒŹă‚€ă‚’ăƒŸăƒ©ăƒŒăƒȘングする"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"閉じる"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"ディă‚čăƒ—ăƒŹă‚€ă«æŽ„ç¶šă—ăŸă—ăŸ"</string>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index cc6f282..9d386ef 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -330,12 +330,17 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"ეკრანის áƒ©áƒáƒœáƒáƒŹáƒ”áƒ áƒ˜"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"დაწყება"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"ჹეწყვეჱა"</string>
-    <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
-    <skip />
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"áƒ©áƒáƒŹáƒ”áƒ áƒáƒĄáƒ—áƒáƒœ დაკავჹირებული პრობლემა"</string>
+    <string name="qs_record_issue_start" msgid="2979831312582567056">"დაწყება"</string>
+    <string name="qs_record_issue_stop" msgid="3531747965741982657">"გაჩერება"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"თჄვენი მოწყობილობის გამოáƒȘდილების რა ნაწილზე მოჼდა ზეგავლენა?"</string>
+    <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"აირჩიეთ პრობლემის ჱიპი"</string>
+    <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"ეკრანის áƒ©áƒáƒœáƒáƒŹáƒ”áƒ áƒ˜"</string>
+  <string-array name="qs_record_issue_types">
+    <item msgid="2947988124014085798">"áƒ”áƒ€áƒ”áƒ„áƒąáƒŁáƒ áƒáƒ‘áƒ"</item>
+    <item msgid="1627504621139124393">"სამომჼმარებლო áƒ˜áƒœáƒąáƒ”áƒ áƒ€áƒ”áƒ˜áƒĄáƒ˜"</item>
+    <item msgid="8309220355268900335">"ბაჱარეა"</item>
+  </string-array>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"áƒȘალი ჼელის რეჟიმი"</string>
     <string name="quick_settings_contrast_label" msgid="988087460210159123">"კონჱრასჱი"</string>
     <string name="quick_settings_contrast_standard" msgid="2538227821968061832">"სჱანდარჱული"</string>
@@ -408,12 +413,9 @@
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • იჱენება • სრულ დაჱენვამდე <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"áƒ’áƒáƒ“áƒáƒ€áƒŁáƒ áƒȘლეთ მარáƒȘჼნივ, რათა დაიწყოთ საერთო áƒĄáƒáƒźáƒ”áƒšáƒ›áƒ«áƒŠáƒ•áƒáƒœáƒ”áƒšáƒ"</string>
     <string name="button_to_open_widget_editor" msgid="5599945944349057600">"გაჼსენით ვიჯეჱის áƒ áƒ”áƒ“áƒáƒ„áƒąáƒáƒ áƒ˜"</string>
-    <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
-    <skip />
-    <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
-    <skip />
-    <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
-    <skip />
+    <string name="button_to_remove_widget" msgid="3948204829181214098">"ამოჹლა"</string>
+    <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"ვიჯეჱის დამაჱება"</string>
+    <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"მზადაა"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"მომჼმარებლის გადართვა"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ჩამოლლადი მენიუ"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ამ სესიის ყველა აპი და მონაáƒȘემი წაიჹლება."</string>
@@ -867,7 +869,6 @@
     <string name="auto_saver_title" msgid="6873691178754086596">"ჹეეჼეთ ბაჱარეის დამზოგის დასაგეგმად"</string>
     <string name="auto_saver_text" msgid="3214960308353838764">"áƒ©áƒáƒ˜áƒ áƒ—áƒáƒĄ, როáƒȘა ბაჱარეა დაáƒȘლის პირას არიქ"</string>
     <string name="no_auto_saver_action" msgid="7467924389609773835">"არა, გმადლობთ"</string>
-    <string name="heap_dump_tile_name" msgid="2464189856478823046">"SysUI გროვის გამოჱანა"</string>
     <string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"გამოიყენება"</string>
     <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"აპლიკაáƒȘიების მიერ გამოიყენება თჄვენი <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
     <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"დააყენეთ ნაგულისჼმევი ჹენიჹვნების აპი პარამეჱრებჹი"</string>
     <string name="install_app" msgid="5066668100199613936">"აპის ინსჱალაáƒȘია"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"აირეკლოს გარე ეკრანზე?"</string>
-    <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"ორმაგ ეკრანზე ნებისმიერი მიმდინარე áƒáƒ„áƒąáƒ˜áƒ•áƒáƒ‘áƒ ლეჩერდება"</string>
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"თჄვენი ჹიდა ეკრანი აირეკლება. თჄვენი წინა ეკრანი გამოირთვება."</string>
     <string name="mirror_display" msgid="2515262008898122928">"ეკრანის არეკვლა"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"დაჼურვა"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"ეკრანი დაკავჹირებულია"</string>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index 2a9be81..d2c60f1 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -330,12 +330,17 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Đ­ĐșŃ€Đ°ĐœĐŽŃ‹ жазу"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Бастау"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"ĐąĐŸÒ›Ń‚Đ°Ń‚Ńƒ"</string>
-    <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
-    <skip />
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"ĐÒ›Đ°ŃƒĐŽŃ‹ жазу"</string>
+    <string name="qs_record_issue_start" msgid="2979831312582567056">"Бастау"</string>
+    <string name="qs_record_issue_stop" msgid="3531747965741982657">"ĐąĐŸÒ›Ń‚Đ°Ń‚Ńƒ"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"ÒšÒ±Ń€Ń‹Đ»Ò“Ń‹ Ò›Ń‹Đ·ĐŒĐ”Ń‚Ń–ĐœŃ–ÒŁ Ò›Đ°ĐœĐŽĐ°Đč Ń‚ÒŻŃ€Ń–ĐœĐ” әсДр Дтті?"</string>
+    <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"ĐœÓ™ŃĐ”Đ»Đ” Ń‚ÒŻŃ€Ń–Đœ Ń‚Đ°ÒŁĐŽĐ°ÒŁŃ‹Đ·."</string>
+    <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Đ­ĐșŃ€Đ°ĐœĐŽŃ‹ жазу"</string>
+  <string-array name="qs_record_issue_types">
+    <item msgid="2947988124014085798">"ÓšĐœŃ–ĐŒĐŽŃ–Đ»Ń–Đș"</item>
+    <item msgid="1627504621139124393">"ПаĐčĐŽĐ°Đ»Đ°ĐœŃƒŃˆŃ‹ ĐžĐœŃ‚Đ”Ń€Ń„Đ”Đčсі"</item>
+    <item msgid="8309220355268900335">"Đ‘Đ°Ń‚Đ°Ń€Đ”Ń"</item>
+  </string-array>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Бір Ò›ĐŸĐ»ĐŒĐ”Đœ Đ±Đ°ŃÒ›Đ°Ń€Ńƒ Ń€Đ”Đ¶ĐžĐŒŃ–"</string>
     <string name="quick_settings_contrast_label" msgid="988087460210159123">"ĐšĐŸĐœŃ‚Ń€Đ°ŃŃ‚"</string>
     <string name="quick_settings_contrast_standard" msgid="2538227821968061832">"ĐĄŃ‚Đ°ĐœĐŽĐ°Ń€Ń‚Ń‚Ń‹ Ń€Đ”Đ¶ĐžĐŒ"</string>
@@ -408,12 +413,9 @@
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Đ—Đ°Ń€ŃĐŽŃ‚Đ°Đ»Ń‹Đż жатыр. • ĐąĐŸĐ»ŃƒŃ‹ĐœĐ° <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> Ò›Đ°Đ»ĐŽŃ‹."</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"ĐžŃ€Ń‚Đ°Ò› ĐŸÒ›ŃƒĐ»Ń‹Ò›Ń‚Ń‹ ашу ÒŻŃˆŃ–Đœ ŃĐŸĐ»Ò“Đ° Ò›Đ°Ń€Đ°Đč ŃŃ‹Ń€Ò“Ń‹Ń‚Ń‹ÒŁŃ‹Đ·."</string>
     <string name="button_to_open_widget_editor" msgid="5599945944349057600">"ВОЎжДт рДЎаĐșŃ‚ĐŸŃ€Ń‹Đœ ашу"</string>
-    <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
-    <skip />
-    <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
-    <skip />
-    <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
-    <skip />
+    <string name="button_to_remove_widget" msgid="3948204829181214098">"ӹшіру"</string>
+    <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"ВОЎжДт Ò›ĐŸŃŃƒ"</string>
+    <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"ДаĐčŃ‹Đœ"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ПаĐčĐŽĐ°Đ»Đ°ĐœŃƒŃˆŃ‹ĐœŃ‹ ауыстыру"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"Đ°ŃˆŃ‹Đ»ĐŒĐ°Đ»Ń‹ ĐŒÓ™Đ·Ń–Ń€"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Осы ŃĐ”Đ°ĐœŃŃ‚Đ°Ò“Ń‹ Đ±Đ°Ń€Đ»Ń‹Ò› Ò›ĐŸĐ»ĐŽĐ°ĐœĐ±Đ° ĐŒĐ”Đœ ЎДрДĐș Đ¶ĐŸĐčылаЎы."</string>
@@ -867,7 +869,6 @@
     <string name="auto_saver_title" msgid="6873691178754086596">"ĐąÒŻĐčĐŒĐ”ĐœŃ– Ń‚ÒŻŃ€Ń‚Ń–Đż, Đ‘Đ°Ń‚Đ°Ń€Đ”ŃĐœŃ‹ ÒŻĐœĐ”ĐŒĐŽĐ”Ńƒ Ń€Đ”Đ¶ĐžĐŒŃ–Đœ Ń€Đ”Ń‚Ń‚Đ”ÒŁŃ–Đ·"</string>
     <string name="auto_saver_text" msgid="3214960308353838764">"Đ‘Đ°Ń‚Đ°Ń€Đ”ŃĐœŃ‹ÒŁ Đ·Đ°Ń€ŃĐŽŃ‹ Đ±Ń–Ń‚ŃƒĐłĐ” Đ¶Đ°Ò›Ń‹ĐœĐŽĐ°Ò“Đ°ĐœĐŽĐ° Ò›ĐŸŃŃ‹ÒŁŃ‹Đ·."</string>
     <string name="no_auto_saver_action" msgid="7467924389609773835">"Đ–ĐŸÒ›, Ń€Đ°Ò›ĐŒĐ”Ń‚"</string>
-    <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string>
     <string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"ÒšĐŸĐ»ĐŽĐ°ĐœŃ‹ŃŃ‚Đ°"</string>
     <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"ÒšĐŸĐ»ĐŽĐ°ĐœĐ±Đ°Đ»Đ°Ń€ĐŽĐ° <xliff:g id="TYPES_LIST">%s</xliff:g> паĐčĐŽĐ°Đ»Đ°ĐœŃ‹Đ»ŃƒĐŽĐ°."</string>
     <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"ĐŸĐ°Ń€Đ°ĐŒĐ”Ń‚Ń€Đ»Đ”Ń€ĐŽĐ”Đœ әЎДпĐșі жазба Ò›ĐŸĐ»ĐŽĐ°ĐœĐ±Đ°ŃŃ‹Đœ ĐŸŃ€ĐœĐ°Ń‚Ń‹ÒŁŃ‹Đ·."</string>
     <string name="install_app" msgid="5066668100199613936">"ÒšĐŸĐ»ĐŽĐ°ĐœĐ±Đ°ĐœŃ‹ ĐŸŃ€ĐœĐ°Ń‚Ńƒ"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"ĐĄŃ‹Ń€Ń‚Ò›Ń‹ эĐșŃ€Đ°Đœ Đ°Ń€Ò›Ń‹Đ»Ń‹ Ўа ĐșÓ©Ń€ŃĐ”Ń‚Ńƒ ĐșДрДĐș пД?"</string>
-    <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"ЕĐșі эĐșŃ€Đ°ĐœĐŽĐ° Ò›Đ°Đ·Ń–Ń€ ĐŸŃ€Ń‹ĐœĐŽĐ°Đ»Ń‹Đż Đ¶Đ°Ń‚Ò›Đ°Đœ ĐșДз ĐșĐ”Đ»ĐłĐ”Đœ әрДĐșДт Ń‚ĐŸÒ›Ń‚Đ°Ń‚Ń‹Đ»Đ°ĐŽŃ‹."</string>
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"ІшĐșі эĐșŃ€Đ°Đœ ĐșÓ©ŃˆŃ–Ń€ĐŒĐ”ŃŃ– ĐșөрсДтілДЎі. ĐĐ»ĐŽŃ‹ÒŁÒ“Ń‹ эĐșŃ€Đ°Đœ Ó©ŃˆŃ–Ń€Ń–Đ»Đ”ĐŽŃ–."</string>
     <string name="mirror_display" msgid="2515262008898122928">"ĐšÓ©Ń€ŃĐ”Ń‚Ńƒ"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Đ–Đ°Đ±Ńƒ"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"ДОсплДĐč Ò›ĐŸŃŃ‹Đ»ĐŽŃ‹"</string>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index feb3af0..ec81523 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -330,12 +330,17 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"áž€áž¶ážšážážâ€‹ážœážžážŠáŸážąážŒâ€‹ážąáŸáž€áŸ’ážšáž„áŸ‹"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"ចាប់ផ្ដសម"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"ឈប់"</string>
-    <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
-    <skip />
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"កត់ត្រាបញ្ហា"</string>
+    <string name="qs_record_issue_start" msgid="2979831312582567056">"ចាប់ផ្ដសម"</string>
+    <string name="qs_record_issue_stop" msgid="3531747965741982657">"បញ្ឈប់"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"ážážŸáž•áŸ’áž“áŸ‚áž€ážąáŸ’ážœážžáž“áŸƒáž”áž‘áž–áž·ážŸáŸ„áž’áž”áŸ’ážšážŸáž”áŸ’ážšáž¶ážŸáŸ‹áž§áž”áž€ážšážŽáŸážšáž”ážŸáŸ‹ážąáŸ’áž“áž€áž”áž¶áž“ážšáž„áž€áž¶ážšáž”áŸ‰áŸ‡áž–áž¶áž›áŸ‹?"</string>
+    <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"ជ្រសសរសសប្រភេទបញ្ហា"</string>
+    <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"áž€áž¶ážšážážâ€‹ážœážžážŠáŸážąážŒâ€‹ážąáŸáž€áŸ’ážšáž„áŸ‹"</string>
+  <string-array name="qs_record_issue_types">
+    <item msgid="2947988124014085798">"ប្រតិបត្តិការ"</item>
+    <item msgid="1627504621139124393">"ផ្ទៃប៉ះ"</item>
+    <item msgid="8309220355268900335">"ថ្ម"</item>
+  </string-array>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"មុខងារប្រសដៃម្ខាង"</string>
     <string name="quick_settings_contrast_label" msgid="988087460210159123">"កម្រិត​រំលេចពណ៌"</string>
     <string name="quick_settings_contrast_standard" msgid="2538227821968061832">"ស្តង់ដារ"</string>
@@ -408,12 +413,9 @@
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • កំពុងសាកថ្ម • ពេញក្នុងរយៈពេល <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"ážąážŒážŸáž‘áŸ…áž†áŸ’ážœáŸáž„ ដសម្បឞចាប់ផ្ដសមមេរៀនសហគមន៍"</string>
     <string name="button_to_open_widget_editor" msgid="5599945944349057600">"បសកកម្មវិធឞកែធាតុ​ក្រាហ្វិក"</string>
-    <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
-    <skip />
-    <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
-    <skip />
-    <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
-    <skip />
+    <string name="button_to_remove_widget" msgid="3948204829181214098">"ដកចេញ"</string>
+    <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"បញ្ចឌលធាតុ​ក្រាហ្វិក"</string>
+    <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"រវចរាល់"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"áž”áŸ’ážŠážŒážšâ€‹ážąáŸ’áž“áž€â€‹áž”áŸ’ážšážŸ"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ម៉ážșនុយ​ទាញចុះ"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"កម្មវិធឞ áž“áž·áž„áž‘áž·áž“áŸ’áž“áž“áŸáž™â€‹áž‘áž¶áŸ†áž„ážąážŸáŸ‹â€‹áž€áŸ’áž“áž»áž„â€‹ážœáž‚áŸ’áž‚â€‹áž“áŸáŸ‡â€‹áž“ážčង​ត្រឌវ​លុប។"</string>
@@ -867,7 +869,6 @@
     <string name="auto_saver_title" msgid="6873691178754086596">"ចុច​ដសម្បឞ​កំណត់​កាលវិភាគ​មុខងារ​សន្សំ​ថ្ម"</string>
     <string name="auto_saver_text" msgid="3214960308353838764">"áž”ážŸáž€â€‹áž“áŸ…áž–áŸáž›â€‹ážáŸ’áž˜â€‹áž‘áŸ†áž“áž„áž‡áž¶â€‹ážąážŸáŸ‹"</string>
     <string name="no_auto_saver_action" msgid="7467924389609773835">"ទេ ឹរគុណ"</string>
-    <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string>
     <string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"កំពុង​ប្រស"</string>
     <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"កម្មវិធឞ​កំពុងប្រស <xliff:g id="TYPES_LIST">%s</xliff:g> របស់ឱ្នក។"</string>
     <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"កំណត់កម្មវិធឞកំណត់ចំណាំលំនាំដសមនៅក្នុងការកំណត់"</string>
     <string name="install_app" msgid="5066668100199613936">"ážŠáŸ†ážĄážŸáž„â€‹áž€áž˜áŸ’áž˜ážœáž·áž’ážž"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"áž”áž‰áŸ’áž…áž¶áŸ†áž„áž‘áŸ…â€‹áž•áŸ’áž‘áž¶áŸ†áž„ážąáŸáž€áŸ’ážšáž„áŸ‹â€‹ážáž¶áž„áž€áŸ’ážšáŸ…ážŹ?"</string>
-    <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"បច្ចុប្បន្ន ážšáž¶áž›áŸ‹ážŸáž€áž˜áŸ’áž˜áž—áž¶áž–ážąáŸáž€áŸ’ážšáž„áŸ‹áž‘áŸ’ážœáŸážŠáŸ‚áž›áž€áŸ†áž–áž»áž„ážŠáŸ†ážŽážŸážšáž€áž¶ážšáž“ážčងត្រឌវបានបញ្ឈប់"</string>
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"ážąáŸáž€áŸ’ážšáž„áŸ‹ážáž¶áž„áž€áŸ’áž“áž»áž„ážšáž”ážŸáŸ‹ážąáŸ’áž“áž€áž“ážčងត្រឌវបានធ្វស​សមកាលកម្មទៅវិញទៅមក។ ážąáŸáž€áŸ’ážšáž„áŸ‹áž˜áž»ážážšáž”ážŸáŸ‹ážąáŸ’áž“áž€áž“ážčងត្រឌវបានបិទ។"</string>
     <string name="mirror_display" msgid="2515262008898122928">"áž”áž‰áŸ’áž…áž¶áŸ†áž„áž‘áŸ…áž•áŸ’áž‘áž¶áŸ†áž„ážąáŸáž€áŸ’ážšáž„áŸ‹"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"ច្រានចោល"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"áž•áŸ’áž‘áž¶áŸ†áž„ážąáŸáž€áŸ’ážšáž„áŸ‹ážáŸ’ážšážŒážœáž”áž¶áž“áž—áŸ’áž‡áž¶áž”áŸ‹"</string>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index fa35189..33f4528 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -93,7 +93,7 @@
     <string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"àČ•àł†àČłàȗàČżàČš àČŹàłŒàȂàČĄàȰàČż àČ¶àł‡àȕàČĄàČŸ <xliff:g id="PERCENT">%1$d</xliff:g>"</string>
     <string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"àȎàČĄàČ­àČŸàȗàČŠ àČŹàłŒàȂàČĄàȰàČż àČ¶àł‡àȕàČĄàČŸ <xliff:g id="PERCENT">%1$d</xliff:g>"</string>
     <string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"àČŹàČČàČ­àČŸàȗàČŠ àČŹàłŒàȂàČĄàȰàČż àČ¶àł‡àȕàČĄàČŸ <xliff:g id="PERCENT">%1$d</xliff:g>"</string>
-    <string name="screenshot_work_profile_notification" msgid="203041724052970693">"àČ•àł†àČČàČžàČŠ àČȘàłàČ°àłŠàČ«àłˆàČČàł‌àČšàČČàłàČČàČżàČš <xliff:g id="APP">%1$s</xliff:g> àČšàČČàłàČČàČż àȉàČłàČżàČžàČČàČŸàȗàČżàČŠàł†"</string>
+    <string name="screenshot_work_profile_notification" msgid="203041724052970693">"àČ•àł†àČČàČžàČŠ àČȘàłàČ°àłŠàČ«àłˆàČČàł‌àČšàČČàłàČČàČżàČš <xliff:g id="APP">%1$s</xliff:g> àČšàČČàłàČČàČż àČžàł‡àČ”àł àČźàČŸàČĄàČČàČŸàȗàČżàČŠàł†"</string>
     <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"àČ«àłˆàČČàł‌àȗàČłàł"</string>
     <string name="screenshot_detected_template" msgid="7940376642921719915">"àȈ àČžàłàČ•àłàČ°àł€àČšàł‌àȶàČŸàČŸàł àȅàČšàłàČšàł <xliff:g id="APPNAME">%1$s</xliff:g> àČȘàČ€àłàČ€àł†àČčàČšàłàȚàČżàČŠàł†."</string>
     <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> àČčàČŸàČ—àł‚ àČ€àł†àČ°àł†àČŠàČżàČ°àłàČ” àȇàČ€àȰ àČ†àłàČŻàČȘàł‌àȗàČłàł àȈ àČžàłàČ•àłàČ°àł€àČšàł‌àȶàČŸàČŸàł àȅàČšàłàČšàł àČȘàČ€àłàČ€àł†àČčàČšàłàȚàČżàČ”àł†."</string>
@@ -330,12 +330,17 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"àČžàłàČ•àłàČ°àł€àČšàł àČ°àł†àȕàČŸàČ°àłàČĄàł"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"àČȘàłàȰàČŸàȰàȂàČ­àČżàČžàČż"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"àČšàČżàČČàłàČČàČżàČžàČż"</string>
-    <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
-    <skip />
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"àČ°àł†àȕàČŸàČ°àłàČĄàł àČŠàł‹àČ·"</string>
+    <string name="qs_record_issue_start" msgid="2979831312582567056">"àČȘàłàȰàČŸàȰàȂàČ­àČżàČžàČż"</string>
+    <string name="qs_record_issue_stop" msgid="3531747965741982657">"àČšàČżàČČàłàČČàČżàČžàČż"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"àČžàČŸàȧàČš àČŹàČłàČžàłàČ”àČŸàȗ àČšàł€àČ”àł àČŻàČŸàČ” àČ°àł€àČ€àČżàČŻ àČžàČźàČžàłàČŻàł† àȎàČŠàłàȰàČżàČžàłàČ€àłàČ€àł€àȰàČż?"</string>
+    <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"àČžàČźàČžàłàČŻàł†àČŻ àČȘàłàȰàȕàČŸàȰàČ”àČšàłàČšàł àȆàČŻàłàČ•àł†àČźàČŸàČĄàČż"</string>
+    <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"àČžàłàČ•àłàČ°àł€àČšàł àČ°àł†àȕàČŸàČ°àłàČĄàł"</string>
+  <string-array name="qs_record_issue_types">
+    <item msgid="2947988124014085798">"àȕàČŸàČ°àłàČŻàČ•àłàČ·àČźàČ€àł†"</item>
+    <item msgid="1627504621139124393">"àČŹàČłàČ•àł†àČŠàČŸàȰàȰ àȇàȂàȟàČ°àł‌àČ«àł‡àČžàł"</item>
+    <item msgid="8309220355268900335">"àČŹàłàČŻàČŸàȟàȰàČż"</item>
+  </string-array>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"àȒàȂàČŠàł àČ•àłˆ àČźàł‹àČĄàł"</string>
     <string name="quick_settings_contrast_label" msgid="988087460210159123">"àȕàČŸàȂàČŸàłàȰàČŸàČžàłàČŸàł‌‌"</string>
     <string name="quick_settings_contrast_standard" msgid="2538227821968061832">"àČȘàłàȰàČźàČŸàČŁàČżàČ€"</string>
@@ -408,12 +413,9 @@
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • àȚàČŸàČ°àłàČœàł àȆàČ—àłàČ€àłàČ€àČżàČŠàł† • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> àČŠàČČàłàČČàČż àČȘàł‚àČ°àłàČŁàČ—àłŠàČłàłàČłàłàČ€àłàČ€àČŠàł†"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"àČžàČźàłàČŠàČŸàČŻàČŠ àČŸàłàČŻàłàČŸàł‹àȰàČżàČŻàČČàł àȅàČšàłàČšàł àČȘàłàȰàČŸàȰàȂàČ­àČżàČžàČČàł àȎàČĄàČ•àłàČ•àł† àČžàłàČ”àłˆàČȘàł àČźàČŸàČĄàČż"</string>
     <string name="button_to_open_widget_editor" msgid="5599945944349057600">"àČ”àČżàČœàł†àČŸàł àȎàČĄàČżàȟàČ°àł àȅàČšàłàČšàł àČ€àł†àČ°àł†àČŻàČżàȰàČż"</string>
-    <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
-    <skip />
-    <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
-    <skip />
-    <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
-    <skip />
+    <string name="button_to_remove_widget" msgid="3948204829181214098">"àČ€àł†àČ—àł†àČŠàłàČčàČŸàȕàČż"</string>
+    <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"àČ”àČżàČœàł†àČŸàł àȅàČšàłàČšàł àČžàł‡àȰàČżàČžàČż"</string>
+    <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"àČźàłàȗàČżàČŠàČżàČŠàł†"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"àČŹàČłàČ•àł†àČŠàČŸàȰàȰàČšàłàČšàł àČŹàČŠàČČàČżàČžàČż"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"àČȘàłàČČàł‌àČĄàłŒàČšàł àČźàł†àČšàł"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"àȈ àČžàł†àČ·àČšàł‌àČšàČČàłàČČàČżàČš àȎàČČàłàČČ àȅàČȘàłàČČàČżàČ•àł‡àȶàČšàł‌àȗàČłàł àČźàČ€àłàČ€àł àČĄàł‡àȟàČŸàČ”àČšàłàČšàł àȅàČłàČżàČžàČČàČŸàČ—àłàČ€àłàČ€àČŠàł†."</string>
@@ -867,7 +869,6 @@
     <string name="auto_saver_title" msgid="6873691178754086596">"àČŹàłàČŻàČŸàȟàȰàČż àČžàł‡àČ”àČ°àł‌ àȅàČšàłàČšàł àČšàČżàȗàČŠàČżàČ—àłŠàČłàČżàČžàČČàł àČŸàłàČŻàČŸàČȘàł‌ àČźàČŸàČĄàČż"</string>
     <string name="auto_saver_text" msgid="3214960308353838764">"àČŹàłàČŻàČŸàȟàȰàČż àȖàČŸàČČàČżàČŻàČŸàČ—àłàČ” àČžàČŸàČ§àłàČŻàČ€àł† àȇàČŠàłàČŠàČŸàȗ àȆàČšàł àČźàČŸàČĄàČż"</string>
     <string name="no_auto_saver_action" msgid="7467924389609773835">"àČŹàł‡àČĄ"</string>
-    <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string>
     <string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"àČŹàČłàČ•àł†àČŻàČČàłàČČàČżàČŠàł†"</string>
     <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"àČšàČżàČźàłàČź <xliff:g id="TYPES_LIST">%s</xliff:g> àȅàČšàłàČšàł àČ†àłàČŻàČȘàł‌àȗàČłàł àČŹàČłàČžàłàČ€àłàČ€àČżàČ”àł†."</string>
     <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"àČžàł†àČŸàłàȟàČżàȂàČ—àł‌àȗàČłàČČàłàČČàČż àČĄàł€àČ«àČŸàČČàłàČŸàł àȟàČżàČȘàłàČȘàČŁàČżàȗàČł àČ†àłàČŻàČȘàł àȅàČšàłàČšàł àČžàł†àČŸàł àČźàČŸàČĄàČż"</string>
     <string name="install_app" msgid="5066668100199613936">"àČ†àłàČŻàČȘàł àȇàČšàł‌àČžàłàȟàČŸàČČàł àČźàČŸàČĄàČż"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"àČŹàČŸàČčàłàČŻ àČĄàČżàČžàł‌àČȘàłàČČàł‡àČ—àł† àČȘàłàȰàČ€àČżàČŹàČżàȂàČŹàČżàČžàČŹàł‡àČ•àł†?"</string>
-    <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"àČȘàłàȰàČžàłàČ€àłàČ€ àȚàČŸàČČàČšàł†àČŻàČČàłàČČàČżàČ°àłàČ” àČŻàČŸàČ”àłàČŠàł‡ àČĄàłàČŻàłàČŻàČČàł àČžàłàČ•àłàČ°àł€àČšàł àȚàČŸàłàČ”àȟàČżàČ•àł†àČŻàČšàłàČšàł àČšàČżàČČàłàČČàČżàČžàČČàČŸàČ—àłàČ€àłàČ€àČŠàł†"</string>
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"àČšàČżàČźàłàČź àȒàČłàȗàČżàČš àČĄàČżàČžàł‌àČȘàłàČČàł‡ àȅàČšàłàČšàł àČȘàłàȰàČ€àČżàČŹàČżàȂàČŹàČżàČžàČČàČŸàČ—àłàČ€àłàČ€àČŠàł†. àČšàČżàČźàłàČź àČ«àłàȰàȂàČŸàł àČĄàČżàČžàł‌àČȘàłàČČàł‡ àȅàČšàłàČšàł àȆàČ«àł àČźàČŸàČĄàČČàČŸàČ—àłàČ€àłàČ€àČŠàł†."</string>
     <string name="mirror_display" msgid="2515262008898122928">"àČźàČżàȰàČ°àł àČĄàČżàČžàł‌àČȘàłàČČàł‡"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"àČ”àȜàČŸàČ—àłŠàČłàČżàČžàČż"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"àČĄàČżàČžàł‌àČȘàłàČČàł‡ àȕàČšàł†àČ•àłàČŸàł àȆàȗàČżàČŠàł†"</string>
diff --git a/packages/SystemUI/res/values-kn/tiles_states_strings.xml b/packages/SystemUI/res/values-kn/tiles_states_strings.xml
index 16e82ea..876562d 100644
--- a/packages/SystemUI/res/values-kn/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-kn/tiles_states_strings.xml
@@ -33,147 +33,147 @@
     <!-- no translation found for tile_states_default:2 (9192445505551219506) -->
   <string-array name="tile_states_internet">
     <item msgid="5499482407653291407">"àČČàČ­àłàČŻàČ”àČżàČČàłàČČ"</item>
-    <item msgid="3048856902433862868">"àȆàČ«àł àČźàČŸàČĄàČż"</item>
+    <item msgid="3048856902433862868">"àȆàČ«àł"</item>
     <item msgid="6877982264300789870">"àȆàČšàł àČźàČŸàČĄàČż"</item>
   </string-array>
   <string-array name="tile_states_wifi">
     <item msgid="8054147400538405410">"àČČàČ­àłàČŻàČ”àČżàČČàłàČČ"</item>
-    <item msgid="4293012229142257455">"àȆàČ«àł àČźàČŸàČĄàČż"</item>
+    <item msgid="4293012229142257455">"àȆàČ«àł"</item>
     <item msgid="6221288736127914861">"àȆàČšàł àČźàČŸàČĄàČż"</item>
   </string-array>
   <string-array name="tile_states_cell">
     <item msgid="1235899788959500719">"àČČàČ­àłàČŻàČ”àČżàČČàłàČČ"</item>
-    <item msgid="2074416252859094119">"àȆàČ«àł àČźàČŸàČĄàČż"</item>
+    <item msgid="2074416252859094119">"àȆàČ«àł"</item>
     <item msgid="287997784730044767">"àȆàČšàł àČźàČŸàČĄàČż"</item>
   </string-array>
   <string-array name="tile_states_battery">
     <item msgid="6311253873330062961">"àČČàČ­àłàČŻàČ”àČżàČČàłàČČ"</item>
-    <item msgid="7838121007534579872">"àȆàČ«àł àČźàČŸàČĄàČż"</item>
+    <item msgid="7838121007534579872">"àȆàČ«àł"</item>
     <item msgid="1578872232501319194">"àȆàČšàł àČźàČŸàČĄàČż"</item>
   </string-array>
   <string-array name="tile_states_dnd">
     <item msgid="467587075903158357">"àČČàČ­àłàČŻàČ”àČżàČČàłàČČ"</item>
-    <item msgid="5376619709702103243">"àȆàČ«àł àČźàČŸàČĄàČż"</item>
+    <item msgid="5376619709702103243">"àȆàČ«àł"</item>
     <item msgid="4875147066469902392">"àȆàČšàł àČźàČŸàČĄàČż"</item>
   </string-array>
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"àČČàČ­àłàČŻàČ”àČżàČČàłàČČ"</item>
-    <item msgid="5044688398303285224">"àȆàČ«àł àČźàČŸàČĄàČż"</item>
+    <item msgid="5044688398303285224">"àȆàČ«àł"</item>
     <item msgid="8527389108867454098">"àȆàČšàł àČźàČŸàČĄàČż"</item>
   </string-array>
   <string-array name="tile_states_rotation">
     <item msgid="4578491772376121579">"àČČàČ­àłàČŻàČ”àČżàČČàłàČČ"</item>
-    <item msgid="5776427577477729185">"àȆàČ«àł àČźàČŸàČĄàČż"</item>
+    <item msgid="5776427577477729185">"àȆàČ«àł"</item>
     <item msgid="7105052717007227415">"àȆàČšàł àČźàČŸàČĄàČż"</item>
   </string-array>
   <string-array name="tile_states_bt">
     <item msgid="5330252067413512277">"àČČàČ­àłàČŻàČ”àČżàČČàłàČČ"</item>
-    <item msgid="5315121904534729843">"àȆàČ«àł àČźàČŸàČĄàČż"</item>
+    <item msgid="5315121904534729843">"àȆàČ«àł"</item>
     <item msgid="503679232285959074">"àȆàČšàł àČźàČŸàČĄàČż"</item>
   </string-array>
   <string-array name="tile_states_airplane">
     <item msgid="1985366811411407764">"àČČàČ­àłàČŻàČ”àČżàČČàłàČČ"</item>
-    <item msgid="4801037224991420996">"àȆàČ«àł àČźàČŸàČĄàČż"</item>
+    <item msgid="4801037224991420996">"àȆàČ«àł"</item>
     <item msgid="1982293347302546665">"àȆàČšàł àČźàČŸàČĄàČż"</item>
   </string-array>
   <string-array name="tile_states_location">
     <item msgid="3316542218706374405">"àČČàČ­àłàČŻàČ”àČżàČČàłàČČ"</item>
-    <item msgid="4813655083852587017">"àȆàČ«àł àČźàČŸàČĄàČż"</item>
+    <item msgid="4813655083852587017">"àȆàČ«àł"</item>
     <item msgid="6744077414775180687">"àȆàČšàł àČźàČŸàČĄàČż"</item>
   </string-array>
   <string-array name="tile_states_hotspot">
     <item msgid="3145597331197351214">"àČČàČ­àłàČŻàČ”àČżàČČàłàČČ"</item>
-    <item msgid="5715725170633593906">"àȆàČ«àł àČźàČŸàČĄàČż"</item>
+    <item msgid="5715725170633593906">"àȆàČ«àł"</item>
     <item msgid="2075645297847971154">"àȆàČšàł àČźàČŸàČĄàČż"</item>
   </string-array>
   <string-array name="tile_states_color_correction">
     <item msgid="2840507878437297682">"àČČàČ­àłàČŻàČ”àČżàČČàłàČČ"</item>
-    <item msgid="1909756493418256167">"àȆàČ«àł àČźàČŸàČĄàČż"</item>
+    <item msgid="1909756493418256167">"àȆàČ«àł"</item>
     <item msgid="4531508423703413340">"àȆàČšàł àČźàČŸàČĄàČż"</item>
   </string-array>
   <string-array name="tile_states_inversion">
     <item msgid="3638187931191394628">"àČČàČ­àłàČŻàČ”àČżàČČàłàČČ"</item>
-    <item msgid="9103697205127645916">"àȆàČ«àł àČźàČŸàČĄàČż"</item>
+    <item msgid="9103697205127645916">"àȆàČ«àł"</item>
     <item msgid="8067744885820618230">"àȆàČšàł àČźàČŸàČĄàČż"</item>
   </string-array>
   <string-array name="tile_states_saver">
     <item msgid="39714521631367660">"àČČàČ­àłàČŻàČ”àČżàČČàłàČČ"</item>
-    <item msgid="6983679487661600728">"àȆàČ«àł àČźàČŸàČĄàČż"</item>
+    <item msgid="6983679487661600728">"àȆàČ«àł"</item>
     <item msgid="7520663805910678476">"àȆàČšàł àČźàČŸàČĄàČż"</item>
   </string-array>
   <string-array name="tile_states_dark">
     <item msgid="2762596907080603047">"àČČàČ­àłàČŻàČ”àČżàČČàłàČČ"</item>
-    <item msgid="400477985171353">"àȆàČ«àł àČźàČŸàČĄàČż"</item>
+    <item msgid="400477985171353">"àȆàČ«àł"</item>
     <item msgid="630890598801118771">"àȆàČšàł àČźàČŸàČĄàČż"</item>
   </string-array>
   <string-array name="tile_states_work">
     <item msgid="389523503690414094">"àČČàČ­àłàČŻàČ”àČżàČČàłàČČ"</item>
-    <item msgid="8045580926543311193">"àȆàČ«àł àČźàČŸàČĄàČż"</item>
+    <item msgid="8045580926543311193">"àȆàČ«àł"</item>
     <item msgid="4913460972266982499">"àȆàČšàł àČźàČŸàČĄàČż"</item>
   </string-array>
   <string-array name="tile_states_cast">
     <item msgid="6032026038702435350">"àČČàČ­àłàČŻàČ”àČżàČČàłàČČ"</item>
-    <item msgid="1488620600954313499">"àȆàČ«àł àČźàČŸàČĄàČż"</item>
+    <item msgid="1488620600954313499">"àȆàČ«àł"</item>
     <item msgid="588467578853244035">"àȆàČšàł àČźàČŸàČĄàČż"</item>
   </string-array>
   <string-array name="tile_states_night">
     <item msgid="7857498964264855466">"àČČàČ­àłàČŻàČ”àČżàČČàłàČČ"</item>
-    <item msgid="2744885441164350155">"àȆàČ«àł àČźàČŸàČĄàČż"</item>
+    <item msgid="2744885441164350155">"àȆàČ«àł"</item>
     <item msgid="151121227514952197">"àȆàČšàł àČźàČŸàČĄàČż"</item>
   </string-array>
   <string-array name="tile_states_screenrecord">
     <item msgid="1085836626613341403">"àČČàČ­àłàČŻàČ”àČżàČČàłàČČ"</item>
-    <item msgid="8259411607272330225">"àȆàČ«àł àČźàČŸàČĄàČż"</item>
+    <item msgid="8259411607272330225">"àȆàČ«àł"</item>
     <item msgid="578444932039713369">"àȆàČšàł àČźàČŸàČĄàČż"</item>
   </string-array>
   <string-array name="tile_states_reverse">
     <item msgid="3574611556622963971">"àČČàČ­àłàČŻàČ”àČżàČČàłàČČ"</item>
-    <item msgid="8707481475312432575">"àȆàČ«àł àČźàČŸàČĄàČż"</item>
+    <item msgid="8707481475312432575">"àȆàČ«àł"</item>
     <item msgid="8031106212477483874">"àȆàČšàł àČźàČŸàČĄàČż"</item>
   </string-array>
   <string-array name="tile_states_reduce_brightness">
     <item msgid="1839836132729571766">"àČČàČ­àłàČŻàČ”àČżàČČàłàČČ"</item>
-    <item msgid="4572245614982283078">"àȆàČ«àł àČźàČŸàČĄàČż"</item>
+    <item msgid="4572245614982283078">"àȆàČ«àł"</item>
     <item msgid="6536448410252185664">"àȆàČšàł àČźàČŸàČĄàČż"</item>
   </string-array>
   <string-array name="tile_states_cameratoggle">
     <item msgid="6680671247180519913">"àČČàČ­àłàČŻàČ”àČżàČČàłàČČ"</item>
-    <item msgid="4765607635752003190">"àȆàČ«àł àČźàČŸàČĄàČż"</item>
+    <item msgid="4765607635752003190">"àȆàČ«àł"</item>
     <item msgid="1697460731949649844">"àȆàČšàł àČźàČŸàČĄàČż"</item>
   </string-array>
   <string-array name="tile_states_mictoggle">
     <item msgid="6895831614067195493">"àČČàČ­àłàČŻàČ”àČżàČČàłàČČ"</item>
-    <item msgid="3296179158646568218">"àȆàČ«àł àČźàČŸàČĄàČż"</item>
+    <item msgid="3296179158646568218">"àȆàČ«àł"</item>
     <item msgid="8998632451221157987">"àȆàČšàł àČźàČŸàČĄàČż"</item>
   </string-array>
   <string-array name="tile_states_controls">
     <item msgid="8199009425335668294">"àČČàČ­àłàČŻàČ”àČżàČČàłàČČ"</item>
-    <item msgid="4544919905196727508">"àȆàČ«àł àČźàČŸàČĄàČż"</item>
+    <item msgid="4544919905196727508">"àȆàČ«àł"</item>
     <item msgid="3422023746567004609">"àȆàČšàł àČźàČŸàČĄàČż"</item>
   </string-array>
   <string-array name="tile_states_wallet">
     <item msgid="4177615438710836341">"àČČàČ­àłàČŻàČ”àČżàČČàłàČČ"</item>
-    <item msgid="7571394439974244289">"àȆàČ«àł àČźàČŸàČĄàČż"</item>
+    <item msgid="7571394439974244289">"àȆàČ«àł"</item>
     <item msgid="6866424167599381915">"àȆàČšàł àČźàČŸàČĄàČż"</item>
   </string-array>
   <string-array name="tile_states_qr_code_scanner">
     <item msgid="7435143266149257618">"àČČàČ­àłàČŻàČ”àČżàČČàłàČČ"</item>
-    <item msgid="3301403109049256043">"àȆàČ«àł àČźàČŸàČĄàČż"</item>
+    <item msgid="3301403109049256043">"àȆàČ«àł"</item>
     <item msgid="8878684975184010135">"àȆàČšàł àČźàČŸàČĄàČż"</item>
   </string-array>
   <string-array name="tile_states_alarm">
     <item msgid="4936533380177298776">"àČČàČ­àłàČŻàČ”àČżàČČàłàČČ"</item>
-    <item msgid="2710157085538036590">"àȆàČ«àł àČźàČŸàČĄàČż"</item>
+    <item msgid="2710157085538036590">"àȆàČ«àł"</item>
     <item msgid="7809470840976856149">"àȆàČšàł àČźàČŸàČĄàČż"</item>
   </string-array>
   <string-array name="tile_states_onehanded">
     <item msgid="8189342855739930015">"àČČàČ­àłàČŻàČ”àČżàČČàłàČČ"</item>
-    <item msgid="146088982397753810">"àȆàČ«àł àČźàČŸàČĄàČż"</item>
+    <item msgid="146088982397753810">"àȆàČ«àł"</item>
     <item msgid="460891964396502657">"àȆàČšàł àČźàČŸàČĄàČż"</item>
   </string-array>
   <string-array name="tile_states_dream">
     <item msgid="6184819793571079513">"àČČàČ­àłàČŻàČ”àČżàČČàłàČČ"</item>
-    <item msgid="8014986104355098744">"àȆàČ«àł àČźàČŸàČĄàČż"</item>
+    <item msgid="8014986104355098744">"àȆàČ«àł"</item>
     <item msgid="5966994759929723339">"àȆàČšàł àČźàČŸàČĄàČż"</item>
   </string-array>
   <string-array name="tile_states_font_scaling">
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index 3ccbed3..897b266 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -330,12 +330,17 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"화멎 ë…č화"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"시작"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"쀑지"</string>
-    <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
-    <skip />
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"ëŹžì œ êž°ëĄ"</string>
+    <string name="qs_record_issue_start" msgid="2979831312582567056">"시작"</string>
+    <string name="qs_record_issue_stop" msgid="3531747965741982657">"쀑지"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"êž°êž° êČœí—˜ì˜ ì–Žë–€ 부분에 영햄읎 있었나요?"</string>
+    <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"ëŹžì œ 유형 선택"</string>
+    <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"화멎 ë…č화"</string>
+  <string-array name="qs_record_issue_types">
+    <item msgid="2947988124014085798">"성늄"</item>
+    <item msgid="1627504621139124393">"ì‚Źìš©ìž 읞터페읎슀"</item>
+    <item msgid="8309220355268900335">"배터멬"</item>
+  </string-array>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"한 손 ì‚Źìš© ëȘšë“œ"</string>
     <string name="quick_settings_contrast_label" msgid="988087460210159123">"대ëč„"</string>
     <string name="quick_settings_contrast_standard" msgid="2538227821968061832">"표쀀"</string>
@@ -408,12 +413,9 @@
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • 충전 쀑 • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> 후 충전 ì™„ëŁŒ"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"êł”ë™ íŠœí† ëŠŹì–Œì„ 시작하렀멎 왌ìȘœìœŒëĄœ 슀와읎프하섞요"</string>
     <string name="button_to_open_widget_editor" msgid="5599945944349057600">"ìœ„ì Ż 펞집Ʞ ì—Žêž°"</string>
-    <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
-    <skip />
-    <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
-    <skip />
-    <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
-    <skip />
+    <string name="button_to_remove_widget" msgid="3948204829181214098">"삭제"</string>
+    <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"ìœ„ì Ż 추가"</string>
+    <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"ì™„ëŁŒ"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ì‚Źìš©ìž 전환"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"풀닀욎 메뉎"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"읎 섞션에 있는 ëȘšë“  ì•±êłŒ 데읎터가 삭제됩니닀."</string>
@@ -867,7 +869,6 @@
     <string name="auto_saver_title" msgid="6873691178754086596">"íƒ­í•˜ì—Ź 절전 ëȘšë“œ 예앜"</string>
     <string name="auto_saver_text" msgid="3214960308353838764">"ë°°í„°ëŠŹê°€ 소진될 êȃ 같윌멎 ì‚Źìš© 섀정"</string>
     <string name="no_auto_saver_action" msgid="7467924389609773835">"ì‚Źìš© 안핚"</string>
-    <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string>
     <string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"ì‚Źìš© 쀑"</string>
     <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"ì• í”ŒëŠŹìŒ€ìŽì…˜ìŽ <xliff:g id="TYPES_LIST">%s</xliff:g>을(넌) ì‚Źìš© 쀑입니닀."</string>
     <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"섀정에서 êž°ëłž 메ëȘš 앱 섀정"</string>
     <string name="install_app" msgid="5066668100199613936">"앱 ì„€ìč˜"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"왞부 ë””ìŠ€í”Œë ˆìŽëĄœ ëŻžëŸŹë§í•˜ì‹œêČ ìŠ”ë‹ˆêčŒ?"</string>
-    <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"싀행 쀑읞 ëȘšë“  듀얌 화멎읎 쀑지됩니닀."</string>
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"낎부 디슀플레읎가 ëŻžëŸŹë§ë©ë‹ˆë‹€. 전멎 디슀플레읎는 êșŒì§‘니닀."</string>
     <string name="mirror_display" msgid="2515262008898122928">"디슀플레읎 믾러링"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"ë‹«êž°"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"디슀플레읎 연êȰ됚"</string>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index 76d3809..2f091ec 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -330,12 +330,17 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Đ­ĐșŃ€Đ°ĐœĐŽŃ‹ Đ¶Đ°Đ·ĐŽŃ‹Ń€ŃƒŃƒ"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"БаштаЮыĐș"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"ĐąĐŸĐșŃ‚ĐŸŃ‚ŃƒŃƒ"</string>
-    <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
-    <skip />
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"ĐœĐ°ŃĐ”Đ»Đ”ĐœĐž Đ¶Đ°Đ·ĐŽŃ‹Ń€ŃƒŃƒ"</string>
+    <string name="qs_record_issue_start" msgid="2979831312582567056">"Đ‘Đ°ŃˆŃ‚ĐŸĐŸ"</string>
+    <string name="qs_record_issue_stop" msgid="3531747965741982657">"ĐąĐŸĐșŃ‚ĐŸŃ‚ŃƒŃƒ"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"ĐąÒŻĐ·ĐŒÓ©ĐłÒŻÒŁÒŻĐ·ĐŽÒŻĐœ ĐșаĐčсы Đ±Ó©Đ»ÒŻĐłÒŻĐœÓ© ĐșДЎДргОсО тоĐčĐŽĐž?"</string>
+    <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"ĐœĐ°ŃĐ”Đ»Đ”ĐœĐžĐœ Ń‚ÒŻŃ€ÒŻĐœ Ń‚Đ°ĐœĐŽĐŸĐŸ"</string>
+    <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Đ­ĐșŃ€Đ°ĐœĐŽĐ°Đœ ĐČĐžĐŽĐ”ĐŸ жазЎырып алуу"</string>
+  <string-array name="qs_record_issue_types">
+    <item msgid="2947988124014085798">"Đ˜ŃˆŃ‚ĐžĐœ ĐŒĐ°ĐčĐœĐ°ĐżŃ‚ŃƒŃƒĐ»ŃƒĐłŃƒ"</item>
+    <item msgid="1627504621139124393">"ĐšĐŸĐ»ĐŽĐŸĐœŃƒŃƒŃ‡ŃƒĐœŃƒĐœ ĐžĐœŃ‚Đ”Ń€Ń„Đ”ĐčсО"</item>
+    <item msgid="8309220355268900335">"Đ‘Đ°Ń‚Đ°Ń€Đ”Ń"</item>
+  </string-array>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Бор ĐșĐŸĐ» Ń€Đ”Đ¶ĐžĐŒĐž"</string>
     <string name="quick_settings_contrast_label" msgid="988087460210159123">"ĐšĐŸĐœŃ‚Ń€Đ°ŃŃ‚"</string>
     <string name="quick_settings_contrast_standard" msgid="2538227821968061832">"ĐšĐ°ĐŽĐžĐŒĐșĐž"</string>
@@ -408,12 +413,9 @@
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ĐšŃƒĐ±Đ°Ń‚Ń‚Đ°Đ»ŃƒŃƒĐŽĐ° • ĐąĐŸĐ»ĐłĐŸĐœĐłĐŸ чДĐčĐžĐœ <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> ĐșалЎы"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Жалпы ÒŻĐčŃ€Ó©Ń‚ĐșÒŻŃ‡Ń‚ÒŻ ĐžŃˆŃ‚Đ”Ń‚ÒŻÒŻ ÒŻŃ‡ÒŻĐœ ŃĐŸĐ»ĐłĐŸ ŃÒŻŃ€ÒŻÒŁÒŻĐ·"</string>
     <string name="button_to_open_widget_editor" msgid="5599945944349057600">"ВОЎжДт Ń‚ÒŻĐ·Ó©Ń‚ĐșÒŻŃ‡Ń‚ÒŻ ачуу"</string>
-    <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
-    <skip />
-    <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
-    <skip />
-    <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
-    <skip />
+    <string name="button_to_remove_widget" msgid="3948204829181214098">"ÓšŃ‡ÒŻŃ€ÒŻÒŻ"</string>
+    <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"ВОЎжДт ĐșĐŸŃˆŃƒŃƒ"</string>
+    <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Đ‘ÒŻŃ‚Ń‚ÒŻ"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ĐšĐŸĐ»ĐŽĐŸĐœŃƒŃƒŃ‡ŃƒĐœŃƒ ĐșĐŸŃ‚ĐŸŃ€ŃƒŃƒ"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ылЎыĐč Ń‚ÒŻŃˆÒŻÒŻŃ‡ÒŻ ĐŒĐ”ĐœŃŽ"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Đ‘ŃƒĐ» ŃĐ”Đ°ĐœŃŃ‚Đ°ĐłŃ‹ барЎыĐș ĐșĐŸĐ»ĐŽĐŸĐœĐŒĐŸĐ»ĐŸŃ€ Đ¶Đ°ĐœĐ° аларга баĐčĐ»Đ°ĐœŃ‹ŃˆŃ‚ŃƒŃƒ ĐœĐ”Ń€ŃĐ”Đ»Đ”Ń€ Ó©Ń‡ÒŻŃ€ÒŻĐ»Ó©Ń‚."</string>
@@ -867,7 +869,6 @@
     <string name="auto_saver_title" msgid="6873691178754086596">"Đ‘Đ°Ń‚Đ°Ń€Đ”ŃĐœŃ‹ ÒŻĐœÓ©ĐŒĐŽÓ©ĐłÒŻŃ‡Ń‚ÒŻĐœ Ń‚Đ°Ń€Ń‚ĐžĐ±ĐžĐœ Đ¶Ó©ĐœĐŽÓ©Ó© ÒŻŃ‡ÒŻĐœ Đ±Đ°ŃŃ‹ÒŁŃ‹Đ·"</string>
     <string name="auto_saver_text" msgid="3214960308353838764">"Đ‘Đ°Ń‚Đ°Ń€Đ”ŃĐœŃ‹Đœ ĐșŃƒĐ±Đ°Ń‚Ń‹ Ń‚ÒŻĐłÓ©ĐœÒŻĐż ĐșĐ°Đ»ĐłĐ°ĐœĐŽĐ°, ĐșÒŻĐčĐłÒŻĐ·ÒŻĐ»ŃÒŻĐœ"</string>
     <string name="no_auto_saver_action" msgid="7467924389609773835">"Đ–ĐŸĐș, Ń€Đ°Ń…ĐŒĐ°Ń‚"</string>
-    <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string>
     <string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"ĐšĐŸĐ»ĐŽĐŸĐœŃƒĐ»ŃƒŃƒĐŽĐ°"</string>
     <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"ĐšĐŸĐ»ĐŽĐŸĐœĐŒĐŸĐ»ĐŸŃ€ Ń‚Ó©ĐŒÓ©ĐœĐșÒŻĐ»Ó©Ń€ĐŽÒŻ паĐčĐŽĐ°Đ»Đ°ĐœŃ‹Đż Đ¶Đ°Ń‚Ń‹ŃˆĐ°Ń‚: <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
     <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"ĐŸĐ°Ń€Đ°ĐŒĐ”Ń‚Ń€Đ»Đ”Ń€ĐŽĐ”Đœ ĐŽĐ”ĐŒĐ”ĐčĐșĐž ĐșысĐșа Đ¶Đ°Đ·ŃƒŃƒĐ»Đ°Ń€ ĐșĐŸĐ»ĐŽĐŸĐœĐŒĐŸŃŃƒĐœ Ń‚ŃƒŃƒŃ€Đ°Đ»Đ°ÒŁŃ‹Đ·"</string>
     <string name="install_app" msgid="5066668100199613936">"ĐšĐŸĐ»ĐŽĐŸĐœĐŒĐŸĐœŃƒ ĐŸŃ€ĐœĐŸŃ‚ŃƒŃƒ"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"бышĐșы эĐșŃ€Đ°ĐœĐłĐ° чыгарасызбы?"</string>
-    <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"УчурЮа ĐžŃˆŃ‚Đ”Đż жатĐșĐ°Đœ барЎыĐș ĐșĐŸŃˆ эĐșŃ€Đ°Đœ араĐșДттДрО Ń‚ĐŸĐșŃ‚ĐŸŃ‚ŃƒĐ»Đ°Ń‚"</string>
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"ИчĐșĐž эĐșŃ€Đ°ĐœŃ‹ÒŁŃ‹Đ· башĐșа эĐșŃ€Đ°ĐœĐłĐ° чыгарылат. ĐĐ»ĐŽŃ‹ÒŁĐșы эĐșŃ€Đ°ĐœŃ‹ÒŁŃ‹Đ· Ó©Ń‡ÒŻŃ€ÒŻĐ»Ó©Ń‚."</string>
     <string name="mirror_display" msgid="2515262008898122928">"бышĐșы эĐșŃ€Đ°Đœ"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Đ–Đ°Đ±ŃƒŃƒ"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Đ­ĐșŃ€Đ°Đœ Ń‚ŃƒŃ‚Đ°ŃˆŃ‚Ń‹Ń€Ń‹Đ»ĐŽŃ‹"</string>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index cf5ef3a..c0c5d44 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -330,12 +330,17 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"àșšàș±àș™àș—àș¶àșà»œà»‰àșČàșˆà»"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"ເàș„àș”່àșĄ"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"àșąàșžàș”"</string>
-    <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
-    <skip />
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"àșšàș±àș™àș«àșČàșà»ˆàșœàș§àșàș±àșšàșàșČàș™àșšàș±àș™àș—àș¶àș"</string>
+    <string name="qs_record_issue_start" msgid="2979831312582567056">"ເàș„àș”່àșĄ"</string>
+    <string name="qs_record_issue_stop" msgid="3531747965741982657">"àșąàșžàș”"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"àșȘ່àș§àș™à»ƒàș”àș‚àș­àș‡àș›àș°àșȘàș»àșšàșàșČàș™àș­àșžàș›àș°àșàș­àș™àș‚àș­àș‡àș—່àșČàș™à»„àș”້àșźàș±àșšàșœàș»àș™àșàș°àș—àș»àșš?"</string>
+    <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"ເàș„àș·àș­àșàș›àș°à»€àșžàș”àșšàș±àș™àș«àșČ"</string>
+    <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"àșšàș±àș™àș—àș¶àșà»œà»‰àșČàșˆà»"</string>
+  <string-array name="qs_record_issue_types">
+    <item msgid="2947988124014085798">"àș›àș°àșȘàșŽàș”àș—àșŽàșžàșČàșš"</item>
+    <item msgid="1627504621139124393">"àșȘ່àș§àș™àș•àșŽàș”àș•ໍ່àșœàșč້ໃàșŠà»‰"</item>
+    <item msgid="8309220355268900335">"ແàșšàș±àș”ເàș•àș”àșŁàș”"</item>
+  </string-array>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"ໂໝàș”àșĄàș·àș”àșœàș§"</string>
     <string name="quick_settings_contrast_label" msgid="988087460210159123">"àș„àș­àș™àș—àșŁàșČàșȘ"</string>
     <string name="quick_settings_contrast_standard" msgid="2538227821968061832">"àșĄàșČàș”àș•àș°àș–àșČàș™"</string>
@@ -408,12 +413,9 @@
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • àșàșłàș„àș±àș‡àșȘàșČàșà»„àșŸ • àșˆàș°à»€àș•àș±àșĄà»ƒàș™àș­àș”àș <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"àș›àș±àș”àșŠà»‰àșČàșà»€àșžàș·à»ˆàș­à»€àș„àș”່àșĄàșšàș»àș”ແàș™àș°àș™àșłàșȘ່àș§àș™àșàșČàș‡"</string>
     <string name="button_to_open_widget_editor" msgid="5599945944349057600">"ເàș›àș”àș”àș•àș»àș§à»àșà»‰à»„àș‚àș§àșŽàș”ເàșˆàș±àș”"</string>
-    <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
-    <skip />
-    <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
-    <skip />
-    <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
-    <skip />
+    <string name="button_to_remove_widget" msgid="3948204829181214098">"àș„àș¶àșšàș­àș­àș"</string>
+    <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"ເàșžàș”່àșĄàș§àșŽàș”ເàșˆàș±àș”"</string>
+    <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"ແàș„້àș§à»†"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"àșȘàș°àș«àșŒàș±àșšàșœàșč້ໃàșŠà»‰"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ເàșĄàș™àșčແàșšàșšàș”àș¶àș‡àș„àș»àș‡"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ແàș­àș±àșšàșŻâ€‹à»àș„àș°â€‹àș‚ໍ້​àșĄàșčàș™â€‹àș—àș±àș‡â€‹à»àș»àș”​ໃàș™â€‹à»€àșŠàș”​àșŠàș±àș™â€‹àș™àș”້​àșˆàș°â€‹àș–àș·àșâ€‹àș„àș¶àșšâ€‹àș­àș­àș."</string>
@@ -867,7 +869,6 @@
     <string name="auto_saver_title" msgid="6873691178754086596">"ແàș•àș°à»€àșžàș·à»ˆàș­àș•àș±à»‰àș‡àșàșČàș™à»€àș›àș”àș”àș•àș»àș§àș›àș°àșąàș±àș”ແàșšàș±àș”ເàș•àș”àșŁàș”"</string>
     <string name="auto_saver_text" msgid="3214960308353838764">"ເàș›àș”àș”ໃàșŠà»‰à»€àșĄàș·à»ˆàș­à»àșšàș±àș”ເàș•àș”àșŁàș”ໜ້àșČàșˆàș°à»ƒàșà»‰à»àș»àș”"</string>
     <string name="no_auto_saver_action" msgid="7467924389609773835">"àșšà»à»ˆ, àș‚àș­àșšà»ƒàșˆ"</string>
-    <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string>
     <string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"àșàșłàș„àș±àș‡àș™àșłà»ƒàșŠà»‰àșąàșč່"</string>
     <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"ແàș­àș±àșšàșžàș„àșŽà»€àș„àșŠàș±àș™àșàșłàș„àș±àș‡à»ƒàșŠà»‰ <xliff:g id="TYPES_LIST">%s</xliff:g> àș‚àș­àș‡àș—່àșČàș™."</string>
     <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"àș•àș±à»‰àș‡àș„່àșČແàș­àș±àșšàșˆàș»àș”àșšàș±àș™àș—àș¶àșà»€àș„àș”່àșĄàș•àș»à»‰àș™à»ƒàș™àșàșČàș™àș•àș±à»‰àș‡àș„່àșČ"</string>
     <string name="install_app" msgid="5066668100199613936">"àș•àșŽàș”àș•àș±à»‰àș‡à»àș­àș±àșš"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"àșȘàșČàșà»ƒàșȘ່àșˆà»àșȘàș°à»àș”àș‡àșœàș»àș™àșžàșČàșàș™àș­àșàșšà»?"</string>
-    <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"àșàșČàș™à»€àș„àș·à»ˆàș­àș™à»„àș«àș§à»ƒàș”àșà»àș•àșČàșĄàș—àș”່àșàșłàș„àș±àș‡àș”àșłà»€àș™àș”àș™àșàșČàș™àșąàșč່ Dual Screen àșˆàș°àșąàșžàș”àș„àș»àș‡"</string>
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"àșàșČàș™àșȘàș°à»àș”àș‡àșœàș»àș™àș—àșČàș‡à»ƒàș™àș‚àș­àș‡àș—່àșČàș™àșˆàș°àș–àș·àșàșȘàș°àș—້àș­àș™. àșàșČàș™àșȘàș°à»àș”àș‡àșœàș»àș™àș—àșČàș‡à»œà»‰àșČàș‚àș­àș‡àș—່àșČàș™àșˆàș°àș–àș·àșàș›àșŽàș”ໄàș§à»‰."</string>
     <string name="mirror_display" msgid="2515262008898122928">"àșˆà»àșȘàș°à»àș”àș‡àșœàș»àș™à»àșšàșšàșȘàș°àș—້àș­àș™"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"àș›àșŽàș”ໄàș§à»‰"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"ເàșŠàș·à»ˆàș­àșĄàș•ໍ່àșˆà»à»àș„້àș§"</string>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index ddad0be..83d2724 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -330,12 +330,17 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Ekrano ÄŻrašas"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Pradėti"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Stabdyti"</string>
-    <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
-    <skip />
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"Äźrašyti problemą"</string>
+    <string name="qs_record_issue_start" msgid="2979831312582567056">"Pradėti"</string>
+    <string name="qs_record_issue_stop" msgid="3531747965741982657">"Stabdyti"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Kuri ÄŻrenginio funkcija buvo paveikta?"</string>
+    <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Pasirinkite problemos tipą"</string>
+    <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Ekrano ÄŻrašas"</string>
+  <string-array name="qs_record_issue_types">
+    <item msgid="2947988124014085798">"Našumas"</item>
+    <item msgid="1627504621139124393">"Naudotojo sąsaja"</item>
+    <item msgid="8309220355268900335">"Akumuliatorius"</item>
+  </string-array>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Vienos rankos reĆŸimas"</string>
     <string name="quick_settings_contrast_label" msgid="988087460210159123">"Kontrastas"</string>
     <string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Äźprastas"</string>
@@ -408,12 +413,9 @@
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Äźkraunama • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> iki visiško ÄŻkrovimo"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Perbraukite kairėn, paleistumėte bendruomenės mokomąją medĆŸiagą"</string>
     <string name="button_to_open_widget_editor" msgid="5599945944349057600">"Atidaryti valdiklio redagavimo programą"</string>
-    <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
-    <skip />
-    <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
-    <skip />
-    <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
-    <skip />
+    <string name="button_to_remove_widget" msgid="3948204829181214098">"Pašalinti"</string>
+    <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Pridėti valdiklį"</string>
+    <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Atlikta"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Perjungti naudotoją"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"išplečiamasis meniu"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Bus ištrintos visos šios sesijos programos ir duomenys."</string>
@@ -867,7 +869,6 @@
     <string name="auto_saver_title" msgid="6873691178754086596">"Palietę planuokite akumuliatoriaus tausojimo priemonės veikimą"</string>
     <string name="auto_saver_text" msgid="3214960308353838764">"Äźjunkite, jei akumuliatorius gali greitai išsekti"</string>
     <string name="no_auto_saver_action" msgid="7467924389609773835">"Ne, ačiĆ«"</string>
-    <string name="heap_dump_tile_name" msgid="2464189856478823046">"Pat. „SysUI“ krĆ«vą"</string>
     <string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"Naudojama"</string>
     <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Programos naudoja: <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
     <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Nustatykite numatytąją uĆŸrašĆł programą Nustatymuose"</string>
     <string name="install_app" msgid="5066668100199613936">"ļdiegti programą"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Bendrinti ekrano vaizdą išoriniame ekrane?"</string>
-    <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"Bet kokia šiuo metu vykdoma dvigubo ekrano veikla bus sustabdyta"</string>
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Bus bendrinamas vidinio rodinio ekrano vaizdas. Priekinis rodinys bus išjungtas."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Bendrinti ekrano vaizdą"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Atsisakyti"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Ekranas prijungtas"</string>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index 707c656..4bc71c3 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -330,12 +330,17 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Ekrāna ierakstÄ«šana"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Sākt"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Apturēt"</string>
-    <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
-    <skip />
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"Problēmas ierakstÄ«šana"</string>
+    <string name="qs_record_issue_start" msgid="2979831312582567056">"Sākt"</string>
+    <string name="qs_record_issue_stop" msgid="3531747965741982657">"Apturēt"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Kuras ierīces funkcijas tika ietekmētas?"</string>
+    <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Atlasiet problēmas veidu"</string>
+    <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Ekrāna ierakstÄ«šana"</string>
+  <string-array name="qs_record_issue_types">
+    <item msgid="2947988124014085798">"Veiktspēja"</item>
+    <item msgid="1627504621139124393">"Lietotāja saskarne"</item>
+    <item msgid="8309220355268900335">"Akumulators"</item>
+  </string-array>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Vienas rokas reĆŸÄ«ms"</string>
     <string name="quick_settings_contrast_label" msgid="988087460210159123">"Kontrasts"</string>
     <string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Standarta"</string>
@@ -408,12 +413,9 @@
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Notiek uzlāde • Laiks lÄ«dz pilnai uzlādei: <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Velciet pa kreisi, lai palaistu kopienas pamācību."</string>
     <string name="button_to_open_widget_editor" msgid="5599945944349057600">"Atvērt logrīku redaktoru"</string>
-    <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
-    <skip />
-    <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
-    <skip />
-    <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
-    <skip />
+    <string name="button_to_remove_widget" msgid="3948204829181214098">"NoƆemt"</string>
+    <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Pievienot logrīku"</string>
+    <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Gatavs"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Mainīt lietotāju"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"novelkamā izvēlne"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Tiks dzēstas visas šÄ«s sesijas lietotnes un dati."</string>
@@ -867,7 +869,6 @@
     <string name="auto_saver_title" msgid="6873691178754086596">"Pieskarieties, lai iestatÄ«tu akumulatora enerÄŁijas taupÄ«šanas reĆŸÄ«ma grafiku"</string>
     <string name="auto_saver_text" msgid="3214960308353838764">"Ieslēgt, ja akumulators var izlādēties"</string>
     <string name="no_auto_saver_action" msgid="7467924389609773835">"Nē, paldies"</string>
-    <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string>
     <string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"Pašlaik izmanto"</string>
     <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Lietojumprogrammas izmanto šÄdas funkcijas: <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
     <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Iestatījumos iestatiet noklusējuma piezīmju lietotni."</string>
     <string name="install_app" msgid="5066668100199613936">"Instalēt lietotni"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Vai spoguÄŒot ārējā displejā?"</string>
-    <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"Visas pašlaik aktÄ«vās divu ekrānu darbÄ«bas tiks apturētas"</string>
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"JĆ«su iekšÄ“jais displejs tiks spoguÄŒots. JĆ«su priekšÄ“jais displejs tiks izslēgts."</string>
     <string name="mirror_display" msgid="2515262008898122928">"SpoguÄŒot displeju"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Nerādīt"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Pievienots displejs"</string>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index 24cd49a..28a2a02 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -330,12 +330,17 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"ĐĄĐœĐžĐŒĐ°ŃšĐ” Đ”ĐșŃ€Đ°Đœ"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Đ—Đ°ĐżĐŸŃ‡ĐœĐž"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"ĐĄĐŸĐżŃ€Đž"</string>
-    <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
-    <skip />
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"ЕĐČĐžĐŽĐ”ĐœŃ‚ĐžŃ€Đ°Ń˜Ń‚Đ” ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒ"</string>
+    <string name="qs_record_issue_start" msgid="2979831312582567056">"Đ—Đ°ĐżĐŸŃ‡ĐœĐ”Ń‚Đ”"</string>
+    <string name="qs_record_issue_stop" msgid="3531747965741982657">"ĐĄĐŸĐżŃ€Đ”Ń‚Đ”"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"ĐšĐŸŃ˜ ЎДл ĐŸĐŽ ĐŽĐŸĐ¶ĐžĐČуĐČĐ°ŃšĐ”Ń‚ĐŸ ĐœĐ° ŃƒŃ€Đ”ĐŽĐŸŃ‚ бДшД Đ·Đ°ŃĐ”ĐłĐœĐ°Ń‚?"</string>
+    <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Đ˜Đ·Đ±Đ”Ń€Đ”Ń‚Đ” топ ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒ"</string>
+    <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"ĐĄĐœĐžĐŒĐ°ŃšĐ” Đ”ĐșŃ€Đ°Đœ"</string>
+  <string-array name="qs_record_issue_types">
+    <item msgid="2947988124014085798">"ИзĐČДЎба"</item>
+    <item msgid="1627504621139124393">"ĐšĐŸŃ€ĐžŃĐœĐžŃ‡ĐșĐž ĐžĐœŃ‚Đ”Ń€Ń„Đ”Ń˜Ń"</item>
+    <item msgid="8309220355268900335">"Đ‘Đ°Ń‚Đ”Ń€ĐžŃ˜Đ°"</item>
+  </string-array>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Đ Đ”Đ¶ĐžĐŒ ŃĐŸ Đ”ĐŽĐœĐ° раĐșа"</string>
     <string name="quick_settings_contrast_label" msgid="988087460210159123">"ĐšĐŸĐœŃ‚Ń€Đ°ŃŃ‚"</string>
     <string name="quick_settings_contrast_standard" msgid="2538227821968061832">"ĐĄŃ‚Đ°ĐœĐŽĐ°Ń€ĐŽĐ”Đœ"</string>
@@ -408,12 +413,9 @@
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ĐĄĐ” ĐżĐŸĐ»ĐœĐž • ĐŸĐŸĐ»ĐœĐ° ĐżĐŸ <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"ĐŸĐŸĐČлДчДтД ĐœĐ°Đ»Đ”ĐČĐŸ за Ўа ĐłĐŸ Đ·Đ°ĐżĐŸŃ‡ĐœĐ”Ń‚Đ” Đ·Đ°Đ”ĐŽĐœĐžŃ‡ĐșĐŸŃ‚ĐŸ упатстĐČĐŸ"</string>
     <string name="button_to_open_widget_editor" msgid="5599945944349057600">"Đ“ĐŸ ĐŸŃ‚ĐČĐŸŃ€Đ° ŃƒŃ€Đ”ĐŽĐœĐžĐșĐŸŃ‚ ĐœĐ° ĐČĐžŃŸĐ”Ń‚Đž"</string>
-    <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
-    <skip />
-    <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
-    <skip />
-    <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
-    <skip />
+    <string name="button_to_remove_widget" msgid="3948204829181214098">"ĐžŃ‚ŃŃ‚Ń€Đ°ĐœŃƒĐČа"</string>
+    <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Đ”ĐŸĐŽĐ°Ń˜Ń‚Đ” ĐČĐžŃŸĐ”Ń‚"</string>
+    <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Đ“ĐŸŃ‚ĐŸĐČĐŸ"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ĐŸŃ€ĐŸĐŒĐ”ĐœĐž ĐłĐŸ ĐșĐŸŃ€ĐžŃĐœĐžĐșĐŸŃ‚"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"паѓачĐșĐŸ ĐŒĐ”ĐœĐž"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ХОтД аплОĐșацоо Đž ĐżĐŸĐŽĐ°Ń‚ĐŸŃ†Đž ĐČĐŸ ŃĐ”ŃĐžŃ˜Đ°Ń‚Đ° ŃœĐ” сД ĐžĐ·Đ±Ń€ĐžŃˆĐ°Ń‚."</string>
@@ -867,7 +869,6 @@
     <string name="auto_saver_title" msgid="6873691178754086596">"Đ”ĐŸĐżŃ€Đ”Ń‚Đ” за Ўа заĐșажДтД „КтДЎач ĐœĐ° Đ±Đ°Ń‚Đ”Ń€ĐžŃ˜Đ°“"</string>
     <string name="auto_saver_text" msgid="3214960308353838764">"ВĐșĐ»ŃƒŃ‡Đž аĐșĐŸ Đ” ĐČĐ”Ń€ĐŸŃ˜Đ°Ń‚ĐœĐŸ ĐŽĐ”Đșа Đ±Đ°Ń‚Đ”Ń€ĐžŃ˜Đ°Ń‚Đ° ŃœĐ” сД ĐžŃĐżŃ€Đ°Đ·ĐœĐž"</string>
     <string name="no_auto_saver_action" msgid="7467924389609773835">"ĐĐ”, фала"</string>
-    <string name="heap_dump_tile_name" msgid="2464189856478823046">"ИзĐČаЎО SysUI-слОĐșа"</string>
     <string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"Đ’ĐŸ ŃƒĐżĐŸŃ‚Ń€Đ”Đ±Đ°"</string>
     <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"АплОĐșацООтД ĐșĐŸŃ€ĐžŃŃ‚Đ°Ń‚ <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
     <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"ĐŸĐŸŃŃ‚Đ°ĐČДтД ŃŃ‚Đ°ĐœĐŽĐ°Ń€ĐŽĐœĐ° аплОĐșацоја за бДлДшĐșĐž ĐČĐŸ „ĐŸĐŸŃŃ‚Đ°ĐČĐșĐž“"</string>
     <string name="install_app" msgid="5066668100199613936">"Đ˜ĐœŃŃ‚Đ°Đ»ĐžŃ€Đ°Ń˜Ń‚Đ” ја аплОĐșацојата"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Да сД прДслОĐșа ĐœĐ° ĐœĐ°ĐŽĐČĐŸŃ€Đ”ŃˆĐœĐžĐŸŃ‚ Đ”ĐșŃ€Đ°Đœ?"</string>
-    <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"ĐĄĐ”ĐșĐŸŃ˜Đ° аĐșтоĐČĐœĐŸŃŃ‚ ŃˆŃ‚ĐŸ тДĐșĐŸĐČĐœĐŸ сД ОзĐČршуĐČа ĐœĐ° Dual Screen ŃœĐ” сД ŃĐŸĐżŃ€Đ”"</string>
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Đ’Đ°ŃˆĐžĐŸŃ‚ ĐČĐœĐ°Ń‚Ń€Đ”ŃˆĐ”Đœ Đ”ĐșŃ€Đ°Đœ ŃœĐ” сД ĐŸŃ‚ŃĐ»ĐžĐșа. Đ’Đ°ŃˆĐžĐŸŃ‚ ĐżŃ€Đ”ĐŽĐ”Đœ Đ”ĐșŃ€Đ°Đœ ŃœĐ” сД ОсĐșĐ»ŃƒŃ‡Đž."</string>
     <string name="mirror_display" msgid="2515262008898122928">"ĐŸŃ€Đ”ŃĐ»ĐžĐșај Đ”ĐșŃ€Đ°Đœ"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"ОтфрлО"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"ЕĐșŃ€Đ°ĐœĐŸŃ‚ Đ” ĐżĐŸĐČŃ€Đ·Đ°Đœ"</string>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index 1f153c5..057f0b0 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -330,12 +330,17 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"àŽžà”‌àŽ•à”àŽ°à”€à”» àŽ±à”†àŽ•à”àŽ•à”‹à”ŒàŽĄà”"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"àŽ†àŽ°àŽ‚àŽ­àŽżàŽ•à”àŽ•à”àŽ•"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"àŽšàŽżàŽ°à”‍àŽ€à”àŽ€à”àŽ•"</string>
-    <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
-    <skip />
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"àŽȘà”àŽ°àŽ¶à”‌àŽšàŽ‚ àŽ±à”†àŽ•à”àŽ•à”‹à”ŒàŽĄà” àŽšà”†àŽŻà”àŽŻà”àŽ•"</string>
+    <string name="qs_record_issue_start" msgid="2979831312582567056">"àŽ†àŽ°àŽ‚àŽ­àŽżàŽ•à”àŽ•à”àŽ•"</string>
+    <string name="qs_record_issue_stop" msgid="3531747965741982657">"àŽšàŽżà”ŒàŽ€à”àŽ€à”àŽ•"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"àŽšàŽżàŽ™à”àŽ™àŽłà”àŽŸà”† àŽ‰àŽȘàŽ•àŽ°àŽŁ àŽ…àŽšà”àŽ­àŽ”àŽ€à”àŽ€àŽżàŽšà”‍àŽ±à”† àŽàŽ€à” àŽ­àŽŸàŽ—àŽźàŽŸàŽŁà” àŽŹàŽŸàŽ§àŽżàŽšà”àŽšàŽ€à”?"</string>
+    <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"àŽȘà”àŽ°àŽ¶à”àŽš àŽ€àŽ°àŽ‚ àŽ€àŽżàŽ°àŽžà”àŽžà”†àŽŸà”àŽ•à”àŽ•à”àŽ•"</string>
+    <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"àŽžà”‌àŽ•à”àŽ°à”€à”» àŽ±à”†àŽ•à”àŽ•à”‹à”ŒàŽĄà”"</string>
+  <string-array name="qs_record_issue_types">
+    <item msgid="2947988124014085798">"àŽȘà”àŽ°àŽ•àŽŸàŽšàŽ‚"</item>
+    <item msgid="1627504621139124393">"àŽ‰àŽȘàŽŻà”‹àŽ•à”àŽ€à”ƒ àŽ‡àŽšà”‍àŽ±àŽ°à”‍àŽ«à”‡àŽžà”"</item>
+    <item msgid="8309220355268900335">"àŽŹàŽŸàŽ±à”àŽ±àŽ±àŽż"</item>
+  </string-array>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"àŽ’àŽ±à”àŽ±àŽ•à”àŽ•à”ˆ àŽźà”‹àŽĄà”"</string>
     <string name="quick_settings_contrast_label" msgid="988087460210159123">"àŽ•à”‹à”șàŽŸà”àŽ°àŽŸàŽžà”àŽ±à”àŽ±à”"</string>
     <string name="quick_settings_contrast_standard" msgid="2538227821968061832">"àŽžà”‌àŽ±à”àŽ±àŽŸà”»àŽĄà”‡à”ŒàŽĄà”"</string>
@@ -408,12 +413,9 @@
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • àŽšàŽŸà”ŒàŽœà” àŽšà”†àŽŻà”àŽŻà”àŽšà”àŽšà” • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>-à”œ àŽȘà”‚à”ŒàŽ€à”àŽ€àŽżàŽŻàŽŸàŽ•à”àŽ‚"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"àŽ•àŽźà”àŽźà”àŽŻà”‚àŽŁà”œ àŽŸà”àŽŻà”‚àŽŸà”àŽŸà”‹àŽ±àŽżàŽŻà”œ àŽ†àŽ°àŽ‚àŽ­àŽżàŽ•à”àŽ•àŽŸà”» àŽ‡àŽŸàŽ€à”àŽ€à”‹àŽŸà”àŽŸà” àŽžà”àŽ”à”ˆàŽȘà”àŽȘà” àŽšà”†àŽŻà”àŽŻà”àŽ•"</string>
     <string name="button_to_open_widget_editor" msgid="5599945944349057600">"àŽ”àŽżàŽœàŽ±à”àŽ±à” àŽŽàŽĄàŽżàŽ±à”àŽ±à”Œ àŽ€à”àŽ±àŽ•à”àŽ•à”àŽ•"</string>
-    <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
-    <skip />
-    <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
-    <skip />
-    <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
-    <skip />
+    <string name="button_to_remove_widget" msgid="3948204829181214098">"àŽšà”€àŽ•à”àŽ•àŽ‚ àŽšà”†àŽŻà”àŽŻà”àŽ•"</string>
+    <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"àŽ”àŽżàŽœàŽ±à”àŽ±à” àŽšà”‡à”ŒàŽ•à”àŽ•à”àŽ•"</string>
+    <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"àŽȘà”‚à”ŒàŽ€à”àŽ€àŽżàŽŻàŽŸàŽŻàŽż"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"àŽ‰àŽȘàŽŻà”‹àŽ•à”àŽ€àŽŸàŽ”à” àŽźàŽŸàŽ±à”àŽ•"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"àŽȘà”à”ŸàŽĄà”—à”ș àŽźà”†àŽšà”"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"àŽˆ àŽžà”†àŽ·àŽšàŽżàŽČà”† àŽŽàŽČà”àŽČàŽŸ àŽ†àŽȘà”àŽȘà”àŽ•àŽłà”àŽ‚ àŽĄàŽŸàŽ±à”àŽ±àŽŻà”àŽ‚ àŽ‡àŽČà”àŽČàŽŸàŽ€àŽŸàŽ•à”àŽ•à”àŽ‚."</string>
@@ -867,7 +869,6 @@
     <string name="auto_saver_title" msgid="6873691178754086596">"àŽŹàŽŸàŽ±à”àŽ±àŽ±àŽż àŽČàŽŸàŽ­àŽżàŽ•à”àŽ•à”œ àŽ·à”†àŽĄà”àŽŻà”‚à”Ÿ àŽšà”†àŽŻà”àŽŻàŽŸà”» àŽŸàŽŸàŽȘà”àŽȘà” àŽšà”†àŽŻà”àŽŻà”àŽ•"</string>
     <string name="auto_saver_text" msgid="3214960308353838764">"àŽŹàŽŸàŽ±à”àŽ±àŽ±àŽż àŽšàŽŸà”ŒàŽœà” àŽ€à”€àŽ°àŽŸà”» àŽžàŽŸàŽ§à”àŽŻàŽ€àŽŻà”àŽŁà”àŽŸà”†àŽ™à”àŽ•àŽżà”œ àŽ“àŽŁàŽŸàŽ•à”àŽ•à”àŽ•"</string>
     <string name="no_auto_saver_action" msgid="7467924389609773835">"àŽ”à”‡àŽŁà”àŽŸ"</string>
-    <string name="heap_dump_tile_name" msgid="2464189856478823046">"SysUI àŽčà”€àŽȘà”àŽȘà” àŽĄàŽźà”àŽȘà” àŽšà”†àŽŻà”àŽŻà”àŽ•"</string>
     <string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"àŽ‰àŽȘàŽŻà”‹àŽ—àŽ€à”àŽ€àŽżàŽČàŽŸàŽŁà”"</string>
     <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"àŽ†àŽȘà”àŽȘà”àŽ•à”Ÿ àŽšàŽżàŽ™à”àŽ™àŽłà”àŽŸà”† <xliff:g id="TYPES_LIST">%s</xliff:g> àŽ‰àŽȘàŽŻà”‹àŽ—àŽżàŽ•à”àŽ•à”àŽšà”àŽšà”."</string>
     <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"àŽ•à”àŽ°àŽźà”€àŽ•àŽ°àŽŁàŽ€à”àŽ€àŽżà”œ àŽ•à”àŽ±àŽżàŽȘà”àŽȘà”àŽ•à”ŸàŽ•à”àŽ•à”àŽłà”àŽł àŽĄàŽżàŽ«à”‹à”ŸàŽŸà”àŽŸà” àŽ†àŽȘà”àŽȘà” àŽžàŽœà”àŽœà”€àŽ•àŽ°àŽżàŽ•à”àŽ•à”àŽ•"</string>
     <string name="install_app" msgid="5066668100199613936">"àŽ†àŽȘà”àŽȘà” àŽ‡à”»àŽžà”àŽ±à”àŽ±àŽŸà”Ÿ àŽšà”†àŽŻà”àŽŻà”‚"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"àŽŹàŽŸàŽčà”àŽŻ àŽĄàŽżàŽžà”‌àŽȘà”àŽČà”‡àŽŻàŽżàŽČà”‡àŽ•à”àŽ•à” àŽźàŽżàŽ±à”Œ àŽšà”†àŽŻà”àŽŻàŽŁà”‹?"</string>
-    <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"àŽšàŽżàŽČàŽ”àŽżà”œ àŽ±à”ș àŽšà”†àŽŻà”àŽŻà”àŽšà”àŽš àŽàŽ€à”ŠàŽ°à” àŽĄà”àŽŻà”àŽ”à”œ àŽžà”àŽ•à”àŽ°à”€à”» àŽ†àŽ•à”àŽ±à”àŽ±àŽżàŽ”àŽżàŽ±à”àŽ±àŽżàŽŻà”àŽ‚ àŽšàŽżà”ŒàŽ€à”àŽ€à”àŽ‚"</string>
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"àŽšàŽżàŽ™à”àŽ™àŽłà”àŽŸà”† àŽ‡àŽšà”àŽšà”Œ àŽĄàŽżàŽžà”àŽȘà”àŽČà”‡ àŽźàŽżàŽ±à”Œ àŽšà”†àŽŻà”àŽŻà”àŽ‚. àŽšàŽżàŽ™à”àŽ™àŽłà”àŽŸà”† àŽ«à”àŽ°àŽŁà”àŽŸà” àŽĄàŽżàŽžà”àŽȘà”àŽČà”‡ àŽ“àŽ«àŽŸàŽ•à”àŽ‚."</string>
     <string name="mirror_display" msgid="2515262008898122928">"àŽźàŽżàŽ±à”Œ àŽĄàŽżàŽžà”‌àŽȘà”àŽČà”‡"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"àŽĄàŽżàŽžà”‌àŽźàŽżàŽžà” àŽšà”†àŽŻà”àŽŻà”àŽ•"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"àŽĄàŽżàŽžà”àŽȘà”àŽČà”‡ àŽ•àŽŁàŽ•à”àŽ±à”àŽ±à” àŽšà”†àŽŻà”àŽ€àŽżàŽ°àŽżàŽ•à”àŽ•à”àŽšà”àŽšà”"</string>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index 91f7180..933652b 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -330,12 +330,17 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Đ”ŃĐ»ĐłŃŃ†ĐžĐčĐœ ÒŻĐčĐ»ĐŽŃĐ» бОчОх"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Đ­Ń…Đ»ÒŻÒŻĐ»ŃŃ…"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Đ—ĐŸĐłŃĐŸĐŸŃ…"</string>
-    <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
-    <skip />
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"ĐŃŃƒŃƒĐŽĐ»Ń‹Đł бОчОх"</string>
+    <string name="qs_record_issue_start" msgid="2979831312582567056">"Đ­Ń…Đ»ÒŻÒŻĐ»ŃŃ…"</string>
+    <string name="qs_record_issue_stop" msgid="3531747965741982657">"Đ—ĐŸĐłŃĐŸĐŸŃ…"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"ĐąĐ°ĐœŃ‹ Ń‚Ó©Ń…Ó©Ó©Ń€Ó©ĐŒĐ¶ĐžĐčĐœ Ń…ŃŃ€ŃĐłĐ»ŃŃĐœĐžĐč Đ°Đ»ŃŒ хэсэгт ĐœÓ©Đ»Ó©Ó©Đ»ŃÓ©Đœ Đ±Ń?"</string>
+    <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"ĐŃŃƒŃƒĐŽĐ»Ń‹Đœ төрөл ŃĐŸĐœĐłĐŸĐœĐŸ уу"</string>
+    <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Đ”ŃĐ»ĐłŃŃ†ĐžĐčĐœ Đ±ĐžŃ‡Đ»ŃĐł"</string>
+  <string-array name="qs_record_issue_types">
+    <item msgid="2947988124014085798">"Đ“ÒŻĐčŃ†ŃŃ‚ĐłŃĐ»"</item>
+    <item msgid="1627504621139124393">"Đ„ŃŃ€ŃĐłĐ»ŃĐłŃ‡ĐžĐčĐœ ĐžĐœŃ‚Đ”Ń€Ń„Đ”Đčс"</item>
+    <item msgid="8309220355268900335">"БатарДĐč"</item>
+  </string-array>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Нэг ĐłĐ°Ń€Ń‹Đœ ĐłĐŸŃ€ĐžĐŒ"</string>
     <string name="quick_settings_contrast_label" msgid="988087460210159123">"Ялгарал"</string>
     <string name="quick_settings_contrast_standard" msgid="2538227821968061832">"ĐĄŃ‚Đ°ĐœĐŽĐ°Ń€Ń‚"</string>
@@ -408,12 +413,9 @@
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ĐŠŃĐœŃĐłĐ»ŃĐ¶ баĐčĐœĐ° • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>-Đœ Юараа ĐŽÒŻÒŻŃ€ĐœŃ"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"НоĐčтоĐčĐœ праĐșтоĐș Ń…ĐžŃ‡ŃŃĐ»ĐžĐčĐł ŃŃ…Đ»ÒŻÒŻĐ»ŃŃ…ĐžĐčĐœ Ń‚ŃƒĐ»ĐŽ Đ·ÒŻÒŻĐœ тоĐčш ŃˆŃƒĐŽĐ°Ń€ĐœĐ° уу"</string>
     <string name="button_to_open_widget_editor" msgid="5599945944349057600">"ВОЎжДт засĐČарлагчОĐčĐł ĐœŃŃŃ…"</string>
-    <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
-    <skip />
-    <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
-    <skip />
-    <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
-    <skip />
+    <string name="button_to_remove_widget" msgid="3948204829181214098">"Єасах"</string>
+    <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"ВОЎжДт ĐœŃĐŒŃŃ…"</string>
+    <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Đ‘ĐŸĐ»ŃĐŸĐœ"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Đ„ŃŃ€ŃĐłĐ»ŃĐłŃ‡ĐžĐčĐł ŃŃĐ»ĐłŃŃ…"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"эĐČŃ…ĐŒŃĐ» цэс"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Đ­ĐœŃ Ń…Đ°Ń€ĐžĐ»Ń†Đ°Đœ ÒŻĐčлЎлОĐčĐœ Đ±ÒŻŃ… апп Đ±ĐŸĐ»ĐŸĐœ Юата устах Đ±ĐŸĐ»ĐœĐŸ."</string>
@@ -867,7 +869,6 @@
     <string name="auto_saver_title" msgid="6873691178754086596">"БатарДĐč Ń…ŃĐŒĐœŃĐłŃ‡ ĐŸĐœŃ†Đ»ĐŸĐłĐžĐčĐł хуĐČĐ°Đ°Ń€ĐžĐ»Đ°Ń…Ń‹Đœ Ń‚ŃƒĐ»ĐŽ Ń‚ĐŸĐČшох"</string>
     <string name="auto_saver_text" msgid="3214960308353838764">"БатарДĐč Юуусах ĐłŃĐ¶ баĐčгаа ÒŻĐ”ĐŽ Đ°ŃĐ°Đ°ĐœĐ° уу"</string>
     <string name="no_auto_saver_action" msgid="7467924389609773835">"ÒźĐłÒŻĐč, Đ±Đ°ŃŃ€Đ»Đ°Đ»Đ°Đ°"</string>
-    <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string>
     <string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"АшОглаж баĐčгаа"</string>
     <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"АппууЎ Ń‚Đ°ĐœŃ‹ <xliff:g id="TYPES_LIST">%s</xliff:g>-Đł ашОглаж баĐčĐœĐ°."</string>
     <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"ĐąĐŸŃ…ĐžŃ€ĐłĐŸĐŸĐœĐŽ Ń‚ŃĐŒĐŽŃĐłĐ»ŃĐ»ĐžĐčĐœ Ó©ĐłÓ©ĐłĐŽĐŒÓ©Đ» апп Ń‚ĐŸŃ…ĐžŃ€ŃƒŃƒĐ»ĐœĐ° уу"</string>
     <string name="install_app" msgid="5066668100199613936">"Аппыг ŃŃƒŃƒĐ»ĐłĐ°Ń…"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Đ“Đ°ĐŽĐœŃ‹ ĐŽŃĐ»ĐłŃŃ†ŃĐŽ Ń‚ŃƒŃĐłĐ°Đ» ÒŻÒŻŃĐłŃŃ… ÒŻÒŻ?"</string>
-    <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"ĐžĐŽĐŸĐŸ ажОллаж буĐč алОĐČаа Ń…ĐŸŃ ĐŽŃĐ»ĐłŃŃ†ĐžĐčĐœ ÒŻĐčĐ» ажОллагааг Đ·ĐŸĐłŃĐŸĐŸĐœĐŸ"</string>
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"ĐąĐ°ĐœŃ‹ ĐŽĐŸŃ‚ĐŸĐŸĐŽ ĐŽŃĐ»ĐłŃŃ†ĐžĐčĐœ Ń‚ŃƒŃĐłĐ°Đ»Ń‹Đł ÒŻÒŻŃĐłŃĐœŃ. ĐąĐ°ĐœŃ‹ урЮ Ń‚Đ°Đ»Ń‹Đœ ĐŽŃĐ»ĐłŃŃ†ĐžĐčĐł ŃƒĐœŃ‚Ń€Đ°Đ°ĐœĐ°."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Đ”ŃĐ»ĐłŃŃ†ĐžĐčĐœ Ń‚ŃƒŃĐłĐ°Đ» ÒŻÒŻŃĐłŃŃ…"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Єаах"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Đ”ŃĐ»ĐłŃŃ† Ń…ĐŸĐ»Đ±ĐŸĐłĐŽŃĐŸĐœ"</string>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index 39e2c5c..aad269b 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -330,12 +330,17 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"à€žà„à€•à„à€°à„€à€š à€°à„‡à€•à„‰à€°à„à€Ą"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"à€žà„à€°à„‚"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"à€„à€Ÿà€‚à€Źà€Ÿ"</string>
-    <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
-    <skip />
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"à€žà€źà€žà„à€Żà€Ÿ à€°à„‡à€•à„‰à€°à„à€Ą à€•à€°à€Ÿ"</string>
+    <string name="qs_record_issue_start" msgid="2979831312582567056">"à€žà„à€°à„à€”à€Ÿà€€ à€•à€°à€Ÿ"</string>
+    <string name="qs_record_issue_stop" msgid="3531747965741982657">"à€„à€Ÿà€‚à€Źà€”à€Ÿ"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"à€€à„à€źà€šà„à€Żà€Ÿ à€Ąà€żà€”à„à€čà€Ÿà€‡à€žà€Źà€Ÿà€Źà€€ à€•à„‹à€Łà€€à„à€Żà€Ÿ à€…à€šà„à€­à€”à€Ÿà€”à€° à€Șà€°à€żà€Łà€Ÿà€ź à€à€Ÿà€Čà€Ÿ?"</string>
+    <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"à€žà€źà€žà„à€Żà„‡à€šà€Ÿ à€Șà„à€°à€•à€Ÿà€° à€šà€żà€”à€Ąà€Ÿ"</string>
+    <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"à€žà„à€•à„à€°à„€à€š à€°à„‡à€•à„‰à€°à„à€Ą"</string>
+  <string-array name="qs_record_issue_types">
+    <item msgid="2947988124014085798">"à€Șà€°à€«à„‰à€°à„à€źà€šà„à€ž"</item>
+    <item msgid="1627504621139124393">"à€Żà„‚à€à€° à€‡à€‚à€Ÿà€°à€«à„‡à€ž"</item>
+    <item msgid="8309220355268900335">"à€Źà„…à€Ÿà€°à„€"</item>
+  </string-array>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"à€à€•à€čà€Ÿà€€à„€ à€źà„‹à€Ą"</string>
     <string name="quick_settings_contrast_label" msgid="988087460210159123">"à€•à„‰à€‚à€Ÿà„à€°à€Ÿà€žà„à€Ÿ"</string>
     <string name="quick_settings_contrast_standard" msgid="2538227821968061832">"à€žà€Ÿà€§à€Ÿà€°à€Ł"</string>
@@ -408,12 +413,9 @@
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • à€šà€Ÿà€°à„à€œ à€čà„‹à€€ à€†à€čà„‡ • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> à€źà€§à„à€Żà„‡ à€Șà„‚à€°à„à€Ł à€čà„‹à€ˆà€Č"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"à€žà€Ÿà€źà„à€Šà€Ÿà€Żà€żà€• à€Ÿà„à€Żà„‚à€Ÿà„‹à€°à€żà€Żà€Č à€žà„à€°à„‚ à€•à€°à€Łà„à€Żà€Ÿà€žà€Ÿà€ à„€ à€Ąà€Ÿà€”à„€à€•à€Ąà„‡ à€žà„à€”à€Ÿà€‡à€Ș à€•à€°à€Ÿ"</string>
     <string name="button_to_open_widget_editor" msgid="5599945944349057600">"à€”à€żà€œà„‡à€Ÿ à€žà€‚à€Șà€Ÿà€Šà€• à€‰à€˜à€Ąà€Ÿ"</string>
-    <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
-    <skip />
-    <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
-    <skip />
-    <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
-    <skip />
+    <string name="button_to_remove_widget" msgid="3948204829181214098">"à€•à€Ÿà€ąà„‚à€š à€Ÿà€Ÿà€•à€Ÿ"</string>
+    <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"à€”à€żà€œà„‡à€Ÿ à€œà„‹à€Ąà€Ÿ"</string>
+    <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"à€Șà„‚à€°à„à€Ł à€à€Ÿà€Čà„‡"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"à€”à€Ÿà€Șà€°à€•à€°à„à€€à€Ÿ à€žà„à€”à€żà€š à€•à€°à€Ÿ"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"à€Șà„à€Čà€Ąà€Ÿà€‰à€š à€źà„‡à€šà„‚"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"à€Żà€Ÿ à€žà€€à„à€°à€Ÿà€€à„€à€Č à€žà€°à„à€” à€…‍à„…à€Șà„à€ž à€†à€Łà€ż à€Ąà„‡à€Ÿà€Ÿ à€čà€Ÿà€”à€Čà€Ÿ à€œà€Ÿà€ˆà€Č."</string>
@@ -867,7 +869,6 @@
     <string name="auto_saver_title" msgid="6873691178754086596">"à€Źà„…à€Ÿà€°à„€ à€žà„‡à€”à„à€čà€° à€¶à„‡à€Ąà„à€Żà„‚à€Č à€•à€°à€Łà„à€Żà€Ÿà€žà€Ÿà€ à„€ à€Ÿà„…à€Ș à€•à€°à€Ÿ"</string>
     <string name="auto_saver_text" msgid="3214960308353838764">"à€Źà„…à€Ÿà€°à„€ à€žà€‚à€Șà€Łà„à€Żà€Ÿà€šà„€ à€¶à€•à„à€Żà€€à€Ÿ à€…à€žà€Čà„à€Żà€Ÿà€ž à€žà„à€°à„‚ à€•à€°à€Ÿ"</string>
     <string name="no_auto_saver_action" msgid="7467924389609773835">"à€šà€Ÿà€čà„€ à€šà€•à„‹"</string>
-    <string name="heap_dump_tile_name" msgid="2464189856478823046">"SysUI à€čà„€à€Ș à€Ąà€‚à€Ș à€•à€°à€Ÿ"</string>
     <string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"à€”à€Ÿà€Șà€°à€Ÿà€€ à€†à€čà„‡"</string>
     <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"à„Čà€Șà„à€Čà€żà€•à„‡à€¶à€šà„à€ž à€€à„à€źà€šà„‡ <xliff:g id="TYPES_LIST">%s</xliff:g> à€”à€Ÿà€Șà€°à€€ à€†à€čà„‡."</string>
     <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"à€žà„‡à€Ÿà€żà€‚à€—à„à€œ à€źà€§à„à€Żà„‡ à€Ąà„€à€«à„‰à€Čà„à€Ÿ à€Ÿà€żà€Șà€Ÿ à€…‍à„…à€Ș à€žà„‡à€Ÿ à€•à€°à€Ÿ"</string>
     <string name="install_app" msgid="5066668100199613936">"à€…‍à„…à€Ș à€‡à€‚à€žà„à€Ÿà„‰à€Č à€•à€°à€Ÿ"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"à€Źà€Ÿà€čà„à€Ż à€Ąà€żà€žà„à€Șà„à€Čà„‡à€”à€° à€źà€żà€°à€° à€•à€°à€Ÿà€Żà€šà„‡ à€†à€čà„‡ à€•à€Ÿ?"</string>
-    <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"à€žà€§à„à€Żà€Ÿ à€žà„à€°à„‚ à€…à€žà€Čà„‡à€Čà„€ à€•à„‹à€Łà€€à„€à€čà„€ à€Ąà„à€Żà„à€…à€Č à€žà„à€•à„à€°à„€à€š à€…‍à„…à€•à„à€Ÿà€żà€”à„à€čà€żà€Ÿà„€ à€„à€Ÿà€‚à€Źà€”à€Čà„€ à€œà€Ÿà€ˆà€Č"</string>
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"à€€à„à€źà€šà€Ÿ à€…à€‚à€€à€°à„à€—à€€ à€Ąà€żà€žà„à€Șà„à€Čà„‡ à€źà€żà€°à€° à€•à„‡à€Čà€Ÿ à€œà€Ÿà€ˆà€Č. à€€à„à€źà€šà€Ÿ à€Șà„à€ąà„€à€Č à€Ąà€żà€žà„à€Șà„à€Čà„‡ à€Źà€‚à€Š à€•à„‡à€Čà€Ÿ à€œà€Ÿà€ˆà€Č."</string>
     <string name="mirror_display" msgid="2515262008898122928">"à€Ąà€żà€žà„à€Șà„à€Čà„‡ à€źà€żà€°à€° à€•à€°à€Ÿ"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"à€Ąà€żà€žà€źà€żà€ž à€•à€°à€Ÿ"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"à€Ąà€żà€žà„à€Șà„à€Čà„‡ à€•à€šà„‡à€•à„à€Ÿ à€•à„‡à€Čà€Ÿ à€†à€čà„‡"</string>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index 732a180..8287d2b 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -330,12 +330,17 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Rakam skrin"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Mula"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Berhenti"</string>
-    <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
-    <skip />
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"Rekodkan masalah"</string>
+    <string name="qs_record_issue_start" msgid="2979831312582567056">"Mula"</string>
+    <string name="qs_record_issue_stop" msgid="3531747965741982657">"Hentikan"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Pengalaman peranti yang manakah yang terjejas?"</string>
+    <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Pilih jenis masalah"</string>
+    <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Rakam skrin"</string>
+  <string-array name="qs_record_issue_types">
+    <item msgid="2947988124014085798">"Prestasi"</item>
+    <item msgid="1627504621139124393">"Antara Muka Pengguna"</item>
+    <item msgid="8309220355268900335">"Bateri"</item>
+  </string-array>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Mod sebelah tangan"</string>
     <string name="quick_settings_contrast_label" msgid="988087460210159123">"Kontras"</string>
     <string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Standard"</string>
@@ -408,12 +413,9 @@
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Mengecas • Penuh dalam masa <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Leret ke kiri untuk memulakan tutorial umum"</string>
     <string name="button_to_open_widget_editor" msgid="5599945944349057600">"Buka editor widget"</string>
-    <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
-    <skip />
-    <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
-    <skip />
-    <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
-    <skip />
+    <string name="button_to_remove_widget" msgid="3948204829181214098">"Alih keluar"</string>
+    <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Tambahkan widget"</string>
+    <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Selesai"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Tukar pengguna"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menu tarik turun"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Semua apl dan data dalam sesi ini akan dipadam."</string>
@@ -867,7 +869,6 @@
     <string name="auto_saver_title" msgid="6873691178754086596">"Ketik untuk menjadualkan Penjimat Bateri"</string>
     <string name="auto_saver_text" msgid="3214960308353838764">"Hidupkan apabila bateri berkemungkinan habis"</string>
     <string name="no_auto_saver_action" msgid="7467924389609773835">"Tidak perlu"</string>
-    <string name="heap_dump_tile_name" msgid="2464189856478823046">"DumpSys"</string>
     <string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"Sedang digunakan"</string>
     <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Aplikasi sedang menggunakan <xliff:g id="TYPES_LIST">%s</xliff:g> anda."</string>
     <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Tetapkan apl nota lalai dalam Tetapan"</string>
     <string name="install_app" msgid="5066668100199613936">"Pasang apl"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Paparkan pada paparan luaran?"</string>
-    <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"Sebarang aktiviti dwiskrin yang sedang dijalankan akan dihentikan"</string>
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Paparan dalaman anda akan dicerminkan. Paparan depan anda akan dimatikan."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Segerakkan paparan"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Ketepikan"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Paparan disambungkan"</string>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index 069a833..25afad4 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -330,12 +330,17 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"စခရငá€ș ရိုကá€șကူှရနá€ș"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"စတငá€șရနá€ș"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"ရပá€șရနá€ș"</string>
-    <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
-    <skip />
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"á€•á€Œá€żá€”á€Źá€€á€­á€Ż မဟတá€șတမá€șှတငá€șခဌငá€șှ"</string>
+    <string name="qs_record_issue_start" msgid="2979831312582567056">"စတငá€șပါ"</string>
+    <string name="qs_record_issue_stop" msgid="3531747965741982657">"ရပá€șပါ"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"စကá€șá€Ąá€žá€Żá€¶á€žá€•á€Œá€Żá€™á€Ÿá€Żá မညá€șသည့á€șအပိုငá€șှကို သကá€șရေဏကá€șသလá€Č။"</string>
+    <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"á€•á€Œá€żá€”á€Źá€Ąá€™á€»á€­á€Żá€žá€Ąá€…á€Źá€ž ရလေသရနá€ș"</string>
+    <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"ဖနá€șá€žá€Źá€žá€•á€Œá€„á€șရိုကá€șကူှရနá€ș"</string>
+  <string-array name="qs_record_issue_types">
+    <item msgid="2947988124014085798">"စလမá€șသဆေဏငá€șရညá€ș"</item>
+    <item msgid="1627504621139124393">"á€žá€Żá€¶á€žá€žá€°á€Ąá€á€œá€€á€ș á€€á€Œá€Źá€žá€á€¶á€…á€”á€…á€ș"</item>
+    <item msgid="8309220355268900335">"ဘကá€șထရြ"</item>
+  </string-array>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"လကá€șတစá€șဖကá€șသုံသမုဒá€ș"</string>
     <string name="quick_settings_contrast_label" msgid="988087460210159123">"ဆန့á€șကျငá€șဘကá€ș"</string>
     <string name="quick_settings_contrast_standard" msgid="2538227821968061832">"á€•á€Żá€¶á€™á€Ÿá€”á€ș"</string>
@@ -408,12 +413,9 @@
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • á€Ąá€Źá€žá€žá€œá€„á€șသနေသညá€ș • á€Ąá€Źá€žá€•á€Œá€Šá€·á€șရနá€ș <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> လိုသညá€ș"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"á€Ąá€™á€»á€Źá€žá€žá€Żá€¶á€žá€›á€Ÿá€„á€șှလငá€șသပို့ချချကá€ș စတငá€șရနá€ș ဘယá€șá€žá€­á€Żá€·á€•á€œá€á€șဆလá€Čပါ"</string>
     <string name="button_to_open_widget_editor" msgid="5599945944349057600">"ဝိဂျကá€șတညá€șသဖဌတá€șစနစá€ș ဖလင့á€șရနá€ș"</string>
-    <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
-    <skip />
-    <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
-    <skip />
-    <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
-    <skip />
+    <string name="button_to_remove_widget" msgid="3948204829181214098">"ဖယá€șá€›á€Ÿá€Źá€žá€›á€”á€ș"</string>
+    <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"ဝိဂျကá€șထည့á€șရနá€ș"</string>
+    <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"á€•á€Œá€źá€žá€•á€Œá€ź"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"á€Ąá€žá€Żá€¶á€žá€•á€Œá€Żá€žá€°á€€á€­á€Ż á€•á€Œá€±á€Źá€„á€șှလá€Čရနá€ș"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ဆလá€Čချမဟနူသ"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ဒဟချိတá€șဆကá€șá€™á€Ÿá€Ż ထá€Čက အကá€șပá€șမျဏသ á€Ąá€Źá€žá€œá€Żá€¶á€ž နဟင့á€ș ဒေတဏကို ဖျကá€șပစá€șမညá€ș။"</string>
@@ -465,7 +467,7 @@
     <string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"ဥသံတိတá€ș á€Ąá€€á€Œá€±á€Źá€„á€șá€žá€€á€Œá€Źá€žá€á€»á€€á€șá€™á€»á€Źá€žá€Ąá€Źá€žá€œá€Żá€¶á€žá€€á€­á€Ż ရဟငá€șှလငá€șှရနá€ș"</string>
     <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"á€Ąá€€á€Œá€±á€Źá€„á€șá€žá€€á€Œá€Źá€žá€á€»á€€á€șမျဏသကို \'á€™á€”á€Ÿá€±á€Źá€„á€·á€șယဟကá€șရ\' က ခေတá€čတရပá€șထာှသညá€ș"</string>
     <string name="media_projection_action_text" msgid="3634906766918186440">"ယခု စတငá€șပါ"</string>
-    <string name="empty_shade_text" msgid="8935967157319717412">"á€Ąá€€á€Œá€±á€Źá€„á€șá€žá€€á€Œá€Źá€žá€á€»á€€á€șမျဏသ မရဟိ"</string>
+    <string name="empty_shade_text" msgid="8935967157319717412">"á€Ąá€€á€Œá€±á€Źá€„á€șá€žá€€á€Œá€Źá€žá€á€»á€€á€ș မရဟိပါ"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"á€Ąá€€á€Œá€±á€Źá€„á€șá€žá€€á€Œá€Źá€žá€á€»á€€á€șသစá€ș မရဟိပါ"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"á€Ąá€€á€Œá€±á€Źá€„á€șá€žá€€á€Œá€Źá€žá€á€»á€€á€șá€Ÿá€±á€Źá€„á€șသကဌည့á€șရနá€ș á€œá€±á€Źá€·á€á€șဖလင့á€șပါ"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"ကစကá€șပစá€čစညá€șှကို သင့á€șမိဘက စဟမံခန့á€șခလá€Čသညá€ș"</string>
@@ -867,7 +869,6 @@
     <string name="auto_saver_title" msgid="6873691178754086596">"\'ဘကá€șထရြ အာှထိနá€șှ\' ဥချိနá€șသတá€șမဟတá€șရနá€ș á€Ąá€á€œá€€á€ș တို့ပါ"</string>
     <string name="auto_saver_text" msgid="3214960308353838764">"ဘကá€șထရြကုနá€șá€á€«á€”á€źá€žá€á€œá€„á€ș ဖလင့á€șပါ"</string>
     <string name="no_auto_saver_action" msgid="7467924389609773835">"á€™á€œá€­á€Żá€•á€«"</string>
-    <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string>
     <string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"á€Ąá€žá€Żá€¶á€žá€•á€Œá€Żá€”á€±á€žá€Šá€ș"</string>
     <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"á€Ąá€•á€œá€źá€€á€±á€žá€›á€Ÿá€„á€șသမျဏသက သငá€ș၏ <xliff:g id="TYPES_LIST">%s</xliff:g> ကို á€Ąá€žá€Żá€¶á€žá€•á€Œá€Żá€”á€±á€žá€Šá€ș။"</string>
     <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">"၊ "</string>
@@ -1207,7 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"ဆကá€șတငá€șá€™á€»á€Źá€žá€á€œá€„á€ș မူရငá€șသမဟတá€șစုမျဏသဥကá€șပá€ș သတá€șမဟတá€șပါ"</string>
     <string name="install_app" msgid="5066668100199613936">"အကá€șပá€ș ထည့á€șသလငá€șှရနá€ș"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"ပဌငá€șပဖနá€șá€žá€Źá€žá€•á€Œá€„á€șသို့ စကရငá€șá€•á€œá€Źá€žá€™á€œá€Źá€žá‹"</string>
-    <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"လကá€șá€›á€Ÿá€­á€œá€Żá€•á€șဆေဏငá€șနေသေဏ Dual Screen လုပá€șဆေဏငá€șချကá€șမျဏသ ရပá€șá€žá€œá€Źá€žá€•á€«á€™á€Šá€ș"</string>
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"á€Ąá€á€œá€„á€șသပဌကလကá€șကို စကရငá€șá€•á€œá€Źá€žá€•á€«á€™á€Šá€ș။ ရဟေ့မျကá€șá€”á€Ÿá€Źá€•á€Œá€„á€șပဌကလကá€șကို ပိတá€șမညá€ș။"</string>
     <string name="mirror_display" msgid="2515262008898122928">"ဖနá€șá€žá€Źá€žá€•á€Œá€„á€șကို စကရငá€șá€•á€œá€Źá€žá€›á€”á€ș"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"ပယá€șရနá€ș"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"ဖနá€șá€žá€Źá€žá€•á€Œá€„á€șကို ချိတá€șဆကá€șလိုကá€șá€•á€«á€•á€Œá€ź"</string>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 0b5a0b2..d915743 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -330,12 +330,17 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Skjermopptak"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Start"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Stopp"</string>
-    <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
-    <skip />
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"Registrer problem"</string>
+    <string name="qs_record_issue_start" msgid="2979831312582567056">"Start"</string>
+    <string name="qs_record_issue_stop" msgid="3531747965741982657">"Stopp"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Hvilken del av enhetsopplevelsen din ble påvirket?"</string>
+    <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Velg problemtype"</string>
+    <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Skjermopptak"</string>
+  <string-array name="qs_record_issue_types">
+    <item msgid="2947988124014085798">"Ytelse"</item>
+    <item msgid="1627504621139124393">"Brukergrensesnitt"</item>
+    <item msgid="8309220355268900335">"Batteri"</item>
+  </string-array>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Enhåndsmodus"</string>
     <string name="quick_settings_contrast_label" msgid="988087460210159123">"Kontrast"</string>
     <string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Standard"</string>
@@ -408,12 +413,9 @@
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Lader • Fulladet om <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Sveip til venstre for å starte fellesveiledningen"</string>
     <string name="button_to_open_widget_editor" msgid="5599945944349057600">"Åpne redigeringsverktøyet for moduler"</string>
-    <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
-    <skip />
-    <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
-    <skip />
-    <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
-    <skip />
+    <string name="button_to_remove_widget" msgid="3948204829181214098">"Fjern"</string>
+    <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Legg til modul"</string>
+    <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Ferdig"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Bytt bruker"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"rullegardinmeny"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alle apper og data i denne økten blir slettet."</string>
@@ -867,7 +869,6 @@
     <string name="auto_saver_title" msgid="6873691178754086596">"Trykk for å planlegge batterisparing"</string>
     <string name="auto_saver_text" msgid="3214960308353838764">"Slå på når det er sannsynlig at du går tom for batteri"</string>
     <string name="no_auto_saver_action" msgid="7467924389609773835">"Nei takk"</string>
-    <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI-heap"</string>
     <string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"I bruk"</string>
     <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Apper bruker <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
     <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Du kan velge en standardapp for notater i Innstillinger"</string>
     <string name="install_app" msgid="5066668100199613936">"Installer appen"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Vil du speile til en ekstern skjerm?"</string>
-    <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"Eventuell nåværende aktivitet på to skjermer stoppes"</string>
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Den indre skjermen speiles. Den ytre skjermen slås av."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Speil skjermen"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Lukk"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"En skjerm er koblet til"</string>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index 9c6ffc5..4abb4a4 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -269,8 +269,8 @@
     <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"à€žà„à€•à„à€°à€żà€š à€žà„à€”à€€à€ƒà€˜à„à€źà„à€šà„‡"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"à€Čà„‹à€•à„‡à€žà€š"</string>
     <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"à€žà„à€•à„à€°à€żà€š à€žà„‡à€­à€°"</string>
-    <string name="quick_settings_camera_label" msgid="5612076679385269339">"à€•à„à€Żà€Ÿà€źà„‡à€°à€Ÿ à€Șà„à€°à€Żà„‹à€— à€—à€°à„à€šà„‡ à€…à€šà„à€źà€€à€ż"</string>
-    <string name="quick_settings_mic_label" msgid="8392773746295266375">"à€źà€Ÿà€‡à€• à€Șà„à€°à€Żà„‹à€— à€—à€°à„à€šà„‡ à€…à€šà„à€źà€€à€ż"</string>
+    <string name="quick_settings_camera_label" msgid="5612076679385269339">"à€•à„à€Żà€Ÿà€źà„‡à€°à€Ÿ à€à€•à„à€žà„‡à€ž"</string>
+    <string name="quick_settings_mic_label" msgid="8392773746295266375">"à€źà€Ÿà€‡à€• à€à€•à„à€žà„‡à€ž"</string>
     <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"à€‰à€Șà€Čà€Źà„à€§ à€›"</string>
     <string name="quick_settings_camera_mic_blocked" msgid="4710884905006788281">"à€Źà„à€Čà€• à€—à€°à€żà€à€•à„‹ à€›"</string>
     <string name="quick_settings_media_device_label" msgid="8034019242363789941">"à€źà€żà€Ąà€żà€Żà€Ÿ à€‰à€Șà€•à€°à€Ł"</string>
@@ -330,12 +330,17 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"à€žà„à€•à„à€°à€żà€š à€°à„‡à€•à€°à„à€Ą"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"à€žà„à€°à„ à€—à€°à„à€šà„à€čà„‹à€žà„"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"à€°à„‹à€•à„à€šà„à€čà„‹à€žà„"</string>
-    <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
-    <skip />
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"à€žà€źà€žà„à€Żà€Ÿ à€°à„‡à€•à€°à„à€Ą à€—à€°à„à€šà„à€čà„‹à€žà„"</string>
+    <string name="qs_record_issue_start" msgid="2979831312582567056">"à€žà„à€°à„ à€—à€°à„à€šà„à€čà„‹à€žà„"</string>
+    <string name="qs_record_issue_stop" msgid="3531747965741982657">"à€°à„‹à€•à„à€šà„à€čà„‹à€žà„"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"à€€à€Șà€Ÿà€ˆà€‚à€•à„‹ à€Ąà€żà€­à€Ÿà€‡à€žà€•à„‹ à€•à„à€š à€šà€Ÿà€čà€żà€ à€žà„à€”à€żà€§à€Ÿ à€Șà„à€°à€­à€Ÿà€”à€żà€€ à€­à€à€•à„‹ à€›?"</string>
+    <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"à€žà€źà€žà„à€Żà€Ÿà€•à„‹ à€Șà„à€°à€•à€Ÿà€° à€šà€Żà€š à€—à€°à„à€šà„à€čà„‹à€žà„"</string>
+    <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"à€žà„à€•à„à€°à€żà€š à€°à„‡à€•à€°à„à€Ą"</string>
+  <string-array name="qs_record_issue_types">
+    <item msgid="2947988124014085798">"à€Șà€°à„à€«à€°à„à€źà„‡à€šà„à€ž"</item>
+    <item msgid="1627504621139124393">"à€Żà„à€œà€° à€‡à€šà„à€Ÿà€°à€«à„‡à€ž"</item>
+    <item msgid="8309220355268900335">"à€Źà„à€Żà€Ÿà€Ÿà„à€°à„€"</item>
+  </string-array>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"à€à€• à€čà€Ÿà€€à„‡ à€źà„‹à€Ą"</string>
     <string name="quick_settings_contrast_label" msgid="988087460210159123">"à€•à€šà„à€Ÿà„à€°à€Ÿà€žà„à€Ÿ"</string>
     <string name="quick_settings_contrast_standard" msgid="2538227821968061832">"à€Ąà€żà€«à€Čà„à€Ÿ"</string>
@@ -408,12 +413,9 @@
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • à€šà€Ÿà€°à„à€œ à€čà„à€à€Šà„ˆ à€› • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> à€źà€Ÿ à€«à„à€Č à€šà€Ÿà€°à„à€œ à€čà„à€šà„‡ à€›"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"à€•à€źà„à€Żà„à€šà€Č à€Ÿà„à€Żà„à€Ÿà„‹à€°à€żà€Żà€Č à€žà„à€°à„ à€—à€°à„à€š à€Źà€Ÿà€Żà€Ÿà€à€€à€żà€° à€žà„à€”à€Ÿà€‡à€Ș à€—à€°à„à€šà„à€čà„‹à€žà„"</string>
     <string name="button_to_open_widget_editor" msgid="5599945944349057600">"à€”à€żà€œà„‡à€Ÿ à€à€Ąà€żà€Ÿà€° à€–à„‹à€Čà„à€šà„à€čà„‹à€žà„"</string>
-    <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
-    <skip />
-    <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
-    <skip />
-    <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
-    <skip />
+    <string name="button_to_remove_widget" msgid="3948204829181214098">"à€čà€Ÿà€Ÿà€‰à€šà„à€čà„‹à€žà„"</string>
+    <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"à€”à€żà€œà„‡à€Ÿ à€čà€Ÿà€Čà„à€šà„à€čà„‹à€žà„"</string>
+    <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"à€Șà„‚à€°à€Ÿ à€­à€Żà„‹"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"à€Șà„à€°à€Żà„‹à€—à€•à€°à„à€€à€Ÿ à€«à„‡à€°à„à€šà„à€čà„‹à€žà„"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"à€Șà„à€Čà€Ąà€Ÿà€‰à€š à€źà„‡à€šà„"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"à€Żà„‹ à€žà€€à„à€°à€źà€Ÿ à€­à€à€•à€Ÿ à€žà€Źà„ˆ à€à€Șà€čà€°à„‚ à€° à€Ąà„‡à€Ÿà€Ÿ à€źà„‡à€Ÿà€Ÿà€‡à€šà„‡ à€›à„€"</string>
@@ -867,7 +869,6 @@
     <string name="auto_saver_title" msgid="6873691178754086596">"à€Źà„à€Żà€Ÿà€Ÿà„à€°à„€ à€žà„‡à€­à€°à€•à„‹ à€žà€źà€Żà€€à€Ÿà€Čà€żà€•à€Ÿ à€Źà€šà€Ÿà€‰à€š à€Ÿà„à€Żà€Ÿà€Ș à€—à€°à„à€šà„à€čà„‹à€žà„"</string>
     <string name="auto_saver_text" msgid="3214960308353838764">"à€Źà„à€Żà€Ÿà€Ÿà„à€°à„€ à€žà€•à€żà€šà„‡ à€žà€źà„à€­à€Ÿà€”à€šà€Ÿ à€­à€à€źà€Ÿ à€…à€š à€—à€°à„à€šà„à€čà„‹à€žà„"</string>
     <string name="no_auto_saver_action" msgid="7467924389609773835">"à€Șà€°à„à€Šà„ˆà€š, à€§à€šà„à€Żà€”à€Ÿà€Š"</string>
-    <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string>
     <string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"à€Șà„à€°à€Żà„‹à€—à€źà€Ÿ à€›"</string>
     <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"à€à€Șà€čà€°à„‚à€Čà„‡ à€€à€Șà€Ÿà€ˆà€‚à€•à„‹ <xliff:g id="TYPES_LIST">%s</xliff:g> à€Șà„à€°à€Żà„‹à€— à€—à€°à„à€Šà„ˆ à€›à€šà„‌à„€"</string>
     <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"à€žà„‡à€Ÿà€żà€™à€źà€Ÿ à€—à€ˆ à€šà„‹à€Ÿ à€Źà€šà€Ÿà€‰à€šà„‡ à€Ąà€żà€«à€Čà„à€Ÿ à€à€Ș à€€à„‹à€•à„à€šà„à€čà„‹à€žà„"</string>
     <string name="install_app" msgid="5066668100199613936">"à€à€Ș à€‡à€šà„à€žà„à€Ÿà€Č à€—à€°à„à€šà„à€čà„‹à€žà„"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"à€Źà€Ÿà€čà„à€Ż à€Ąà€żà€žà„à€Șà„à€Čà„‡à€źà€Ÿ à€źà€żà€°à€° à€—à€°à„à€šà„‡ à€čà„‹?"</string>
-    <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"à€Ąà„à€…à€Č à€žà„à€•à„à€°à€żà€šà€žà€źà„à€Źà€šà„à€§à„€ à€čà€Ÿà€Č à€šà€Čà€żà€°à€čà„‡à€•à„‹ à€•à„à€šà„ˆ à€Șà€šà€ż à€—à€€à€żà€”à€żà€§à€ż à€°à„‹à€•à€żà€šà„‡ à€›"</string>
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"à€€à€Șà€Ÿà€ˆà€‚à€•à„‹ à€­à€żà€€à„à€°à„€ à€Ąà€żà€žà„à€Șà„à€Čà„‡ à€źà€żà€°à€° à€—à€°à€żà€šà„‡ à€›à„€ à€€à€Șà€Ÿà€ˆà€‚à€•à„‹ à€…à€—à€Ÿà€Ąà€żà€•à„‹ à€Ąà€żà€žà„à€Șà„à€Čà„‡ à€…à€« à€—à€°à€żà€šà„‡ à€›à„€"</string>
     <string name="mirror_display" msgid="2515262008898122928">"à€Ąà€żà€žà„à€Șà„à€Čà„‡ à€źà€żà€°à€° à€—à€°à„à€šà„à€čà„‹à€žà„"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"à€–à€Ÿà€°à„‡à€œ à€—à€°à„à€šà„à€čà„‹à€žà„"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"à€Ąà€żà€žà„à€Șà„à€Čà„‡ à€•à€šà„‡à€•à„à€Ÿ à€—à€°à€żà€à€•à„‹ à€›"</string>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 4dae290..0576730 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -253,7 +253,7 @@
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Geen gekoppelde apparaten beschikbaar"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Tik om een apparaat te verbinden of de verbinding te verbreken"</string>
     <string name="pair_new_bluetooth_devices" msgid="4601767620843349645">"Nieuw apparaat koppelen"</string>
-    <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Alles bekijken"</string>
+    <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Alles tonen"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Bluetooth gebruiken"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Verbonden"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Opgeslagen"</string>
@@ -330,12 +330,17 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Schermopname"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Starten"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Stoppen"</string>
-    <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
-    <skip />
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"Probleem vastleggen"</string>
+    <string name="qs_record_issue_start" msgid="2979831312582567056">"Starten"</string>
+    <string name="qs_record_issue_stop" msgid="3531747965741982657">"Stoppen"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Op welk onderdeel van de apparaatfunctionaliteit had dit effect?"</string>
+    <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Probleemtype selecteren"</string>
+    <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Schermopname"</string>
+  <string-array name="qs_record_issue_types">
+    <item msgid="2947988124014085798">"Prestaties"</item>
+    <item msgid="1627504621139124393">"Gebruikersinterface"</item>
+    <item msgid="8309220355268900335">"Batterij"</item>
+  </string-array>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Bediening met 1 hand"</string>
     <string name="quick_settings_contrast_label" msgid="988087460210159123">"Contrast"</string>
     <string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Standaard"</string>
@@ -408,12 +413,9 @@
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Opladen • Vol over <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Swipe naar links om de communitytutorial te starten"</string>
     <string name="button_to_open_widget_editor" msgid="5599945944349057600">"De widget-editor openen"</string>
-    <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
-    <skip />
-    <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
-    <skip />
-    <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
-    <skip />
+    <string name="button_to_remove_widget" msgid="3948204829181214098">"Verwijderen"</string>
+    <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Widget toevoegen"</string>
+    <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Klaar"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Gebruiker wijzigen"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"pull-downmenu"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alle apps en gegevens in deze sessie worden verwijderd."</string>
@@ -867,7 +869,6 @@
     <string name="auto_saver_title" msgid="6873691178754086596">"Tikken om Batterijbesparing aan te zetten"</string>
     <string name="auto_saver_text" msgid="3214960308353838764">"Aanzetten als de batterij waarschijnlijk leeg raakt"</string>
     <string name="no_auto_saver_action" msgid="7467924389609773835">"Nee, bedankt"</string>
-    <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string>
     <string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"In gebruik"</string>
     <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Apps gebruiken je <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
     <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Standaard notitie-app instellen in Instellingen"</string>
     <string name="install_app" msgid="5066668100199613936">"App installeren"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Spiegelen naar extern scherm?"</string>
-    <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"Alle dual screen-activiteiten die actief zijn, worden gestopt"</string>
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Het scherm aan de binnenkant wordt gemirrord. Het scherm aan de voorkant wordt uitgezet."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Scherm spiegelen"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Sluiten"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Scherm verbonden"</string>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index bc599ba..cb58e64 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -330,12 +330,17 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"àŹžà­àŹ•à­àŹ°àŹżàŹš àŹ°à­‡àŹ•àŹ°à­àŹĄ"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"àŹ†àŹ°àŹźà­àŹ­ àŹ•àŹ°àŹšà­àŹ€à­"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"àŹŹàŹšà­àŹŠ àŹ•àŹ°àŹšà­àŹ€à­"</string>
-    <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
-    <skip />
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"àŹžàŹźàŹžà­à­ŸàŹŸàŹ° àŹ°à­‡àŹ•àŹ°à­àŹĄ àŹ•àŹ°àŹšà­àŹ€à­"</string>
+    <string name="qs_record_issue_start" msgid="2979831312582567056">"àŹ†àŹ°àŹźà­àŹ­ àŹ•àŹ°àŹšà­àŹ€à­"</string>
+    <string name="qs_record_issue_stop" msgid="3531747965741982657">"àŹŹàŹšà­àŹŠ àŹ•àŹ°àŹšà­àŹ€à­"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"àŹ†àŹȘàŹŁàŹ™à­àŹ• àŹĄàŹżàŹ­àŹŸàŹ‡àŹž àŹ…àŹšà­àŹ­à­‚àŹ€àŹżàŹ° àŹ•à­‡àŹ‰àŹ àŹ…àŹ‚àŹ¶ àŹȘà­àŹ°àŹ­àŹŸàŹŹàŹżàŹ€ àŹčà­‹àŹ‡àŹ›àŹż?"</string>
+    <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"àŹžàŹźàŹžà­à­ŸàŹŸàŹ° àŹȘà­àŹ°àŹ•àŹŸàŹ° àŹšà­ŸàŹš àŹ•àŹ°àŹšà­àŹ€à­"</string>
+    <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"àŹžà­àŹ•à­àŹ°àŹżàŹš àŹ°à­‡àŹ•àŹ°à­àŹĄ"</string>
+  <string-array name="qs_record_issue_types">
+    <item msgid="2947988124014085798">"àŹȘàŹ°àŹ«àŹ°àŹźàŹŸàŹšà­àŹž"</item>
+    <item msgid="1627504621139124393">"à­Ÿà­àŹœàŹ° àŹ‡àŹŁà­àŹŸàŹ°àŹ«à­‡àŹž"</item>
+    <item msgid="8309220355268900335">"àŹŹà­‡àŹŸà­‡àŹ°à­€"</item>
+  </string-array>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"àŹàŹ•-àŹčàŹŸàŹ€ àŹźà­‹àŹĄ"</string>
     <string name="quick_settings_contrast_label" msgid="988087460210159123">"àŹ•àŹŁà­àŹŸà­àŹ°àŹŸàŹ·à­àŹŸ"</string>
     <string name="quick_settings_contrast_standard" msgid="2538227821968061832">"àŹ·à­àŹŸàŹŸàŹŁà­àŹĄàŹŸàŹ°à­àŹĄ"</string>
@@ -408,12 +413,9 @@
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • àŹšàŹŸàŹ°à­àŹœ àŹčà­‡àŹ‰àŹ›àŹż • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>àŹ°à­‡ àŹžàŹźà­àŹȘà­‚àŹ°à­àŹŁà­àŹŁ àŹčà­‡àŹŹ"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"àŹ•àŹźà­à­Ÿà­àŹšàŹŸàŹČ àŹŸà­à­Ÿà­àŹŸà­‹àŹ°àŹżàŹ†àŹČ àŹ†àŹ°àŹźà­àŹ­ àŹ•àŹ°àŹżàŹŹàŹŸ àŹȘàŹŸàŹ‡àŹ àŹŹàŹŸàŹźàŹ•à­ àŹžà­à­±àŹŸàŹ‡àŹȘ àŹ•àŹ°àŹšà­àŹ€à­"</string>
     <string name="button_to_open_widget_editor" msgid="5599945944349057600">"à­±àŹżàŹœà­‡àŹŸ àŹàŹĄàŹżàŹŸàŹ° àŹ–à­‹àŹČàŹšà­àŹ€à­"</string>
-    <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
-    <skip />
-    <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
-    <skip />
-    <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
-    <skip />
+    <string name="button_to_remove_widget" msgid="3948204829181214098">"àŹ•àŹŸàŹąàŹŒàŹż àŹŠàŹżàŹ…àŹšà­àŹ€à­"</string>
+    <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"à­±àŹżàŹœà­‡àŹŸ àŹŻà­‹àŹ— àŹ•àŹ°àŹšà­àŹ€à­"</string>
+    <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"àŹčà­‹àŹ‡àŹ—àŹČàŹŸ"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"à­Ÿà­àŹœàŹ°à­‍ àŹŹàŹŠàŹłàŹŸàŹšà­àŹ€à­"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"àŹȘୁàŹČàŹĄàŹŸàŹ‰àŹš àŹźà­‡àŹšà­"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"àŹàŹčàŹż àŹžà­‡àŹžàŹšàŹ° àŹžàŹźàŹžà­àŹ€ àŹ†àŹȘ୍‌ àŹ“ àŹĄàŹŸàŹŸàŹŸ àŹĄàŹżàŹČàŹżàŹŸà­‌ àŹčà­‹àŹ‡àŹŻàŹżàŹŹà„€"</string>
@@ -867,7 +869,6 @@
     <string name="auto_saver_title" msgid="6873691178754086596">"àŹŹà­à­ŸàŹŸàŹŸà­‡àŹ°à­€ àŹžà­‡àŹ­àŹ°à­‌ àŹ…àŹšà­‌ àŹčà­‡àŹŹàŹŸàŹ° àŹžàŹźà­Ÿ àŹžà­‡àŹŸà­‌ àŹ•àŹ°àŹżàŹŹàŹŸàŹ•à­ àŹŸàŹŸàŹȘ୍‌ àŹ•àŹ°àŹšà­àŹ€à­"</string>
     <string name="auto_saver_text" msgid="3214960308353838764">"àŹŹà­à­ŸàŹŸàŹŸà­‡àŹ°à­€ àŹžàŹ°àŹżàŹŹàŹŸàŹ•à­ àŹ„àŹżàŹŹàŹŸ àŹžàŹźà­ŸàŹ°à­‡ àŹšàŹŸàŹČୁ àŹ•àŹ°àŹšà­àŹ€à­"</string>
     <string name="no_auto_saver_action" msgid="7467924389609773835">"àŹšàŹŸàŹčàŹżàŹ, àŹ„àŹŸàŹ‰"</string>
-    <string name="heap_dump_tile_name" msgid="2464189856478823046">"SysUI àŹčàŹżàŹȘ୍ àŹĄàŹźà­àŹȘ୍ àŹ•àŹ°àŹšà­àŹ€à­"</string>
     <string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"àŹŹà­à­ŸàŹŹàŹčàŹŸàŹ° àŹčà­‡àŹ‰àŹ›àŹż"</string>
     <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"àŹ†àŹȘ୍àŹČàŹżàŹ•à­‡àŹžàŹšà­‍àŹ—à­àŹĄàŹżàŹ• àŹ†àŹȘàŹŁàŹ™à­àŹ• <xliff:g id="TYPES_LIST">%s</xliff:g> àŹŹà­à­ŸàŹŹàŹčàŹŸàŹ° àŹ•àŹ°à­àŹ›àŹšà­àŹ€àŹżà„€"</string>
     <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"àŹžà­‡àŹŸàŹżàŹ‚àŹžàŹ°à­‡ àŹĄàŹżàŹ«àŹČà­àŹŸ àŹšà­‹àŹŸà­àŹž àŹ†àŹȘ àŹžà­‡àŹŸ àŹ•àŹ°àŹšà­àŹ€à­"</string>
     <string name="install_app" msgid="5066668100199613936">"àŹ†àŹȘ àŹ‡àŹšàŹ·à­àŹŸàŹČ àŹ•àŹ°àŹšà­àŹ€à­"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"àŹàŹ•à­àŹžàŹŸàŹ°à­àŹšàŹČ àŹĄàŹżàŹžàŹȘ୍àŹČà­‡àŹ•à­ àŹźàŹżàŹ°àŹ° àŹ•àŹ°àŹżàŹŹà­‡?"</string>
-    <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"àŹŹàŹ°à­àŹ€à­àŹ€àŹźàŹŸàŹš àŹšàŹŸàŹČà­àŹ„àŹżàŹŹàŹŸ àŹŻà­‡ àŹ•à­ŒàŹŁàŹžàŹż àŹĄà­àŹ†àŹČ àŹžà­àŹ•à­àŹ°àŹżàŹš àŹ•àŹŸàŹ°à­àŹŻà­à­ŸàŹ•àŹłàŹŸàŹȘ àŹŹàŹšà­àŹŠ àŹčà­‹àŹ‡àŹŻàŹżàŹŹ"</string>
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"àŹ†àŹȘàŹŁàŹ™à­àŹ• àŹ‡àŹšàŹ° àŹĄàŹżàŹžàŹȘ୍àŹČà­‡àŹ•à­ àŹźàŹżàŹ°àŹ° àŹ•àŹ°àŹŸàŹŻàŹżàŹŹà„€ àŹ†àŹȘàŹŁàŹ™à­àŹ• àŹ«à­àŹ°àŹŁà­àŹŸ àŹĄàŹżàŹžàŹȘ୍àŹČà­‡àŹ•à­ àŹŹàŹšà­àŹŠ àŹ•àŹ°àŹŸàŹŻàŹżàŹŹà„€"</string>
     <string name="mirror_display" msgid="2515262008898122928">"àŹĄàŹżàŹžàŹȘ୍àŹČେ àŹźàŹżàŹ°àŹ° àŹ•àŹ°àŹšà­àŹ€à­"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"àŹ–àŹŸàŹ°àŹœ àŹ•àŹ°àŹšà­àŹ€à­"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"àŹĄàŹżàŹžàŹȘ୍àŹČେ àŹ•àŹšà­‡àŹ•à­àŹŸ àŹ•àŹ°àŹŸàŹŻàŹŸàŹ‡àŹ›àŹż"</string>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index a0718ab..0340bd1 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -330,12 +330,17 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"àšžàš•à©àš°à©€àšš àš°àšżàš•àšŸàš°àšĄ"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"àšžàšŒà©àš°à©‚ àš•àš°à©‹"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"àš°à©‹àš•à©‹"</string>
-    <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
-    <skip />
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"àšžàšźà©±àšžàšżàš† àš°àšżàš•àšŸàš°àšĄ àš•àš°à©‹"</string>
+    <string name="qs_record_issue_start" msgid="2979831312582567056">"àšžàšŒà©àš°à©‚ àš•àš°à©‹"</string>
+    <string name="qs_record_issue_stop" msgid="3531747965741982657">"àšŹà©°àšŠ àš•àš°à©‹"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"àš€à©àščàšŸàšĄà©‡ àšĄà©€àš”àšŸàšˆàšž àšŠà©€ àš•àšżàščੜੀ àšžà©àš”àšżàš§àšŸ àšȘà©àš°àš­àšŸàš”àšżàš€ àščà©‹àšˆ àšžà©€?"</string>
+    <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"àšžàšźà©±àšžàšżàš† àšŠà©€ àš•àšżàšžàšź àššà©àšŁà©‹"</string>
+    <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"àšžàš•à©àš°à©€àšš àš°àšżàš•àšŸàš°àšĄ"</string>
+  <string-array name="qs_record_issue_types">
+    <item msgid="2947988124014085798">"àš•àšŸàš°àš—à©àšœàšŒàšŸàš°à©€"</item>
+    <item msgid="1627504621139124393">"àšŻà©‚àšœàšŒàš° àš‡à©°àšŸàš°àš«àšŒà©‡àšž"</item>
+    <item msgid="8309220355268900335">"àšŹà©ˆàšŸàš°à©€"</item>
+  </string-array>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"àš‡à©±àš• àščà©±àš„ àšźà©‹àšĄ"</string>
     <string name="quick_settings_contrast_label" msgid="988087460210159123">"àš•à©°àšŸà©àš°àšŸàšžàšŸ"</string>
     <string name="quick_settings_contrast_standard" msgid="2538227821968061832">"àšźàšżàš†àš°à©€"</string>
@@ -408,12 +413,9 @@
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • àššàšŸàš°àšœ àščੋ àš°àšżàščàšŸ àščੈ • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> àš”àšżà©±àšš àšȘà©‚àš°àšŸ àššàšŸàš°àšœ àščà©‹àš”à©‡àš—àšŸ"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"àš­àšŸàšˆàššàšŸàš°àš• àšŸàšżàšŠàšŸà©‹àš°à©€àš…àšČ àšžàšŒà©àš°à©‚ àš•àš°àšš àšČàšˆ àš–à©±àšŹà©‡ àšȘàšŸàšžà©‡ àš”à©±àšČ àšžàš”àšŸàšˆàšȘ àš•àš°à©‹"</string>
     <string name="button_to_open_widget_editor" msgid="5599945944349057600">"àš”àšżàšœà©‡àšŸ àšžà©°àšȘàšŸàšŠàš• àš–à©‹àšČ੍àščੋ"</string>
-    <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
-    <skip />
-    <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
-    <skip />
-    <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
-    <skip />
+    <string name="button_to_remove_widget" msgid="3948204829181214098">"àščàšŸàšŸàš“"</string>
+    <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"àš”àšżàšœà©‡àšŸ àšžàšŒàšŸàšźàšČ àš•àš°à©‹"</string>
+    <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"àščੋ àš—àšżàš†"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"àš”àš°àš€à©‹àš‚àš•àšŸàš° àšžàš”àšżà©±àšš àš•àš°à©‹"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"àšȘੁੱàšČàšĄàšŸàšŠàšš àšźà©€àššà©‚"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"àš‡àšž àšžà©ˆàšžàšŒàšš àš”àšżàššàšČà©€àš†àš‚ àšžàšŸàš°à©€àš†àš‚ àšàšȘàšŸàš‚ àš…àš€à©‡ àšĄàšŸàšŸàšŸ àššà©‚à©° àšźàšżàšŸàšŸ àšŠàšżà©±àš€àšŸ àšœàšŸàšàš—àšŸà„€"</string>
@@ -867,7 +869,6 @@
     <string name="auto_saver_title" msgid="6873691178754086596">"àšŹà©ˆàšŸàš°à©€ àšžà©‡àš”àš° àšŠà©€ àšžàšźàšŸàš‚-àšžà©‚àššà©€ àš€àšżàš†àš° àš•àš°àšš àšČàšˆ àšŸà©ˆàšȘ àš•àš°à©‹"</string>
     <string name="auto_saver_text" msgid="3214960308353838764">"àšŹà©ˆàšŸàš°à©€ àš–àš€àšź àščà©‹àšŁ àšŠà©€ àšžà©°àš­àšŸàš”àššàšŸ \'àš€à©‡ àššàšŸàšČੂ àščà©à©°àšŠàšŸ àščੈ"</string>
     <string name="no_auto_saver_action" msgid="7467924389609773835">"àššàščà©€àš‚ àš§à©°àššàš”àšŸàšŠ"</string>
-    <string name="heap_dump_tile_name" msgid="2464189856478823046">"SysUI àščੀàšȘ àšĄà©°àšȘ àš•àš°à©‹"</string>
     <string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"àš”àš°àš€à©‹àš‚ àš”àšżà©±àšš"</string>
     <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"àšàšȘàšČà©€àš•à©‡àšžàšŒàššàšŸàš‚ àš€à©àščàšŸàšĄà©‡ <xliff:g id="TYPES_LIST">%s</xliff:g> àšŠà©€ àš”àš°àš€à©‹àš‚ àš•àš° àš°àščà©€àš†àš‚ àščàššà„€"</string>
     <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"àšžà©ˆàšŸàšżà©°àš—àšŸàš‚ àš”àšżà©±àšš àšœàšŸ àš•à©‡ àšȘà©‚àš°àš”-àššàšżàš°àš§àšŸàš°àšżàš€ àššà©‹àšŸ àšàšȘ àššà©‚à©° àšžà©ˆà©±àšŸ àš•àš°à©‹"</string>
     <string name="install_app" msgid="5066668100199613936">"àšàšȘ àšžàš„àšŸàšȘàš€ àš•àš°à©‹"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"àš•à©€ àšŹàšŸàščàš°à©€ àšĄàšżàšžàšȘàšČੇ \'àš€à©‡ àšȘà©àš°àš€àšżàšŹàšżà©°àšŹàšżàš€ àš•àš°àššàšŸ àščੈ?"</string>
-    <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"àš‡àšž àš”à©‡àšČੇ àššà©±àšČ àš°àščੀ àš•àšżàšžà©‡ àš”à©€ Dual Screen àšžàš°àš—àš°àšźà©€ àšŹà©°àšŠ àš•àš° àšŠàšżà©±àš€àšŸ àšœàšŸàš”à©‡àš—àšŸ"</string>
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"àš€à©àščàšŸàšĄà©€ àš…à©°àšŠàš°à©‚àššà©€ àšĄàšżàšžàšȘàšČੇ àšȘà©àš°àš€à©€àšŹàšżà©°àšŹàš€ àš•à©€àš€à©€ àšœàšŸàš”à©‡àš—à©€à„€ àš€à©àščàšŸàšĄà©€ àš…àš—àšČੀ àšĄàšżàšžàšȘàšČੇ àšŹà©°àšŠ àš•àš° àšŠàšżà©±àš€à©€ àšœàšŸàš”à©‡àš—à©€à„€"</string>
     <string name="mirror_display" msgid="2515262008898122928">"àšĄàšżàšžàšȘàšČੇ àššà©‚à©° àšȘà©àš°àš€àšżàšŹàšżà©°àšŹàšżàš€ àš•àš°à©‹"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"àš–àšŸàš°àšœ àš•àš°à©‹"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"àšĄàšżàšžàšȘàšČੇ àššà©‚à©° àš•àššà©ˆàš•àšŸ àš•à©€àš€àšŸ àš—àšżàš†"</string>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 4585c1b..aa24818 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -330,12 +330,17 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Nagrywanie ekranu"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Rozpocznij"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Zatrzymaj"</string>
-    <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
-    <skip />
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"Zarejestruj problem"</string>
+    <string name="qs_record_issue_start" msgid="2979831312582567056">"Rozpocznij"</string>
+    <string name="qs_record_issue_stop" msgid="3531747965741982657">"Zatrzymaj"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Którego aspektu korzystania z urządzenia dotyczyƂ problem?"</string>
+    <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Wybierz typ problemu"</string>
+    <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Nagrywanie ekranu"</string>
+  <string-array name="qs_record_issue_types">
+    <item msgid="2947988124014085798">"Wydajnoƛć"</item>
+    <item msgid="1627504621139124393">"Interfejs"</item>
+    <item msgid="8309220355268900335">"Bateria"</item>
+  </string-array>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Tryb jednej ręki"</string>
     <string name="quick_settings_contrast_label" msgid="988087460210159123">"Kontrast"</string>
     <string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Standardowy"</string>
@@ -408,12 +413,9 @@
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Ɓadowanie • PeƂne naƂadowanie za <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Aby uruchomić wspólny samouczek, przeciągnij palcem w lewo"</string>
     <string name="button_to_open_widget_editor" msgid="5599945944349057600">"Otwórz edytor widĆŒetów"</string>
-    <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
-    <skip />
-    <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
-    <skip />
-    <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
-    <skip />
+    <string name="button_to_remove_widget" msgid="3948204829181214098">"UsuƄ"</string>
+    <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Dodaj widĆŒet"</string>
+    <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Gotowe"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"PrzeƂącz uĆŒytkownika"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menu"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Wszystkie aplikacje i dane w tej sesji zostaną usunięte."</string>
@@ -867,7 +869,6 @@
     <string name="auto_saver_title" msgid="6873691178754086596">"Kliknij, by zaplanować dziaƂanie oszczędzania baterii"</string>
     <string name="auto_saver_text" msgid="3214960308353838764">"Oszczędzanie baterii wƂącza się, jeƛli bateria jest prawie wyczerpana"</string>
     <string name="no_auto_saver_action" msgid="7467924389609773835">"Nie, dziękuję"</string>
-    <string name="heap_dump_tile_name" msgid="2464189856478823046">"Zrzut stosu SysUI"</string>
     <string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"W uĆŒyciu"</string>
     <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Aplikacje uĆŒywają: <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
     <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Ustaw domyƛlną aplikację do obsƂugi notatek w Ustawieniach"</string>
     <string name="install_app" msgid="5066668100199613936">"Zainstaluj aplikację"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Powielić na wyƛwietlaczu zewnętrznym?"</string>
-    <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"KaĆŒda aktualnie uruchomiona aktywnoƛć na dwóch ekranach zostanie zatrzymana"</string>
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Powstanie odbicie lustrzane Twojego wewnętrznego ekranu. Przedni ekran zostanie wyƂączony."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Powielaj obraz"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Zamknij"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Wyƛwietlacz podƂączony"</string>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index 81ddb63..c65c56e 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -330,12 +330,17 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Gravação de tela"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Iniciar"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Parar"</string>
-    <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
-    <skip />
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"Problema na gravação"</string>
+    <string name="qs_record_issue_start" msgid="2979831312582567056">"Iniciar"</string>
+    <string name="qs_record_issue_stop" msgid="3531747965741982657">"Parar"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Que parte da sua experiência no dispositivo foi afetada?"</string>
+    <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Selecionar tipo de problema"</string>
+    <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Gravação de tela"</string>
+  <string-array name="qs_record_issue_types">
+    <item msgid="2947988124014085798">"Desempenho"</item>
+    <item msgid="1627504621139124393">"Interface do usuário"</item>
+    <item msgid="8309220355268900335">"Bateria"</item>
+  </string-array>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modo para uma mão"</string>
     <string name="quick_settings_contrast_label" msgid="988087460210159123">"Contraste"</string>
     <string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Padrão"</string>
@@ -408,12 +413,9 @@
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Carregando • Conclusão em <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Deslize para a esquerda para iniciar o tutorial compartilhado"</string>
     <string name="button_to_open_widget_editor" msgid="5599945944349057600">"Abrir o editor de widgets"</string>
-    <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
-    <skip />
-    <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
-    <skip />
-    <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
-    <skip />
+    <string name="button_to_remove_widget" msgid="3948204829181214098">"Remover"</string>
+    <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Adicionar widget"</string>
+    <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Concluído"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Trocar usuário"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menu suspenso"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Todos os apps e dados nesta sessão serão excluídos."</string>
@@ -867,7 +869,6 @@
     <string name="auto_saver_title" msgid="6873691178754086596">"Toque para programar o recurso Economia de bateria"</string>
     <string name="auto_saver_text" msgid="3214960308353838764">"Ativada quando há possibilidade de a bateria acabar"</string>
     <string name="no_auto_saver_action" msgid="7467924389609773835">"Não"</string>
-    <string name="heap_dump_tile_name" msgid="2464189856478823046">"Despejar heap SysUI"</string>
     <string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"Em uso"</string>
     <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Aplicativos estão usando <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
     <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Defina o app de notas padrão nas Configurações"</string>
     <string name="install_app" msgid="5066668100199613936">"Instalar o app"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Espelhar para a tela externa?"</string>
-    <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"Qualquer atividade em tela dupla que esteja sendo executada no momento será interrompida"</string>
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Seu display interno será espelhado. O display frontal será desligado."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Espelhar tela"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Dispensar"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Tela conectada"</string>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index b519a7d..0b9580d 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -333,6 +333,14 @@
     <string name="qs_record_issue_label" msgid="8166290137285529059">"Registar problema"</string>
     <string name="qs_record_issue_start" msgid="2979831312582567056">"Iniciar"</string>
     <string name="qs_record_issue_stop" msgid="3531747965741982657">"Parar"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Que parte da experiência do disposit. foi afetada?"</string>
+    <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Selecione o tipo de problema"</string>
+    <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Gravação de ecrã"</string>
+  <string-array name="qs_record_issue_types">
+    <item msgid="2947988124014085798">"Desempenho"</item>
+    <item msgid="1627504621139124393">"Interface do utilizador"</item>
+    <item msgid="8309220355268900335">"Bateria"</item>
+  </string-array>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modo para uma mão"</string>
     <string name="quick_settings_contrast_label" msgid="988087460210159123">"Contraste"</string>
     <string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Padrão"</string>
@@ -861,7 +869,6 @@
     <string name="auto_saver_title" msgid="6873691178754086596">"Tocar para agendar a Poupança de bateria"</string>
     <string name="auto_saver_text" msgid="3214960308353838764">"Ativar quando for provável que a bateria se esgote"</string>
     <string name="no_auto_saver_action" msgid="7467924389609773835">"Não"</string>
-    <string name="heap_dump_tile_name" msgid="2464189856478823046">"Despejar pilha SysUI"</string>
     <string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"Em utilização"</string>
     <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"As aplicações estão a utilizar o(a) <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
     <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1201,7 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Predefina a app de notas nas Definições"</string>
     <string name="install_app" msgid="5066668100199613936">"Instalar app"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Espelhar para o ecrã externo?"</string>
-    <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"Qualquer atividade de Dual Screen atualmente em curso vai ser interrompida"</string>
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"O seu ecrã interior vai ser espelhado. O seu ecrã frontal vai ser desativado."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Espelhar ecrã"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Ignorar"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Ecrã ligado"</string>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 81ddb63..c65c56e 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -330,12 +330,17 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Gravação de tela"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Iniciar"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Parar"</string>
-    <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
-    <skip />
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"Problema na gravação"</string>
+    <string name="qs_record_issue_start" msgid="2979831312582567056">"Iniciar"</string>
+    <string name="qs_record_issue_stop" msgid="3531747965741982657">"Parar"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Que parte da sua experiência no dispositivo foi afetada?"</string>
+    <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Selecionar tipo de problema"</string>
+    <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Gravação de tela"</string>
+  <string-array name="qs_record_issue_types">
+    <item msgid="2947988124014085798">"Desempenho"</item>
+    <item msgid="1627504621139124393">"Interface do usuário"</item>
+    <item msgid="8309220355268900335">"Bateria"</item>
+  </string-array>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modo para uma mão"</string>
     <string name="quick_settings_contrast_label" msgid="988087460210159123">"Contraste"</string>
     <string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Padrão"</string>
@@ -408,12 +413,9 @@
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Carregando • Conclusão em <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Deslize para a esquerda para iniciar o tutorial compartilhado"</string>
     <string name="button_to_open_widget_editor" msgid="5599945944349057600">"Abrir o editor de widgets"</string>
-    <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
-    <skip />
-    <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
-    <skip />
-    <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
-    <skip />
+    <string name="button_to_remove_widget" msgid="3948204829181214098">"Remover"</string>
+    <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Adicionar widget"</string>
+    <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Concluído"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Trocar usuário"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menu suspenso"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Todos os apps e dados nesta sessão serão excluídos."</string>
@@ -867,7 +869,6 @@
     <string name="auto_saver_title" msgid="6873691178754086596">"Toque para programar o recurso Economia de bateria"</string>
     <string name="auto_saver_text" msgid="3214960308353838764">"Ativada quando há possibilidade de a bateria acabar"</string>
     <string name="no_auto_saver_action" msgid="7467924389609773835">"Não"</string>
-    <string name="heap_dump_tile_name" msgid="2464189856478823046">"Despejar heap SysUI"</string>
     <string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"Em uso"</string>
     <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Aplicativos estão usando <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
     <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Defina o app de notas padrão nas Configurações"</string>
     <string name="install_app" msgid="5066668100199613936">"Instalar o app"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Espelhar para a tela externa?"</string>
-    <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"Qualquer atividade em tela dupla que esteja sendo executada no momento será interrompida"</string>
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Seu display interno será espelhado. O display frontal será desligado."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Espelhar tela"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Dispensar"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Tela conectada"</string>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index bc3fbe9..b2fdef3 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -330,12 +330,17 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Înregistrarea ecranului"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Începe"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Oprește"</string>
-    <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
-    <skip />
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"Problemă legată de înregistrare"</string>
+    <string name="qs_record_issue_start" msgid="2979831312582567056">"Începe"</string>
+    <string name="qs_record_issue_stop" msgid="3531747965741982657">"Oprește"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Ce parte a experienței pe dispozitiv a fost afectată?"</string>
+    <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Selectează tipul problemei"</string>
+    <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Înregistrarea ecranului"</string>
+  <string-array name="qs_record_issue_types">
+    <item msgid="2947988124014085798">"Performanță"</item>
+    <item msgid="1627504621139124393">"Interfața de utilizare"</item>
+    <item msgid="8309220355268900335">"Baterie"</item>
+  </string-array>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modul cu o mână"</string>
     <string name="quick_settings_contrast_label" msgid="988087460210159123">"Contrast"</string>
     <string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Standard"</string>
@@ -408,12 +413,9 @@
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Se încarcă • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> până la încărcarea completă"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Glisează spre stânga pentru a începe tutorialul pentru comunitate"</string>
     <string name="button_to_open_widget_editor" msgid="5599945944349057600">"Deschide editorul de widgeturi"</string>
-    <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
-    <skip />
-    <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
-    <skip />
-    <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
-    <skip />
+    <string name="button_to_remove_widget" msgid="3948204829181214098">"Elimină"</string>
+    <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Adaugă un widget"</string>
+    <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Gata"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Schimbă utilizatorul"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"meniu vertical"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Toate aplicațiile și datele din această sesiune vor fi șterse."</string>
@@ -867,7 +869,6 @@
     <string name="auto_saver_title" msgid="6873691178754086596">"Atinge pentru a programa Economisirea energiei"</string>
     <string name="auto_saver_text" msgid="3214960308353838764">"Pornește dacă e probabil ca bateria să se descarce"</string>
     <string name="no_auto_saver_action" msgid="7467924389609773835">"Nu, mulțumesc"</string>
-    <string name="heap_dump_tile_name" msgid="2464189856478823046">"Extrage memoria SysUI"</string>
     <string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"În uz"</string>
     <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Aplicațiile folosesc <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
     <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Setează aplicația prestabilită de note din Setări"</string>
     <string name="install_app" msgid="5066668100199613936">"Instalează aplicația"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Oglindești pe ecranul extern?"</string>
-    <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"Orice activitate care rulează pe două ecrane va fi oprită"</string>
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Ecranul interior va fi oglindit. Ecranul frontal va fi dezactivat."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Afișare în oglindă"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Închide"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Ecran conectat"</string>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 2e8a257..ff4bd45 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -330,12 +330,17 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Запось ĐČĐžĐŽĐ”ĐŸ с эĐșŃ€Đ°ĐœĐ°"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Начать"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"ĐžŃŃ‚Đ°ĐœĐŸĐČоть"</string>
-    <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
-    <skip />
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"Запось ĐœĐ”ĐžŃĐżŃ€Đ°ĐČĐœĐŸŃŃ‚Đž"</string>
+    <string name="qs_record_issue_start" msgid="2979831312582567056">"Начать"</string>
+    <string name="qs_record_issue_stop" msgid="3531747965741982657">"ĐžŃŃ‚Đ°ĐœĐŸĐČоть"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"ĐĄ Ń‡Đ”ĐŒ сĐČŃĐ·Đ°ĐœĐ° ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒĐ°, с ĐșĐŸŃ‚ĐŸŃ€ĐŸĐč ĐČы ŃŃ‚ĐŸĐ»ĐșĐœŃƒĐ»ĐžŃŃŒ?"</string>
+    <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"ВыбДрОтД топ ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒŃ‹"</string>
+    <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Запось эĐșŃ€Đ°ĐœĐ°"</string>
+  <string-array name="qs_record_issue_types">
+    <item msgid="2947988124014085798">"ĐŸŃ€ĐŸĐžĐ·ĐČĐŸĐŽĐžŃ‚Đ”Đ»ŃŒĐœĐŸŃŃ‚ŃŒ"</item>
+    <item msgid="1627504621139124393">"Đ˜ĐœŃ‚Đ”Ń€Ń„Đ”Đčс"</item>
+    <item msgid="8309220355268900335">"Đ‘Đ°Ń‚Đ°Ń€Đ”Ń"</item>
+  </string-array>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Đ Đ”Đ¶ĐžĐŒ упраĐČĐ»Đ”ĐœĐžŃ ĐŸĐŽĐœĐŸĐč руĐșĐŸĐč"</string>
     <string name="quick_settings_contrast_label" msgid="988087460210159123">"ĐšĐŸĐœŃ‚Ń€Đ°ŃŃ‚ĐœĐŸŃŃ‚ŃŒ"</string>
     <string name="quick_settings_contrast_standard" msgid="2538227821968061832">"ĐĄŃ‚Đ°ĐœĐŽĐ°Ń€Ń‚ĐœĐ°Ń"</string>
@@ -408,12 +413,9 @@
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ЗаряЮĐșа • ĐžŃŃ‚Đ°Đ»ĐŸŃŃŒ <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Đ§Ń‚ĐŸĐ±Ń‹ ĐŸĐ·ĐœĐ°ĐșĐŸĐŒĐžŃ‚ŃŒŃŃ с руĐșĐŸĐČĐŸĐŽŃŃ‚ĐČĐŸĐŒ, ĐżŃ€ĐŸĐČДЎОтД ĐżĐŸ эĐșŃ€Đ°ĐœŃƒ ĐČлДĐČĐŸ"</string>
     <string name="button_to_open_widget_editor" msgid="5599945944349057600">"ОтĐșрыть рДЎаĐșŃ‚ĐŸŃ€ ĐČĐžĐŽĐ¶Đ”Ń‚ĐŸĐČ"</string>
-    <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
-    <skip />
-    <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
-    <skip />
-    <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
-    <skip />
+    <string name="button_to_remove_widget" msgid="3948204829181214098">"ĐŁĐŽĐ°Đ»ĐžŃ‚ŃŒ"</string>
+    <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Đ”ĐŸĐ±Đ°ĐČоть ĐČОЎжДт"</string>
+    <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Đ“ĐŸŃ‚ĐŸĐČĐŸ"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ĐĄĐŒĐ”ĐœĐžŃ‚ŃŒ ĐżĐŸĐ»ŃŒĐ·ĐŸĐČĐ°Ń‚Đ”Đ»Ń."</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"расĐșрыĐČĐ°ŃŽŃ‰Đ”Đ”ŃŃ ĐŒĐ”ĐœŃŽ"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ВсД ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžŃ Đž ĐŽĐ°ĐœĐœŃ‹Đ” ŃŃ‚ĐŸĐłĐŸ ĐżŃ€ĐŸŃ„ĐžĐ»Ń Đ±ŃƒĐŽŃƒŃ‚ ŃƒĐŽĐ°Đ»Đ”ĐœŃ‹."</string>
@@ -465,7 +467,7 @@
     <string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"ОтĐșĐ»ĐŸĐœĐžŃ‚ŃŒ ĐČсД бДззĐČŃƒŃ‡ĐœŃ‹Đ” уĐČĐ”ĐŽĐŸĐŒĐ»Đ”ĐœĐžŃ"</string>
     <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"В Ń€Đ”Đ¶ĐžĐŒĐ” \"ĐĐ” Đ±Đ”ŃĐżĐŸĐșĐŸĐžŃ‚ŃŒ\" уĐČĐ”ĐŽĐŸĐŒĐ»Đ”ĐœĐžŃ Đ·Đ°Đ±Đ»ĐŸĐșĐžŃ€ĐŸĐČĐ°ĐœŃ‹"</string>
     <string name="media_projection_action_text" msgid="3634906766918186440">"Начать"</string>
-    <string name="empty_shade_text" msgid="8935967157319717412">"ĐĐ”Ń‚ уĐČĐ”ĐŽĐŸĐŒĐ»Đ”ĐœĐžĐč"</string>
+    <string name="empty_shade_text" msgid="8935967157319717412">"ĐĐ”Ń‚ уĐČĐ”ĐŽĐŸĐŒĐ»Đ”ĐœĐžĐč."</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"ĐĐŸĐČых уĐČĐ”ĐŽĐŸĐŒĐ»Đ”ĐœĐžĐč ĐœĐ”Ń‚"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Đ Đ°Đ·Đ±Đ»ĐŸĐșоруĐčтД, Ń‡Ń‚ĐŸĐ±Ń‹ уĐČĐžĐŽĐ”Ń‚ŃŒ уĐČĐ”ĐŽĐŸĐŒĐ»Đ”ĐœĐžŃ"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"ĐŁŃŃ‚Ń€ĐŸĐčстĐČĐŸĐŒ упраĐČĐ»ŃĐ”Ń‚ ĐŸĐŽĐžĐœ Оз Ń€ĐŸĐŽĐžŃ‚Đ”Đ»Đ”Đč."</string>
@@ -867,7 +869,6 @@
     <string name="auto_saver_title" msgid="6873691178754086596">"ĐĐ°Đ¶ĐŒĐžŃ‚Đ”, Ń‡Ń‚ĐŸĐ±Ń‹ ĐœĐ°ŃŃ‚Ń€ĐŸĐžŃ‚ŃŒ Ń€Đ”Đ¶ĐžĐŒ ŃĐœĐ”Ń€ĐłĐŸŃĐ±Đ”Ń€Đ”Đ¶Đ”ĐœĐžŃ"</string>
     <string name="auto_saver_text" msgid="3214960308353838764">"ВĐșĐ»ŃŽŃ‡Đ°Ń‚ŃŒ, ДслО ĐČŃ‹ŃĐŸĐșа ĐČĐ”Ń€ĐŸŃŃ‚ĐœĐŸŃŃ‚ŃŒ, Ń‡Ń‚ĐŸ Đ±Đ°Ń‚Đ°Ń€Đ”Ń сĐșĐŸŃ€ĐŸ Ń€Đ°Đ·Ń€ŃĐŽĐžŃ‚ŃŃ"</string>
     <string name="no_auto_saver_action" msgid="7467924389609773835">"ĐĐ”Ń‚, ŃĐżĐ°ŃĐžĐ±ĐŸ"</string>
-    <string name="heap_dump_tile_name" msgid="2464189856478823046">"ĐŸĐ”Ń€Đ”ĐŽĐ°Ń‡Đ° SysUI"</string>
     <string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"Đ˜ŃĐżĐŸĐ»ŃŒĐ·ŃƒĐ”Ń‚ŃŃ"</string>
     <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"В ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžŃŃ… ĐžŃĐżĐŸĐ»ŃŒĐ·ŃƒĐ”Ń‚ŃŃ <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
     <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"ЗаЮаĐčтД ŃŃ‚Đ°ĐœĐŽĐ°Ń€Ń‚ĐœĐŸĐ” ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžĐ” ĐŽĐ»Ń Đ·Đ°ĐŒĐ”Ń‚ĐŸĐș ĐČ ĐœĐ°ŃŃ‚Ń€ĐŸĐčĐșах."</string>
     <string name="install_app" msgid="5066668100199613936">"ĐŁŃŃ‚Đ°ĐœĐŸĐČоть ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžĐ”"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Đ”ŃƒĐ±Đ»ĐžŃ€ĐŸĐČать ĐœĐ° ĐČĐœĐ”ŃˆĐœĐžĐč ЎОсплДĐč?"</string>
-    <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"Đ€ŃƒĐœĐșцоя ĐŸĐŽĐœĐŸĐČŃ€Đ”ĐŒĐ”ĐœĐœĐŸĐłĐŸ ĐžŃĐżĐŸĐ»ŃŒĐ·ĐŸĐČĐ°ĐœĐžŃ ĐŽĐČух эĐșŃ€Đ°ĐœĐŸĐČ Đ±ŃƒĐŽĐ”Ń‚ ĐŸŃŃ‚Đ°ĐœĐŸĐČĐ»Đ”ĐœĐ°, ДслО ĐŸĐœĐ° аĐșтоĐČĐœĐ°."</string>
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Đ”Đ»Ń ĐČĐœŃƒŃ‚Ń€Đ”ĐœĐœĐ”ĐłĐŸ эĐșŃ€Đ°ĐœĐ° ĐČĐșĐ»ŃŽŃ‡ĐžŃ‚ŃŃ ĐŽŃƒĐ±Đ»ĐžŃ€ĐŸĐČĐ°ĐœĐžĐ”. ĐŸĐ”Ń€Đ”ĐŽĐœĐžĐč эĐșŃ€Đ°Đœ Đ±ŃƒĐŽĐ”Ń‚ ĐŸŃ‚ĐșĐ»ŃŽŃ‡Đ”Đœ."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Đ”ŃƒĐ±Đ»ĐžŃ€ĐŸĐČать"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"ЗаĐșрыть"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Đ­ĐșŃ€Đ°Đœ ĐżĐŸĐŽĐșĐ»ŃŽŃ‡Đ”Đœ"</string>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index 2405bbf..4b4d08b 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -330,12 +330,17 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"තිර ඎටිගත කිරීඞ"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"ආරඞ්භ කරන්න"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"නතර කරන්න"</string>
-    <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
-    <skip />
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"ඎටිගත කිරීඞේ ගැටගුව"</string>
+    <string name="qs_record_issue_start" msgid="2979831312582567056">"අරà¶čන්න"</string>
+    <string name="qs_record_issue_stop" msgid="3531747965741982657">"නවත්වන්න"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"ඔබේ උඎාංග à¶…à¶­à·Šà¶Żà·à¶šà·“à¶žà·š කුඞන කොටසට බගඎෑවේ à¶Ż?"</string>
+    <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"ගැටගු වර්ගà¶ș තෝරන්න"</string>
+    <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"තිර ඎටිගත කිරීඞ"</string>
+  <string-array name="qs_record_issue_types">
+    <item msgid="2947988124014085798">"කාර්à¶ș සාධනà¶ș"</item>
+    <item msgid="1627504621139124393">"ඎරිශීගක අතුරු ඞුහුණත"</item>
+    <item msgid="8309220355268900335">"බැටරිà¶ș"</item>
+  </string-array>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"තනි අත් à¶Žà·Š‍රකාරà¶ș"</string>
     <string name="quick_settings_contrast_label" msgid="988087460210159123">"අසඞානතාව"</string>
     <string name="quick_settings_contrast_standard" msgid="2538227821968061832">"සඞ්ඞත"</string>
@@ -408,12 +413,9 @@
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ආරෝඎණà¶ș වෙඞින් • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>කින් සඞ්ඎූර්ණ වේ"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"à¶Žà·œà¶Żà·” නිබන්ධනà¶ș ආරඞ්භ කිරීඞට වඞට ස්වà¶șà·’à¶Žà·Š කරන්න"</string>
     <string name="button_to_open_widget_editor" msgid="5599945944349057600">"à·€à·’à¶ąà¶§à·Š සංස්කාරකà¶ș විවෘත කරන්න"</string>
-    <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
-    <skip />
-    <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
-    <skip />
-    <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
-    <skip />
+    <string name="button_to_remove_widget" msgid="3948204829181214098">"ඉවත් කරන්න"</string>
+    <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"à·€à·’à¶ąà¶§à·Šà¶§à·”à·€ එක් කරන්න"</string>
+    <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"නිඞà¶șි"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ඎරිශීගක ඞාරුව"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"නිඎතන ඞෙනුව"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ඞෙඞ සැසිà¶șේ සිà¶șළුඞ à¶șà·™à¶Żà·”à¶žà·Š සහ à¶Żà¶­à·Šà¶­ ඞකාවී."</string>
@@ -867,7 +869,6 @@
     <string name="auto_saver_title" msgid="6873691178754086596">"බැටරි සුරැකුඞ කාගසටහන්ගත කිරීඞට තට්ටු කරන්න"</string>
     <string name="auto_saver_text" msgid="3214960308353838764">"බැටරිà¶ș අවසන් වීඞට à¶șà¶± විට සක්‍රීà¶ș කරන්න"</string>
     <string name="no_auto_saver_action" msgid="7467924389609773835">"එඎා ස්තූතිà¶șි"</string>
-    <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string>
     <string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"භාවිතà¶șේ ඇත"</string>
     <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"à¶șà·™à¶Żà·”à¶žà·Š ඔබේ <xliff:g id="TYPES_LIST">%s</xliff:g> භාවිත කරඞින් සිටී."</string>
     <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"සැකසීඞ් තුළ ඎෙරනිඞි සටහන් à¶șà·™à¶Żà·”à¶ž සකසන්න"</string>
     <string name="install_app" msgid="5066668100199613936">"à¶șà·™à¶Żà·”à¶ž à·ƒà·Šà¶źà·à¶Žà¶±à¶ș කරන්න"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"බාහිර à·ƒà¶‚à¶Żà¶»à·Šà·à¶šà¶șà¶§ à¶Żà¶»à·Šà¶Žà¶«à¶ș කරන්න à¶Ż?"</string>
-    <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"à¶Żà·à¶±à¶§ ක්‍රිà¶șාත්ඞක වන ඕනෑඞ à¶Żà·Šà·€à·’à¶­à·Šà·€ තිර ක්‍රිà¶șාකාරකඞක් නවත්වනු ඇත"</string>
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"ඔබේ අභ්‍à¶șන්තර à·ƒà¶‚à¶Żà¶»à·Šà·à¶šà¶ș à¶Žà·’à·…à·’à¶¶à·’à¶čු වනු ඇත. ඔබේ à¶‰à¶Żà·’à¶»à·’à¶Žà·ƒ à·ƒà¶‚à¶Żà¶»à·Šà·à¶šà¶ș ක්‍රිà¶șාවිරහිත වනු ඇත."</string>
     <string name="mirror_display" msgid="2515262008898122928">"à·ƒà¶‚à¶Żà¶»à·Šà·à¶šà¶ș à¶Żà¶»à·Šà¶Žà¶«à¶ș කරන්න"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"අස් කරන්න"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"à·ƒà¶‚à¶Żà¶»à·Šà·à¶šà¶ș සඞ්බන්ධ කර ඇත"</string>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index f53b5bf..9e9507e 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -330,12 +330,17 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Rekordér obrazovky"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"ZačaĆ„"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"UkončiĆ„"</string>
-    <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
-    <skip />
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"Problém s nahrávaním"</string>
+    <string name="qs_record_issue_start" msgid="2979831312582567056">"Začnite"</string>
+    <string name="qs_record_issue_stop" msgid="3531747965741982657">"Zastavte"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Čo v zariadení bolo ovplyvnené?"</string>
+    <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Vyberte typ problému"</string>
+    <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Rekordér obrazovky"</string>
+  <string-array name="qs_record_issue_types">
+    <item msgid="2947988124014085798">"Výkon"</item>
+    <item msgid="1627504621139124393">"PouĆŸívateÄŸské rozhranie"</item>
+    <item msgid="8309220355268900335">"Batéria"</item>
+  </string-array>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"ReĆŸim jednej ruky"</string>
     <string name="quick_settings_contrast_label" msgid="988087460210159123">"Kontrast"</string>
     <string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Štandardný"</string>
@@ -408,12 +413,9 @@
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Nabíja sa • Do úplného nabitia zostáva <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Potiahnutím doÄŸava spustite komunitný návod"</string>
     <string name="button_to_open_widget_editor" msgid="5599945944349057600">"OtvoriĆ„ editor miniaplikácií"</string>
-    <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
-    <skip />
-    <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
-    <skip />
-    <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
-    <skip />
+    <string name="button_to_remove_widget" msgid="3948204829181214098">"OdstrániĆ„"</string>
+    <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"PridaĆ„ miniaplikáciu"</string>
+    <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Hotovo"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Prepnutie pouĆŸívateÄŸa"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"rozbaÄŸovacia ponuka"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Všetky aplikácie a údaje v tejto relácii budú odstránené."</string>
@@ -867,7 +869,6 @@
     <string name="auto_saver_title" msgid="6873691178754086596">"Klepnutím naplánujete aktivovanie Šetriča batérie"</string>
     <string name="auto_saver_text" msgid="3214960308353838764">"Zapnite, keď je batéria takmer vybitá"</string>
     <string name="no_auto_saver_action" msgid="7467924389609773835">"Nie, vďaka"</string>
-    <string name="heap_dump_tile_name" msgid="2464189856478823046">"Výpis haldy SysUI"</string>
     <string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"PouĆŸíva sa"</string>
     <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Aplikácie pouĆŸívajú zoznam <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
     <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Nastavte predvolenú aplikáciu na poznámky v Nastaveniach"</string>
     <string name="install_app" msgid="5066668100199613936">"InštalovaĆ„ aplikáciu"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Chcete zrkadliĆ„ na externú obrazovku?"</string>
-    <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"AkákoÄŸvek momentálne prebiehajúca aktivita vo funkcii Dual Screen bude zastavená"</string>
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Vnútorná obrazovka bude zrkadlená. Predná obrazovka bude vypnutá."</string>
     <string name="mirror_display" msgid="2515262008898122928">"ZrkadliƄ obrazovku"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"ZavrieƄ"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Obrazovka je pripojená"</string>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index ef56c4b..cba5416 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -330,12 +330,17 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Snemanje zaslona"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Začni"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Ustavi"</string>
-    <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
-    <skip />
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"Snemanje teĆŸave"</string>
+    <string name="qs_record_issue_start" msgid="2979831312582567056">"Začetek"</string>
+    <string name="qs_record_issue_stop" msgid="3531747965741982657">"Ustavitev"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Na kateri del izkušnje z napravo je to vplivalo?"</string>
+    <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Izberite vrsto teĆŸave"</string>
+    <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Snemanje zaslona"</string>
+  <string-array name="qs_record_issue_types">
+    <item msgid="2947988124014085798">"Učinkovitost delovanja"</item>
+    <item msgid="1627504621139124393">"Uporabniški vmesnik"</item>
+    <item msgid="8309220355268900335">"Baterija"</item>
+  </string-array>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Enoročni način"</string>
     <string name="quick_settings_contrast_label" msgid="988087460210159123">"Kontrast"</string>
     <string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Standardni"</string>
@@ -408,12 +413,9 @@
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Polnjenje • Napolnjeno čez <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Povlecite levo, da zaĆŸenete vadnico za skupnost"</string>
     <string name="button_to_open_widget_editor" msgid="5599945944349057600">"Odpiranje urejevalnika pripomočkov"</string>
-    <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
-    <skip />
-    <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
-    <skip />
-    <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
-    <skip />
+    <string name="button_to_remove_widget" msgid="3948204829181214098">"Odstrani"</string>
+    <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Dodajanje pripomočka"</string>
+    <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Končano"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Preklop med uporabniki"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"spustni meni"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Vse aplikacije in podatki v tej seji bodo izbrisani."</string>
@@ -867,7 +869,6 @@
     <string name="auto_saver_title" msgid="6873691178754086596">"Dotaknite se za načrtovanje varčevanja z energijo baterije"</string>
     <string name="auto_saver_text" msgid="3214960308353838764">"Vklop, če je verjetno, da se bo baterija izpraznila"</string>
     <string name="no_auto_saver_action" msgid="7467924389609773835">"Ne, hvala"</string>
-    <string name="heap_dump_tile_name" msgid="2464189856478823046">"Izvoz kopice SysUI"</string>
     <string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"V uporabi"</string>
     <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Aplikacije uporabljajo <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
     <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Nastavite privzeto aplikacijo za zapiske v nastavitvah."</string>
     <string name="install_app" msgid="5066668100199613936">"Namesti aplikacijo"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Ćœelite zrcaliti na zunanji zaslon?"</string>
-    <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"Vse dejavnosti na dveh zaslonih, ki se trenutno izvajajo, bodo ustavljene."</string>
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Notranji zaslon bo zrcaljen. Sprednji zaslon bo izklopljen."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Zrcali zaslon"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Opusti"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Zaslon je povezan"</string>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index b8824c7..b35668f 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -330,12 +330,17 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Regjistrimi i ekranit"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Nis"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Ndalo"</string>
-    <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
-    <skip />
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"Regjistro problemin"</string>
+    <string name="qs_record_issue_start" msgid="2979831312582567056">"Nis"</string>
+    <string name="qs_record_issue_stop" msgid="3531747965741982657">"Ndalo"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Cila pjesë e përvojës me pajisjen është prekur?"</string>
+    <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Zgjidh llojin e problemit"</string>
+    <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Regjistrim i ekranit"</string>
+  <string-array name="qs_record_issue_types">
+    <item msgid="2947988124014085798">"Performanca"</item>
+    <item msgid="1627504621139124393">"Ndërfaqja e përdoruesit"</item>
+    <item msgid="8309220355268900335">"Bateria"</item>
+  </string-array>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modaliteti i përdorimit me një dorë"</string>
     <string name="quick_settings_contrast_label" msgid="988087460210159123">"Kontrasti"</string>
     <string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Standard"</string>
@@ -408,12 +413,9 @@
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Po karikohet • Plot për <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Rrëshqit shpejt majtas për të filluar udhëzuesin e përbashkët"</string>
     <string name="button_to_open_widget_editor" msgid="5599945944349057600">"Hap modifikuesin e miniaplikacionit"</string>
-    <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
-    <skip />
-    <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
-    <skip />
-    <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
-    <skip />
+    <string name="button_to_remove_widget" msgid="3948204829181214098">"Hiq"</string>
+    <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Shto miniaplikacionin"</string>
+    <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"U krye"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Ndërro përdorues"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menyja me tërheqje poshtë"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Të gjitha aplikacionet dhe të dhënat në këtë sesion do të fshihen."</string>
@@ -867,7 +869,6 @@
     <string name="auto_saver_title" msgid="6873691178754086596">"Trokit për të planifikuar \"Kursyesin e baterisë\""</string>
     <string name="auto_saver_text" msgid="3214960308353838764">"Aktivizoje kur bateria mund të mbarojë"</string>
     <string name="no_auto_saver_action" msgid="7467924389609773835">"Jo"</string>
-    <string name="heap_dump_tile_name" msgid="2464189856478823046">"Hidh grumbullin SysUI"</string>
     <string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"Në përdorim"</string>
     <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Aplikacionet po përdorin <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
     <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,8 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Cakto aplikacionin e parazgjedhur të shënimeve te \"Cilësimet\""</string>
     <string name="install_app" msgid="5066668100199613936">"Instalo aplikacionin"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Të pasqyrohet në ekranin e jashtëm?"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (2917631104216376315) -->
-    <skip />
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Ekrani i brendshëm do të pasqyrohet. Ekrani i parmë do të çaktivizohet."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Pasqyro ekranin"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Hiq"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Ekrani është lidhur"</string>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index 1eb4519..1d45fbd 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -330,12 +330,17 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"ĐĄĐœĐžĐŒĐ°ŃšĐ” Đ”ĐșŃ€Đ°ĐœĐ°"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"ĐŸĐŸŃ‡ĐœĐžŃ‚Đ”"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"ЗаустаĐČОтД"</string>
-    <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
-    <skip />
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"ЕĐČĐžĐŽĐ”ĐœŃ‚ĐžŃ€Đ°Ń˜Ń‚Đ” ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒ"</string>
+    <string name="qs_record_issue_start" msgid="2979831312582567056">"ĐŸĐŸĐșŃ€Đ”ĐœĐž"</string>
+    <string name="qs_record_issue_stop" msgid="3531747965741982657">"ЗаустаĐČĐž"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"На ĐșĐŸŃ˜Đž ĐŽĐ”ĐŸ ĐŽĐŸĐ¶ĐžĐČљаја ĐœĐ° ŃƒŃ€Đ”Ń’Đ°Ń˜Ńƒ јД ĐŸĐČĐŸ ŃƒŃ‚ĐžŃ†Đ°Đ»ĐŸ?"</string>
+    <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Đ˜Đ·Đ°Đ±Đ”Ń€ĐžŃ‚Đ” топ ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒĐ°"</string>
+    <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"ĐĄĐœĐžĐŒĐ°ŃšĐ” Đ”ĐșŃ€Đ°ĐœĐ°"</string>
+  <string-array name="qs_record_issue_types">
+    <item msgid="2947988124014085798">"ĐŁŃ‡ĐžĐœĐ°Đș"</item>
+    <item msgid="1627504621139124393">"ĐšĐŸŃ€ĐžŃĐœĐžŃ‡ĐșĐž ĐžĐœŃ‚Đ”Ń€Ń„Đ”Ń˜Ń"</item>
+    <item msgid="8309220355268900335">"Đ‘Đ°Ń‚Đ”Ń€ĐžŃ˜Đ°"</item>
+  </string-array>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Đ Đ”Đ¶ĐžĐŒ Ń˜Đ”ĐŽĐœĐŸĐŒ руĐșĐŸĐŒ"</string>
     <string name="quick_settings_contrast_label" msgid="988087460210159123">"ĐšĐŸĐœŃ‚Ń€Đ°ŃŃ‚"</string>
     <string name="quick_settings_contrast_standard" msgid="2538227821968061832">"ĐĄŃ‚Đ°ĐœĐŽĐ°Ń€ĐŽĐœĐŸ"</string>
@@ -408,12 +413,9 @@
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ĐŸŃƒĐœĐž сД • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> ĐŽĐŸ Đșраја ĐżŃƒŃšĐ”ŃšĐ°"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"ĐŸŃ€Đ”ĐČŃƒŃ†ĐžŃ‚Đ” улДĐČĐŸ Ўа бОстД Đ·Đ°ĐżĐŸŃ‡Đ”Đ»Đž Đ·Đ°Ń˜Đ”ĐŽĐœĐžŃ‡ĐșĐž ĐČĐŸĐŽĐžŃ‡"</string>
     <string name="button_to_open_widget_editor" msgid="5599945944349057600">"ОтĐČĐŸŃ€Đž ŃƒŃ€Đ”Ń’ĐžĐČач ĐČĐžŃŸĐ”Ń‚Đ°"</string>
-    <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
-    <skip />
-    <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
-    <skip />
-    <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
-    <skip />
+    <string name="button_to_remove_widget" msgid="3948204829181214098">"ĐŁĐșĐ»ĐŸĐœĐž"</string>
+    <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Đ”ĐŸĐŽĐ°Ń˜ ĐČĐžŃŸĐ”Ń‚"</string>
+    <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Đ“ĐŸŃ‚ĐŸĐČĐŸ"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Đ—Đ°ĐŒĐ”ĐœĐž ĐșĐŸŃ€ĐžŃĐœĐžĐșа"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"паЮајућо ĐŒĐ”ĐœĐž"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ĐĄĐČĐ” аплОĐșĐ°Ń†ĐžŃ˜Đ” Đž ĐżĐŸĐŽĐ°Ń†Đž у ĐŸĐČĐŸŃ˜ сДсОјО ћД бОтО ĐžĐ·Đ±Ń€ĐžŃĐ°ĐœĐž."</string>
@@ -867,7 +869,6 @@
     <string name="auto_saver_title" msgid="6873691178754086596">"Đ”ĐŸĐŽĐžŃ€ĐœĐžŃ‚Đ” Ўа бОстД ĐœĐ°ĐżŃ€Đ°ĐČОлО Ń€Đ°ŃĐżĐŸŃ€Đ”ĐŽ за ŃƒŃˆŃ‚Đ”ĐŽŃƒ Đ±Đ°Ń‚Đ”Ń€ĐžŃ˜Đ”"</string>
     <string name="auto_saver_text" msgid="3214960308353838764">"ĐŁĐșŃ™ŃƒŃ‡ĐžŃ‚Đ” аĐșĐŸ ћД Đ±Đ°Ń‚Đ”Ń€ĐžŃ˜Đ° ĐČĐ”Ń€ĐŸĐČĐ°Ń‚ĐœĐŸ Ўа сД ĐžŃĐżŃ€Đ°Đ·ĐœĐž"</string>
     <string name="no_auto_saver_action" msgid="7467924389609773835">"ĐĐ”, хĐČала"</string>
-    <string name="heap_dump_tile_name" msgid="2464189856478823046">"ИзЎĐČĐŸŃ˜Đž SysUI ĐŒĐ”ĐŒ."</string>
     <string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"ĐŁ ŃƒĐżĐŸŃ‚Ń€Đ”Đ±Đž"</string>
     <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"АплОĐșĐ°Ń†ĐžŃ˜Đ” ĐșĐŸŃ€ĐžŃŃ‚Đ” <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
     <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"ĐŸĐŸĐŽĐ”ŃĐžŃ‚Đ” ĐżĐŸĐŽŃ€Đ°Đ·ŃƒĐŒĐ”ĐČĐ°ĐœŃƒ аплОĐșацоју за бДлДшĐșĐ” у ĐŸĐŸĐŽĐ”ŃˆĐ°ĐČĐ°ŃšĐžĐŒĐ°"</string>
     <string name="install_app" msgid="5066668100199613936">"Đ˜ĐœŃŃ‚Đ°Đ»ĐžŃ€Đ°Ń˜ аплОĐșацоју"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"ЖДлОтД лО Ўа прДслОĐșатД ĐœĐ° ŃĐżĐŸŃ™ŃšĐž Đ”ĐșŃ€Đ°Đœ?"</string>
-    <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"БОћД Đ·Đ°ŃƒŃŃ‚Đ°ĐČŃ™Đ”ĐœĐ° сĐČаĐșа аĐșтоĐČĐœĐŸŃŃ‚ ĐŽĐČĐŸŃ˜ĐœĐŸĐł Đ”ĐșŃ€Đ°ĐœĐ° ĐșĐŸŃ˜Đ° јД у Ń‚ĐŸĐșу"</string>
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"ĐŁĐœŃƒŃ‚Ń€Đ°ŃˆŃšĐž Đ”ĐșŃ€Đ°Đœ ћД сД прДслОĐșато. ĐŸŃ€Đ”ĐŽŃšĐž Đ”ĐșŃ€Đ°Đœ ћД сД ОсĐșључото."</string>
     <string name="mirror_display" msgid="2515262008898122928">"ĐŸŃ€Đ”ŃĐ»ĐžĐșај Đ”ĐșŃ€Đ°Đœ"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"ОЎбацО"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"ЕĐșŃ€Đ°Đœ јД ĐżĐŸĐČĐ”Đ·Đ°Đœ"</string>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index c998e6d..0d6272f 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -330,12 +330,17 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Skärminspelning"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Starta"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Stoppa"</string>
-    <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
-    <skip />
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"Registrera problem"</string>
+    <string name="qs_record_issue_start" msgid="2979831312582567056">"Starta"</string>
+    <string name="qs_record_issue_stop" msgid="3531747965741982657">"Stoppa"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Vilken enhetsupplevelse påverkades?"</string>
+    <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Välj problemtyp"</string>
+    <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Skärminspelning"</string>
+  <string-array name="qs_record_issue_types">
+    <item msgid="2947988124014085798">"Prestanda"</item>
+    <item msgid="1627504621139124393">"Användargränssnitt"</item>
+    <item msgid="8309220355268900335">"Batteri"</item>
+  </string-array>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Enhandsläge"</string>
     <string name="quick_settings_contrast_label" msgid="988087460210159123">"Kontrast"</string>
     <string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Standard"</string>
@@ -408,12 +413,9 @@
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Laddas • Fulladdat om <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Svep åt vänster för att börja med gruppguiden"</string>
     <string name="button_to_open_widget_editor" msgid="5599945944349057600">"Öppna widgetredigeraren"</string>
-    <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
-    <skip />
-    <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
-    <skip />
-    <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
-    <skip />
+    <string name="button_to_remove_widget" msgid="3948204829181214098">"Ta bort"</string>
+    <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Lägg till widget"</string>
+    <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Klar"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Byt användare"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"rullgardinsmeny"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alla appar och data i denna session kommer att raderas."</string>
@@ -867,7 +869,6 @@
     <string name="auto_saver_title" msgid="6873691178754086596">"Tryck för att skapa ett schema för batterisparläget"</string>
     <string name="auto_saver_text" msgid="3214960308353838764">"Aktivera när batteriet håller på att ta slut"</string>
     <string name="no_auto_saver_action" msgid="7467924389609773835">"Nej tack"</string>
-    <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI-heap"</string>
     <string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"Används"</string>
     <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"<xliff:g id="TYPES_LIST">%s</xliff:g> används av appar."</string>
     <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1115,7 +1116,7 @@
     <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Söker efter nätverk …"</string>
     <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Det gick inte att ansluta till nätverket"</string>
     <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Du ansluts inte till wifi automatiskt för närvarande"</string>
-    <string name="see_all_networks" msgid="3773666844913168122">"Visa alla"</string>
+    <string name="see_all_networks" msgid="3773666844913168122">"Se alla"</string>
     <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Koppla bort Ethernet för att växla nätverk"</string>
     <string name="wifi_scan_notify_message" msgid="3753839537448621794">"I syfte att förbättra upplevelsen med enheten kan appar och tjänster fortfarande söka efter wifi-nätverk när som helst, även om wifi har inaktiverats. Du kan ändra detta i inställningarna för wifi-sökning. "<annotation id="link">"Ändra"</annotation></string>
     <string name="turn_off_airplane_mode" msgid="8425587763226548579">"Inaktivera flygplansläge"</string>
@@ -1207,7 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Ställ in en standardapp för anteckningar i inställningarna"</string>
     <string name="install_app" msgid="5066668100199613936">"Installera appen"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Vill du spegla till extern skärm?"</string>
-    <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"Pågående Dual Screen-aktivitet stoppas"</string>
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Den inre skärmen speglas. Den främre skärmen stängs av."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Spegla skärm"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Ignorera"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Skärm har anslutits"</string>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index fc742d8..1bd2ed7 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -252,7 +252,7 @@
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Hakuna vifaa vilivyooanishwa vinavyopatikana"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Gusa ili uunganishe au utenganishe kifaa"</string>
-    <string name="pair_new_bluetooth_devices" msgid="4601767620843349645">"Oanisha kifaa kipya"</string>
+    <string name="pair_new_bluetooth_devices" msgid="4601767620843349645">"Unganisha kifaa kipya"</string>
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Angalia vyote"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Tumia Bluetooth"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Imeunganishwa"</string>
@@ -330,12 +330,17 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Rekodi ya skrini"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Anza kurekodi"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Acha kurekodi"</string>
-    <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
-    <skip />
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"Hitilafu ya Kurekodi"</string>
+    <string name="qs_record_issue_start" msgid="2979831312582567056">"Anza"</string>
+    <string name="qs_record_issue_stop" msgid="3531747965741982657">"Simamisha"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Ni sehemu gani ya matumizi ya kifaa iliathiriwa?"</string>
+    <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Chagua aina ya tatizo"</string>
+    <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Rekodi ya skrini"</string>
+  <string-array name="qs_record_issue_types">
+    <item msgid="2947988124014085798">"Utendaji"</item>
+    <item msgid="1627504621139124393">"Kiolesura cha Mtumiaji"</item>
+    <item msgid="8309220355268900335">"Betri"</item>
+  </string-array>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Hali ya kutumia kwa mkono mmoja"</string>
     <string name="quick_settings_contrast_label" msgid="988087460210159123">"Utofautishaji"</string>
     <string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Kawaida"</string>
@@ -408,12 +413,9 @@
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Inachaji • Itajaa baada ya <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Telezesha kidole kushoto ili uanze mafunzo ya pamoja"</string>
     <string name="button_to_open_widget_editor" msgid="5599945944349057600">"Fungua kihariri cha wijeti"</string>
-    <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
-    <skip />
-    <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
-    <skip />
-    <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
-    <skip />
+    <string name="button_to_remove_widget" msgid="3948204829181214098">"Ondoa"</string>
+    <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Ongeza wijeti"</string>
+    <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Nimemaliza"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Badili mtumiaji"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menyu ya kuvuta chini"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Data na programu zote katika kipindi hiki zitafutwa."</string>
@@ -867,7 +869,6 @@
     <string name="auto_saver_title" msgid="6873691178754086596">"Gusa ili uratibu wakati wa kuwasha Kiokoa Betri"</string>
     <string name="auto_saver_text" msgid="3214960308353838764">"Washa wakati betri inakaribia kuisha"</string>
     <string name="no_auto_saver_action" msgid="7467924389609773835">"Hapana"</string>
-    <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string>
     <string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"Inatumika"</string>
     <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Programu zinatumia <xliff:g id="TYPES_LIST">%s</xliff:g> yako."</string>
     <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,8 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Teua programu chaguomsingi ya madokezo katika Mipangilio"</string>
     <string name="install_app" msgid="5066668100199613936">"Sakinisha programu"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Ungependa kuonyesha kwenye skrini ya nje?"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (2917631104216376315) -->
-    <skip />
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Mwonekano wa ndani wa kifaa chako utaakisiwa. Mwonekano wa mbele wa kifaa chako utazimwa."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Akisi skrini"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Ondoa"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Skrini imeunganishwa"</string>
diff --git a/packages/SystemUI/res/values-sw600dp/config.xml b/packages/SystemUI/res/values-sw600dp/config.xml
index 1f671ac..f1017d8 100644
--- a/packages/SystemUI/res/values-sw600dp/config.xml
+++ b/packages/SystemUI/res/values-sw600dp/config.xml
@@ -40,6 +40,9 @@
     <!-- Whether to show bottom sheets edge to edge -->
     <bool name="config_edgeToEdgeBottomSheetDialog">false</bool>
 
+    <!-- Flag to activate drag notification to contents feature -->
+    <bool name="config_notificationToContents">true</bool>
+
     <!-- A collection of defaults for the quick affordances on the lock screen. Each item must be a
     string with two parts: the ID of the slot and the comma-delimited list of affordance IDs,
     separated by a colon ':' character. For example: <item>bottom_end:home,wallet</item>. The
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index afba7a92..0cd076f 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -330,12 +330,17 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"àźžàŻàź•àźżàź°àŻ€àź©àŻ àź°àŻ†àź•àŻàź•àźŸàź°àŻàźŸàŻ"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"àź€àŻŠàźŸàź™àŻàź•àŻ"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"àźšàźżàź±àŻàź€àŻàź€àŻ"</string>
-    <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
-    <skip />
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"àźšàźżàź•àŻàź•àźČàŻˆ àź°àŻ†àź•àŻàź•àźŸàź°àŻàźŸàŻ àźšàŻ†àźŻàŻàź€àźČàŻ"</string>
+    <string name="qs_record_issue_start" msgid="2979831312582567056">"àź€àŻŠàźŸàź™àŻàź•àŻàź™àŻàź•àźłàŻ"</string>
+    <string name="qs_record_issue_stop" msgid="3531747965741982657">"àźšàźżàź±àŻàź€àŻàź€àŻàź™àŻàź•àźłàŻ"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"àźšàźŸàź€àź© àź…àź©àŻàźȘàź”àź€àŻàź€àźżàź©àŻ àźŽàźšàŻàź€àźȘàŻ àźȘàź•àŻàź€àźż àźȘàźŸàź€àźżàź•àŻàź•àźȘàŻàźȘàźŸàŻàźŸàź€àŻ?"</string>
+    <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"àźšàźżàź•àŻàź•àźČàŻ àź”àź•àŻˆàźŻàŻˆàź€àŻ àź€àŻ‡àź°àŻàź”àŻàźšàŻ†àźŻàŻàź•"</string>
+    <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"àźžàŻàź•àźżàź°àŻ€àź©àŻ àź°àŻ†àź•àŻàź•àźŸàź°àŻàźŸàŻ"</string>
+  <string-array name="qs_record_issue_types">
+    <item msgid="2947988124014085798">"àźšàŻ†àźŻàźČàŻàź€àźżàź±àź©àŻ"</item>
+    <item msgid="1627504621139124393">"àźȘàźŻàź©àź°àŻ àź‡àźŸàŻˆàźźàŻàź•àźźàŻ"</item>
+    <item msgid="8309220355268900335">"àźȘàŻ‡àźŸàŻàźŸàź°àźż"</item>
+  </string-array>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"àź’àź±àŻàź±àŻˆàź•àŻ àź•àŻˆàźȘàŻ àźȘàźŻàź©àŻàźźàŻàź±àŻˆ"</string>
     <string name="quick_settings_contrast_label" msgid="988087460210159123">"àź’àźłàźż àźźàźŸàź±àŻàźȘàźŸàźŸàŻ"</string>
     <string name="quick_settings_contrast_standard" msgid="2538227821968061832">"àź‡àźŻàźČàŻàźȘàŻàźšàźżàźČàŻˆ"</string>
@@ -408,12 +413,9 @@
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • àźšàźŸàź°àŻàźœàźŸàź•àźżàź±àź€àŻ • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> àź‡àźČàŻ àźźàŻàźŽàŻàź”àź€àŻàźźàŻ àźšàźŸàź°àŻàźœàźŸàź•àŻàźźàŻ"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"àźšàźźàŻ‚àź•àźȘàŻ àźȘàźŻàźżàź±àŻàźšàźżàźŻàŻˆàź€àŻ àź€àŻŠàźŸàź™àŻàź• àź‡àźŸàź€àŻàźȘàŻàź±àźźàŻ àźžàŻàź”àŻˆàźȘàŻ àźšàŻ†àźŻàŻàźŻàŻàź™àŻàź•àźłàŻ"</string>
     <string name="button_to_open_widget_editor" msgid="5599945944349057600">"àź”àźżàźŸàŻàźœàŻ†àźŸàŻ àźŽàźŸàźżàźŸàŻàźŸàź°àŻˆàź€àŻ àź€àźżàź±àź•àŻàź•àŻàźźàŻ"</string>
-    <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
-    <skip />
-    <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
-    <skip />
-    <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
-    <skip />
+    <string name="button_to_remove_widget" msgid="3948204829181214098">"àź…àź•àź±àŻàź±àŻàźźàŻ"</string>
+    <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"àź”àźżàźŸàŻàźœàŻ†àźŸàŻàźŸàŻˆàźšàŻ àźšàŻ‡àź°àŻ"</string>
+    <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"àźźàŻàźŸàźżàźšàŻàź€àź€àŻ"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"àźȘàźŻàź©àź°àŻˆ àźźàźŸàź±àŻàź±àŻ"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"àź•àŻ€àźŽàŻ àź‡àźŽàŻàź•àŻàź•àŻàźźàŻ àźźàŻ†àź©àŻ"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"àź‡àźšàŻàź€ àź…àźźàź°àŻàź”àźżàź©àŻ àźŽàźČàŻàźČàźŸ àź†àźȘàŻàźžàŻàźźàŻ àź€àź°àź”àŻàźźàŻ àźšàŻ€àź•àŻàź•àźȘàŻàźȘàźŸàŻàźźàŻ."</string>
@@ -867,7 +869,6 @@
     <string name="auto_saver_title" msgid="6873691178754086596">"àźȘàŻ‡àźŸàŻàźŸàź°àźż àźšàŻ‡àźźàźżàźȘàŻàźȘàźŸàź©àŻˆ àź†àź©àŻ àźšàŻ†àźŻàŻàź”àź€àŻ àź€àŻŠàźŸàź°àŻàźȘàźŸàź•àź€àŻ àź€àźżàźŸàŻàźŸàźźàźżàźŸ, àź€àźŸàŻàźŸàź”àŻàźźàŻ"</string>
     <string name="auto_saver_text" msgid="3214960308353838764">"àźȘàŻ‡àźŸàŻàźŸàź°àźż àź€àŻ€àź°àŻàźšàŻàź€àŻàźȘàŻ‹àź•àŻàźźàŻ àźšàźżàźČàŻˆàźŻàźżàźČàŻ àź‡àź°àŻàź•àŻàź•àŻàźźàŻ àźȘàŻ‹àź€àŻ àź†àź©àŻ àźšàŻ†àźŻàŻàźŻàźȘàŻàźȘàźŸàŻàźźàŻ"</string>
     <string name="no_auto_saver_action" msgid="7467924389609773835">"àź”àŻ‡àźŁàŻàźŸàźŸàźźàŻ"</string>
-    <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string>
     <string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"àź‰àźȘàźŻàŻ‹àź•àź€àŻàź€àźżàźČàŻ àź‰àźłàŻàźłàź€àŻ"</string>
     <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"àź‰àź™àŻàź•àźłàŻ <xliff:g id="TYPES_LIST">%s</xliff:g> àź†àź•àźżàźŻàź”àź±àŻàź±àŻˆ àź†àźȘàŻàźžàŻ àźȘàźŻàź©àŻàźȘàźŸàŻàź€àŻàź€àŻàź•àźżàź©àŻàź±àź©."</string>
     <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,8 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"àź•àŻàź±àźżàźȘàŻàźȘàŻ àźŽàźŸàŻàźȘàŻàźȘàź€àź±àŻàź•àźŸàź© àź‡àźŻàźČàŻàźȘàŻàźšàźżàźČàŻˆ àź†àźȘàŻàźžàŻˆ àź…àźźàŻˆàźȘàŻàźȘàŻàź•àźłàźżàźČàŻ àź…àźźàŻˆàźŻàŻàź™àŻàź•àźłàŻ"</string>
     <string name="install_app" msgid="5066668100199613936">"àź†àźȘàŻàźžàŻˆ àźšàźżàź±àŻàź”àŻàź™àŻàź•àźłàŻ"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"àź”àŻ†àźłàźżàźȘàŻàźȘàŻàź±àź•àŻ àź•àźŸàźŸàŻàźšàźżàź•àŻàź•àŻ àźźàźżàź°àź°àŻ àźšàŻ†àźŻàŻàźŻàź”àźŸ?"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (2917631104216376315) -->
-    <skip />
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"àź‰àź™àŻàź•àźłàŻ àź‰àźŸàŻàźȘàŻàź± àźŸàźżàźžàŻàźȘàźżàźłàŻ‡ àźȘàźżàź°àź€àźżàźȘàźČàźżàź•àŻàź•àźȘàŻàźȘàźŸàŻàźźàŻ. àź‰àź™àŻàź•àźłàŻ àźźàŻàź©àŻàźȘàŻàź± àźŸàźżàźžàŻàźȘàźżàźłàŻ‡ àźźàŻàźŸàź•àŻàź•àźȘàŻàźȘàźŸàŻàźźàŻ."</string>
     <string name="mirror_display" msgid="2515262008898122928">"àźŸàźżàźžàŻàźȘàźżàźłàŻ‡àźŻàŻˆ àźźàźżàź°àź°àŻ àźšàŻ†àźŻàŻ"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"àź”àŻ‡àźŁàŻàźŸàźŸàźźàŻ"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"àźŸàźżàźžàŻàźȘàŻàźłàŻ‡ àź‡àźŁàŻˆàź•àŻàź•àźȘàŻàźȘàźŸàŻàźŸàź€àŻ"</string>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index 6621dae..6a59812 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -330,12 +330,17 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"ఞ్క్రీచ్ à°°à°żà°•à°Ÿà°°à±à°Ąà±"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"à°Șà±à°°à°Ÿà°°à°‚à°­à°żà°‚à°šà°‚à°Ąà°ż"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"ఆà°Șు"</string>
-    <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
-    <skip />
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"à°žà°źà°žà±à°Żà°šà± à°°à°żà°•à°Ÿà°°à±à°Ąà± à°šà±‡à°Żà°Ąà°‚"</string>
+    <string name="qs_record_issue_start" msgid="2979831312582567056">"à°Șà±à°°à°Ÿà°°à°‚à°­à°żà°‚à°šà°‚à°Ąà°ż"</string>
+    <string name="qs_record_issue_stop" msgid="3531747965741982657">"ఆà°Șà°żà°”à±‡à°Żà°‚à°Ąà°ż"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"à°źà±€ à°Șà°°à°żà°•à°° అచుభఔంà°Čో ఏ à°­à°Ÿà°—à°‚ à°Șà±à°°à°­à°Ÿà°”à°żà°€à°źà±ˆà°‚à°Šà°ż?"</string>
+    <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"à°žà°źà°žà±à°Ż à°°à°•à°Ÿà°šà±à°šà°ż à°Žà°‚à°šà±à°•à±‹à°‚à°Ąà°ż"</string>
+    <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"ఞ్క్రీచ్ à°°à°żà°•à°Ÿà°°à±à°Ąà±"</string>
+  <string-array name="qs_record_issue_types">
+    <item msgid="2947988124014085798">"à°Șà°šà°żà°€à±€à°°à±"</item>
+    <item msgid="1627504621139124393">"à°Żà±‚à°œà°°à± ఇంటర్‌ఫేఞ్"</item>
+    <item msgid="8309220355268900335">"à°Źà±à°Żà°Ÿà°Ÿà°°à±€"</item>
+  </string-array>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"ఔచ్-à°čà±à°Żà°Ÿà°‚à°Ąà±†à°Ąà± à°źà±‹à°Ąà±"</string>
     <string name="quick_settings_contrast_label" msgid="988087460210159123">"కటంట్రటఞ్ట్"</string>
     <string name="quick_settings_contrast_standard" msgid="2538227821968061832">"à°žà±à°Ÿà°Ÿà°‚à°Ąà°°à±à°Ąà±"</string>
@@ -408,12 +413,9 @@
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ఛటర్జ్ à°…à°”à±à°€à±‹à°‚à°Šà°ż • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>à°Čో à°Șà±‚à°°à±à°€à°żà°—à°Ÿ ఛటర్జ్ à°…à°”à±à°€à±à°‚à°Šà°ż"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"à°•à°źà±à°Żà±‚à°šà°Č్ à°Ÿà±à°Żà±à°Ÿà±‹à°°à°żà°Żà°Č్‌చు à°Șà±à°°à°Ÿà°°à°‚à°­à°żà°‚à°šà°Ąà°Ÿà°šà°żà°•à°ż à°Žà°Ąà°ź ఔైà°Șుచకు ఞ్ఔైà°Ș్ à°šà±‡à°Żà°‚à°Ąà°ż"</string>
     <string name="button_to_open_widget_editor" msgid="5599945944349057600">"à°”à°żà°Ąà±à°œà±†à°Ÿà± à°Žà°Ąà°żà°Ÿà°°à±‌చు à°€à±†à°°à°”à°‚à°Ąà°ż"</string>
-    <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
-    <skip />
-    <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
-    <skip />
-    <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
-    <skip />
+    <string name="button_to_remove_widget" msgid="3948204829181214098">"à°€à±€à°žà°żà°”à±‡à°Żà°‚à°Ąà°ż"</string>
+    <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"à°”à°żà°Ąà±à°œà±†à°Ÿà±‌చు à°œà±‹à°Ąà°żà°‚à°šà°‚à°Ąà°ż"</string>
+    <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"à°Șà±‚à°°à±à°€à°Żà°żà°‚à°Šà°ż"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"à°”à°żà°šà°żà°Żà±‹à°—à°Šà°Ÿà°°à±à°šà°ż à°źà°Ÿà°°à±à°šà±"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"à°Șుà°Č్‌à°Ąà±Œà°šà± à°źà±†à°šà±‚"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ఈ ఞెషచ్‌à°Čà±‹à°šà°ż à°…à°šà±à°šà°ż à°Żà°Ÿà°Ș్‌à°Čు à°źà°°à°żà°Żà± à°Ąà±‡à°Ÿà°Ÿ ఀొà°Čà°—à°żà°‚à°šà°Źà°Ąà°€à°Ÿà°Żà°ż."</string>
@@ -867,7 +869,6 @@
     <string name="auto_saver_title" msgid="6873691178754086596">"à°Źà±à°Żà°Ÿà°Ÿà°°à±€ ఞేఔర్‌à°šà°ż à°·à±†à°Ąà±à°Żà±‚à°Č్ à°šà±‡à°Żà°Ąà°Ÿà°šà°żà°•à°ż à°šà±Šà°•à±à°•à°‚à°Ąà°ż"</string>
     <string name="auto_saver_text" msgid="3214960308353838764">"à°Źà±à°Żà°Ÿà°Ÿà°°à±€ à°›à°Ÿà°°à±à°œà°żà°‚à°—à± à°Șà±‚à°°à±à°€à°żà°—à°Ÿ à°…à°Żà°żà°Șోఀుచ్చ à°€à°°à±à°Łà°‚à°Čో ఆచ్ à°šà±‡à°žà±à°€à±à°‚à°Šà°ż"</string>
     <string name="no_auto_saver_action" msgid="7467924389609773835">"ఔఊ్ఊు, à°§à°šà±à°Żà°”à°Ÿà°Šà°Ÿà°Čు"</string>
-    <string name="heap_dump_tile_name" msgid="2464189856478823046">"à°Ąà°‚à°Ș్ SysUI à°čీà°Ș్"</string>
     <string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"à°”à°żà°šà°żà°Żà±‹à°—à°‚à°Čో à°‰à°‚à°Šà°ż"</string>
     <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"అà°Ș్à°Čà°żà°•à±‡à°·à°šà±‌à°Čు à°źà±€ <xliff:g id="TYPES_LIST">%s</xliff:g>à°šà°ż ఉà°Șà°Żà±‹à°—à°żà°žà±à°€à±à°šà±à°šà°Ÿà°Żà°ż."</string>
     <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"à°žà±†à°Ÿà±à°Ÿà°żà°‚à°—à±‌à°Čà°Čో à°†à°Ÿà±‹à°źà±‡à°Ÿà°żà°•à±‌à°—à°Ÿ à°‰à°‚à°Ąà±‡à°Čà°Ÿ ఒక చోట్ఞ్ à°Żà°Ÿà°Ș్‌చు ఞెట్ à°šà±‡à°žà±à°•à±‹à°‚à°Ąà°ż"</string>
     <string name="install_app" msgid="5066668100199613936">"à°Żà°Ÿà°Ș్‌చు ఇచ్‌ఞ్టటà°Č్ à°šà±‡à°Żà°‚à°Ąà°ż"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"ఎక్ఞ్‌టర్చà°Č్ à°Ąà°żà°žà±‌à°Ș్à°Čే‌à°•à°ż à°źà°żà°°à±à°°à°°à±‌ à°šà±‡à°Żà°Ÿà°Čà°Ÿ?"</string>
-    <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"à°Ș్రఞ్ఀుఀం రచ్ అఔుఀుచ్చ Dual Screen à°Żà°Ÿà°•à±à°Ÿà°żà°”à°żà°Ÿà±€ ఏఊైచట ఉంటే, à°…à°Šà°ż ఆà°Șà°żà°”à±‡à°Żà°Źà°Ąà±à°€à±à°‚à°Šà°ż"</string>
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"à°źà±€ à°Čోà°Șà°Čà°ż à°Ąà°żà°žà±‌à°Ș్à°Čే à°źà°żà°°à±à°°à°°à± à°šà±‡à°Żà°Źà°Ąà±à°€à±à°‚à°Šà°ż. à°źà±€ à°źà±à°‚à°Šà± ఔైà°Șు à°Ąà°żà°žà±‌à°Ș్à°Čే ఆఫ్ à°šà±‡à°Żà°Źà°Ąà±à°€à±à°‚à°Šà°ż."</string>
     <string name="mirror_display" msgid="2515262008898122928">"à°źà°żà°°à±à°°à°°à± à°Ąà°żà°žà±‌à°Ș్à°Čే"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"à°”à°żà°žà±à°źà°°à°żà°‚à°šà°‚à°Ąà°ż"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"à°Ąà°żà°žà±‌à°Ș్à°Čే కచెక్ట్ à°šà±‡à°Żà°Źà°Ąà°żà°‚à°Šà°ż"</string>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index 8c24e4b..dc4c6cf 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -330,12 +330,17 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"àžšàž±àž™àž—àž¶àžàž«àž™àč‰àžČàžˆàž­"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"àč€àžŁàžŽàčˆàžĄ"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"àž«àžąàžžàž”"</string>
-    <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
-    <skip />
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"àž›àž±àžàž«àžČàžàžČàžŁàžšàž±àž™àž—àž¶àž"</string>
+    <string name="qs_record_issue_start" msgid="2979831312582567056">"àč€àžŁàžŽàčˆàžĄ"</string>
+    <string name="qs_record_issue_stop" msgid="3531747965741982657">"àž«àžąàžžàž”"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"àž›àžŁàž°àžȘàžšàžàžČàžŁàž“àčŒàžàžČàžŁàčƒàžŠàč‰àž‡àžČàž™àž­àžžàž›àžàžŁàž“àčŒàžȘàčˆàž§àž™àčƒàž”àž—àž”àčˆàč„àž”àč‰àžŁàž±àžšàžœàž„àžàžŁàž°àž—àžš"</string>
+    <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"àč€àž„àž·àž­àžàž›àžŁàž°àč€àž àž—àž›àž±àžàž«àžČ"</string>
+    <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"àžšàž±àž™àž—àž¶àžàž«àž™àč‰àžČàžˆàž­"</string>
+  <string-array name="qs_record_issue_types">
+    <item msgid="2947988124014085798">"àž›àžŁàž°àžȘàžŽàž—àž˜àžŽàž àžČàžž"</item>
+    <item msgid="1627504621139124393">"àž­àžŽàž™àč€àž—àž­àžŁàčŒàč€àžŸàž‹àžœàžčàč‰àčƒàžŠàč‰"</item>
+    <item msgid="8309220355268900335">"àčàžšàž•àč€àž•àž­àžŁàž”àčˆ"</item>
+  </string-array>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"àč‚àž«àžĄàž”àžĄàž·àž­àč€àž”àž”àžąàž§"</string>
     <string name="quick_settings_contrast_label" msgid="988087460210159123">"àž„àž­àž™àž—àžŁàžČàžȘàž•àčŒ"</string>
     <string name="quick_settings_contrast_standard" msgid="2538227821968061832">"àžĄàžČàž•àžŁàžàžČàž™"</string>
@@ -408,12 +413,9 @@
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • àžàžłàž„àž±àž‡àžŠàžČàžŁàčŒàžˆ • àžˆàž°àč€àž•àč‡àžĄàčƒàž™àž­àž”àž <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"àž›àž±àž”àč„àž›àž—àžČàž‡àž‹àč‰àžČàžąàč€àžžàž·àčˆàž­àč€àžŁàžŽàčˆàžĄàžšàž—àčàž™àž°àž™àžłàžȘàčˆàž§àž™àžàž„àžČàž‡"</string>
     <string name="button_to_open_widget_editor" msgid="5599945944349057600">"àč€àž›àžŽàž”àč€àž„àžŁàž·àčˆàž­àž‡àžĄàž·àž­àčàžàč‰àč„àž‚àž§àžŽàž”àč€àžˆàč‡àž•"</string>
-    <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
-    <skip />
-    <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
-    <skip />
-    <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
-    <skip />
+    <string name="button_to_remove_widget" msgid="3948204829181214098">"àž™àžłàž­àž­àž"</string>
+    <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"àč€àžžàžŽàčˆàžĄàž§àžŽàž”àč€àžˆàč‡àž•"</string>
+    <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"àč€àžȘàžŁàč‡àžˆàžȘàžŽàč‰àž™"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"àžȘàž„àž±àžšàžœàžčàč‰àčƒàžŠàč‰"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"àč€àžĄàž™àžčàčàžšàžšàč€àž„àž·àčˆàž­àž™àž„àž‡"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"àžŁàž°àžšàžšàžˆàž°àž„àžšàčàž­àž›àčàž„àž°àž‚àč‰àž­àžĄàžčàž„àž—àž±àč‰àž‡àž«àžĄàž”àčƒàž™àč€àž‹àžȘàžŠàž±àž™àž™àž”àč‰"</string>
@@ -867,7 +869,6 @@
     <string name="auto_saver_title" msgid="6873691178754086596">"àčàž•àž°àč€àžžàž·àčˆàž­àž•àž±àč‰àž‡àč€àž§àž„àžČàč‚àž«àžĄàž”àž›àžŁàž°àž«àžąàž±àž”àčàžšàž•àč€àž•àž­àžŁàž”àčˆ"</string>
     <string name="auto_saver_text" msgid="3214960308353838764">"àč€àž›àžŽàž”àč€àžĄàž·àčˆàž­àžĄàž”àčàž™àž§àč‚àž™àč‰àžĄàž§àčˆàžČàčàžšàž•àč€àž•àž­àžŁàž”àčˆàžˆàž°àž«àžĄàž”"</string>
     <string name="no_auto_saver_action" msgid="7467924389609773835">"àč„àžĄàčˆàč€àž›àč‡àž™àč„àžŁ"</string>
-    <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string>
     <string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"àčƒàžŠàč‰àž‡àžČàž™àž­àžąàžčàčˆ"</string>
     <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"àž«àž„àžČàžąàčàž­àž›àžžàž„àžŽàč€àž„àžŠàž±àž™àčƒàžŠàč‰<xliff:g id="TYPES_LIST">%s</xliff:g>àž‚àž­àž‡àž„àžžàž“àž­àžąàžčàčˆ"</string>
     <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"àžàžłàž«àž™àž”àčàž­àž›àžàžČàžŁàžˆàž”àžšàž±àž™àž—àž¶àžàč€àžŁàžŽàčˆàžĄàž•àč‰àž™àčƒàž™àžàžČàžŁàž•àž±àč‰àž‡àž„àčˆàžČ"</string>
     <string name="install_app" msgid="5066668100199613936">"àž•àžŽàž”àž•àž±àč‰àž‡àčàž­àž›"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"àžĄàžŽàč€àžŁàž­àžŁàčŒàč„àž›àžąàž±àž‡àžˆàž­àčàžȘàž”àž‡àžœàž„àž àžČàžąàž™àž­àžàč„àž«àžĄ"</string>
-    <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"àžàžŽàžˆàžàžŁàžŁàžĄàž—àž”àčˆàž—àžłàž­àžąàžčàčˆàžšàž™ Dual Screen àžˆàž°àž«àžąàžžàž”àž„àž‡"</string>
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"àžŁàž°àžšàžšàžˆàž°àžĄàžŽàč€àžŁàž­àžŁàčŒàž«àž™àč‰àžČàžˆàž­àž”àč‰àžČàž™àčƒàž™ àčàž„àž°àžˆàž°àž›àžŽàž”àž«àž™àč‰àžČàžˆàž­àž”àč‰àžČàž™àž«àž™àč‰àžČ"</string>
     <string name="mirror_display" msgid="2515262008898122928">"àžĄàžŽàč€àžŁàž­àžŁàčŒàžˆàž­àčàžȘàž”àž‡àžœàž„"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"àž›àžŽàž”"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"àč€àžŠàž·àčˆàž­àžĄàž•àčˆàž­àžˆàž­àčàžȘàž”àž‡àžœàž„àčàž„àč‰àž§"</string>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index f0b9d1f..c09ac97 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -330,12 +330,17 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Pag-record ng screen"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Magsimula"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Ihinto"</string>
-    <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
-    <skip />
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"Mag-record ng Isyu"</string>
+    <string name="qs_record_issue_start" msgid="2979831312582567056">"Magsimula"</string>
+    <string name="qs_record_issue_stop" msgid="3531747965741982657">"Ihinto"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Ano\'ng naapektuhang parte ng experience sa device?"</string>
+    <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Piliin ang uri ng isyu"</string>
+    <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Pag-record ng screen"</string>
+  <string-array name="qs_record_issue_types">
+    <item msgid="2947988124014085798">"Performance"</item>
+    <item msgid="1627504621139124393">"User Interface"</item>
+    <item msgid="8309220355268900335">"Baterya"</item>
+  </string-array>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"One-hand mode"</string>
     <string name="quick_settings_contrast_label" msgid="988087460210159123">"Contrast"</string>
     <string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Standard"</string>
@@ -408,12 +413,9 @@
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Nagcha-charge • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> na lang para mapuno"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Mag-swipe pakaliwa para simulan ang communal na tutorial"</string>
     <string name="button_to_open_widget_editor" msgid="5599945944349057600">"Buksan ang editor ng widget"</string>
-    <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
-    <skip />
-    <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
-    <skip />
-    <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
-    <skip />
+    <string name="button_to_remove_widget" msgid="3948204829181214098">"Alisin"</string>
+    <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Magdagdag ng widget"</string>
+    <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Tapos na"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Magpalit ng user"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"pulldown menu"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Ide-delete ang lahat ng app at data sa session na ito."</string>
@@ -867,7 +869,6 @@
     <string name="auto_saver_title" msgid="6873691178754086596">"I-tap para iiskedyul ang Pantipid ng Baterya"</string>
     <string name="auto_saver_text" msgid="3214960308353838764">"I-on kapag malamang na maubos ang baterya"</string>
     <string name="no_auto_saver_action" msgid="7467924389609773835">"Hindi, salamat na lang"</string>
-    <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string>
     <string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"Ginagamit"</string>
     <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Ginagamit ng mga application ang iyong <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
     <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Magtakda ng default na app sa pagtatala sa Mga Setting"</string>
     <string name="install_app" msgid="5066668100199613936">"I-install ang app"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"I-mirror sa external na display?"</string>
-    <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"Ihihinto ang anumang aktibidad sa dual screen na kasalukuyang tumatakbo."</string>
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Imi-mirror ang inner display mo. Io-off ang iyong front display."</string>
     <string name="mirror_display" msgid="2515262008898122928">"I-mirror ang display"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"I-dismiss"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Naikonekta ang display"</string>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index f9d90d4..ee1909b 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -330,12 +330,17 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Ekran kaydı"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Baßlat"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Durdur"</string>
-    <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
-    <skip />
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"Sorunu Kaydedin"</string>
+    <string name="qs_record_issue_start" msgid="2979831312582567056">"Baßlayın"</string>
+    <string name="qs_record_issue_stop" msgid="3531747965741982657">"Durdurun"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Cihaz deneyiminiz ne ßekilde etkilendi?"</string>
+    <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Sorun türünü seçin"</string>
+    <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Ekran kaydedicisi"</string>
+  <string-array name="qs_record_issue_types">
+    <item msgid="2947988124014085798">"Performans"</item>
+    <item msgid="1627504621139124393">"Kullanıcı Arayüzü"</item>
+    <item msgid="8309220355268900335">"Pil"</item>
+  </string-array>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Tek el modu"</string>
     <string name="quick_settings_contrast_label" msgid="988087460210159123">"Kontrast"</string>
     <string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Standart"</string>
@@ -408,12 +413,9 @@
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ƞarj oluyor • Dolmasına <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> kaldı"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Ortak eğitimi baßlatmak için sola kaydırın"</string>
     <string name="button_to_open_widget_editor" msgid="5599945944349057600">"Widget düzenleyiciyi açın"</string>
-    <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
-    <skip />
-    <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
-    <skip />
-    <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
-    <skip />
+    <string name="button_to_remove_widget" msgid="3948204829181214098">"Kaldır"</string>
+    <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Widget ekle"</string>
+    <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Bitti"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Kullanıcı değißtirme"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"açılır menü"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Bu oturumdaki tüm uygulamalar ve veriler silinecek."</string>
@@ -867,7 +869,6 @@
     <string name="auto_saver_title" msgid="6873691178754086596">"Pil Tasarrufunu programlamak için dokunun"</string>
     <string name="auto_saver_text" msgid="3214960308353838764">"Piliniz bitecek gibiyse bu özelliği açın"</string>
     <string name="no_auto_saver_action" msgid="7467924389609773835">"Hayır, teßekkürler"</string>
-    <string name="heap_dump_tile_name" msgid="2464189856478823046">"SysUI Yığın Dökümü"</string>
     <string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"Kullanımda"</string>
     <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Uygulamalar ßunları kullanıyor: <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
     <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Ayarlar\'ı kullanarak varsayılan notlar ayarlayın"</string>
     <string name="install_app" msgid="5066668100199613936">"Uygulamayı yükle"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Harici ekrana yansıtılsın mı?"</string>
-    <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"ƞu anda devam eden tüm Dual Screen etkinlikleri durdurulacak"</string>
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"İç ekranınız yansıtılacak. Ön ekranınız kapatılacak."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Ekranı yansıt"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Kapat"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Ekran bağlandı"</string>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index c5211e8..04a97bc 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -330,12 +330,17 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Запос Đ”ĐșŃ€Đ°ĐœĐ°"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"ĐŸĐŸŃ‡Đ°Ń‚Đž"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Đ—ŃƒĐżĐžĐœĐžŃ‚Đž"</string>
-    <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
-    <skip />
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"Запос ĐżĐŸĐŒĐžĐ»ĐșĐž"</string>
+    <string name="qs_record_issue_start" msgid="2979831312582567056">"ĐŸĐŸŃ‡Đ°Ń‚Đž"</string>
+    <string name="qs_record_issue_stop" msgid="3531747965741982657">"Đ—ŃƒĐżĐžĐœĐžŃ‚Đž"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"На яĐșĐžĐč аспДĐșт Ń€ĐŸĐ±ĐŸŃ‚Đž ĐżŃ€ĐžŃŃ‚Ń€ĐŸŃŽ ĐČĐżĐ»ĐžĐœŃƒĐ»Đ° ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒĐ°?"</string>
+    <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Đ’ĐžĐ±Đ”Ń€Ń–Ń‚ŃŒ топ ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒĐž"</string>
+    <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Запос ĐČŃ–ĐŽĐ”ĐŸ Đ· Đ”ĐșŃ€Đ°ĐœĐ°"</string>
+  <string-array name="qs_record_issue_types">
+    <item msgid="2947988124014085798">"ĐŸŃ€ĐŸĐŽŃƒĐșтоĐČĐœŃ–ŃŃ‚ŃŒ"</item>
+    <item msgid="1627504621139124393">"Đ†ĐœŃ‚Đ”Ń€Ń„Đ”Đčс ĐșĐŸŃ€ĐžŃŃ‚ŃƒĐČача"</item>
+    <item msgid="8309220355268900335">"АĐșŃƒĐŒŃƒĐ»ŃŃ‚ĐŸŃ€"</item>
+  </string-array>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Đ Đ”Đ¶ĐžĐŒ ĐșĐ”Ń€ŃƒĐČĐ°ĐœĐœŃ ĐŸĐŽĐœŃ–Ń”ŃŽ руĐșĐŸŃŽ"</string>
     <string name="quick_settings_contrast_label" msgid="988087460210159123">"ĐšĐŸĐœŃ‚Ń€Đ°ŃŃ‚"</string>
     <string name="quick_settings_contrast_standard" msgid="2538227821968061832">"ĐĄŃ‚Đ°ĐœĐŽĐ°Ń€Ń‚ĐœĐžĐč"</string>
@@ -408,12 +413,9 @@
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Đ—Đ°Ń€ŃĐŽĐ¶Đ°ĐœĐœŃ • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> ĐŽĐŸ ĐżĐŸĐČĐœĐŸĐłĐŸ Đ·Đ°Ń€ŃĐŽŃƒ"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"ĐŸŃ€ĐŸĐČĐ”ĐŽŃ–Ń‚ŃŒ ĐżĐ°Đ»ŃŒŃ†Đ”ĐŒ ŃƒĐ»Ń–ĐČĐŸ, Ń‰ĐŸĐ± ĐČіЮĐșрото ŃĐżŃ–Đ»ŃŒĐœĐžĐč ĐœĐ°ĐČŃ‡Đ°Đ»ŃŒĐœĐžĐč ĐżĐŸŃŃ–Đ±ĐœĐžĐș"</string>
     <string name="button_to_open_widget_editor" msgid="5599945944349057600">"ВіЮĐșрото рДЎаĐșŃ‚ĐŸŃ€ ĐČіЎжДтіĐČ"</string>
-    <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
-    <skip />
-    <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
-    <skip />
-    <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
-    <skip />
+    <string name="button_to_remove_widget" msgid="3948204829181214098">"ВОЎалОтО"</string>
+    <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Đ”ĐŸĐŽĐ°Ń‚Đž ĐČіЎжДт"</string>
+    <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Đ“ĐŸŃ‚ĐŸĐČĐŸ"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Đ—ĐŒŃ–ĐœĐžŃ‚Đž ĐșĐŸŃ€ĐžŃŃ‚ŃƒĐČача"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ŃĐżĐ°ĐŽĐœĐ” ĐŒĐ”ĐœŃŽ"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Усі ĐŽĐŸĐŽĐ°Ń‚ĐșĐž Đč ĐŽĐ°ĐœŃ– Đ· Ń†ŃŒĐŸĐłĐŸ ŃĐ”Đ°ĐœŃŃƒ буЎД ĐČĐžĐŽĐ°Đ»Đ”ĐœĐŸ."</string>
@@ -867,7 +869,6 @@
     <string name="auto_saver_title" msgid="6873691178754086596">"ĐąĐŸŃ€ĐșĐœŃ–Ń‚ŃŒŃŃ, Ń‰ĐŸĐ± ĐœĐ°Đ»Đ°ŃˆŃ‚ŃƒĐČато Ń€Đ”Đ¶ĐžĐŒ Đ”ĐœĐ”Ń€ĐłĐŸĐ·Đ±Đ”Ń€Đ”Đ¶Đ”ĐœĐœŃ"</string>
     <string name="auto_saver_text" msgid="3214960308353838764">"Đ’ĐŒĐžĐșато, ĐșĐŸĐ»Đž Đ·Đ°Ń€ŃĐŽ аĐșŃƒĐŒŃƒĐ»ŃŃ‚ĐŸŃ€Đ° заĐșŃ–ĐœŃ‡ŃƒŃ”Ń‚ŃŒŃŃ"</string>
     <string name="no_auto_saver_action" msgid="7467924389609773835">"Ні, ЮяĐșую"</string>
-    <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string>
     <string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"ВоĐșĐŸŃ€ĐžŃŃ‚ĐŸĐČується"</string>
     <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Đ”ĐŸĐŽĐ°Ń‚ĐșĐž ĐČĐžĐșĐŸŃ€ĐžŃŃ‚ĐŸĐČують <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
     <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,8 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"ĐŸŃ€ĐžĐ·ĐœĐ°Ń‡Ń‚Đ” ŃŃ‚Đ°ĐœĐŽĐ°Ń€Ń‚ĐœĐžĐč ĐŽĐŸĐŽĐ°Ń‚ĐŸĐș ĐŽĐ»Ń ĐœĐŸŃ‚Đ°Ń‚ĐŸĐș у ĐœĐ°Đ»Đ°ŃˆŃ‚ŃƒĐČĐ°ĐœĐœŃŃ…"</string>
     <string name="install_app" msgid="5066668100199613936">"ĐŁŃŃ‚Đ°ĐœĐŸĐČото ĐŽĐŸĐŽĐ°Ń‚ĐŸĐș"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Đ”ŃƒĐ±Đ»ŃŽĐČато ĐœĐ° Đ·ĐŸĐČĐœŃ–ŃˆĐœŃŒĐŸĐŒŃƒ Đ”ĐșŃ€Đ°ĐœŃ–?"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (2917631104216376315) -->
-    <skip />
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Ваш ĐČĐœŃƒŃ‚Ń€Ń–ŃˆĐœŃ–Đč Đ”ĐșŃ€Đ°Đœ буЎД ĐżŃ€ĐŸĐŽŃƒĐ±Đ»ŃŒĐŸĐČĐ°ĐœĐŸ. ĐŸĐ”Ń€Đ”ĐŽĐœŃ–Đč Đ”ĐșŃ€Đ°Đœ буЎД ĐČĐžĐŒĐșĐœĐ”ĐœĐŸ."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Đ”ŃƒĐ±Đ»ŃŽĐČато Đ”ĐșŃ€Đ°Đœ"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"ЗаĐșрото"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"ДОсплДĐč піЮ’Ń”ĐŽĐœĐ°ĐœĐŸ"</string>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index 01995c8..fd984b9 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -330,12 +330,17 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Ű§ŰłÚ©Ű±ÛŒÙ† Ű±ÛŒÚ©Ű§Ű±Úˆ"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"ŰŽŰ±ÙˆŰč Ú©Ű±ÛŒÚș"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Ű±ÙˆÚ©ÛŒÚș"</string>
-    <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
-    <skip />
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"Ù…ŰłŰŠÙ„Û Ű±ÛŒÚ©Ű§Ű±Úˆ Ú©Ű±ÛŒÚș"</string>
+    <string name="qs_record_issue_start" msgid="2979831312582567056">"ŰŽŰ±ÙˆŰč Ú©Ű±ÛŒÚș"</string>
+    <string name="qs_record_issue_stop" msgid="3531747965741982657">"Ű±ÙˆÚ©ÛŒÚș"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"ŰąÙŸ کے ŰąÙ„Û’ کے ŰȘŰŹŰ±ŰšÛ’ کۧ کون ۳ۧ Ű­Ű”Û مŰȘۧ۫۱ ÛÙˆŰ§ŰŸ"</string>
+    <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Ù…ŰłŰŠÙ„Û کی Ù‚ŰłÙ… منŰȘ۟ۚ Ú©Ű±ÛŒÚș"</string>
+    <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Ű§ŰłÚ©Ű±ÛŒÙ† Ű±ÛŒÚ©Ű§Ű±Úˆ"</string>
+  <string-array name="qs_record_issue_types">
+    <item msgid="2947988124014085798">"Ú©Ű§Ű±Ú©Ű±ŰŻÚŻÛŒ"</item>
+    <item msgid="1627504621139124393">"یوŰČ۱ Ű§Ù†ÙčŰ±ÙÛŒŰł"</item>
+    <item msgid="8309220355268900335">"ŰšÛŒÙčŰ±ÛŒ"</item>
+  </string-array>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Ű§ÛŒÚ© ÛŰ§ŰȘÚŸ کی ÙˆŰ¶Űč"</string>
     <string name="quick_settings_contrast_label" msgid="988087460210159123">"کنÙč۱ۧ۳Ùč"</string>
     <string name="quick_settings_contrast_standard" msgid="2538227821968061832">"مŰčÛŒŰ§Ű±ÛŒ"</string>
@@ -408,12 +413,9 @@
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • چۧ۱ۏ ہو Ű±ÛŰ§ ہے • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> میÚș مکمل"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"کمیونل ÙčیوÙčÙˆŰ±ÛŒÙ„ ŰŽŰ±ÙˆŰč Ú©Ű±Ù†Û’ کے لیے ŰšŰ§ŰŠÛŒÚș ŰłÙˆŰ§ŰŠÙŸ Ú©Ű±ÛŒÚș"</string>
     <string name="button_to_open_widget_editor" msgid="5599945944349057600">"ÙˆÛŒŰŹÛŒÙč Ű§ÛŒÚˆÛŒÙč۱ کو کڟولیÚș"</string>
-    <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
-    <skip />
-    <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
-    <skip />
-    <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
-    <skip />
+    <string name="button_to_remove_widget" msgid="3948204829181214098">"ہÙčŰ§ŰŠÛŒÚș"</string>
+    <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"ÙˆÛŒŰŹÛŒÙč ŰŽŰ§Ù…Ù„ Ú©Ű±ÛŒÚș"</string>
+    <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"ہو ÚŻÛŒŰ§"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Ű”Ű§Ű±Ù ŰłÙˆŰŠÚ† Ú©Ű±ÛŒÚș"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ٟل ÚˆŰ§Ű€Ù† مینیو"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ۧ۳ ŰłÛŒŰŽÙ† میÚș Ù…ÙˆŰŹÙˆŰŻ ŰłŰšÚŸÛŒ Ű§ÛŒÙŸŰł Ű§ÙˆŰ± ڈیÙčۧ کو Ű­Ű°Ù ک۱ ŰŻÛŒŰ§ ŰŹŰ§ŰŠÛ’ ÚŻŰ§Û”"</string>
@@ -867,7 +869,6 @@
     <string name="auto_saver_title" msgid="6873691178754086596">"ŰšÛŒÙčŰ±ÛŒ ŰłÛŒÙˆŰ± ŰŽÛŒÚˆÙˆÙ„ Ú©Ű±Ù†Û’ کے لیے ŰȘÚŸÙŸŰȘÚŸÙŸŰ§ŰŠÛŒÚș"</string>
     <string name="auto_saver_text" msgid="3214960308353838764">"ŰŹŰš ŰšÛŒÙčŰ±ÛŒ کے ŰźŰȘم ہونے کۧ Ű§Ù…Ú©Ű§Ù† ہو ŰȘو ŰąÙ† Ú©Ű±ÛŒÚș"</string>
     <string name="no_auto_saver_action" msgid="7467924389609773835">"نہیÚș ŰŽÚ©Ű±ÛŒÛ"</string>
-    <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string>
     <string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"ŰČÛŒŰ± ۧ۳ŰȘŰčÙ…Ű§Ù„"</string>
     <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Ű§ÛŒÙŸÙ„ÛŒÚ©ÛŒŰŽÙ†ŰČ ŰąÙŸ کی <xliff:g id="TYPES_LIST">%s</xliff:g> کۧ ۧ۳ŰȘŰčÙ…Ű§Ù„ ک۱ Ű±ÛÛŒ ہیÚș۔"</string>
     <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">"ی "</string>
@@ -1207,7 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"ŰȘ۱ŰȘÛŒŰšŰ§ŰȘ میÚș ÚˆÛŒÙŰ§Ù„Ùč نوÙčŰł Ű§ÛŒÙŸ ŰłÛŒÙč Ú©Ű±ÛŒÚș"</string>
     <string name="install_app" msgid="5066668100199613936">"Ű§ÛŒÙŸ Ű§Ù†ŰłÙčŰ§Ù„ Ú©Ű±ÛŒÚș"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"ŰšÛŒŰ±ÙˆÙ†ÛŒ ÚˆŰłÙŸÙ„Û’ ÙŸŰ± Ù…Ű±Ű± Ú©Ű±ÛŒÚș۟"</string>
-    <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"‏فی Ű§Ù„Ű­Ű§Ù„ چلنے ÙˆŰ§Ù„ÛŒ Dual Screen کی Ú©ÙˆŰŠÛŒ ŰšÚŸÛŒ ŰłŰ±ÚŻŰ±Ù…ÛŒ Ű±ÙˆÚ© ŰŻÛŒ ŰŹŰ§ŰŠÛ’ ÚŻÛŒ"</string>
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"ŰąÙŸ کے Ű§Ù†ŰŻŰ±ÙˆÙ†ÛŒ ÚˆŰłÙŸÙ„Û’ کو ŰŻÙˆ Ű·Ű±ÙÛ Ù…Ű·Ű§ŰšÙ‚ŰȘ ÙŸŰ°ÛŒŰ± ŰšÙ†Ű§ÛŒŰ§ ŰŹŰ§ŰŠÛ’ ÚŻŰ§Û” ŰąÙŸ کۧ ÙŰ±Ù†Ùč ÚˆŰłÙŸÙ„Û’ ŰąÙ ہو ŰŹŰ§ŰŠÛ’ ÚŻŰ§Û”"</string>
     <string name="mirror_display" msgid="2515262008898122928">"ÚˆŰłÙŸÙ„Û’ کو Ù…Ű±Ű± Ú©Ű±ÛŒÚș"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"ۚ۱۟ۧ۳ŰȘ Ú©Ű±ÛŒÚș"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"ÚˆŰłÙŸÙ„Û’ Ù…Ù†ŰłÙ„Ú© ہے"</string>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index bf48146..b9a9832 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -330,12 +330,17 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Ekran yozuvi"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Boshlash"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Toʻxtatish"</string>
-    <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
-    <skip />
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"Yozib olishda xato"</string>
+    <string name="qs_record_issue_start" msgid="2979831312582567056">"Boshlash"</string>
+    <string name="qs_record_issue_stop" msgid="3531747965741982657">"Toʻxtatish"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Qurilma ishlashining qaysi qismiga taÊŒsir qildi?"</string>
+    <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Muammo turini tanlang"</string>
+    <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Ekran yozuvi"</string>
+  <string-array name="qs_record_issue_types">
+    <item msgid="2947988124014085798">"Unumdorlik"</item>
+    <item msgid="1627504621139124393">"Foydalanuvchi interfeysi"</item>
+    <item msgid="8309220355268900335">"Batareya"</item>
+  </string-array>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Ixcham rejim"</string>
     <string name="quick_settings_contrast_label" msgid="988087460210159123">"Kontrast"</string>
     <string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Standart"</string>
@@ -408,12 +413,9 @@
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Quvvat olmoqda • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> qoldi"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Qoʻllanma bilan tanishish uchun chapga suring"</string>
     <string name="button_to_open_widget_editor" msgid="5599945944349057600">"Vidjet muharririni ochish"</string>
-    <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
-    <skip />
-    <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
-    <skip />
-    <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
-    <skip />
+    <string name="button_to_remove_widget" msgid="3948204829181214098">"Olib tashlash"</string>
+    <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Vidjet kiritish"</string>
+    <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Tayyor"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Foydalanuvchini almashtirish"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"tortib tushiriladigan menyu"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Ushbu seansdagi barcha ilovalar va ma’lumotlar o‘chirib tashlanadi."</string>
@@ -867,7 +869,6 @@
     <string name="auto_saver_title" msgid="6873691178754086596">"Quvvat tejash rejimini rejalashtirish uchun bosing"</string>
     <string name="auto_saver_text" msgid="3214960308353838764">"Batareya quvvati kamayishi aniqlanganda yoqilsin"</string>
     <string name="no_auto_saver_action" msgid="7467924389609773835">"Kerak emas"</string>
-    <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string>
     <string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"Foydalanilmoqda"</string>
     <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Ilovalarda ishlatilmoqda: <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
     <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Standart qaydlar ilovasini Sozlamalar orqali tanlang"</string>
     <string name="install_app" msgid="5066668100199613936">"Ilovani oʻrnatish"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Tashqi displeyda aks ettirilsinmi?"</string>
-    <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"Joriy ishga tushgan ikki ekranli faollik toʻxtatiladi"</string>
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Ichki ekran uchun aks ettirish yoqiladi. Old ekran oʻchiriladi."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Displeyni aks ettirish"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Yopish"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Displey ulandi"</string>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index a70c8ad..b1ff9a8 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -269,7 +269,7 @@
     <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Tá»± động xoay màn hình"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"Vị trí"</string>
     <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Trình báșŁo vệ màn hình"</string>
-    <string name="quick_settings_camera_label" msgid="5612076679385269339">"Truy cáș­p máy áșŁnh"</string>
+    <string name="quick_settings_camera_label" msgid="5612076679385269339">"Quyền truy cáș­p camera"</string>
     <string name="quick_settings_mic_label" msgid="8392773746295266375">"Quyền truy cáș­p micrô"</string>
     <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"ÄÆ°á»Łc phép"</string>
     <string name="quick_settings_camera_mic_blocked" msgid="4710884905006788281">"Bị cháș·n"</string>
@@ -330,12 +330,17 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Ghi màn hình"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"BáșŻt đáș§u"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Dừng"</string>
-    <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
-    <skip />
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"Ghi láșĄi váș„n đề"</string>
+    <string name="qs_record_issue_start" msgid="2979831312582567056">"BáșŻt đáș§u"</string>
+    <string name="qs_record_issue_stop" msgid="3531747965741982657">"Dừng"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"BáșĄn gáș·p loáșĄi váș„n đề gì khi dùng thiáșżt bị?"</string>
+    <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Chọn loáșĄi váș„n đề"</string>
+    <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Ghi màn hình"</string>
+  <string-array name="qs_record_issue_types">
+    <item msgid="2947988124014085798">"Hiệu suáș„t"</item>
+    <item msgid="1627504621139124393">"Giao diện người dùng"</item>
+    <item msgid="8309220355268900335">"Pin"</item>
+  </string-array>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Cháșż độ một tay"</string>
     <string name="quick_settings_contrast_label" msgid="988087460210159123">"Độ tÆ°ÆĄng pháșŁn"</string>
     <string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Chuáș©n"</string>
@@ -408,12 +413,9 @@
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Đang sáșĄc • Sáșœ đáș§y sau <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Vuốt sang trái để báșŻt đáș§u xem hướng dáș«n chung"</string>
     <string name="button_to_open_widget_editor" msgid="5599945944349057600">"Mở trình chỉnh sá»­a tiện ích"</string>
-    <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
-    <skip />
-    <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
-    <skip />
-    <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
-    <skip />
+    <string name="button_to_remove_widget" msgid="3948204829181214098">"Xoá"</string>
+    <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Thêm tiện ích"</string>
+    <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Xong"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Chuyển đổi người dùng"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"trình Ä‘ÆĄn kéo xuống"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Táș„t cáșŁ á»©ng dỄng và dữ liệu trong phiên này sáșœ bị xóa."</string>
@@ -867,7 +869,6 @@
     <string name="auto_saver_title" msgid="6873691178754086596">"Nháș„n để lên lịch Trình tiáșżt kiệm pin"</string>
     <string name="auto_saver_text" msgid="3214960308353838764">"Báș­t khi pin sáșŻp háșżt"</string>
     <string name="no_auto_saver_action" msgid="7467924389609773835">"Không, cáșŁm ÆĄn"</string>
-    <string name="heap_dump_tile_name" msgid="2464189856478823046">"Trích xuáș„t bộ nhớ SysUI"</string>
     <string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"Đang Ä‘Æ°á»Łc sá»­ dỄng"</string>
     <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Các ứng dỄng đang dùng <xliff:g id="TYPES_LIST">%s</xliff:g> cá»§a báșĄn."</string>
     <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,8 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Đáș·t ứng dỄng ghi chú máș·c định trong pháș§n Cài đáș·t"</string>
     <string name="install_app" msgid="5066668100199613936">"Cài đáș·t ứng dỄng"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"PháșŁn chiáșżu sang màn hình ngoài?"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (2917631104216376315) -->
-    <skip />
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Màn hình trong cá»§a báșĄn sáșœ Ä‘Æ°á»Łc pháșŁn chiáșżu. Màn hình ngoài cá»§a báșĄn sáșœ táșŻt."</string>
     <string name="mirror_display" msgid="2515262008898122928">"PháșŁn chiáșżu màn hình"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Đóng"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Đã káșżt nối màn hình"</string>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index abc2e31..237fd57 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -298,7 +298,7 @@
     <string name="quick_settings_connecting" msgid="2381969772953268809">"æ­ŁćœšèżžæŽ„…"</string>
     <string name="quick_settings_hotspot_label" msgid="1199196300038363424">"热ç‚č"</string>
     <string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"æ­ŁćœšćŒ€ćŻ…"</string>
-    <string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"攁量节省皋ćșć·ČćŒ€ćŻ"</string>
+    <string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"çœæ”æšĄćŒć·ČćŒ€ćŻ"</string>
     <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{# éƒšèźŸć€‡}other{# éƒšèźŸć€‡}}"</string>
     <string name="quick_settings_flashlight_label" msgid="4904634272006284185">"手甔筒"</string>
     <string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"盾æœșæ­Łćœšäœżç”šäž­"</string>
@@ -330,12 +330,17 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"ć±ćč•ćœ•ćˆ¶"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"ćŒ€ć§‹"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"ćœæ­ą"</string>
-    <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
-    <skip />
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"ćœ•ćˆ¶é—źéą˜"</string>
+    <string name="qs_record_issue_start" msgid="2979831312582567056">"ćŒ€ć§‹"</string>
+    <string name="qs_record_issue_stop" msgid="3531747965741982657">"ćœæ­ą"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"èźŸć€‡äœ“éȘŒçš„ć“ȘäžȘæ–čéąć—ćˆ°ćœ±ć“ïŒŸ"</string>
+    <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"é€‰æ‹©é—źéą˜ç±»ćž‹"</string>
+    <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"ć±ćč•ćœ•ćˆ¶"</string>
+  <string-array name="qs_record_issue_types">
+    <item msgid="2947988124014085798">"性胜"</item>
+    <item msgid="1627504621139124393">"界靱"</item>
+    <item msgid="8309220355268900335">"甔池"</item>
+  </string-array>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"ć•æ‰‹æšĄćŒ"</string>
     <string name="quick_settings_contrast_label" msgid="988087460210159123">"ćŻčæŻ”ćșŠ"</string>
     <string name="quick_settings_contrast_standard" msgid="2538227821968061832">"æ ‡ć‡†"</string>
@@ -408,12 +413,9 @@
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • æ­Łćœšć……ç”” • 氆äșŽ <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>ćŽć……æ»Ą"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"ć‘ć·Šæ»‘ćŠšćłćŻćŻćŠšć…Źć…±æ•™çš‹"</string>
     <string name="button_to_open_widget_editor" msgid="5599945944349057600">"æ‰“ćŒ€ćŸźä»¶çŒ–èŸ‘ć™š"</string>
-    <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
-    <skip />
-    <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
-    <skip />
-    <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
-    <skip />
+    <string name="button_to_remove_widget" msgid="3948204829181214098">"移陀"</string>
+    <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"æ·»ćŠ ćŸźä»¶"</string>
+    <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"ćźŒæˆ"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ćˆ‡æąç”šæˆ·"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"äž‹æ‹‰èœć•"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"æ­€äŒšèŻäž­çš„æ‰€æœ‰ćș”ç”šć’Œæ•°æźéƒœć°†èą«ćˆ é™€ă€‚"</string>
@@ -609,7 +611,7 @@
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"侍揑ć‡ș提ç€șéŸłïŒŒäčŸäžæŒŻćŠš"</string>
     <string name="notification_conversation_summary_low" msgid="1734433426085468009">"侍揑ć‡ș提ç€șéŸłïŒŒäčŸäžæŒŻćŠšïŒ›æ˜Ÿç€ș朹ćŻčèŻéƒšćˆ†çš„é äž‹äœçœź"</string>
     <string name="notification_channel_summary_default" msgid="777294388712200605">"ćŻèƒœäŒšć“é“ƒæˆ–æŒŻćŠšïŒŒć–ć†łäșŽèźŸć€‡èźŸçœź"</string>
-    <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"ćŻèƒœäŒšć“é“ƒæˆ–æŒŻćŠšïŒŒć–ć†łäșŽèźŸć€‡èźŸçœźă€‚默èꀿƒ…憔䞋杄è‡Ș<xliff:g id="APP_NAME">%1$s</xliff:g>的ćŻčèŻäŒšä»„ćŻčèŻæłĄçš„ćœąćŒæ˜Ÿç€ș。"</string>
+    <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"ćŻèƒœäŒšć“é“ƒæˆ–æŒŻćŠšïŒŒć–ć†łäșŽèźŸć€‡èźŸçœźă€‚默èꀿƒ…憔䞋杄è‡Ș<xliff:g id="APP_NAME">%1$s</xliff:g>的ćŻčèŻäŒšä»„æ¶ˆæŻæ°”æłĄçš„ćœąćŒæ˜Ÿç€ș。"</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"èź©çł»ç»Ÿć†łćźšæ˜ŻćŠćș”èź©èźŸć€‡ćœšæ”¶ćˆ°æ­€é€šçŸ„æ—¶ć‘ć‡ș提ç€șéŸłæˆ–æŒŻćŠš"</string>
     <string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"&lt;b&gt;状态&lt;/b&gt;ć·Čæć‡äžș“é»˜èź€”"</string>
     <string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"&lt;b&gt;状态&lt;/b&gt;ć·Č降䜎äžș“静音”"</string>
@@ -637,7 +639,7 @@
     <string name="notification_more_settings" msgid="4936228656989201793">"æ›Žć€šèźŸçœź"</string>
     <string name="notification_app_settings" msgid="8963648463858039377">"è‡Ș漚äč‰"</string>
     <string name="notification_conversation_bubble" msgid="2242180995373949022">"星ç€șæ°”æłĄ"</string>
-    <string name="notification_conversation_unbubble" msgid="6908427185031099868">"移陀ćŻčèŻæłĄ"</string>
+    <string name="notification_conversation_unbubble" msgid="6908427185031099868">"ç§»é™€æ¶ˆæŻæ°”æłĄ"</string>
     <string name="notification_menu_accessibility" msgid="8984166825879886773">"<xliff:g id="APP_NAME">%1$s</xliff:g><xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="notification_menu_gear_description" msgid="6429668976593634862">"é€šçŸ„èźŸçœź"</string>
     <string name="notification_menu_snooze_description" msgid="4740133348901973244">"é€šçŸ„ć»¶ćŽé€‰éĄč"</string>
@@ -736,8 +738,8 @@
     <string name="accessibility_long_click_tile" msgid="210472753156768705">"æ‰“ćŒ€“èźŸçœź”"</string>
     <string name="accessibility_status_bar_headphones" msgid="1304082414912647414">"ć·ČèżžæŽ„ćˆ°è€łæœș"</string>
     <string name="accessibility_status_bar_headset" msgid="2699275863720926104">"ć·ČèżžæŽ„ćˆ°è€łæœș"</string>
-    <string name="data_saver" msgid="3484013368530820763">"攁量节省皋ćș"</string>
-    <string name="accessibility_data_saver_on" msgid="5394743820189757731">"攁量节省皋ćșć·ČćŒ€ćŻ"</string>
+    <string name="data_saver" msgid="3484013368530820763">"çœæ”æšĄćŒ"</string>
+    <string name="accessibility_data_saver_on" msgid="5394743820189757731">"çœæ”æšĄćŒć·ČćŒ€ćŻ"</string>
     <string name="switch_bar_on" msgid="1770868129120096114">"ćŒ€ćŻ"</string>
     <string name="switch_bar_off" msgid="5669805115416379556">"慳闭"</string>
     <string name="tile_unavailable" msgid="3095879009136616920">"侍揯甹"</string>
@@ -867,7 +869,6 @@
     <string name="auto_saver_title" msgid="6873691178754086596">"ç‚čæŒ‰ćłćŻéą„èźŸçœç””æšĄćŒ"</string>
     <string name="auto_saver_text" msgid="3214960308353838764">"ćœšç””æ± ç””é‡ćŻèƒœäŒšè€—ć°œæ—¶ïŒŒçł»ç»ŸäŒšćŒ€ćŻæ­€æšĄćŒ"</string>
     <string name="no_auto_saver_action" msgid="7467924389609773835">"侍甹äș†"</string>
-    <string name="heap_dump_tile_name" msgid="2464189856478823046">"èœŹć‚š SysUI 栆"</string>
     <string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"æ­Łćœšäœżç”š"</string>
     <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"æœ‰ć€šäžȘćș”ç”šæ­Łćœšäœżç”šæ‚šçš„<xliff:g id="TYPES_LIST">%s</xliff:g>。"</string>
     <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">"、 "</string>
@@ -1207,7 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"ćœšèźŸçœźäž­èźŸçœźé»˜èź€èź°äș‹ćș”甚"</string>
     <string name="install_app" msgid="5066668100199613936">"ćź‰èŁ…ćș”甚"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"é•œćƒćˆ°ć€–æŽ„æ˜Ÿç€șć±ïŒŸ"</string>
-    <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"ćœ“ć‰æ­Łćœšèż›èĄŒçš„ä»»äœ•ćŒć±ćč•æŽ»ćŠšéƒœć°†äŒšćœæ­ą"</string>
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"çł»ç»Ÿć°†é•œćƒæ‚šçš„ć†…ć±ïŒŒè€Œć…łé—­ć€–ć±ă€‚"</string>
     <string name="mirror_display" msgid="2515262008898122928">"é•œćƒćˆ°æ˜Ÿç€șć±"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"慳闭"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"星ç€șć±ć·ČèżžæŽ„"</string>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index ac40c48..313af30 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -330,12 +330,17 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"èžąćč•éŒ„ćœ±"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"開構"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"ぜ"</string>
-    <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
-    <skip />
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"éŒ„èŁœć•éĄŒ"</string>
+    <string name="qs_record_issue_start" msgid="2979831312582567056">"開構"</string>
+    <string name="qs_record_issue_stop" msgid="3531747965741982657">"ćœæ­ą"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"ć“Șäș›èŁçœźäœżç”šé«”é©—ć—ćœ±éŸżïŒŸ"</string>
+    <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"遾揖敏題類枋"</string>
+    <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"èžąćč•éŒ„ćœ±"</string>
+  <string-array name="qs_record_issue_types">
+    <item msgid="2947988124014085798">"效胜"</item>
+    <item msgid="1627504621139124393">"äœżç”šè€…ä»‹éą"</item>
+    <item msgid="8309220355268900335">"電池"</item>
+  </string-array>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"ć–źæ‰‹æšĄćŒ"</string>
     <string name="quick_settings_contrast_label" msgid="988087460210159123">"ć°æŻ”"</string>
     <string name="quick_settings_contrast_standard" msgid="2538227821968061832">"æš™æș–"</string>
@@ -408,12 +413,9 @@
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • 慅電䞭 • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>ćŸŒć……æ»żé›»"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"ć‘ć·Šæ»‘ć‹•ćłćŻé–‹ć§‹ć…±ç”šæ•™ć­žèȘČ繋"</string>
     <string name="button_to_open_widget_editor" msgid="5599945944349057600">"é–‹ć•Ÿć°ć·„ć…·ç·šèŒŻć™š"</string>
-    <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
-    <skip />
-    <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
-    <skip />
-    <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
-    <skip />
+    <string name="button_to_remove_widget" msgid="3948204829181214098">"移陀"</string>
+    <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"æ–°ćąžć°ć·„ć…·"</string>
+    <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"ćźŒæˆ"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ćˆ‡æ›äœżç”šè€…"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"äž‹æ‹‰ćŒéžć–ź"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"é€™ć€‹ć·„äœœéšŽæź”äž­çš„æ‰€æœ‰æ‡‰ç”šçš‹ćŒć’Œèł‡æ–™éƒœæœƒèą«ćˆȘ陀。"</string>
@@ -867,7 +869,6 @@
     <string name="auto_saver_title" msgid="6873691178754086596">"èŒ•æŒ‰ćłćŻé ćźšæ…łé›»æšĄćŒè‡Șć‹•é–‹ć•Ÿæ™‚é–“"</string>
     <string name="auto_saver_text" msgid="3214960308353838764">"ćœšé›»æ± é›»é‡ćŻèƒœè€—ç›Ąć‰ć•Ÿç”šă€Œæ…łé›»æšĄćŒă€"</string>
     <string name="no_auto_saver_action" msgid="7467924389609773835">"侍甹äș†ïŒŒèŹèŹ"</string>
-    <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string>
     <string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"äœżç”šäž­"</string>
     <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"æœ‰ć€šć€‹æ‡‰ç”šçš‹ćŒæ­Łćœšäœżç”š<xliff:g id="TYPES_LIST">%s</xliff:g>。"</string>
     <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">"、 "</string>
@@ -1207,7 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"ćœšă€Œèš­ćźšă€äž­æŒ‡ćźšé èš­ç­†èš˜æ‡‰ç”šçš‹ćŒ"</string>
     <string name="install_app" msgid="5066668100199613936">"ćź‰èŁæ‡‰ç”šçš‹ćŒ"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"èŠéĄćƒæŠ•ć°„è‡łć€–éƒšéĄŻç€șć±ć—ŽïŒŸ"</string>
-    <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"盼才é€ČèĄŒçš„é›™èžąćč•æŽ»ć‹•éƒœæœƒćœæ­ą"</string>
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"éĄćƒç•«éąć°‡éĄŻç€șćœšć…§éƒšèžąćč•ïŒŒć‰æ–čèžąćč•ć‰‡æœƒé—œé–‰ă€‚"</string>
     <string name="mirror_display" msgid="2515262008898122928">"鏡惏顯ç€ș"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"關閉"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"ć·Čé€ŁæŽ„éĄŻç€șć±"</string>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index cc34a4a..6a13d3d 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -330,12 +330,17 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"èžąćč•éŒ„ćœ±"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"開構"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"ćœæ­ą"</string>
-    <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
-    <skip />
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"èš˜éŒ„ć•éĄŒ"</string>
+    <string name="qs_record_issue_start" msgid="2979831312582567056">"開構"</string>
+    <string name="qs_record_issue_stop" msgid="3531747965741982657">"ćœæ­ą"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"ć“Șäș›èŁçœźäœżç”šé«”é©—ć—ćˆ°ćœ±éŸżïŒŸ"</string>
+    <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"遾揖敏題類枋"</string>
+    <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"èžąćč•éŒ„ćœ±"</string>
+  <string-array name="qs_record_issue_types">
+    <item msgid="2947988124014085798">"效胜"</item>
+    <item msgid="1627504621139124393">"äœżç”šè€…ä»‹éą"</item>
+    <item msgid="8309220355268900335">"電池"</item>
+  </string-array>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"ć–źæ‰‹æšĄćŒ"</string>
     <string name="quick_settings_contrast_label" msgid="988087460210159123">"ć°æŻ”"</string>
     <string name="quick_settings_contrast_standard" msgid="2538227821968061832">"æš™æș–"</string>
@@ -408,12 +413,9 @@
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • 慅電䞭 • ć°‡æ–Œ <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>ćŸŒć……éŁœ"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"ć‘ć·Šæ»‘ć‹•ćłćŻć•Ÿć‹•é€šç”šæ•™ć­žèȘČ繋"</string>
     <string name="button_to_open_widget_editor" msgid="5599945944349057600">"é–‹ć•Ÿć°ć·„ć…·ç·šèŒŻć™š"</string>
-    <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
-    <skip />
-    <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
-    <skip />
-    <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
-    <skip />
+    <string name="button_to_remove_widget" msgid="3948204829181214098">"移陀"</string>
+    <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"æ–°ćąžć°ć·„ć…·"</string>
+    <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"ćźŒæˆ"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ćˆ‡æ›äœżç”šè€…"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"äž‹æ‹‰ćŒéžć–ź"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"é€™ć€‹ć·„äœœéšŽæź”äž­çš„æ‰€æœ‰æ‡‰ç”šçš‹ćŒć’Œèł‡æ–™éƒœæœƒé­ćˆ°ćˆȘ陀。"</string>
@@ -867,7 +869,6 @@
     <string name="auto_saver_title" msgid="6873691178754086596">"èŒ•è§žćłćŻæŽ’ćźšçœé›»æšĄćŒè‡Șć‹•é–‹ć•Ÿçš„æƒ…æł"</string>
     <string name="auto_saver_text" msgid="3214960308353838764">"ćœšé›»æ± é›»é‡ćłć°‡è€—ç›Ąæ™‚é–‹ć•Ÿ"</string>
     <string name="no_auto_saver_action" msgid="7467924389609773835">"侍甹äș†ïŒŒèŹèŹ"</string>
-    <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string>
     <string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"äœżç”šäž­"</string>
     <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"æœ‰ć€šć€‹æ‡‰ç”šçš‹ćŒæ­Łćœšäœżç”š<xliff:g id="TYPES_LIST">%s</xliff:g>。"</string>
     <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">"、 "</string>
@@ -1207,7 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"ćœšă€Œèš­ćźšă€äž­æŒ‡ćźšé èš­èš˜äș‹æ‡‰ç”šçš‹ćŒ"</string>
     <string name="install_app" msgid="5066668100199613936">"ćź‰èŁæ‡‰ç”šçš‹ćŒ"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"èŠä»„éĄćƒæ–čćŒæŠ•æ”Ÿè‡łć€–éƒšèžąćč•ć—ŽïŒŸ"</string>
-    <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"盼才é€ČèĄŒçš„é›™èžąćč•æŽ»ć‹•éƒœæœƒćœæ­ą"</string>
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"éĄćƒç•«éąć°‡éĄŻç€șćœšć…§èžąćč•ïŒŒć°éąèžąćč•ć‰‡æœƒé—œé–‰ă€‚"</string>
     <string name="mirror_display" msgid="2515262008898122928">"鏡惏顯ç€ș"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"關閉"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"èžąćč•ć·Č連甐"</string>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 83fc628..23862a7 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -330,12 +330,17 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Irekhodi lesikrini"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Qala"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Misa"</string>
-    <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
-    <skip />
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"Rekhoda Inkinga"</string>
+    <string name="qs_record_issue_start" msgid="2979831312582567056">"Qala"</string>
+    <string name="qs_record_issue_stop" msgid="3531747965741982657">"Misa"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Kuthinteke yiphi ingxenye yokusebenzisa idivayisi?"</string>
+    <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Khetha uhlobo lwenkinga"</string>
+    <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Irekhodi lesikrini"</string>
+  <string-array name="qs_record_issue_types">
+    <item msgid="2947988124014085798">"Ukusebenza"</item>
+    <item msgid="1627504621139124393">"Okusetshenziswa Kubonwa"</item>
+    <item msgid="8309220355268900335">"Ibhethri"</item>
+  </string-array>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Imodi yesandla esisodwa"</string>
     <string name="quick_settings_contrast_label" msgid="988087460210159123">"Ukugqama"</string>
     <string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Okujwayelekile"</string>
@@ -408,12 +413,9 @@
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Iyashaja • Izogcwala ngo-<xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Swayiphela kwesokunxele ukuze uqale okokufundisa komphakathi"</string>
     <string name="button_to_open_widget_editor" msgid="5599945944349057600">"Vula isihleli sewijethi"</string>
-    <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
-    <skip />
-    <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
-    <skip />
-    <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
-    <skip />
+    <string name="button_to_remove_widget" msgid="3948204829181214098">"Susa"</string>
+    <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Engeza iwijethi"</string>
+    <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Kwenziwe"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Shintsha umsebenzisi"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"imenyu yokudonsela phansi"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Wonke ama-app nedatha kulesi sikhathi azosuswa."</string>
@@ -867,7 +869,6 @@
     <string name="auto_saver_title" msgid="6873691178754086596">"Thepha ukuze ushejuli isilondolozi sebhethri"</string>
     <string name="auto_saver_text" msgid="3214960308353838764">"Vula uma ibhethri sekungenzeka liphele"</string>
     <string name="no_auto_saver_action" msgid="7467924389609773835">"Cha ngiyabonga"</string>
-    <string name="heap_dump_tile_name" msgid="2464189856478823046">"I-Dump SysUI Heap"</string>
     <string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"Kuyasebenza"</string>
     <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Izinhlelo zokusebenza zisebenzisa i-<xliff:g id="TYPES_LIST">%s</xliff:g> yakho."</string>
     <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Setha i-app yamanothi azenzakalelayo Kumsethingi"</string>
     <string name="install_app" msgid="5066668100199613936">"Faka i-app"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Fanisa nesibonisi sangaphandle?"</string>
-    <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"Noma yimuphi umsebenzi wezikrini ezimbili ezisebenzayo uzomiswa"</string>
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Isibonisi sakho sangaphakathi sizoboniswa. Isibonisi sakho sangaphambili sizovalwa."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Isibonisi sokufanisa"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Chitha"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Isibonisi sixhunyiwe"</string>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index e01a2aa..17719d1 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -538,15 +538,15 @@
        -->
     <string translatable="false" name="config_frontBuiltInDisplayCutoutProtection"></string>
 
-    <!--  ID for the camera of outer display that needs extra protection -->
+    <!-- ID for the camera of outer display that needs extra protection -->
     <string translatable="false" name="config_protectedCameraId"></string>
-    <!--  Physical ID for the camera of outer display that needs extra protection -->
+    <!-- Physical ID for the camera of outer display that needs extra protection -->
     <string translatable="false" name="config_protectedPhysicalCameraId"></string>
 
     <!-- Similar to config_frontBuiltInDisplayCutoutProtection but for inner display. -->
     <string translatable="false" name="config_innerBuiltInDisplayCutoutProtection"></string>
 
-    <!-- ID for the camera of inner display that needs extra protection -->
+    <!-- ID for the camera of inner display that needs extra protection. -->
     <string translatable="false" name="config_protectedInnerCameraId"></string>
     <!-- Physical ID for the camera of inner display that needs extra protection -->
     <string translatable="false" name="config_protectedInnerPhysicalCameraId"></string>
@@ -650,13 +650,20 @@
     <!-- Whether to use window background blur for the volume dialog. -->
     <bool name="config_volumeDialogUseBackgroundBlur">false</bool>
 
-    <!-- The properties of the face auth camera in pixels -->
+    <!-- The properties of the face auth front camera for outer display in pixels -->
     <integer-array name="config_face_auth_props">
         <!-- sensorLocationX -->
         <!-- sensorLocationY -->
         <!--sensorRadius -->
     </integer-array>
 
+    <!-- The properties of the face auth front camera for inner display in pixels -->
+    <integer-array name="config_inner_face_auth_props">
+        <!-- sensorLocationX -->
+        <!-- sensorLocationY -->
+        <!--sensorRadius -->
+    </integer-array>
+
     <!-- Overrides the behavior of the face unlock keyguard bypass setting:
          0 - Don't override the setting (default)
          1 - Override the setting to always bypass keyguard
@@ -962,11 +969,20 @@
     <!-- Whether to show bottom sheets edge to edge -->
     <bool name="config_edgeToEdgeBottomSheetDialog">true</bool>
 
+    <!-- Device specific config that controls whether rest to unlock feature is supported.  -->
+    <bool name="config_restToUnlockSupported">false</bool>
+
+    <!--
+    Time in milliseconds the user has to touch the side FPS sensor to successfully authenticate when
+    the screen is turned off with AOD not enabled.
+    TODO(b/302332976) Get this value from the HAL if they can provide an API for it.
+    -->
+    <integer name="config_restToUnlockDurationScreenOff">500</integer>
     <!--
     Time in milliseconds the user has to touch the side FPS sensor to successfully authenticate
     TODO(b/302332976) Get this value from the HAL if they can provide an API for it.
     -->
-    <integer name="config_restToUnlockDuration">300</integer>
+    <integer name="config_restToUnlockDurationDefault">300</integer>
 
     <!--
     Width in pixels of the Side FPS sensor.
@@ -986,4 +1002,7 @@
         <item>com.android.switchaccess.SwitchAccessService</item>
         <item>com.google.android.apps.accessibility.voiceaccess.JustSpeakService</item>
     </string-array>
+
+    <!--  Whether to use a machine learning model for back gesture falsing. -->
+    <bool name="config_useBackGestureML">true</bool>
 </resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index ee89ede..90d8cdb 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -608,6 +608,11 @@
     <dimen name="volume_panel_slice_vertical_padding">8dp</dimen>
     <dimen name="volume_panel_slice_horizontal_padding">24dp</dimen>
 
+    <dimen name="volume_panel_corner_radius">52dp</dimen>
+    <dimen name="volume_panel_content_padding">24dp</dimen>
+    <dimen name="volume_panel_bottom_bar_horizontal_padding">24dp</dimen>
+    <dimen name="volume_panel_bottom_bar_bottom_padding">20dp</dimen>
+
     <!-- Size of each item in the ringer selector drawer. -->
     <dimen name="volume_ringer_drawer_item_size">42dp</dimen>
     <dimen name="volume_ringer_drawer_item_size_half">21dp</dimen>
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index d511cab..6e035e8 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -225,6 +225,8 @@
     <item type="id" name="communal_tutorial_indicator" />
     <item type="id" name="nssl_placeholder_barrier_bottom" />
     <item type="id" name="ambient_indication_container" />
+    <item type="id" name="status_view_media_container" />
+    <item type="id" name="smart_space_barrier_bottom" />
 
     <!-- Privacy dialog -->
     <item type="id" name="privacy_dialog_close_app_button" />
@@ -260,8 +262,9 @@
     <item type="id" name="device_entry_icon_fg" />
     <item type="id" name="device_entry_icon_bg" />
 
-    <!--Id for the device-entry UDFPS icon that lives in the alternate bouncer. -->
+    <!--Id for the device-entry UDFPS related views that live in the alternate bouncer. -->
     <item type="id" name="alternate_bouncer_udfps_icon_view" />
+    <item type="id" name="alternate_bouncer_udfps_accessibility_overlay" />
 
     <!-- Id for the udfps accessibility overlay -->
     <item type="id" name="udfps_accessibility_overlay" />
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index f4b25a7..f55a11e2 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -449,11 +449,11 @@
     <!-- Content description after successful auth when confirmation required -->
     <string name="fingerprint_dialog_authenticated_confirmation">Press the unlock icon to continue</string>
     <!-- Message shown to inform the user a face cannot be recognized and fingerprint should instead be used.[CHAR LIMIT=50] -->
-    <string name="fingerprint_dialog_use_fingerprint_instead">Can\u2019t recognize face. Use fingerprint instead.</string>
+    <string name="fingerprint_dialog_use_fingerprint_instead">Face not recognized. Use fingerprint instead.</string>
     <!-- Message shown to inform the user a face cannot be recognized and fingerprint should instead be used.[CHAR LIMIT=50] -->
     <string name="keyguard_face_failed_use_fp">@string/fingerprint_dialog_use_fingerprint_instead</string>
     <!-- Message shown to inform the user a face cannot be recognized. [CHAR LIMIT=25] -->
-    <string name="keyguard_face_failed">Can\u2019t recognize face</string>
+    <string name="keyguard_face_failed">Face not recognized</string>
     <!-- Message shown to suggest using fingerprint sensor to authenticate after another biometric failed. [CHAR LIMIT=25] -->
     <string name="keyguard_suggest_fingerprint">Use fingerprint instead</string>
     <!-- Message shown to inform the user that face unlock is not available. [CHAR LIMIT=59] -->
@@ -1079,6 +1079,16 @@
 
     <!-- Description for the button that opens the widget editor on click. [CHAR LIMIT=50] -->
     <string name="button_to_open_widget_editor">Open the widget editor</string>
+    <!-- Text for CTA button that launches the hub mode widget editor on click. [CHAR LIMIT=50] -->
+    <string name="cta_tile_button_to_open_widget_editor">Customize</string>
+    <!-- Text for CTA button that dismisses the tile on click. [CHAR LIMIT=50] -->
+    <string name="cta_tile_button_to_dismiss">Dismiss</string>
+    <!-- Label for CTA tile to edit the glanceable hub [CHAR LIMIT=100] -->
+    <string name="cta_label_to_edit_widget">Add, remove, and reorder your widgets in this space</string>
+    <!-- Label for CTA tile that opens widget picker on click in edit mode [CHAR LIMIT=50] -->
+    <string name="cta_label_to_open_widget_picker">Add more widgets</string>
+    <!-- Text for the popup to be displayed after dismissing the CTA tile. [CHAR LIMIT=50] -->
+    <string name="popup_on_dismiss_cta_tile_text">Long press to customize widgets</string>
     <!-- Description for the button that removes a widget on click. [CHAR LIMIT=50] -->
     <string name="button_to_remove_widget">Remove</string>
     <!-- Text for the button that launches the hub mode widget picker. [CHAR LIMIT=50] -->
@@ -2274,6 +2284,8 @@
     <string name="notification_channel_storage">Storage</string>
     <!-- Title for the notification channel for hints and suggestions. [CHAR LIMIT=NONE] -->
     <string name="notification_channel_hints">Hints</string>
+    <!-- Title for the notification channel for accessibility related (i.e. accessibility floating menu). [CHAR LIMIT=NONE] -->
+    <string name="notification_channel_accessibility">Accessibility</string>
 
     <!-- App label of the instant apps notification [CHAR LIMIT=60] -->
     <string name="instant_apps">Instant Apps</string>
@@ -2544,6 +2556,11 @@
     <string name="accessibility_floating_button_docking_tooltip">Move button to the edge to hide it temporarily</string>
     <!-- Text for the undo action button of the message view of the accessibility floating menu to perform undo operation. [CHAR LIMIT=30]-->
     <string name="accessibility_floating_button_undo">Undo</string>
+    <!-- Notification title shown when accessibility floating button is in hidden state. [CHAR LIMIT=NONE] -->
+    <string name="accessibility_floating_button_hidden_notification_title">Accessibility button hidden</string>
+    <!-- Notification content text to explain user can tap notification to bring back accessibility floating button. [CHAR LIMIT=NONE] -->
+    <string name="accessibility_floating_button_hidden_notification_text">Tap to show accessibility button</string>
+
     <!-- Text for the message view with undo action of the accessibility floating menu to show which feature shortcut was removed. [CHAR LIMIT=30]-->
     <string name="accessibility_floating_button_undo_message_label_text"><xliff:g id="feature name" example="Magnification">%s</xliff:g> shortcut removed</string>
     <!-- Text for the message view with undo action of the accessibility floating menu to show how many features shortcuts were removed. [CHAR LIMIT=30]-->
@@ -3262,10 +3279,13 @@
     <!-- Label for a button that, when clicked, sends the user to the app store to install an app. [CHAR LIMIT=64]. -->
     <string name="install_app">Install app</string>
 
+    <!-- Instructions informing the user they can swipe up on the lockscreen to dismiss [CHAR LIMIT=48]-->
+    <string name="dismissible_keyguard_swipe">Swipe to continue</string>
+
     <!--- Title of the dialog appearing when an external display is connected, asking whether to start mirroring [CHAR LIMIT=NONE]-->
     <string name="connected_display_dialog_start_mirroring">Mirror to external display?</string>
     <!--- Body of the mirroring dialog, shown when dual display is enabled. This signals that enabling mirroring will stop concurrent displays on a foldable device. [CHAR LIMIT=NONE]-->
-    <string name="connected_display_dialog_dual_display_stop_warning">Any dual screen activity currently running will be stopped</string>
+    <string name="connected_display_dialog_dual_display_stop_warning">Your inner display will be mirrored. Your front display will be turned off.</string>
     <!--- Label of the "enable display" button of the dialog appearing when an external display is connected [CHAR LIMIT=NONE]-->
     <string name="mirror_display">Mirror display</string>
     <!--- Label of the dismiss button of the dialog appearing when an external display is connected [CHAR LIMIT=NONE]-->
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index c48dd9d..7d7c050 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -921,7 +921,7 @@
     <style name="Theme.ControlsActivity" parent="@android:style/Theme.DeviceDefault.NoActionBar">
         <item name="android:windowActivityTransitions">true</item>
         <item name="android:windowContentTransitions">false</item>
-        <item name="android:windowIsTranslucent">false</item>
+        <item name="android:windowIsTranslucent">true</item>
         <item name="android:windowBackground">@android:color/black</item>
         <item name="android:windowAnimationStyle">@null</item>
         <item name="android:statusBarColor">@android:color/black</item>
@@ -930,6 +930,15 @@
         <item name="wallpaperTextColor">@*android:color/primary_text_material_dark</item>
     </style>
 
+    <style name="Theme.VolumePanelActivity" parent="@style/Theme.SystemUI">
+        <item name="android:windowIsTranslucent">true</item>
+        <item name="android:windowBackground">@android:color/transparent</item>
+        <item name="android:windowActionBar">false</item>
+        <item name="android:windowNoTitle">true</item>
+        <!-- Setting a placeholder will avoid using the SystemUI icon on the splash screen.  -->
+        <item name="android:windowSplashScreenAnimatedIcon">@drawable/ic_blank</item>
+    </style>
+
     <style name="Theme.UserSwitcherFullscreenDialog" parent="@android:style/Theme.DeviceDefault.NoActionBar.Fullscreen">
         <item name="android:statusBarColor">@color/user_switcher_fullscreen_bg</item>
         <item name="android:windowBackground">@color/user_switcher_fullscreen_bg</item>
diff --git a/packages/SystemUI/shared/Android.bp b/packages/SystemUI/shared/Android.bp
index 5b59e7d..326c7ef 100644
--- a/packages/SystemUI/shared/Android.bp
+++ b/packages/SystemUI/shared/Android.bp
@@ -48,8 +48,8 @@
     ],
     static_libs: [
         "BiometricsSharedLib",
+        "PlatformAnimationLib",
         "PluginCoreLib",
-        "SystemUIAnimationLib",
         "SystemUIPluginLib",
         "SystemUIUnfoldLib",
         "SystemUISharedLib-Keyguard",
@@ -63,6 +63,7 @@
         "kotlinx_coroutines",
         "dagger2",
         "jsr330",
+        "com_android_systemui_shared_flags_lib",
     ],
     resource_dirs: [
         "res",
diff --git a/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/UdfpsOverlayParams.kt b/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/UdfpsOverlayParams.kt
index 65c5a49..e31fb89 100644
--- a/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/UdfpsOverlayParams.kt
+++ b/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/UdfpsOverlayParams.kt
@@ -17,6 +17,8 @@
 package com.android.systemui.biometrics.shared.model
 
 import android.graphics.Rect
+import android.hardware.fingerprint.FingerprintSensorProperties.SensorType
+import android.hardware.fingerprint.FingerprintSensorProperties.TYPE_UDFPS_OPTICAL
 import android.view.Surface
 import android.view.Surface.Rotation
 
@@ -39,6 +41,8 @@
  * the native resolution.
  *
  * [rotation] current rotation of the display.
+ *
+ * [sensorType] fingerprint sensor type
  */
 data class UdfpsOverlayParams(
     val sensorBounds: Rect = Rect(),
@@ -46,7 +50,8 @@
     val naturalDisplayWidth: Int = 0,
     val naturalDisplayHeight: Int = 0,
     val scaleFactor: Float = 1f,
-    @Rotation val rotation: Int = Surface.ROTATION_0
+    @Rotation val rotation: Int = Surface.ROTATION_0,
+    @SensorType val sensorType: Int = TYPE_UDFPS_OPTICAL
 ) {
 
     /** Same as [sensorBounds], but in native resolution. */
diff --git a/packages/SystemUI/shared/lint-baseline.xml b/packages/SystemUI/shared/lint-baseline.xml
deleted file mode 100644
index 4bd6729..0000000
--- a/packages/SystemUI/shared/lint-baseline.xml
+++ /dev/null
@@ -1,708 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 7.1.0-dev" type="baseline" client="" name="" variant="all" version="7.1.0-dev">
-
-    <issue
-        id="NewApi"
-        message="Call requires API level R (current min is 26): `android.os.RemoteException#rethrowFromSystemServer`"
-        errorLine1="            throw e.rethrowFromSystemServer();"
-        errorLine2="                    ~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java"
-            line="90"
-            column="21"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 26): `android.graphics.Bitmap#getHardwareBuffer`"
-        errorLine1="                mBuffer != null ? mBuffer.getHardwareBuffer() : null, mRect);"
-        errorLine2="                                          ~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/recents/view/AppTransitionAnimationSpecCompat.java"
-            line="39"
-            column="43"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 26): `new android.graphics.ParcelableColorSpace`"
-        errorLine1="                        ? new ParcelableColorSpace(ColorSpace.get(ColorSpace.Named.SRGB))"
-        errorLine2="                          ~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/BitmapUtil.java"
-            line="57"
-            column="27"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 26): `new android.graphics.ParcelableColorSpace`"
-        errorLine1="                        : new ParcelableColorSpace(bitmap.getColorSpace());"
-        errorLine2="                          ~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/BitmapUtil.java"
-            line="58"
-            column="27"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 26): `android.graphics.Bitmap#getHardwareBuffer`"
-        errorLine1="        bundle.putParcelable(KEY_BUFFER, bitmap.getHardwareBuffer());"
-        errorLine2="                                                ~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/BitmapUtil.java"
-            line="61"
-            column="49"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Cast from `ParcelableColorSpace` to `Parcelable` requires API level 31 (current min is 26)"
-        errorLine1="        bundle.putParcelable(KEY_COLOR_SPACE, colorSpace);"
-        errorLine2="                                              ~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/BitmapUtil.java"
-            line="62"
-            column="47"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 29 (current min is 26): `android.graphics.Bitmap#wrapHardwareBuffer`"
-        errorLine1="        return Bitmap.wrapHardwareBuffer(Objects.requireNonNull(buffer),"
-        errorLine2="                      ~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/BitmapUtil.java"
-            line="84"
-            column="23"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 26): `android.graphics.ParcelableColorSpace#getColorSpace`"
-        errorLine1="                colorSpace.getColorSpace());"
-        errorLine2="                           ~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/BitmapUtil.java"
-            line="85"
-            column="28"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 29 (current min is 26): `new android.view.SurfaceControl.Transaction`"
-        errorLine1="        final SurfaceControl.Transaction tx = new SurfaceControl.Transaction();"
-        errorLine2="                                              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java"
-            line="122"
-            column="47"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 29 (current min is 26): `new android.util.ArraySet`"
-        errorLine1="        mPluginActions = new ArraySet&lt;>(mSharedPrefs.getStringSet(PLUGIN_ACTIONS, null));"
-        errorLine2="                         ~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginPrefs.java"
-            line="41"
-            column="26"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 29 (current min is 26): `new android.util.ArraySet`"
-        errorLine1="        return new ArraySet&lt;>(mPluginActions);"
-        errorLine2="               ~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginPrefs.java"
-            line="45"
-            column="16"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 28 (current min is 26): `android.graphics.Bitmap#createBitmap`"
-        errorLine1="        return Bitmap.createBitmap(picture);"
-        errorLine2="                      ~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/recents/view/RecentsTransition.java"
-            line="113"
-            column="23"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 29 (current min is 26): `new android.view.SurfaceControl.Builder`"
-        errorLine1="            mSurface = new SurfaceControl.Builder()"
-        errorLine2="                       ~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java"
-            line="116"
-            column="24"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Builder#setName`"
-        errorLine1="                    .setName(&quot;Transition Unrotate&quot;)"
-        errorLine2="                     ~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java"
-            line="117"
-            column="22"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Builder#setParent`"
-        errorLine1="                    .setParent(parent)"
-        errorLine2="                     ~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java"
-            line="119"
-            column="22"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Builder#build`"
-        errorLine1="                    .build();"
-        errorLine2="                     ~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java"
-            line="120"
-            column="22"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Transaction#reparent`"
-        errorLine1="            t.reparent(child, mSurface);"
-        errorLine2="              ~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java"
-            line="137"
-            column="15"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 29 (current min is 26): `new android.view.SurfaceControl.Transaction`"
-        errorLine1="            SurfaceControl.Transaction t = new SurfaceControl.Transaction();"
-        errorLine2="                                           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java"
-            line="143"
-            column="44"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Transaction#reparent`"
-        errorLine1="                t.reparent(mRotateChildren.get(i), rootLeash);"
-        errorLine2="                  ~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java"
-            line="145"
-            column="19"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Transaction#apply`"
-        errorLine1="            t.apply();"
-        errorLine2="              ~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java"
-            line="148"
-            column="15"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Transaction#setLayer`"
-        errorLine1="                        t.setLayer(counterLauncher.mSurface, launcherLayer);"
-        errorLine2="                          ~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java"
-            line="200"
-            column="27"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Transaction#setLayer`"
-        errorLine1="                        t.setLayer(counterLauncher.mSurface, info.getChanges().size() * 3);"
-        errorLine2="                          ~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java"
-            line="206"
-            column="27"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Transaction#setLayer`"
-        errorLine1="                            t.setLayer(leash, info.getChanges().size() * 3 - i);"
-        errorLine2="                              ~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java"
-            line="216"
-            column="31"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Transaction#setAlpha`"
-        errorLine1="                        t.setAlpha(wallpapersCompat[i].leash.getSurfaceControl(), 1.f);"
-        errorLine2="                          ~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java"
-            line="223"
-            column="27"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Transaction#setLayer`"
-        errorLine1="                            t.setLayer(counterWallpaper.mSurface, -1);"
-        errorLine2="                              ~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java"
-            line="233"
-            column="31"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Transaction#apply`"
-        errorLine1="                t.apply();"
-        errorLine2="                  ~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java"
-            line="238"
-            column="19"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Field requires API level 29 (current min is 26): `android.app.ActivityManager.RunningTaskInfo#taskId`"
-        errorLine1="        taskId = change.getTaskInfo() != null ? change.getTaskInfo().taskId : -1;"
-        errorLine2="                                                ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java"
-            line="101"
-            column="49"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Field requires API level 29 (current min is 26): `android.app.TaskInfo#taskId`"
-        errorLine1="        taskId = change.getTaskInfo() != null ? change.getTaskInfo().taskId : -1;"
-        errorLine2="                                                ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java"
-            line="192"
-            column="49"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Field requires API level 29 (current min is 26): `android.app.ActivityManager.RunningTaskInfo#isRunning`"
-        errorLine1="            isNotInRecents = !change.getTaskInfo().isRunning;"
-        errorLine2="                              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java"
-            line="116"
-            column="31"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Field requires API level 29 (current min is 26): `android.app.TaskInfo#isRunning`"
-        errorLine1="            isNotInRecents = !change.getTaskInfo().isRunning;"
-        errorLine2="                              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java"
-            line="210"
-            column="31"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl#release`"
-        errorLine1="        leash.mSurfaceControl.release();"
-        errorLine2="                              ~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java"
-            line="159"
-            column="31"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl#release`"
-        errorLine1="            mStartLeash.release();"
-        errorLine2="                        ~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java"
-            line="161"
-            column="25"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Transaction#setLayer`"
-        errorLine1="                        t.setLayer(change.getLeash(), info.getChanges().size() * 3 - i);"
-        errorLine2="                          ~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java"
-            line="97"
-            column="27"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Transaction#setAlpha`"
-        errorLine1="                    t.setAlpha(wallpapers[i].leash.mSurfaceControl, 1);"
-        errorLine2="                      ~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java"
-            line="105"
-            column="23"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Transaction#apply`"
-        errorLine1="                t.apply();"
-        errorLine2="                  ~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java"
-            line="107"
-            column="19"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl#isValid`"
-        errorLine1="        return mSurfaceControl != null &amp;&amp; mSurfaceControl.isValid();"
-        errorLine2="                                                          ~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/SurfaceControlCompat.java"
-            line="41"
-            column="59"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level R (current min is 26): `android.view.SurfaceControlViewHost#release`"
-        errorLine1="            mSurfaceControlViewHost.release();"
-        errorLine2="                                    ~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/SurfaceViewRequestReceiver.java"
-            line="61"
-            column="37"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level R (current min is 26): `android.view.SurfaceView#getHostToken`"
-        errorLine1="        bundle.putBinder(KEY_HOST_TOKEN, surfaceView.getHostToken());"
-        errorLine2="                                                     ~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/SurfaceViewRequestUtils.java"
-            line="34"
-            column="54"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 29 (current min is 26): `android.view.SurfaceView#getSurfaceControl`"
-        errorLine1="        bundle.putParcelable(KEY_SURFACE_CONTROL, surfaceView.getSurfaceControl());"
-        errorLine2="                                                              ~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/SurfaceViewRequestUtils.java"
-            line="35"
-            column="63"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Cast from `SurfaceControl` to `Parcelable` requires API level 29 (current min is 26)"
-        errorLine1="        bundle.putParcelable(KEY_SURFACE_CONTROL, surfaceView.getSurfaceControl());"
-        errorLine2="                                                  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/SurfaceViewRequestUtils.java"
-            line="35"
-            column="51"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl#isValid`"
-        errorLine1="                if (mBarrierSurfaceControl == null || !mBarrierSurfaceControl.isValid()) {"
-        errorLine2="                                                                              ~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java"
-            line="107"
-            column="79"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 29 (current min is 26): `new android.view.SurfaceControl.Transaction`"
-        errorLine1="                Transaction t = new Transaction();"
-        errorLine2="                                ~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java"
-            line="113"
-            column="33"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Transaction#apply`"
-        errorLine1="                    t.apply();"
-        errorLine2="                      ~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java"
-            line="122"
-            column="23"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Transaction#setAlpha`"
-        errorLine1="                t.setAlpha(surface, alpha);"
-        errorLine2="                  ~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java"
-            line="361"
-            column="19"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Transaction#setLayer`"
-        errorLine1="                t.setLayer(surface, layer);"
-        errorLine2="                  ~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java"
-            line="364"
-            column="19"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Field requires API level 29 (current min is 26): `android.app.TaskInfo#origActivity`"
-        errorLine1="            ComponentName sourceComponent = t.origActivity != null"
-        errorLine2="                                            ~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java"
-            line="73"
-            column="45"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Field requires API level 29 (current min is 26): `android.app.TaskInfo#origActivity`"
-        errorLine1="                    ? t.origActivity"
-        errorLine2="                      ~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java"
-            line="75"
-            column="23"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Field requires API level 29 (current min is 26): `android.app.TaskInfo#taskId`"
-        errorLine1="            this.id = t.taskId;"
-        errorLine2="                      ~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java"
-            line="78"
-            column="23"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Field requires API level 29 (current min is 26): `android.app.TaskInfo#baseIntent`"
-        errorLine1="            this.baseIntent = t.baseIntent;"
-        errorLine2="                              ~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java"
-            line="80"
-            column="31"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Field requires API level 29 (current min is 26): `android.app.TaskInfo#taskDescription`"
-        errorLine1="        ActivityManager.TaskDescription td = taskInfo.taskDescription;"
-        errorLine2="                                             ~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java"
-            line="242"
-            column="46"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Field requires API level 29 (current min is 26): `android.app.TaskInfo#topActivity`"
-        errorLine1="                taskInfo.supportsSplitScreenMultiWindow, isLocked, td, taskInfo.topActivity);"
-        errorLine2="                                                                       ~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java"
-            line="246"
-            column="72"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Field requires API level 29 (current min is 26): `android.app.TaskInfo#topActivity`"
-        errorLine1="        return info.topActivity;"
-        errorLine2="               ~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskInfoCompat.java"
-            line="49"
-            column="16"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Field requires API level 29 (current min is 26): `android.app.TaskInfo#taskDescription`"
-        errorLine1="        return info.taskDescription;"
-        errorLine2="               ~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskInfoCompat.java"
-            line="53"
-            column="16"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Field requires API level 29 (current min is 26): `android.app.ActivityManager.RunningTaskInfo#taskId`"
-        errorLine1="        onTaskMovedToFront(taskInfo.taskId);"
-        errorLine2="                           ~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java"
-            line="70"
-            column="28"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Field requires API level 29 (current min is 26): `android.app.TaskInfo#taskId`"
-        errorLine1="        onTaskMovedToFront(taskInfo.taskId);"
-        errorLine2="                           ~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java"
-            line="70"
-            column="28"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 29 (current min is 26): `android.graphics.Bitmap#wrapHardwareBuffer`"
-        errorLine1="                thumbnail = Bitmap.wrapHardwareBuffer(buffer, snapshot.getColorSpace());"
-        errorLine2="                                   ~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java"
-            line="69"
-            column="36"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 26): `android.app.WallpaperColors#getColorHints`"
-        errorLine1="                    (colors.getColorHints() &amp; WallpaperColors.HINT_SUPPORTS_DARK_THEME) != 0;"
-        errorLine2="                            ~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/TonalCompat.java"
-            line="42"
-            column="29"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 29 (current min is 26): `new android.view.SurfaceControl.Transaction`"
-        errorLine1="        mTransaction = new Transaction();"
-        errorLine2="                       ~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java"
-            line="31"
-            column="24"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Transaction#apply`"
-        errorLine1="        mTransaction.apply();"
-        errorLine2="                     ~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java"
-            line="35"
-            column="22"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Transaction#setBufferSize`"
-        errorLine1="        mTransaction.setBufferSize(surfaceControl.mSurfaceControl, w, h);"
-        errorLine2="                     ~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java"
-            line="54"
-            column="22"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Transaction#setLayer`"
-        errorLine1="        mTransaction.setLayer(surfaceControl.mSurfaceControl, z);"
-        errorLine2="                     ~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java"
-            line="59"
-            column="22"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Transaction#setAlpha`"
-        errorLine1="        mTransaction.setAlpha(surfaceControl.mSurfaceControl, alpha);"
-        errorLine2="                     ~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java"
-            line="64"
-            column="22"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Transaction#apply`"
-        errorLine1="            t.apply();"
-        errorLine2="              ~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/ViewRootImplCompat.java"
-            line="64"
-            column="15"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 29 (current min is 26): `android.content.res.Resources#getFloat`"
-        errorLine1="                .getFloat(Resources.getSystem().getIdentifier("
-        errorLine2="                 ~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/WallpaperManagerCompat.java"
-            line="46"
-            column="18"/>
-    </issue>
-
-</issues>
diff --git a/packages/SystemUI/shared/res/values/ids.xml b/packages/SystemUI/shared/res/values/ids.xml
new file mode 100644
index 0000000..1ff2f0e
--- /dev/null
+++ b/packages/SystemUI/shared/res/values/ids.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2023 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<resources>
+    <!-- ID of the smartspace card view. -->
+    <item type="id" name="bc_smartspace_view" />
+    <!-- ID of the smartspace date view. -->
+    <item type="id" name="date_smartspace_view" />
+    <!-- ID of the smartspace weather view. -->
+    <item type="id" name="weather_smartspace_view" />
+</resources>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/navigationbar/buttons/KeyButtonRipple.java b/packages/SystemUI/shared/src/com/android/systemui/shared/navigationbar/KeyButtonRipple.java
similarity index 98%
rename from packages/SystemUI/shared/src/com/android/systemui/navigationbar/buttons/KeyButtonRipple.java
rename to packages/SystemUI/shared/src/com/android/systemui/shared/navigationbar/KeyButtonRipple.java
index f005af3..92473e8 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/navigationbar/buttons/KeyButtonRipple.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/navigationbar/KeyButtonRipple.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.navigationbar.buttons;
+package com.android.systemui.shared.navigationbar;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -125,7 +125,7 @@
     /**
      *  @param onInvisibleRunnable run after we are next drawn invisibly. Only used once.
      */
-    void setOnInvisibleRunnable(Runnable onInvisibleRunnable) {
+    public void setOnInvisibleRunnable(Runnable onInvisibleRunnable) {
         mOnInvisibleRunnable = onInvisibleRunnable;
     }
 
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstance.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstance.java
index 92f66902..87cc86f 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstance.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstance.java
@@ -38,6 +38,7 @@
 import java.io.File;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.function.BiConsumer;
 import java.util.function.Supplier;
 
 /**
@@ -57,7 +58,7 @@
     private final PluginFactory<T> mPluginFactory;
     private final String mTag;
 
-    private boolean mIsDebug = false;
+    private BiConsumer<String, String> mLogConsumer = null;
     private Context mPluginContext;
     private T mPlugin;
 
@@ -86,38 +87,34 @@
         return mTag;
     }
 
-    public boolean getIsDebug() {
-        return mIsDebug;
+    public void setLogFunc(BiConsumer logConsumer) {
+        mLogConsumer = logConsumer;
     }
 
-    public void setIsDebug(boolean debug) {
-        mIsDebug = debug;
-    }
-
-    private void logDebug(String message) {
-        if (mIsDebug) {
-            Log.i(mTag, message);
+    private void log(String message) {
+        if (mLogConsumer != null) {
+            mLogConsumer.accept(mTag, message);
         }
     }
 
     /** Alerts listener and plugin that the plugin has been created. */
-    public void onCreate() {
+    public synchronized void onCreate() {
         boolean loadPlugin = mListener.onPluginAttached(this);
         if (!loadPlugin) {
             if (mPlugin != null) {
-                logDebug("onCreate: auto-unload");
+                log("onCreate: auto-unload");
                 unloadPlugin();
             }
             return;
         }
 
         if (mPlugin == null) {
-            logDebug("onCreate auto-load");
+            log("onCreate auto-load");
             loadPlugin();
             return;
         }
 
-        logDebug("onCreate: load callbacks");
+        log("onCreate: load callbacks");
         mPluginFactory.checkVersion(mPlugin);
         if (!(mPlugin instanceof PluginFragment)) {
             // Only call onCreate for plugins that aren't fragments, as fragments
@@ -128,8 +125,8 @@
     }
 
     /** Alerts listener and plugin that the plugin is being shutdown. */
-    public void onDestroy() {
-        logDebug("onDestroy");
+    public synchronized void onDestroy() {
+        log("onDestroy");
         unloadPlugin();
         mListener.onPluginDetached(this);
     }
@@ -143,12 +140,13 @@
     /**
      * Loads and creates the plugin if it does not exist.
      */
-    public void loadPlugin() {
+    public synchronized void loadPlugin() {
         if (mPlugin != null) {
-            logDebug("Load request when already loaded");
+            log("Load request when already loaded");
             return;
         }
 
+        // Both of these calls take about 1 - 1.5 seconds in test runs
         mPlugin = mPluginFactory.createPlugin();
         mPluginContext = mPluginFactory.createPluginContext();
         if (mPlugin == null || mPluginContext == null) {
@@ -156,7 +154,7 @@
             return;
         }
 
-        logDebug("Loaded plugin; running callbacks");
+        log("Loaded plugin; running callbacks");
         mPluginFactory.checkVersion(mPlugin);
         if (!(mPlugin instanceof PluginFragment)) {
             // Only call onCreate for plugins that aren't fragments, as fragments
@@ -171,13 +169,13 @@
      *
      * This will free the associated memory if there are not other references.
      */
-    public void unloadPlugin() {
+    public synchronized void unloadPlugin() {
         if (mPlugin == null) {
-            logDebug("Unload request when already unloaded");
+            log("Unload request when already unloaded");
             return;
         }
 
-        logDebug("Unloading plugin, running callbacks");
+        log("Unloading plugin, running callbacks");
         mListener.onPluginUnloaded(mPlugin, this);
         if (!(mPlugin instanceof PluginFragment)) {
             // Only call onDestroy for plugins that aren't fragments, as fragments
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 823bd2d..7088829 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
@@ -88,18 +88,23 @@
      */
     void onNavButtonsDarkIntensityChanged(float darkIntensity) = 22;
 
-     /**
-      * Sent when split keyboard shortcut is triggered to enter stage split.
-      */
-     void enterStageSplitFromRunningApp(boolean leftOrTop) = 25;
+    /**
+     * Sent when when navigation bar luma sampling is enabled or disabled.
+     */
+    void onNavigationBarLumaSamplingEnabled(int displayId, boolean enable) = 23;
 
-     /**
-      * Sent when the surface for navigation bar is created or changed
-      */
-     void onNavigationBarSurface(in SurfaceControl surface) = 26;
+    /**
+     * Sent when split keyboard shortcut is triggered to enter stage split.
+     */
+    void enterStageSplitFromRunningApp(boolean leftOrTop) = 25;
 
-     /**
-      * Sent when the task bar stash state is toggled.
-      */
-     void onTaskbarToggled() = 27;
+    /**
+     * Sent when the surface for navigation bar is created or changed
+     */
+    void onNavigationBarSurface(in SurfaceControl surface) = 26;
+
+    /**
+     * Sent when the task bar stash state is toggled.
+     */
+    void onTaskbarToggled() = 27;
 }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButtonView.java b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButtonView.java
index a4b6451..2145166 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButtonView.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButtonView.java
@@ -30,7 +30,7 @@
 
 import androidx.annotation.DimenRes;
 
-import com.android.systemui.navigationbar.buttons.KeyButtonRipple;
+import com.android.systemui.shared.navigationbar.KeyButtonRipple;
 
 public class FloatingRotationButtonView extends ImageView {
 
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
index 131eb6b..c08b083 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
@@ -20,10 +20,11 @@
 import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON;
 import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
 
+import static com.android.systemui.shared.Flags.shadeAllowBackGesture;
+
 import android.annotation.IntDef;
 import android.content.Context;
 import android.content.res.Resources;
-import android.os.SystemProperties;
 import android.view.ViewConfiguration;
 import android.view.WindowManagerPolicyConstants;
 
@@ -132,8 +133,7 @@
             SYSUI_STATE_WAKEFULNESS_TRANSITION | SYSUI_STATE_AWAKE;
 
     // Whether the back gesture is allowed (or ignored) by the Shade
-    public static final boolean ALLOW_BACK_GESTURE_IN_SHADE = SystemProperties.getBoolean(
-            "persist.wm.debug.shade_allow_back_gesture", false);
+    public static final boolean ALLOW_BACK_GESTURE_IN_SHADE = shadeAllowBackGesture();
 
     @Retention(RetentionPolicy.SOURCE)
     @IntDef({SYSUI_STATE_SCREEN_PINNING,
diff --git a/packages/SystemUI/shared/src/com/android/systemui/statusbar/policy/CallbackController.java b/packages/SystemUI/shared/src/com/android/systemui/statusbar/policy/CallbackController.java
index 047ff75..9f82201 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/statusbar/policy/CallbackController.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/statusbar/policy/CallbackController.java
@@ -21,6 +21,11 @@
 import androidx.lifecycle.LifecycleEventObserver;
 import androidx.lifecycle.LifecycleOwner;
 
+/**
+ * Implementation of the collection used and thread guarantees are left to the discretion of the
+ * client. Consider using {@link com.android.systemui.util.ListenerSet} to prevent concurrent
+ * modification exceptions.
+ */
 public interface CallbackController<T> {
 
     /** Add a callback */
diff --git a/packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt b/packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt
index 1ee58de..5e76801 100644
--- a/packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt
@@ -41,6 +41,7 @@
 import com.android.systemui.util.settings.SecureSettings
 import java.io.PrintWriter
 import javax.inject.Inject
+import dagger.Lazy
 
 /**
  * Handles active unlock settings changes.
@@ -51,6 +52,7 @@
     private val secureSettings: SecureSettings,
     private val contentResolver: ContentResolver,
     private val selectedUserInteractor: SelectedUserInteractor,
+    private val keyguardUpdateMonitor: Lazy<KeyguardUpdateMonitor>,
     dumpManager: DumpManager
 ) : Dumpable {
 
@@ -96,7 +98,6 @@
         UNDER_DISPLAY_FINGERPRINT(3),
     }
 
-    var keyguardUpdateMonitor: KeyguardUpdateMonitor? = null
     private var requestActiveUnlockOnWakeup = false
     private var requestActiveUnlockOnUnlockIntent = false
     private var requestActiveUnlockOnBioFail = false
@@ -316,7 +317,7 @@
             return false
         }
 
-        keyguardUpdateMonitor?.let {
+        keyguardUpdateMonitor.get().let {
             val anyFaceEnrolled = it.isFaceEnabledAndEnrolled
             val anyFingerprintEnrolled = it.isUnlockWithFingerprintPossible(
                     selectedUserInteractor.getSelectedUserId())
@@ -369,13 +370,13 @@
         }")
 
         pw.println("Current state:")
-        keyguardUpdateMonitor?.let {
+        keyguardUpdateMonitor.get().let {
             pw.println("   shouldRequestActiveUnlockOnUnlockIntentFromBiometricEnrollment=" +
                     "${shouldRequestActiveUnlockOnUnlockIntentFromBiometricEnrollment()}")
             pw.println("   isFaceEnabledAndEnrolled=${it.isFaceEnabledAndEnrolled}")
             pw.println("   fpUnlockPossible=${
                 it.isUnlockWithFingerprintPossible(selectedUserInteractor.getSelectedUserId())}")
             pw.println("   udfpsEnrolled=${it.isUdfpsEnrolled}")
-        } ?: pw.println("   keyguardUpdateMonitor is uninitialized")
+        }
     }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
index bcc2044..82410fd 100644
--- a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
@@ -33,6 +33,7 @@
 import androidx.annotation.VisibleForTesting
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.repeatOnLifecycle
+import com.android.systemui.Flags.migrateClocksToBlueprint
 import com.android.systemui.broadcast.BroadcastDispatcher
 import com.android.systemui.customization.R
 import com.android.systemui.dagger.qualifiers.Background
@@ -325,6 +326,10 @@
                     }
                 }
 
+                if (visible) {
+                    refreshTime()
+                }
+
                 smallTimeListener?.update(shouldTimeListenerRun)
                 largeTimeListener?.update(shouldTimeListenerRun)
             }
@@ -346,6 +351,19 @@
                 weatherData = data
                 clock?.run { events.onWeatherDataChanged(data) }
             }
+
+            override fun onTimeChanged() {
+                refreshTime()
+            }
+
+            private fun refreshTime() {
+                if (!migrateClocksToBlueprint()) {
+                    return
+                }
+
+                clock?.smallClock?.events?.onTimeTick()
+                clock?.largeClock?.events?.onTimeTick()
+            }
         }
 
     private val zenModeCallback = object : ZenModeController.Callback {
@@ -558,7 +576,8 @@
             isRunning = true
             when (clockFace.config.tickRate) {
                 ClockTickRate.PER_MINUTE -> {
-                    /* Handled by KeyguardClockSwitchController */
+                    // Handled by KeyguardClockSwitchController and
+                    // by KeyguardUpdateMonitorCallback#onTimeChanged.
                 }
                 ClockTickRate.PER_SECOND -> executor.execute(secondsRunnable)
                 ClockTickRate.PER_FRAME -> {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index 74b975c..de7c12d 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -22,6 +22,7 @@
 import static com.android.keyguard.KeyguardClockSwitch.LARGE;
 import static com.android.keyguard.KeyguardClockSwitch.SMALL;
 import static com.android.systemui.Flags.migrateClocksToBlueprint;
+import static com.android.systemui.Flags.smartspaceRelocateToBottom;
 import static com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED;
 import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
 
@@ -421,6 +422,10 @@
             return;
         }
 
+        if (smartspaceRelocateToBottom()) {
+            return;
+        }
+
         mSmartspaceView = mSmartspaceController.buildAndConnectView(mView);
         LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
                 MATCH_PARENT, WRAP_CONTENT);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
index 714fe64..66f965a 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
@@ -30,13 +30,13 @@
 import com.android.internal.util.LatencyTracker;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
+import com.android.systemui.Flags;
 import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor;
 import com.android.systemui.bouncer.ui.BouncerMessageView;
 import com.android.systemui.bouncer.ui.binder.BouncerMessageViewBinder;
 import com.android.systemui.classifier.FalsingCollector;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
 import com.android.systemui.log.BouncerLogger;
 import com.android.systemui.res.R;
 import com.android.systemui.statusbar.policy.DevicePostureController;
@@ -108,7 +108,7 @@
 
     private void updateMessageAreaVisibility() {
         if (mMessageAreaController == null) return;
-        if (mFeatureFlags.isEnabled(Flags.REVAMPED_BOUNCER_MESSAGES)) {
+        if (Flags.revampedBouncerMessages()) {
             mMessageAreaController.disable();
         } else {
             mMessageAreaController.setIsVisible(true);
@@ -182,15 +182,13 @@
     public void bindMessageView(
             @NonNull BouncerMessageInteractor bouncerMessageInteractor,
             KeyguardMessageAreaController.Factory messageAreaControllerFactory,
-            BouncerLogger bouncerLogger,
-            FeatureFlags featureFlags) {
+            BouncerLogger bouncerLogger) {
         BouncerMessageView bouncerMessageView = (BouncerMessageView) mView.getBouncerMessageView();
         if (bouncerMessageView != null) {
             BouncerMessageViewBinder.bind(bouncerMessageView,
                     bouncerMessageInteractor,
                     messageAreaControllerFactory,
-                    bouncerLogger,
-                    featureFlags);
+                    bouncerLogger);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index a8bf229..05eeac6f 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -133,7 +133,8 @@
     static final int BOUNCER_DISMISS_EXTENDED_ACCESS = 3;
     // Bouncer is dismissed due to sim card unlock code entered.
     static final int BOUNCER_DISMISS_SIM = 4;
-
+    // Bouncer dismissed after being allowed to dismiss by forceDismissiblekeyguard
+    static final int BOUNCER_DISMISSIBLE_KEYGUARD = 5;
     private static final String TAG = "KeyguardSecurityView";
 
     // Make the view move slower than the finger, as if the spring were applying force.
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index cce2018..8e5d0da 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -21,6 +21,7 @@
 
 import static com.android.keyguard.KeyguardSecurityContainer.BOUNCER_DISMISS_BIOMETRIC;
 import static com.android.keyguard.KeyguardSecurityContainer.BOUNCER_DISMISS_EXTENDED_ACCESS;
+import static com.android.keyguard.KeyguardSecurityContainer.BOUNCER_DISMISSIBLE_KEYGUARD;
 import static com.android.keyguard.KeyguardSecurityContainer.BOUNCER_DISMISS_NONE_SECURITY;
 import static com.android.keyguard.KeyguardSecurityContainer.BOUNCER_DISMISS_PASSWORD;
 import static com.android.keyguard.KeyguardSecurityContainer.BOUNCER_DISMISS_SIM;
@@ -31,7 +32,6 @@
 import static com.android.keyguard.KeyguardSecurityModel.SecurityMode.SimPuk;
 import static com.android.systemui.DejankUtils.whitelistIpcs;
 import static com.android.systemui.flags.Flags.LOCKSCREEN_ENABLE_LANDSCAPE;
-import static com.android.systemui.flags.Flags.REVAMPED_BOUNCER_MESSAGES;
 
 import android.app.ActivityManager;
 import android.app.admin.DevicePolicyManager;
@@ -79,10 +79,10 @@
 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
 import com.android.systemui.classifier.FalsingA11yDelegate;
 import com.android.systemui.classifier.FalsingCollector;
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor;
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor;
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.flags.Flags;
-import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor;
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
 import com.android.systemui.log.SessionTracker;
 import com.android.systemui.plugins.ActivityStarter;
@@ -136,7 +136,7 @@
     private final SessionTracker mSessionTracker;
     private final Optional<SideFpsController> mSideFpsController;
     private final FalsingA11yDelegate mFalsingA11yDelegate;
-    private final KeyguardFaceAuthInteractor mKeyguardFaceAuthInteractor;
+    private final DeviceEntryFaceAuthInteractor mDeviceEntryFaceAuthInteractor;
     private final BouncerMessageInteractor mBouncerMessageInteractor;
     private int mTranslationY;
     private final KeyguardTransitionInteractor mKeyguardTransitionInteractor;
@@ -217,7 +217,7 @@
         @Override
         public void onUserInput() {
             mBouncerMessageInteractor.onPrimaryBouncerUserInput();
-            mKeyguardFaceAuthInteractor.onPrimaryBouncerUserInput();
+            mDeviceEntryFaceAuthInteractor.onPrimaryBouncerUserInput();
         }
 
         @Override
@@ -348,11 +348,11 @@
     private final SwipeListener mSwipeListener = new SwipeListener() {
         @Override
         public void onSwipeUp() {
-            if (mKeyguardFaceAuthInteractor.canFaceAuthRun()) {
+            if (mDeviceEntryFaceAuthInteractor.canFaceAuthRun()) {
                 mKeyguardSecurityCallback.userActivity();
             }
-            mKeyguardFaceAuthInteractor.onSwipeUpOnBouncer();
-            if (mKeyguardFaceAuthInteractor.isFaceAuthEnabledAndEnrolled()) {
+            mDeviceEntryFaceAuthInteractor.onSwipeUpOnBouncer();
+            if (mDeviceEntryFaceAuthInteractor.isFaceAuthEnabledAndEnrolled()) {
                 mUpdateMonitor.requestActiveUnlock(
                         ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT,
                         "swipeUpOnBouncer");
@@ -457,7 +457,7 @@
             TelephonyManager telephonyManager,
             ViewMediatorCallback viewMediatorCallback,
             AudioManager audioManager,
-            KeyguardFaceAuthInteractor keyguardFaceAuthInteractor,
+            DeviceEntryFaceAuthInteractor deviceEntryFaceAuthInteractor,
             BouncerMessageInteractor bouncerMessageInteractor,
             Provider<JavaAdapter> javaAdapter,
             SelectedUserInteractor selectedUserInteractor,
@@ -496,7 +496,7 @@
         mTelephonyManager = telephonyManager;
         mViewMediatorCallback = viewMediatorCallback;
         mAudioManager = audioManager;
-        mKeyguardFaceAuthInteractor = keyguardFaceAuthInteractor;
+        mDeviceEntryFaceAuthInteractor = deviceEntryFaceAuthInteractor;
         mBouncerMessageInteractor = bouncerMessageInteractor;
         mSelectedUserInteractor = selectedUserInteractor;
         mDeviceEntryInteractor = deviceEntryInteractor;
@@ -863,7 +863,11 @@
         boolean finish = false;
         int eventSubtype = -1;
         BouncerUiEvent uiEvent = BouncerUiEvent.UNKNOWN;
-        if (mUpdateMonitor.getUserHasTrust(targetUserId)) {
+        if (mUpdateMonitor.forceIsDismissibleIsKeepingDeviceUnlocked()) {
+            finish = true;
+            eventSubtype = BOUNCER_DISMISSIBLE_KEYGUARD;
+            // TODO: b/308417021 add UI event
+        } else if (mUpdateMonitor.getUserHasTrust(targetUserId)) {
             finish = true;
             eventSubtype = BOUNCER_DISMISS_EXTENDED_ACCESS;
             uiEvent = BouncerUiEvent.BOUNCER_DISMISS_EXTENDED_ACCESS;
@@ -1107,7 +1111,7 @@
     }
 
     private boolean canDisplayUserSwitcher() {
-        return mFeatureFlags.isEnabled(Flags.BOUNCER_USER_SWITCHER);
+        return getContext().getResources().getBoolean(R.bool.config_enableBouncerUserSwitcher);
     }
 
     private void configureMode() {
@@ -1164,7 +1168,7 @@
         mLockPatternUtils.reportFailedPasswordAttempt(userId);
         if (timeoutMs > 0) {
             mLockPatternUtils.reportPasswordLockout(timeoutMs, userId);
-            if (!mFeatureFlags.isEnabled(REVAMPED_BOUNCER_MESSAGES)) {
+            if (!com.android.systemui.Flags.revampedBouncerMessages()) {
                 mView.showTimeoutDialog(userId, timeoutMs, mLockPatternUtils,
                         mSecurityModel.getSecurityMode(userId));
             }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
index c5e7070..5729119 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
@@ -105,6 +105,13 @@
     @Override
     protected void onViewAttached() {
         super.onViewAttached();
+        mKeyguardUpdateMonitor.registerCallback(mUpdateMonitorCallback);
+    }
+
+    @Override
+    protected void onViewDetached() {
+        super.onViewDetached();
+        mKeyguardUpdateMonitor.removeCallback(mUpdateMonitorCallback);
     }
 
     @Override
@@ -128,14 +135,12 @@
     @Override
     public void onResume(int reason) {
         super.onResume(reason);
-        mKeyguardUpdateMonitor.registerCallback(mUpdateMonitorCallback);
         mView.resetState();
     }
 
     @Override
     public void onPause() {
         super.onPause();
-        mKeyguardUpdateMonitor.removeCallback(mUpdateMonitorCallback);
 
         // dismiss the dialog.
         if (mSimUnlockProgressDialog != null) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
index 2a54a4e..d372f5a 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
@@ -20,6 +20,7 @@
 import static androidx.constraintlayout.widget.ConstraintSet.PARENT_ID;
 
 import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_CLOCK_MOVE_ANIMATION;
+import static com.android.systemui.Flags.migrateClocksToBlueprint;
 import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
 
 import android.animation.Animator;
@@ -492,7 +493,12 @@
             boolean splitShadeEnabled,
             boolean shouldBeCentered,
             boolean animate) {
-        mKeyguardClockSwitchController.setSplitShadeCentered(splitShadeEnabled && shouldBeCentered);
+        if (migrateClocksToBlueprint()) {
+            mKeyguardInteractor.setClockShouldBeCentered(mSplitShadeEnabled && shouldBeCentered);
+        } else {
+            mKeyguardClockSwitchController.setSplitShadeCentered(
+                    splitShadeEnabled && shouldBeCentered);
+        }
         if (mStatusViewCentered == shouldBeCentered) {
             return;
         }
@@ -548,8 +554,9 @@
         ClockController clock = mKeyguardClockSwitchController.getClock();
         boolean customClockAnimation = clock != null
                 && clock.getLargeClock().getConfig().getHasCustomPositionUpdatedAnimation();
-
-        if (customClockAnimation) {
+        // When migrateClocksToBlueprint is on, customized clock animation is conducted in
+        // KeyguardClockViewBinder
+        if (customClockAnimation && !migrateClocksToBlueprint()) {
             // Find the clock, so we can exclude it from this transition.
             FrameLayout clockContainerView = mView.findViewById(R.id.lockscreen_clock_view_large);
 
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 9c61a8a..fe96099 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -102,6 +102,7 @@
 import androidx.annotation.Nullable;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.foldables.FoldGracePeriodProvider;
 import com.android.internal.jank.InteractionJankMonitor;
 import com.android.internal.logging.InstanceId;
 import com.android.internal.logging.UiEventLogger;
@@ -111,25 +112,28 @@
 import com.android.settingslib.Utils;
 import com.android.settingslib.WirelessUtils;
 import com.android.settingslib.fuelgauge.BatteryStatus;
+import com.android.systemui.CoreStartable;
 import com.android.systemui.Dumpable;
+import com.android.systemui.Flags;
 import com.android.systemui.biometrics.AuthController;
 import com.android.systemui.biometrics.FingerprintInteractiveToAuthProvider;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.deviceentry.data.repository.FaceWakeUpTriggersConfig;
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor;
+import com.android.systemui.deviceentry.domain.interactor.FaceAuthenticationListener;
+import com.android.systemui.deviceentry.shared.model.AcquiredFaceAuthenticationStatus;
+import com.android.systemui.deviceentry.shared.model.ErrorFaceAuthenticationStatus;
+import com.android.systemui.deviceentry.shared.model.FaceAuthenticationStatus;
+import com.android.systemui.deviceentry.shared.model.FaceDetectionStatus;
+import com.android.systemui.deviceentry.shared.model.FailedFaceAuthenticationStatus;
+import com.android.systemui.deviceentry.shared.model.HelpFaceAuthenticationStatus;
+import com.android.systemui.deviceentry.shared.model.SuccessFaceAuthenticationStatus;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.dump.DumpsysTableLogger;
-import com.android.systemui.keyguard.domain.interactor.FaceAuthenticationListener;
-import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor;
 import com.android.systemui.keyguard.shared.constants.TrustAgentUiEvent;
-import com.android.systemui.keyguard.shared.model.AcquiredFaceAuthenticationStatus;
-import com.android.systemui.keyguard.shared.model.ErrorFaceAuthenticationStatus;
-import com.android.systemui.keyguard.shared.model.FaceAuthenticationStatus;
-import com.android.systemui.keyguard.shared.model.FaceDetectionStatus;
-import com.android.systemui.keyguard.shared.model.FailedFaceAuthenticationStatus;
-import com.android.systemui.keyguard.shared.model.HelpFaceAuthenticationStatus;
-import com.android.systemui.keyguard.shared.model.SuccessFaceAuthenticationStatus;
 import com.android.systemui.log.SessionTracker;
 import com.android.systemui.plugins.clocks.WeatherData;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -175,7 +179,7 @@
  * to be updated.
  */
 @SysUISingleton
-public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpable {
+public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpable, CoreStartable {
 
     private static final String TAG = "KeyguardUpdateMonitor";
     private static final int BIOMETRIC_LOCKOUT_RESET_DELAY_MS = 600;
@@ -216,7 +220,8 @@
     private static final int MSG_BIOMETRIC_ENROLLMENT_STATE_CHANGED = 348;
 
     /** Biometric authentication state: Not listening. */
-    private static final int BIOMETRIC_STATE_STOPPED = 0;
+    @VisibleForTesting
+    protected static final int BIOMETRIC_STATE_STOPPED = 0;
 
     /** Biometric authentication state: Listening. */
     private static final int BIOMETRIC_STATE_RUNNING = 1;
@@ -304,6 +309,10 @@
     private boolean mKeyguardOccluded;
     private boolean mCredentialAttempted;
     private boolean mKeyguardGoingAway;
+    /**
+     * Whether the keyguard is forced into a dismissible state.
+     */
+    private boolean mForceIsDismissible;
     private boolean mGoingToSleep;
     private boolean mPrimaryBouncerFullyShown;
     private boolean mPrimaryBouncerIsOrWillBeShowing;
@@ -347,6 +356,7 @@
     private final LatencyTracker mLatencyTracker;
     private final StatusBarStateController mStatusBarStateController;
     private final Executor mBackgroundExecutor;
+    private final Executor mMainExecutor;
     private final SensorPrivacyManager mSensorPrivacyManager;
     private final ActiveUnlockConfig mActiveUnlockConfig;
     private final IDreamManager mDreamManager;
@@ -354,7 +364,13 @@
     @Nullable
     private final FingerprintManager mFpm;
     @Nullable
-    private KeyguardFaceAuthInteractor mFaceAuthInteractor;
+    private final BiometricManager mBiometricManager;
+    @Nullable
+    private DeviceEntryFaceAuthInteractor mFaceAuthInteractor;
+    @VisibleForTesting
+    protected FoldGracePeriodProvider mFoldGracePeriodProvider =
+            new FoldGracePeriodProvider();
+    private final DevicePostureController mDevicePostureController;
     private final TaskStackChangeListeners mTaskStackChangeListeners;
     private final IActivityTaskManager mActivityTaskManager;
     private final SelectedUserInteractor mSelectedUserInteractor;
@@ -367,10 +383,11 @@
     private List<SubscriptionInfo> mSubscriptionInfo;
     @VisibleForTesting
     protected int mFingerprintRunningState = BIOMETRIC_STATE_STOPPED;
+    private boolean mFingerprintDetectRunning;
     private boolean mIsDreaming;
     private boolean mLogoutEnabled;
     private int mActiveMobileDataSubscription = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
-    private FingerprintInteractiveToAuthProvider mFingerprintInteractiveToAuthProvider;
+    private final FingerprintInteractiveToAuthProvider mFingerprintInteractiveToAuthProvider;
 
     /**
      * Short delay before restarting fingerprint authentication after a successful try. This should
@@ -719,6 +736,14 @@
     }
 
     /**
+     * Updates KeyguardUpdateMonitor's internal state to know the device should remain unlocked
+     * until the next signal to lock. Does nothing if the keyguard is already showing.
+     */
+    public void tryForceIsDismissibleKeyguard() {
+        setForceIsDismissibleKeyguard(true);
+    }
+
+    /**
      * Updates KeyguardUpdateMonitor's internal state to know if keyguard is going away.
      */
     public void setKeyguardGoingAway(boolean goingAway) {
@@ -980,6 +1005,7 @@
         final boolean wasCancellingRestarting = mFingerprintRunningState
                 == BIOMETRIC_STATE_CANCELLING_RESTARTING;
         mFingerprintRunningState = BIOMETRIC_STATE_STOPPED;
+        mFingerprintDetectRunning = false;
         if (wasCancellingRestarting) {
             KeyguardUpdateMonitor.this.updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE);
         } else {
@@ -1088,6 +1114,9 @@
         boolean wasRunning = mFingerprintRunningState == BIOMETRIC_STATE_RUNNING;
         boolean isRunning = fingerprintRunningState == BIOMETRIC_STATE_RUNNING;
         mFingerprintRunningState = fingerprintRunningState;
+        if (mFingerprintRunningState == BIOMETRIC_STATE_STOPPED) {
+            mFingerprintDetectRunning = false;
+        }
         mLogger.logFingerprintRunningState(mFingerprintRunningState);
         // Clients of KeyguardUpdateMonitor don't care about the internal state about the
         // asynchronousness of the cancel cycle. So only notify them if the actually running state
@@ -1260,14 +1289,14 @@
         return getFaceAuthInteractor() != null && getFaceAuthInteractor().isRunning();
     }
 
-    private @Nullable KeyguardFaceAuthInteractor getFaceAuthInteractor() {
+    private @Nullable DeviceEntryFaceAuthInteractor getFaceAuthInteractor() {
         return mFaceAuthInteractor;
     }
 
     /**
      * Set the face auth interactor that should be used for initiating face authentication.
      */
-    public void setFaceAuthInteractor(KeyguardFaceAuthInteractor faceAuthInteractor) {
+    public void setFaceAuthInteractor(DeviceEntryFaceAuthInteractor faceAuthInteractor) {
         if (mFaceAuthInteractor != null) {
             mFaceAuthInteractor.unregisterListener(mFaceAuthenticationListener);
         }
@@ -1350,7 +1379,7 @@
      * @return whether the current user has been authenticated with face. This may be true
      * on the lockscreen if the user doesn't have bypass enabled.
      *
-     * @deprecated Use {@link KeyguardFaceAuthInteractor#isAuthenticated()}
+     * @deprecated Use {@link DeviceEntryFaceAuthInteractor#isAuthenticated()}
      */
     @Deprecated
     public boolean getIsFaceAuthenticated() {
@@ -1358,7 +1387,18 @@
     }
 
     public boolean getUserCanSkipBouncer(int userId) {
-        return getUserHasTrust(userId) || getUserUnlockedWithBiometric(userId);
+        return getUserHasTrust(userId) || getUserUnlockedWithBiometric(userId)
+                || forceIsDismissibleIsKeepingDeviceUnlocked();
+    }
+
+    /**
+     * Whether the keyguard should be kept unlocked for the folding grace period.
+     */
+    public boolean forceIsDismissibleIsKeepingDeviceUnlocked() {
+        if (mFoldGracePeriodProvider.isEnabled()) {
+            return mForceIsDismissible && isUnlockingWithForceKeyguardDismissibleAllowed();
+        }
+        return false;
     }
 
     public boolean getUserHasTrust(int userId) {
@@ -1381,7 +1421,7 @@
 
     /**
      * Returns whether the user is unlocked with face.
-     * @deprecated Use {@link KeyguardFaceAuthInteractor#isAuthenticated()} instead
+     * @deprecated Use {@link DeviceEntryFaceAuthInteractor#isAuthenticated()} instead
      */
     @Deprecated
     public boolean isCurrentUserUnlockedWithFace() {
@@ -1462,6 +1502,10 @@
         return isUnlockingWithBiometricAllowed(true);
     }
 
+    private boolean isUnlockingWithForceKeyguardDismissibleAllowed() {
+        return isUnlockingWithBiometricAllowed(false);
+    }
+
     public boolean isUnlockingWithBiometricAllowed(boolean isStrongBiometric) {
         // StrongAuthTracker#isUnlockingWithBiometricAllowed includes
         // STRONG_AUTH_REQUIRED_AFTER_LOCKOUT which is the same as mFingerprintLockedOutPermanent;
@@ -1798,6 +1842,7 @@
                 public void onFingerprintDetected(int sensorId, int userId,
                         boolean isStrongBiometric) {
                     handleBiometricDetected(userId, FINGERPRINT, isStrongBiometric);
+                    setFingerprintRunningState(BIOMETRIC_STATE_STOPPED);
                 }
             };
 
@@ -1972,6 +2017,7 @@
 
     protected void handleStartedGoingToSleep(int arg1) {
         Assert.isMainThread();
+        setForceIsDismissibleKeyguard(false);
         clearFingerprintRecognized();
         for (int i = 0; i < mCallbacks.size(); i++) {
             KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
@@ -2059,6 +2105,7 @@
     @VisibleForTesting
     void resetBiometricListeningState() {
         mFingerprintRunningState = BIOMETRIC_STATE_STOPPED;
+        mFingerprintDetectRunning = false;
     }
 
     @VisibleForTesting
@@ -2105,6 +2152,7 @@
         mDeviceProvisioned = isDeviceProvisionedInSettingsDb();
         mStrongAuthTracker = new StrongAuthTracker(context);
         mBackgroundExecutor = backgroundExecutor;
+        mMainExecutor = mainExecutor;
         mBroadcastDispatcher = broadcastDispatcher;
         mInteractionJankMonitor = interactionJankMonitor;
         mLatencyTracker = latencyTracker;
@@ -2126,7 +2174,7 @@
         mDevicePolicyManager = devicePolicyManager;
         mPackageManager = packageManager;
         mFpm = fingerprintManager;
-        mActiveUnlockConfig.setKeyguardUpdateMonitor(this);
+        mBiometricManager = biometricManager;
         mConfigFaceAuthSupportedPosture = mContext.getResources().getInteger(
                 R.integer.config_face_auth_supported_posture);
         mFaceWakeUpTriggersConfig = faceWakeUpTriggersConfig;
@@ -2134,10 +2182,14 @@
                 mContext.getResources().getStringArray(
                         R.array.config_fingerprint_listen_on_occluding_activity_packages))
                 .collect(Collectors.toSet());
+        mDevicePostureController = devicePostureController;
         mTaskStackChangeListeners = taskStackChangeListeners;
         mActivityTaskManager = activityTaskManagerService;
         mSelectedUserInteractor = selectedUserInteractor;
 
+        mFingerprintInteractiveToAuthProvider = interactiveToAuthProvider.orElse(null);
+        mIsSystemUser = mUserManager.isSystemUser();
+
         mHandler = new Handler(mainLooper) {
             @Override
             public void handleMessage(Message msg) {
@@ -2252,6 +2304,20 @@
             }
         };
 
+        mTimeFormatChangeObserver = new ContentObserver(mHandler) {
+            @Override
+            public void onChange(boolean selfChange) {
+                mHandler.sendMessage(mHandler.obtainMessage(
+                        MSG_TIME_FORMAT_UPDATE,
+                        Settings.System.getString(
+                                mContext.getContentResolver(),
+                                Settings.System.TIME_12_24)));
+            }
+        };
+    }
+
+    @Override
+    public void start() {
         // Since device can't be un-provisioned, we only need to register a content observer
         // to update mDeviceProvisioned when we are...
         if (!mDeviceProvisioned) {
@@ -2297,7 +2363,7 @@
                 mHandler, UserHandle.ALL);
 
         mSubscriptionManager.addOnSubscriptionsChangedListener(mSubscriptionListener);
-        mUserTracker.addCallback(mUserChangedCallback, mainExecutor);
+        mUserTracker.addCallback(mUserChangedCallback, mMainExecutor);
 
         mTrustManager.registerTrustListener(this);
 
@@ -2318,8 +2384,8 @@
             mFpm.addLockoutResetCallback(mFingerprintLockoutResetCallback);
         }
 
-        if (biometricManager != null) {
-            biometricManager.registerEnabledOnKeyguardCallback(mBiometricEnabledCallback);
+        if (mBiometricManager != null) {
+            mBiometricManager.registerEnabledOnKeyguardCallback(mBiometricEnabledCallback);
         }
 
         // in case authenticators aren't registered yet at this point:
@@ -2327,7 +2393,7 @@
             @Override
             public void onAllAuthenticatorsRegistered(
                     @BiometricAuthenticator.Modality int modality) {
-                mainExecutor.execute(
+                mMainExecutor.execute(
                         () -> updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE));
             }
 
@@ -2335,7 +2401,7 @@
             public void onEnrollmentsChanged(@BiometricAuthenticator.Modality int modality) {
                 mHandler.obtainMessage(MSG_BIOMETRIC_ENROLLMENT_STATE_CHANGED, modality, 0)
                         .sendToTarget();
-                mainExecutor.execute(
+                mMainExecutor.execute(
                         () -> updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE));
             }
 
@@ -2353,12 +2419,11 @@
             }
         });
         if (mConfigFaceAuthSupportedPosture != DEVICE_POSTURE_UNKNOWN) {
-            devicePostureController.addCallback(mPostureCallback);
+            mDevicePostureController.addCallback(mPostureCallback);
         }
         updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE);
 
         mTaskStackChangeListeners.registerTaskStackListener(mTaskStackListener);
-        mIsSystemUser = mUserManager.isSystemUser();
         int user = mSelectedUserInteractor.getSelectedUserId(true);
         mUserIsUnlocked.put(user, mUserManager.isUserUnlocked(user));
         mLogoutEnabled = mDevicePolicyManager.isLogoutEnabled();
@@ -2377,22 +2442,9 @@
         mTelephonyListenerManager.addActiveDataSubscriptionIdListener(mPhoneStateListener);
         initializeSimState();
 
-        mTimeFormatChangeObserver = new ContentObserver(mHandler) {
-            @Override
-            public void onChange(boolean selfChange) {
-                mHandler.sendMessage(mHandler.obtainMessage(
-                        MSG_TIME_FORMAT_UPDATE,
-                        Settings.System.getString(
-                                mContext.getContentResolver(),
-                                Settings.System.TIME_12_24)));
-            }
-        };
-
         mContext.getContentResolver().registerContentObserver(
                 Settings.System.getUriFor(Settings.System.TIME_12_24),
                 false, mTimeFormatChangeObserver, UserHandle.USER_ALL);
-
-        mFingerprintInteractiveToAuthProvider = interactiveToAuthProvider.orElse(null);
     }
 
     private void initializeSimState() {
@@ -2445,7 +2497,7 @@
 
     /**
      * @return true if there's at least one face enrolled
-     * @deprecated Use {@link KeyguardFaceAuthInteractor#isFaceAuthEnabledAndEnrolled()}
+     * @deprecated Use {@link DeviceEntryFaceAuthInteractor#isFaceAuthEnabledAndEnrolled()}
      */
     @Deprecated
     public boolean isFaceEnabledAndEnrolled() {
@@ -2492,8 +2544,10 @@
             return;
         }
         final boolean shouldListenForFingerprint = shouldListenForFingerprint(isUdfpsSupported());
-        final boolean runningOrRestarting = mFingerprintRunningState == BIOMETRIC_STATE_RUNNING
+        final boolean running = mFingerprintRunningState == BIOMETRIC_STATE_RUNNING;
+        final boolean runningOrRestarting = running
                 || mFingerprintRunningState == BIOMETRIC_STATE_CANCELLING_RESTARTING;
+        final boolean runDetect = shouldRunFingerprintDetect();
         if (runningOrRestarting && !shouldListenForFingerprint) {
             if (action == BIOMETRIC_ACTION_START) {
                 mLogger.v("Ignoring stopListeningForFingerprint()");
@@ -2505,10 +2559,24 @@
                 mLogger.v("Ignoring startListeningForFingerprint()");
                 return;
             }
-            startListeningForFingerprint();
+            startListeningForFingerprint(runDetect);
+        } else if (running && runDetect && !mFingerprintDetectRunning) {
+            if (action == BIOMETRIC_ACTION_STOP) {
+                mLogger.v("Ignoring startListeningForFingerprint(detect)");
+                return;
+            }
+            // stop running authentication and start running fingerprint detection
+            stopListeningForFingerprint();
+            startListeningForFingerprint(true);
         }
     }
 
+    private boolean shouldRunFingerprintDetect() {
+        return !isUnlockingWithFingerprintAllowed()
+                || (Flags.runFingerprintDetectOnDismissibleKeyguard()
+                && getUserCanSkipBouncer(mSelectedUserInteractor.getSelectedUserId()));
+    }
+
     /**
      * If a user is encrypted or not.
      * This is NOT related to the lock screen being visible or not.
@@ -2764,7 +2832,6 @@
                         && biometricEnabledForUser
                         && !isUserInLockdown(user);
         final boolean strongerAuthRequired = !isUnlockingWithFingerprintAllowed();
-        final boolean isSideFps = isSfpsSupported() && isSfpsEnrolled();
         final boolean shouldListenBouncerState =
                 !strongerAuthRequired || !mPrimaryBouncerIsOrWillBeShowing;
 
@@ -2810,7 +2877,7 @@
     /**
      * If face auth is allows to scan on this exact moment.
      *
-     * @deprecated Use {@link KeyguardFaceAuthInteractor#canFaceAuthRun()}
+     * @deprecated Use {@link DeviceEntryFaceAuthInteractor#canFaceAuthRun()}
      */
     @Deprecated
     public boolean shouldListenForFace() {
@@ -2827,7 +2894,7 @@
         }
     }
 
-    private void startListeningForFingerprint() {
+    private void startListeningForFingerprint(boolean runDetect) {
         final int userId = mSelectedUserInteractor.getSelectedUserId();
         final boolean unlockPossible = isUnlockWithFingerprintPossible(userId);
         if (mFingerprintCancelSignal != null) {
@@ -2857,18 +2924,20 @@
                         mFingerprintInteractiveToAuthProvider.getVendorExtension(userId));
             }
 
-            if (!isUnlockingWithFingerprintAllowed()) {
+            if (runDetect) {
                 mLogger.v("startListeningForFingerprint - detect");
                 mFpm.detectFingerprint(
                         mFingerprintCancelSignal,
                         mFingerprintDetectionCallback,
                         fingerprintAuthenticateOptions);
+                mFingerprintDetectRunning = true;
             } else {
                 mLogger.v("startListeningForFingerprint");
                 mFpm.authenticate(null /* crypto */, mFingerprintCancelSignal,
                         mFingerprintAuthenticationCallback,
                         null /* handler */,
                         fingerprintAuthenticateOptions);
+                mFingerprintDetectRunning = false;
             }
             setFingerprintRunningState(BIOMETRIC_STATE_RUNNING);
         }
@@ -2879,7 +2948,7 @@
     }
 
     /**
-     * @deprecated Use {@link KeyguardFaceAuthInteractor#isLockedOut()}
+     * @deprecated Use {@link DeviceEntryFaceAuthInteractor#isLockedOut()}
      */
     @Deprecated
     public boolean isFaceLockedOut() {
@@ -2920,7 +2989,7 @@
     }
 
     /**
-     * @deprecated Use {@link KeyguardFaceAuthInteractor#isFaceAuthEnabledAndEnrolled()}
+     * @deprecated Use {@link DeviceEntryFaceAuthInteractor#isFaceAuthEnabledAndEnrolled()}
      */
     @VisibleForTesting
     @Deprecated
@@ -3021,6 +3090,7 @@
     void handleUserSwitching(int userId, Runnable resultCallback) {
         mLogger.logUserSwitching(userId, "from UserTracker");
         Assert.isMainThread();
+        setForceIsDismissibleKeyguard(false);
         clearFingerprintRecognized();
         boolean trustUsuallyManaged = mTrustManager.isTrustUsuallyManaged(userId);
         mLogger.logTrustUsuallyManagedUpdated(userId, mUserTrustIsUsuallyManaged.get(userId),
@@ -3599,6 +3669,31 @@
         }
     }
 
+    private void setForceIsDismissibleKeyguard(boolean forceIsDismissible) {
+        Assert.isMainThread();
+        if (!mFoldGracePeriodProvider.isEnabled()) {
+            // never send updates if the feature isn't enabled
+            return;
+        }
+        if (mKeyguardShowing && forceIsDismissible) {
+            // never keep the device unlocked if the keyguard was already showing
+            mLogger.d("Skip setting forceIsDismissibleKeyguard to true. "
+                    + "Keyguard already showing.");
+            return;
+        }
+        if (mForceIsDismissible != forceIsDismissible) {
+            mForceIsDismissible = forceIsDismissible;
+            mLogger.logForceIsDismissibleKeyguard(mForceIsDismissible);
+            for (int i = 0; i < mCallbacks.size(); i++) {
+                KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+                if (cb != null) {
+                    cb.onForceIsDismissibleChanged(forceIsDismissibleIsKeepingDeviceUnlocked());
+                }
+            }
+        }
+
+    }
+
     public boolean isSimPinVoiceSecure() {
         // TODO: only count SIMs that handle voice
         return isSimPinSecure();
@@ -3860,10 +3955,14 @@
     @Override
     public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
         pw.println("KeyguardUpdateMonitor state:");
+        pw.println("  forceIsDismissible=" + mForceIsDismissible);
+        pw.println("  forceIsDismissibleIsKeepingDeviceUnlocked="
+                + forceIsDismissibleIsKeepingDeviceUnlocked());
         pw.println("  getUserHasTrust()=" + getUserHasTrust(
                 mSelectedUserInteractor.getSelectedUserId()));
         pw.println("  getUserUnlockedWithBiometric()="
                 + getUserUnlockedWithBiometric(mSelectedUserInteractor.getSelectedUserId()));
+        pw.println("  mFingerprintDetectRunning=" + mFingerprintDetectRunning);
         pw.println("  SIM States:");
         for (SimData data : mSimDatas.values()) {
             pw.println("    " + data.toString());
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
index 9d216dce..7ac5ac2 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
@@ -339,4 +339,9 @@
      * On biometric enrollment state changed
      */
     public void onBiometricEnrollmentStateChanged(BiometricSourceType biometricSourceType) { }
+
+    /**
+     * On force is dismissible state changed.
+     */
+    public void onForceIsDismissibleChanged(boolean forceIsDismissible) { }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
index 175fcdb..d5dc85c 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
@@ -26,7 +26,6 @@
 import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset;
 import static com.android.systemui.flags.Flags.DOZING_MIGRATION_1;
 import static com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED;
-import static com.android.systemui.Flags.newAodTransition;
 import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
 
 import android.annotation.SuppressLint;
@@ -395,16 +394,6 @@
             mView.updateIcon(ICON_LOCK, true);
             mView.setContentDescription(mLockedLabel);
             mView.setVisibility(View.VISIBLE);
-        } else if (mIsDozing && newAodTransition()) {
-            mView.animate()
-                    .alpha(0f)
-                    .setDuration(FADE_OUT_DURATION_MS)
-                    .withEndAction(() -> {
-                        mView.clearIcon();
-                        mView.setVisibility(View.INVISIBLE);
-                        mView.setContentDescription(null);
-                    })
-                    .start();
         } else {
             mView.clearIcon();
             mView.setVisibility(View.INVISIBLE);
diff --git a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
index 055ca56..1f4e732 100644
--- a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
+++ b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
@@ -639,4 +639,12 @@
             { "fingerprint acquire message: $int1" }
         )
     }
+    fun logForceIsDismissibleKeyguard(keepUnlocked: Boolean) {
+        logBuffer.log(
+                TAG,
+                DEBUG,
+                { bool1 = keepUnlocked },
+                { "keepUnlockedOnFold changed to: $bool1" }
+        )
+    }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/mediator/ScreenOnCoordinator.kt b/packages/SystemUI/src/com/android/keyguard/mediator/ScreenOnCoordinator.kt
index 603471b..7a560e8 100644
--- a/packages/SystemUI/src/com/android/keyguard/mediator/ScreenOnCoordinator.kt
+++ b/packages/SystemUI/src/com/android/keyguard/mediator/ScreenOnCoordinator.kt
@@ -19,6 +19,7 @@
 import android.annotation.BinderThread
 import android.os.Handler
 import android.os.Trace
+import com.android.systemui.Flags
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.unfold.SysUIUnfoldComponent
@@ -59,8 +60,11 @@
         foldAodAnimationController?.onScreenTurningOn(pendingTasks.registerTask("fold-to-aod"))
 
         pendingTasks.onTasksComplete {
-            mainHandler.post {
+            if (Flags.enableBackgroundKeyguardOndrawnCallback()) {
+                // called by whatever thread completes the last task registered.
                 onDrawn.run()
+            } else {
+                mainHandler.post { onDrawn.run() }
             }
         }
         Trace.endSection()
diff --git a/packages/SystemUI/src/com/android/systemui/CoreStartable.java b/packages/SystemUI/src/com/android/systemui/CoreStartable.java
index c07a4d2..4c9782c 100644
--- a/packages/SystemUI/src/com/android/systemui/CoreStartable.java
+++ b/packages/SystemUI/src/com/android/systemui/CoreStartable.java
@@ -16,8 +16,6 @@
 
 package com.android.systemui;
 
-import android.content.res.Configuration;
-
 import androidx.annotation.NonNull;
 
 import java.io.PrintWriter;
@@ -35,6 +33,9 @@
  *  abstract fun bind(impl: FoobarStartable): CoreStartable
  *  </pre>
  *
+ * If your CoreStartable depends on different CoreStartables starting before it, use a
+ * {@link com.android.systemui.startable.Dependencies} annotation to list out those dependencies.
+ *
  * @see SystemUIApplication#startServicesIfNeeded()
  */
 public interface CoreStartable extends Dumpable {
@@ -42,13 +43,6 @@
     /** Main entry point for implementations. Called shortly after SysUI startup. */
     void start();
 
-    /** Called when the device configuration changes. This will not be called before
-     * {@link #start()}, but it could be called before {@link #onBootCompleted()}.
-     *
-     * @see android.app.Application#onConfigurationChanged(Configuration)  */
-    default void onConfigurationChanged(Configuration newConfig) {
-    }
-
     @Override
     default void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
     }
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index 008de43..d6d5c26 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -68,7 +68,7 @@
 
 import com.android.internal.util.Preconditions;
 import com.android.settingslib.Utils;
-import com.android.systemui.biometrics.AuthController;
+import com.android.systemui.biometrics.data.repository.FacePropertyRepository;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.decor.CutoutDecorProviderFactory;
 import com.android.systemui.decor.DebugRoundedCornerDelegate;
@@ -89,8 +89,10 @@
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.statusbar.commandline.CommandRegistry;
 import com.android.systemui.statusbar.events.PrivacyDotViewController;
+import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.util.concurrency.DelayableExecutor;
 import com.android.systemui.util.concurrency.ThreadFactory;
+import com.android.systemui.util.kotlin.JavaAdapter;
 import com.android.systemui.util.settings.SecureSettings;
 
 import dalvik.annotation.optimization.NeverCompile;
@@ -109,7 +111,8 @@
  * for antialiasing and emulation purposes.
  */
 @SysUISingleton
-public class ScreenDecorations implements CoreStartable, Dumpable {
+public class ScreenDecorations implements
+        CoreStartable, ConfigurationController.ConfigurationListener, Dumpable {
     private static final boolean DEBUG_LOGGING = false;
     private static final String TAG = "ScreenDecorations";
 
@@ -129,8 +132,6 @@
     };
     private final ScreenDecorationsLogger mLogger;
 
-    private final AuthController mAuthController;
-
     private DisplayTracker mDisplayTracker;
     @VisibleForTesting
     protected boolean mIsRegistered;
@@ -181,6 +182,9 @@
     private DisplayCutout mDisplayCutout;
     private boolean mPendingManualConfigUpdate;
 
+    private FacePropertyRepository mFacePropertyRepository;
+    private JavaAdapter mJavaAdapter;
+
     @VisibleForTesting
     protected void showCameraProtection(@NonNull Path protectionPath, @NonNull Rect bounds) {
         if (mFaceScanningFactory.shouldShowFaceScanningAnim()) {
@@ -328,7 +332,8 @@
             PrivacyDotDecorProviderFactory dotFactory,
             FaceScanningProviderFactory faceScanningFactory,
             ScreenDecorationsLogger logger,
-            AuthController authController) {
+            FacePropertyRepository facePropertyRepository,
+            JavaAdapter javaAdapter) {
         mContext = context;
         mSecureSettings = secureSettings;
         mCommandRegistry = commandRegistry;
@@ -340,22 +345,10 @@
         mFaceScanningFactory = faceScanningFactory;
         mFaceScanningViewId = com.android.systemui.res.R.id.face_scanning_anim;
         mLogger = logger;
-        mAuthController = authController;
+        mFacePropertyRepository = facePropertyRepository;
+        mJavaAdapter = javaAdapter;
     }
 
-
-    private final AuthController.Callback mAuthControllerCallback = new AuthController.Callback() {
-        @Override
-        public void onFaceSensorLocationChanged() {
-            mLogger.onSensorLocationChanged();
-            if (mExecutor != null) {
-                mExecutor.execute(
-                        () -> updateOverlayProviderViews(
-                                new Integer[]{mFaceScanningViewId}));
-            }
-        }
-    };
-
     private final ScreenDecorCommand.Callback mScreenDecorCommandCallback = (cmd, pw) -> {
         // If we are exiting debug mode, we can set it (false) and bail, otherwise we will
         // ensure that debug mode is set
@@ -405,7 +398,8 @@
         mExecutor = mThreadFactory.buildDelayableExecutorOnHandler(mHandler);
         mExecutor.execute(this::startOnScreenDecorationsThread);
         mDotViewController.setUiExecutor(mExecutor);
-        mAuthController.addCallback(mAuthControllerCallback);
+        mJavaAdapter.alwaysCollectFlow(mFacePropertyRepository.getSensorLocation(),
+                this::onFaceSensorLocationChanged);
         mCommandRegistry.registerCommand(ScreenDecorCommand.SCREEN_DECOR_CMD_NAME,
                 () -> new ScreenDecorCommand(mScreenDecorCommandCallback));
     }
@@ -575,7 +569,7 @@
 
                     if (mPendingManualConfigUpdate) {
                         mPendingManualConfigUpdate = false;
-                        onConfigurationChanged(mContext.getResources().getConfiguration());
+                        onConfigChanged(mContext.getResources().getConfiguration());
                     }
                 }
             }
@@ -1062,7 +1056,7 @@
     }
 
     @Override
-    public void onConfigurationChanged(Configuration newConfig) {
+    public void onConfigChanged(Configuration newConfig) {
         if (DEBUG_DISABLE_SCREEN_DECORATIONS) {
             Log.i(TAG, "ScreenDecorations is disabled");
             return;
@@ -1318,6 +1312,16 @@
         view.setLayoutParams(params);
     }
 
+    @VisibleForTesting
+    void onFaceSensorLocationChanged(Point location) {
+        mLogger.onSensorLocationChanged();
+        if (mExecutor != null) {
+            mExecutor.execute(
+                    () -> updateOverlayProviderViews(
+                            new Integer[]{mFaceScanningViewId}));
+        }
+    }
+
     public static class DisplayCutoutView extends DisplayCutoutBaseView {
         final List<Rect> mBounds = new ArrayList();
         final Rect mBoundingRect = new Rect();
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorationsModule.kt b/packages/SystemUI/src/com/android/systemui/ScreenDecorationsModule.kt
new file mode 100644
index 0000000..044312b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorationsModule.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui
+
+import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.ClassKey
+import dagger.multibindings.IntoMap
+import dagger.multibindings.IntoSet
+
+@Module
+interface ScreenDecorationsModule {
+    /** Start ScreenDecorations. */
+    @Binds
+    @IntoMap
+    @ClassKey(ScreenDecorations::class)
+    fun bindScreenDecorationsCoreStartable(impl: ScreenDecorations): CoreStartable
+
+    /** Listen to config changes for ScreenDecorations. */
+    @Binds
+    @IntoSet
+    fun bindScreenDecorationsConfigListener(impl: ScreenDecorations): ConfigurationListener
+}
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index c3f6480..8aae206 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui;
 
+import android.annotation.SuppressLint;
 import android.app.ActivityThread;
 import android.app.Application;
 import android.app.Notification;
@@ -26,27 +27,33 @@
 import android.content.pm.ApplicationInfo;
 import android.content.res.Configuration;
 import android.os.Bundle;
-import android.os.Looper;
 import android.os.Process;
-import android.os.SystemProperties;
 import android.os.Trace;
-import android.os.UserHandle;
 import android.util.Log;
 import android.util.TimingsTraceLog;
 import android.view.SurfaceControl;
 import android.view.ThreadedRenderer;
 import android.view.View;
 
-import com.android.systemui.res.R;
+import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
+
 import com.android.internal.protolog.common.ProtoLog;
 import com.android.systemui.dagger.GlobalRootComponent;
 import com.android.systemui.dagger.SysUIComponent;
 import com.android.systemui.dump.DumpManager;
+import com.android.systemui.res.R;
+import com.android.systemui.startable.Dependencies;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.util.NotificationChannels;
 
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayDeque;
+import java.util.Arrays;
 import java.util.Comparator;
+import java.util.HashSet;
 import java.util.Map;
+import java.util.StringJoiner;
 import java.util.TreeMap;
 
 import javax.inject.Provider;
@@ -78,10 +85,17 @@
         ProtoLog.REQUIRE_PROTOLOGTOOL = false;
     }
 
+    @VisibleForTesting
+    @Override
+    public void attachBaseContext(Context base) {
+        super.attachBaseContext(base);
+    }
+
     protected GlobalRootComponent getRootComponent() {
         return mInitializer.getRootComponent();
     }
 
+    @SuppressLint("RegisterReceiverViaContext")
     @Override
     public void onCreate() {
         super.onCreate();
@@ -96,9 +110,11 @@
         mBootCompleteCache = mSysUIComponent.provideBootCacheImpl();
         log.traceEnd();
 
+        GlobalRootComponent rootComponent = mInitializer.getRootComponent();
+
         // Enable Looper trace points.
         // This allows us to see Handler callbacks on traces.
-        Looper.getMainLooper().setTraceTag(Trace.TRACE_TAG_APP);
+        rootComponent.getMainLooper().setTraceTag(Trace.TRACE_TAG_APP);
 
         // Set the application theme that is inherited by all services. Note that setting the
         // application theme in the manifest does only work for activities. Keep this in sync with
@@ -106,15 +122,17 @@
         setTheme(R.style.Theme_SystemUI);
 
         View.setTraceLayoutSteps(
-                SystemProperties.getBoolean("persist.debug.trace_layouts", false));
+                rootComponent.getSystemPropertiesHelper()
+                        .getBoolean("persist.debug.trace_layouts", false));
         View.setTracedRequestLayoutClassClass(
-                SystemProperties.get("persist.debug.trace_request_layout_class", null));
+                rootComponent.getSystemPropertiesHelper()
+                        .get("persist.debug.trace_request_layout_class", null));
 
         if (Flags.enableLayoutTracing()) {
             View.setTraceLayoutSteps(true);
         }
 
-        if (Process.myUserHandle().equals(UserHandle.SYSTEM)) {
+        if (rootComponent.getProcessWrapper().isSystemUser()) {
             IntentFilter bootCompletedFilter = new
                     IntentFilter(Intent.ACTION_LOCKED_BOOT_COMPLETED);
             bootCompletedFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
@@ -175,8 +193,8 @@
     }
 
     /**
-     * Makes sure that all the SystemUI services are running. If they are already running, this is a
-     * no-op. This is needed to conditinally start all the services, as we only need to have it in
+     * Makes sure that all the CoreStartables are running. If they are already running, this is a
+     * no-op. This is needed to conditionally start all the services, as we only need to have it in
      * the main process.
      * <p>This method must only be called from the main thread.</p>
      */
@@ -221,7 +239,8 @@
         if (!mBootCompleteCache.isBootComplete()) {
             // check to see if maybe it was already completed long before we began
             // see ActivityManagerService.finishBooting()
-            if ("1".equals(SystemProperties.get("sys.boot_completed"))) {
+            if ("1".equals(getRootComponent().getSystemPropertiesHelper()
+                    .get("sys.boot_completed"))) {
                 mBootCompleteCache.setBootComplete();
                 if (DEBUG) {
                     Log.v(TAG, "BOOT_COMPLETED was already sent");
@@ -237,17 +256,78 @@
                 Trace.TRACE_TAG_APP);
         log.traceBegin(metricsPrefix);
 
-        int i = 0;
-        for (Map.Entry<Class<?>, Provider<CoreStartable>> entry : startables.entrySet()) {
-            String clsName = entry.getKey().getName();
-            int j = i;  // Copied to make lambda happy.
-            timeInitialization(
-                    clsName,
-                    () -> mServices[j] = startStartable(clsName, entry.getValue()),
-                    log,
-                    metricsPrefix);
-            i++;
+        HashSet<Class<?>> startedStartables = new HashSet<>();
+
+        // Perform a form of topological sort:
+        // 1) Iterate through a queue of all non-started startables
+        //   If the startable has all of its dependencies met
+        //     - start it
+        //   Else
+        //     - enqueue it for the next iteration
+        // 2) If anything was started and the "next" queue is not empty, loop back to 1
+        // 3) If we're done looping and there are any non-started startables left, throw an error.
+        //
+        // This "sort" is not very optimized. We assume that most CoreStartables don't have many
+        // dependencies - zero in fact. We assume two or three iterations of this loop will be
+        // enough. If that ever changes, it may be worth revisiting.
+
+        log.traceBegin("Topologically start Core Startables");
+        boolean startedAny = false;
+        ArrayDeque<Map.Entry<Class<?>, Provider<CoreStartable>>> queue;
+        ArrayDeque<Map.Entry<Class<?>, Provider<CoreStartable>>> nextQueue =
+                new ArrayDeque<>(startables.entrySet());
+        int numIterations = 0;
+
+        int serviceIndex = 0;
+
+        do {
+            queue = nextQueue;
+            nextQueue = new ArrayDeque<>(startables.size());
+
+            while (!queue.isEmpty()) {
+                Map.Entry<Class<?>, Provider<CoreStartable>> entry = queue.removeFirst();
+
+                Class<?> cls = entry.getKey();
+                Dependencies dep = cls.getAnnotation(Dependencies.class);
+                Class<? extends CoreStartable>[] deps = (dep == null ? null : dep.value());
+                if (deps == null || startedStartables.containsAll(Arrays.asList(deps))) {
+                    String clsName = cls.getName();
+                    int i = serviceIndex;  // Copied to make lambda happy.
+                    timeInitialization(
+                            clsName,
+                            () -> mServices[i] = startStartable(clsName, entry.getValue()),
+                            log,
+                            metricsPrefix);
+                    startedStartables.add(cls);
+                    startedAny = true;
+                    serviceIndex++;
+                } else {
+                    nextQueue.add(entry);
+                }
+            }
+            numIterations++;
+        } while (startedAny && !nextQueue.isEmpty()); // if none were started, stop.
+
+        if (!nextQueue.isEmpty()) { // If some startables were left over, throw an error.
+            while (!nextQueue.isEmpty()) {
+                Map.Entry<Class<?>, Provider<CoreStartable>> entry = nextQueue.removeFirst();
+                Class<?> cls = entry.getKey();
+                Dependencies dep = cls.getAnnotation(Dependencies.class);
+                Class<? extends CoreStartable>[] deps = (dep == null ? null : dep.value());
+                StringJoiner stringJoiner = new StringJoiner(", ");
+                for (int i = 0; deps != null && i < deps.length; i++) {
+                    if (!startedStartables.contains(deps[i])) {
+                        stringJoiner.add(deps[i].getName());
+                    }
+                }
+                Log.e(TAG, "Failed to start " + cls.getName()
+                        + ". Missing dependencies: [" + stringJoiner + "]");
+            }
+
+            throw new RuntimeException("Failed to start all CoreStartables. Check logcat!");
         }
+        Log.i(TAG, "Topological CoreStartables completed in " + numIterations + " iterations");
+        log.traceEnd();
 
         if (vendorComponent != null) {
             timeInitialization(
@@ -258,8 +338,8 @@
                     metricsPrefix);
         }
 
-        for (i = 0; i < mServices.length; i++) {
-            final CoreStartable service = mServices[i];
+        for (serviceIndex = 0; serviceIndex < mServices.length; serviceIndex++) {
+            final CoreStartable service = mServices[serviceIndex];
             if (mBootCompleteCache.isBootComplete()) {
                 notifyBootCompleted(service);
             }
@@ -308,10 +388,14 @@
                     Trace.TRACE_TAG_APP, clsName + ".newInstance()");
         }
         try {
-            startable = (CoreStartable) Class.forName(clsName).newInstance();
+            startable = (CoreStartable) Class.forName(clsName)
+                    .getDeclaredConstructor()
+                    .newInstance();
         } catch (ClassNotFoundException
-                | IllegalAccessException
-                | InstantiationException ex) {
+                 | IllegalAccessException
+                 | InstantiationException
+                 | NoSuchMethodException
+                 | InvocationTargetException ex) {
             throw new RuntimeException(ex);
         } finally {
             Trace.endSection();
@@ -344,7 +428,7 @@
     }
 
     @Override
-    public void onConfigurationChanged(Configuration newConfig) {
+    public void onConfigurationChanged(@NonNull Configuration newConfig) {
         if (mServicesStarted) {
             ConfigurationController configController = mSysUIComponent.getConfigurationController();
             if (Trace.isEnabled()) {
@@ -354,19 +438,6 @@
             }
             configController.onConfigurationChanged(newConfig);
             Trace.endSection();
-            int len = mServices.length;
-            for (int i = 0; i < len; i++) {
-                if (mServices[i] != null) {
-                    if (Trace.isEnabled()) {
-                        Trace.traceBegin(
-                                Trace.TRACE_TAG_APP,
-                                mServices[i].getClass().getSimpleName()
-                                        + ".onConfigurationChanged()");
-                    }
-                    mServices[i].onConfigurationChanged(newConfig);
-                    Trace.endSection();
-                }
-            }
         }
     }
 
@@ -376,7 +447,7 @@
 
     @Override
     public void setContextAvailableCallback(
-            SystemUIAppComponentFactoryBase.ContextAvailableCallback callback) {
+            @NonNull SystemUIAppComponentFactoryBase.ContextAvailableCallback callback) {
         mContextAvailableCallback = callback;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java b/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
index 7a8161e..da49201 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
@@ -17,6 +17,7 @@
 package com.android.systemui.accessibility;
 
 import static android.view.WindowManager.ScreenshotSource.SCREENSHOT_ACCESSIBILITY_ACTIONS;
+
 import static com.android.internal.accessibility.common.ShortcutConstants.CHOOSER_PACKAGE_NAME;
 
 import android.accessibilityservice.AccessibilityService;
@@ -57,6 +58,7 @@
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.NotificationShadeWindowController;
 import com.android.systemui.statusbar.phone.StatusBarWindowCallback;
+import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.util.Assert;
 
@@ -71,7 +73,7 @@
  * Class to register system actions with accessibility framework.
  */
 @SysUISingleton
-public class SystemActions implements CoreStartable {
+public class SystemActions implements CoreStartable, ConfigurationController.ConfigurationListener {
     private static final String TAG = "SystemActions";
 
     /**
@@ -234,7 +236,7 @@
     }
 
     @Override
-    public void onConfigurationChanged(Configuration newConfig) {
+    public void onConfigChanged(Configuration newConfig) {
         final Locale locale = mContext.getResources().getConfiguration().getLocales().get(0);
         if (!locale.equals(mLocale)) {
             mLocale = locale;
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/SystemActionsModule.kt b/packages/SystemUI/src/com/android/systemui/accessibility/SystemActionsModule.kt
new file mode 100644
index 0000000..4d6d784
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/SystemActionsModule.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.accessibility
+
+import com.android.systemui.CoreStartable
+import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.ClassKey
+import dagger.multibindings.IntoMap
+import dagger.multibindings.IntoSet
+
+@Module
+interface SystemActionsModule {
+    /** Start SystemActions. */
+    @Binds
+    @IntoMap
+    @ClassKey(SystemActions::class)
+    fun bindSystemActionsStartable(sysui: SystemActions): CoreStartable
+
+    /** Listen to config changes for SystemActions. */
+    @Binds @IntoSet fun bindSystemActionsConfigChanges(sysui: SystemActions): ConfigurationListener
+}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
index 0bd4859..dde9f48 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
@@ -1303,7 +1303,7 @@
         } else if (id == R.id.close_button) {
             setEditMagnifierSizeMode(false);
         } else {
-            animateBounceEffect();
+            animateBounceEffectIfNeeded();
         }
     }
 
@@ -1465,7 +1465,12 @@
         mBounceEffectDuration = duration;
     }
 
-    private void animateBounceEffect() {
+    private void animateBounceEffectIfNeeded() {
+        if (mMirrorView == null) {
+            // run the animation only if the mirror view is not null
+            return;
+        }
+
         final ObjectAnimator scaleAnimator = ObjectAnimator.ofPropertyValuesHolder(mMirrorView,
                 PropertyValuesHolder.ofFloat(View.SCALE_X, 1, mBounceEffectAnimationScale, 1),
                 PropertyValuesHolder.ofFloat(View.SCALE_Y, 1, mBounceEffectAnimationScale, 1));
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DismissAnimationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationController.java
similarity index 92%
rename from packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DismissAnimationController.java
rename to packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationController.java
index b1de127..568b24d 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DismissAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationController.java
@@ -24,6 +24,7 @@
 import androidx.annotation.NonNull;
 import androidx.dynamicanimation.animation.DynamicAnimation;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.wm.shell.common.bubbles.DismissView;
 import com.android.wm.shell.common.magnetictarget.MagnetizedObject;
 
@@ -31,7 +32,7 @@
  * Controls the interaction between {@link MagnetizedObject} and
  * {@link MagnetizedObject.MagneticTarget}.
  */
-class DismissAnimationController {
+class DragToInteractAnimationController {
     private static final boolean ENABLE_FLING_TO_DISMISS_MENU = false;
     private static final float COMPLETELY_OPAQUE = 1.0f;
     private static final float COMPLETELY_TRANSPARENT = 0.0f;
@@ -45,7 +46,7 @@
     private float mMinDismissSize;
     private float mSizePercent;
 
-    DismissAnimationController(DismissView dismissView, MenuView menuView) {
+    DragToInteractAnimationController(DismissView dismissView, MenuView menuView) {
         mDismissView = dismissView;
         mDismissView.setPivotX(dismissView.getWidth() / 2.0f);
         mDismissView.setPivotY(dismissView.getHeight() / 2.0f);
@@ -116,6 +117,11 @@
         mMagnetizedObject.setMagnetListener(magnetListener);
     }
 
+    @VisibleForTesting
+    MagnetizedObject.MagnetListener getMagnetListener() {
+        return mMagnetizedObject.getMagnetListener();
+    }
+
     void maybeConsumeDownMotionEvent(MotionEvent event) {
         mMagnetizedObject.maybeConsumeMotionEvent(event);
     }
@@ -127,7 +133,7 @@
      * @param event that move the magnetized object which is also the menu list view.
      * @return true if the location of the motion events moves within the magnetic field of a
      * target, but false if didn't set
-     * {@link DismissAnimationController#setMagnetListener(MagnetizedObject.MagnetListener)}.
+     * {@link DragToInteractAnimationController#setMagnetListener(MagnetizedObject.MagnetListener)}.
      */
     boolean maybeConsumeMoveMotionEvent(MotionEvent event) {
         return mMagnetizedObject.maybeConsumeMotionEvent(event);
@@ -140,7 +146,7 @@
      * @param event that move the magnetized object which is also the menu list view.
      * @return true if the location of the motion events moves within the magnetic field of a
      * target, but false if didn't set
-     * {@link DismissAnimationController#setMagnetListener(MagnetizedObject.MagnetListener)}.
+     * {@link DragToInteractAnimationController#setMagnetListener(MagnetizedObject.MagnetListener)}.
      */
     boolean maybeConsumeUpMotionEvent(MotionEvent event) {
         return mMagnetizedObject.maybeConsumeMotionEvent(event);
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 34d7cec..a270558 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationController.java
@@ -73,7 +73,7 @@
     private final ValueAnimator mFadeOutAnimator;
     private final Handler mHandler;
     private boolean mIsFadeEffectEnabled;
-    private DismissAnimationController.DismissCallback mDismissCallback;
+    private DragToInteractAnimationController.DismissCallback mDismissCallback;
     private Runnable mSpringAnimationsEndAction;
 
     // Cache the animations state of {@link DynamicAnimation.TRANSLATION_X} and {@link
@@ -171,7 +171,7 @@
     }
 
     void setDismissCallback(
-            DismissAnimationController.DismissCallback dismissCallback) {
+            DragToInteractAnimationController.DismissCallback dismissCallback) {
         mDismissCallback = dismissCallback;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandler.java
index d01590f..52e7b91 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandler.java
@@ -40,13 +40,13 @@
     private final PointF mMenuTranslationDown = new PointF();
     private boolean mIsDragging = false;
     private float mTouchSlop;
-    private final DismissAnimationController mDismissAnimationController;
+    private final DragToInteractAnimationController mDragToInteractAnimationController;
     private Optional<Runnable> mOnActionDownEnd = Optional.empty();
 
     MenuListViewTouchHandler(MenuAnimationController menuAnimationController,
-            DismissAnimationController dismissAnimationController) {
+            DragToInteractAnimationController dragToInteractAnimationController) {
         mMenuAnimationController = menuAnimationController;
-        mDismissAnimationController = dismissAnimationController;
+        mDragToInteractAnimationController = dragToInteractAnimationController;
     }
 
     @Override
@@ -67,7 +67,7 @@
                 mMenuTranslationDown.set(menuView.getTranslationX(), menuView.getTranslationY());
 
                 mMenuAnimationController.cancelAnimations();
-                mDismissAnimationController.maybeConsumeDownMotionEvent(motionEvent);
+                mDragToInteractAnimationController.maybeConsumeDownMotionEvent(motionEvent);
 
                 mOnActionDownEnd.ifPresent(Runnable::run);
                 break;
@@ -78,9 +78,10 @@
                         mMenuAnimationController.onDraggingStart();
                     }
 
-                    mDismissAnimationController.showDismissView(/* show= */ true);
+                    mDragToInteractAnimationController.showDismissView(/* show= */ true);
 
-                    if (!mDismissAnimationController.maybeConsumeMoveMotionEvent(motionEvent)) {
+                    if (!mDragToInteractAnimationController.maybeConsumeMoveMotionEvent(
+                            motionEvent)) {
                         mMenuAnimationController.moveToPositionX(mMenuTranslationDown.x + dx);
                         mMenuAnimationController.moveToPositionYIfNeeded(
                                 mMenuTranslationDown.y + dy);
@@ -94,17 +95,18 @@
                     mIsDragging = false;
 
                     if (mMenuAnimationController.maybeMoveToEdgeAndHide(endX)) {
-                        mDismissAnimationController.showDismissView(/* show= */ false);
+                        mDragToInteractAnimationController.showDismissView(/* show= */ false);
                         mMenuAnimationController.fadeOutIfEnabled();
 
                         return true;
                     }
 
-                    if (!mDismissAnimationController.maybeConsumeUpMotionEvent(motionEvent)) {
+                    if (!mDragToInteractAnimationController.maybeConsumeUpMotionEvent(
+                            motionEvent)) {
                         mVelocityTracker.computeCurrentVelocity(VELOCITY_UNIT_SECONDS);
                         mMenuAnimationController.flingMenuThenSpringToEdge(endX,
                                 mVelocityTracker.getXVelocity(), mVelocityTracker.getYVelocity());
-                        mDismissAnimationController.showDismissView(/* show= */ false);
+                        mDragToInteractAnimationController.showDismissView(/* show= */ false);
                     }
 
                     // Avoid triggering the listener of the item.
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuNotificationFactory.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuNotificationFactory.java
new file mode 100644
index 0000000..b5eeaa1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuNotificationFactory.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.accessibility.floatingmenu;
+
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.os.UserHandle;
+
+import com.android.systemui.res.R;
+import com.android.systemui.util.NotificationChannels;
+
+class MenuNotificationFactory {
+    public static final String ACTION_UNDO =
+            "com.android.systemui.accessibility.floatingmenu.action.UNDO";
+    public static final String ACTION_DELETE =
+            "com.android.systemui.accessibility.floatingmenu.action.DELETE";
+
+    private final Context mContext;
+
+    MenuNotificationFactory(Context context) {
+        mContext = context;
+    }
+
+    public Notification createHiddenNotification() {
+        final CharSequence title = mContext.getText(
+                R.string.accessibility_floating_button_hidden_notification_title);
+        final CharSequence content = mContext.getText(
+                R.string.accessibility_floating_button_hidden_notification_text);
+
+        return new Notification.Builder(mContext, NotificationChannels.ALERTS)
+                .setContentTitle(title)
+                .setContentText(content)
+                .setSmallIcon(R.drawable.ic_settings_24dp)
+                .setContentIntent(buildUndoIntent())
+                .setDeleteIntent(buildDeleteIntent())
+                .setColor(mContext.getResources().getColor(
+                        com.android.internal.R.color.system_notification_accent_color))
+                .setLocalOnly(true)
+                .setCategory(Notification.CATEGORY_SYSTEM)
+                .build();
+    }
+
+    private PendingIntent buildUndoIntent() {
+        final Intent intent = new Intent(ACTION_UNDO);
+
+        return PendingIntent.getBroadcast(mContext, /* requestCode= */ 0, intent,
+                PendingIntent.FLAG_IMMUTABLE);
+
+    }
+
+    private PendingIntent buildDeleteIntent() {
+        final Intent intent = new Intent(ACTION_DELETE);
+
+        return PendingIntent.getBroadcastAsUser(mContext, /* requestCode= */ 0, intent,
+                PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT
+                        | PendingIntent.FLAG_IMMUTABLE, UserHandle.CURRENT);
+
+    }
+}
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 ff3a9e3..6869bba 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java
@@ -26,16 +26,21 @@
 import static com.android.internal.accessibility.util.AccessibilityUtils.getAccessibilityServiceFragmentType;
 import static com.android.internal.accessibility.util.AccessibilityUtils.setAccessibilityServiceState;
 import static com.android.systemui.accessibility.floatingmenu.MenuMessageView.Index;
+import static com.android.systemui.accessibility.floatingmenu.MenuNotificationFactory.ACTION_DELETE;
+import static com.android.systemui.accessibility.floatingmenu.MenuNotificationFactory.ACTION_UNDO;
 import static com.android.systemui.util.PluralMessageFormaterKt.icuMessageFormat;
 
 import android.accessibilityservice.AccessibilityServiceInfo;
 import android.annotation.IntDef;
 import android.annotation.StringDef;
 import android.annotation.SuppressLint;
+import android.app.NotificationManager;
+import android.content.BroadcastReceiver;
 import android.content.ComponentCallbacks;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.graphics.Rect;
@@ -58,6 +63,7 @@
 
 import com.android.internal.accessibility.dialog.AccessibilityTarget;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.messages.nano.SystemMessageProto;
 import com.android.internal.util.Preconditions;
 import com.android.systemui.Flags;
 import com.android.systemui.res.R;
@@ -91,10 +97,12 @@
     private final MenuViewAppearance mMenuViewAppearance;
     private final MenuAnimationController mMenuAnimationController;
     private final AccessibilityManager mAccessibilityManager;
+    private final NotificationManager mNotificationManager;
+    private final MenuNotificationFactory mNotificationFactory;
     private final Handler mHandler = new Handler(Looper.getMainLooper());
     private final IAccessibilityFloatingMenu mFloatingMenu;
     private final SecureSettings mSecureSettings;
-    private final DismissAnimationController mDismissAnimationController;
+    private final DragToInteractAnimationController mDragToInteractAnimationController;
     private final MenuViewModel mMenuViewModel;
     private final Observer<Boolean> mDockTooltipObserver =
             this::onDockTooltipVisibilityChanged;
@@ -103,7 +111,9 @@
     private final Rect mImeInsetsRect = new Rect();
     private boolean mIsMigrationTooltipShowing;
     private boolean mShouldShowDockTooltip;
+    private boolean mIsNotificationShown;
     private Optional<MenuEduTooltipView> mEduTooltipView = Optional.empty();
+    private BroadcastReceiver mNotificationActionReceiver;
 
     @IntDef({
             LayerIndex.MENU_VIEW,
@@ -184,51 +194,69 @@
         mMenuViewAppearance = new MenuViewAppearance(context, windowManager);
         mMenuView = new MenuView(context, mMenuViewModel, mMenuViewAppearance);
         mMenuAnimationController = mMenuView.getMenuAnimationController();
-        mMenuAnimationController.setDismissCallback(this::hideMenuAndShowMessage);
+        if (Flags.floatingMenuDragToHide()) {
+            mMenuAnimationController.setDismissCallback(this::hideMenuAndShowNotification);
+        } else {
+            mMenuAnimationController.setDismissCallback(this::hideMenuAndShowMessage);
+        }
         mMenuAnimationController.setSpringAnimationsEndAction(this::onSpringAnimationsEndAction);
         mDismissView = new DismissView(context);
         DismissViewUtils.setup(mDismissView);
-        mDismissAnimationController = new DismissAnimationController(mDismissView, mMenuView);
-        mDismissAnimationController.setMagnetListener(new MagnetizedObject.MagnetListener() {
+        mNotificationFactory = new MenuNotificationFactory(context);
+        mNotificationManager = context.getSystemService(NotificationManager.class);
+        mDragToInteractAnimationController = new DragToInteractAnimationController(
+                mDismissView, mMenuView);
+        mDragToInteractAnimationController.setMagnetListener(new MagnetizedObject.MagnetListener() {
             @Override
             public void onStuckToTarget(@NonNull MagnetizedObject.MagneticTarget target) {
-                mDismissAnimationController.animateDismissMenu(/* scaleUp= */ true);
+                mDragToInteractAnimationController.animateDismissMenu(/* scaleUp= */ true);
             }
 
             @Override
             public void onUnstuckFromTarget(@NonNull MagnetizedObject.MagneticTarget target,
                     float velocityX, float velocityY, boolean wasFlungOut) {
-                mDismissAnimationController.animateDismissMenu(/* scaleUp= */ false);
+                mDragToInteractAnimationController.animateDismissMenu(/* scaleUp= */ false);
             }
 
             @Override
             public void onReleasedInTarget(@NonNull MagnetizedObject.MagneticTarget target) {
-                hideMenuAndShowMessage();
+                if (Flags.floatingMenuDragToHide()) {
+                    hideMenuAndShowNotification();
+                } else {
+                    hideMenuAndShowMessage();
+                }
                 mDismissView.hide();
-                mDismissAnimationController.animateDismissMenu(/* scaleUp= */ false);
+                mDragToInteractAnimationController.animateDismissMenu(/* scaleUp= */ false);
             }
         });
 
         mMenuListViewTouchHandler = new MenuListViewTouchHandler(mMenuAnimationController,
-                mDismissAnimationController);
+                mDragToInteractAnimationController);
         mMenuView.addOnItemTouchListenerToList(mMenuListViewTouchHandler);
         mMenuView.setMoveToTuckedListener(this);
 
         mMessageView = new MenuMessageView(context);
 
         mMenuView.setOnTargetFeaturesChangeListener(newTargetFeatures -> {
-            if (newTargetFeatures.size() < 1) {
-                return;
-            }
-
-            // During the undo action period, the pending action will be canceled and undo back
-            // to the previous state if users did any action related to the accessibility features.
-            if (mMessageView.getVisibility() == VISIBLE) {
+            if (Flags.floatingMenuDragToHide()) {
+                dismissNotification();
                 undo();
-            }
+            } else {
+                if (newTargetFeatures.size() < 1) {
+                    return;
+                }
 
-            final TextView messageText = (TextView) mMessageView.getChildAt(Index.TEXT_VIEW);
-            messageText.setText(getMessageText(newTargetFeatures));
+                // During the undo action period, the pending action will be canceled and undo back
+                // to the previous state if users did any action related to the accessibility
+                // features.
+                if (mMessageView.getVisibility() == VISIBLE) {
+                    undo();
+                }
+
+
+                final TextView messageText = (TextView) mMessageView.getChildAt(Index.TEXT_VIEW);
+                messageText.setText(getMessageText(newTargetFeatures));
+            }
         });
 
         addView(mMenuView, LayerIndex.MENU_VIEW);
@@ -243,7 +271,7 @@
     @Override
     public void onConfigurationChanged(@NonNull Configuration newConfig) {
         mDismissView.updateResources();
-        mDismissAnimationController.updateResources();
+        mDragToInteractAnimationController.updateResources();
     }
 
     @Override
@@ -455,6 +483,50 @@
         mMenuAnimationController.startShrinkAnimation(() -> mMenuView.setVisibility(GONE));
     }
 
+    private void hideMenuAndShowNotification() {
+        mMenuAnimationController.startShrinkAnimation(() -> mMenuView.setVisibility(GONE));
+        showNotification();
+    }
+
+    private void showNotification() {
+        registerReceiverIfNeeded();
+        if (!mIsNotificationShown) {
+            mNotificationManager.notify(
+                    SystemMessageProto.SystemMessage.NOTE_A11Y_FLOATING_MENU_HIDDEN,
+                    mNotificationFactory.createHiddenNotification());
+            mIsNotificationShown = true;
+        }
+    }
+
+    private void dismissNotification() {
+        unregisterReceiverIfNeeded();
+        if (mIsNotificationShown) {
+            mNotificationManager.cancel(
+                    SystemMessageProto.SystemMessage.NOTE_A11Y_FLOATING_MENU_HIDDEN);
+            mIsNotificationShown = false;
+        }
+    }
+
+    private void registerReceiverIfNeeded() {
+        if (mNotificationActionReceiver != null) {
+            return;
+        }
+        mNotificationActionReceiver = new MenuNotificationActionReceiver();
+        final IntentFilter intentFilter = new IntentFilter();
+        intentFilter.addAction(ACTION_UNDO);
+        intentFilter.addAction(ACTION_DELETE);
+        getContext().registerReceiver(mNotificationActionReceiver, intentFilter,
+                Context.RECEIVER_EXPORTED);
+    }
+
+    private void unregisterReceiverIfNeeded() {
+        if (mNotificationActionReceiver == null) {
+            return;
+        }
+        getContext().unregisterReceiver(mNotificationActionReceiver);
+        mNotificationActionReceiver = null;
+    }
+
     private void undo() {
         mHandler.removeCallbacksAndMessages(/* token= */ null);
         mMessageView.setVisibility(GONE);
@@ -463,4 +535,23 @@
         mMenuView.setVisibility(VISIBLE);
         mMenuAnimationController.startGrowAnimation();
     }
+
+    @VisibleForTesting
+    DragToInteractAnimationController getDragToInteractAnimationController() {
+        return mDragToInteractAnimationController;
+    }
+
+    private class MenuNotificationActionReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            if (ACTION_UNDO.equals(action)) {
+                dismissNotification();
+                undo();
+            } else if (ACTION_DELETE.equals(action)) {
+                dismissNotification();
+                mDismissMenuAction.run();
+            }
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt b/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt
index f2b55f4..49f34f1 100644
--- a/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt
@@ -18,6 +18,7 @@
 
 package com.android.systemui.authentication.data.repository
 
+import android.annotation.UserIdInt
 import android.app.admin.DevicePolicyManager
 import android.content.IntentFilter
 import android.os.UserHandle
@@ -25,6 +26,11 @@
 import com.android.internal.widget.LockscreenCredential
 import com.android.keyguard.KeyguardSecurityModel
 import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel.None
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel.Password
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel.Pattern
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel.Pin
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel.Sim
 import com.android.systemui.authentication.shared.model.AuthenticationResultModel
 import com.android.systemui.broadcast.BroadcastDispatcher
 import com.android.systemui.dagger.SysUISingleton
@@ -124,6 +130,12 @@
     val isPinEnhancedPrivacyEnabled: StateFlow<Boolean>
 
     /**
+     * Checks the given [LockscreenCredential] to see if it's correct, returning an
+     * [AuthenticationResultModel] representing what happened.
+     */
+    suspend fun checkCredential(credential: LockscreenCredential): AuthenticationResultModel
+
+    /**
      * Returns the currently-configured authentication method. This determines how the
      * authentication challenge needs to be completed in order to unlock an otherwise locked device.
      *
@@ -147,10 +159,26 @@
     suspend fun reportLockoutStarted(durationMs: Int)
 
     /**
-     * Checks the given [LockscreenCredential] to see if it's correct, returning an
-     * [AuthenticationResultModel] representing what happened.
+     * Returns the current maximum number of login attempts that are allowed before the device or
+     * profile is wiped.
+     *
+     * If there is no wipe policy, returns `0`.
+     *
+     * @see [DevicePolicyManager.getMaximumFailedPasswordsForWipe]
      */
-    suspend fun checkCredential(credential: LockscreenCredential): AuthenticationResultModel
+    suspend fun getMaxFailedUnlockAttemptsForWipe(): Int
+
+    /**
+     * Returns the user that will be wiped first when too many failed attempts are made to unlock
+     * the device by the selected user. That user is either the same as the current user ID or
+     * belongs to the same profile group.
+     *
+     * When there is no such policy, returns [UserHandle.USER_NULL]. E.g. managed profile user may
+     * be wiped as a result of failed primary profile password attempts when using unified
+     * challenge. Primary user may be wiped as a result of failed password attempts on the managed
+     * profile of an organization-owned device.
+     */
+    @UserIdInt suspend fun getProfileWithMinFailedUnlockAttemptsForWipe(): Int
 }
 
 @SysUISingleton
@@ -164,6 +192,7 @@
     private val getSecurityMode: Function<Int, KeyguardSecurityModel.SecurityMode>,
     private val userRepository: UserRepository,
     private val lockPatternUtils: LockPatternUtils,
+    private val devicePolicyManager: DevicePolicyManager,
     broadcastDispatcher: BroadcastDispatcher,
     mobileConnectionsRepository: MobileConnectionsRepository,
 ) : AuthenticationRepository {
@@ -200,11 +229,7 @@
                     .onStart { emit(Unit) }
                     .map { selectedUserId }
             }
-            .map { selectedUserId ->
-                withContext(backgroundDispatcher) {
-                    blockingAuthenticationMethodInternal(selectedUserId)
-                }
-            }
+            .map(::getAuthenticationMethod)
             .distinctUntilChanged()
 
     override val minPatternLength: Int = LockPatternUtils.MIN_LOCK_PATTERN_SIZE
@@ -242,12 +267,22 @@
         }
     }
 
-    override suspend fun getAuthenticationMethod(): AuthenticationMethodModel {
+    override suspend fun checkCredential(
+        credential: LockscreenCredential
+    ): AuthenticationResultModel {
         return withContext(backgroundDispatcher) {
-            blockingAuthenticationMethodInternal(selectedUserId)
+            try {
+                val matched = lockPatternUtils.checkCredential(credential, selectedUserId) {}
+                AuthenticationResultModel(isSuccessful = matched, lockoutDurationMs = 0)
+            } catch (ex: LockPatternUtils.RequestThrottledException) {
+                AuthenticationResultModel(isSuccessful = false, lockoutDurationMs = ex.timeoutMs)
+            }
         }
     }
 
+    override suspend fun getAuthenticationMethod(): AuthenticationMethodModel =
+        getAuthenticationMethod(selectedUserId)
+
     override suspend fun getPinLength(): Int {
         return withContext(backgroundDispatcher) { lockPatternUtils.getPinLength(selectedUserId) }
     }
@@ -272,27 +307,26 @@
         _hasLockoutOccurred.value = true
     }
 
-    override suspend fun checkCredential(
-        credential: LockscreenCredential
-    ): AuthenticationResultModel {
-        return withContext(backgroundDispatcher) {
-            try {
-                val matched = lockPatternUtils.checkCredential(credential, selectedUserId) {}
-                AuthenticationResultModel(isSuccessful = matched, lockoutDurationMs = 0)
-            } catch (ex: LockPatternUtils.RequestThrottledException) {
-                AuthenticationResultModel(isSuccessful = false, lockoutDurationMs = ex.timeoutMs)
-            }
-        }
-    }
-
     private suspend fun getFailedAuthenticationAttemptCount(): Int {
         return withContext(backgroundDispatcher) {
             lockPatternUtils.getCurrentFailedPasswordAttempts(selectedUserId)
         }
     }
 
+    override suspend fun getMaxFailedUnlockAttemptsForWipe(): Int {
+        return withContext(backgroundDispatcher) {
+            lockPatternUtils.getMaximumFailedPasswordsForWipe(selectedUserId)
+        }
+    }
+
+    override suspend fun getProfileWithMinFailedUnlockAttemptsForWipe(): Int {
+        return withContext(backgroundDispatcher) {
+            devicePolicyManager.getProfileWithMinimumFailedPasswordsForWipe(selectedUserId)
+        }
+    }
+
     private val selectedUserId: Int
-        get() = userRepository.getSelectedUserInfo().id
+        @UserIdInt get() = userRepository.getSelectedUserInfo().id
 
     /**
      * Returns a [StateFlow] that's automatically kept fresh. The passed-in [getFreshValue] is
@@ -336,24 +370,18 @@
         return flow.asStateFlow()
     }
 
-    /**
-     * Returns the authentication method for the given user ID.
-     *
-     * WARNING: this is actually a blocking IPC/"binder" call that's expensive to do on the main
-     * thread. We keep it not marked as `suspend` because we want to be able to run this without a
-     * `runBlocking` which has a ton of performance/blocking problems.
-     */
-    private fun blockingAuthenticationMethodInternal(
-        userId: Int,
-    ): AuthenticationMethodModel {
-        return when (getSecurityMode.apply(userId)) {
-            KeyguardSecurityModel.SecurityMode.PIN -> AuthenticationMethodModel.Pin
-            KeyguardSecurityModel.SecurityMode.SimPin,
-            KeyguardSecurityModel.SecurityMode.SimPuk -> AuthenticationMethodModel.Sim
-            KeyguardSecurityModel.SecurityMode.Password -> AuthenticationMethodModel.Password
-            KeyguardSecurityModel.SecurityMode.Pattern -> AuthenticationMethodModel.Pattern
-            KeyguardSecurityModel.SecurityMode.None -> AuthenticationMethodModel.None
-            KeyguardSecurityModel.SecurityMode.Invalid -> error("Invalid security mode!")
+    /** Returns the authentication method for the given user ID. */
+    private suspend fun getAuthenticationMethod(@UserIdInt userId: Int): AuthenticationMethodModel {
+        return withContext(backgroundDispatcher) {
+            when (getSecurityMode.apply(userId)) {
+                KeyguardSecurityModel.SecurityMode.PIN -> Pin
+                KeyguardSecurityModel.SecurityMode.SimPin,
+                KeyguardSecurityModel.SecurityMode.SimPuk -> Sim
+                KeyguardSecurityModel.SecurityMode.Password -> Password
+                KeyguardSecurityModel.SecurityMode.Pattern -> Pattern
+                KeyguardSecurityModel.SecurityMode.None -> None
+                KeyguardSecurityModel.SecurityMode.Invalid -> error("Invalid security mode!")
+            }
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt b/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt
index c85ffe6..fdccad1 100644
--- a/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt
@@ -16,15 +16,24 @@
 
 package com.android.systemui.authentication.domain.interactor
 
+import android.os.UserHandle
+import com.android.internal.widget.LockPatternUtils
 import com.android.internal.widget.LockPatternView
 import com.android.internal.widget.LockscreenCredential
 import com.android.systemui.authentication.data.repository.AuthenticationRepository
 import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel.Password
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel.Pattern
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel.Pin
 import com.android.systemui.authentication.shared.model.AuthenticationPatternCoordinate
+import com.android.systemui.authentication.shared.model.AuthenticationWipeModel
+import com.android.systemui.authentication.shared.model.AuthenticationWipeModel.WipeTarget
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
 import com.android.systemui.util.time.SystemClock
 import javax.inject.Inject
+import kotlin.math.max
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableSharedFlow
@@ -49,6 +58,7 @@
 constructor(
     @Application private val applicationScope: CoroutineScope,
     private val repository: AuthenticationRepository,
+    private val selectedUserInteractor: SelectedUserInteractor,
 ) {
     /**
      * The currently-configured authentication method. This determines how the authentication
@@ -140,6 +150,35 @@
         get() = repository.lockoutEndTimestamp
 
     /**
+     * Models an imminent wipe risk to the user, profile, or device upon further unsuccessful
+     * authentication attempts.
+     *
+     * Returns `null` when there is no risk of wipe yet, or when there's no wipe policy set by the
+     * DevicePolicyManager.
+     */
+    val upcomingWipe: Flow<AuthenticationWipeModel?> =
+        repository.failedAuthenticationAttempts.map { failedAttempts ->
+            val failedAttemptsBeforeWipe = repository.getMaxFailedUnlockAttemptsForWipe()
+            if (failedAttemptsBeforeWipe == 0) {
+                return@map null // There is no restriction.
+            }
+
+            // The user has a DevicePolicyManager that requests a user/profile to be wiped after N
+            // attempts. Once the grace period is reached, show a dialog every time as a clear
+            // warning until the deletion fires.
+            val remainingAttemptsBeforeWipe = max(0, failedAttemptsBeforeWipe - failedAttempts)
+            if (remainingAttemptsBeforeWipe >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_WIPE_GRACE) {
+                return@map null // There is no current risk of wiping the device.
+            }
+
+            AuthenticationWipeModel(
+                wipeTarget = getWipeTarget(),
+                failedAttempts = failedAttempts,
+                remainingAttempts = remainingAttemptsBeforeWipe,
+            )
+        }
+
+    /**
      * Returns the currently-configured authentication method. This determines how the
      * authentication challenge needs to be completed in order to unlock an otherwise locked device.
      *
@@ -154,7 +193,8 @@
     suspend fun getAuthenticationMethod() = repository.getAuthenticationMethod()
 
     /**
-     * Attempts to authenticate the user and unlock the device.
+     * Attempts to authenticate the user and unlock the device. May trigger lockout or wipe the
+     * user/profile/device data upon failure.
      *
      * If [tryAutoConfirm] is `true`, authentication is attempted if and only if the auth method
      * supports auto-confirming, and the input's length is at least the required length. Otherwise,
@@ -175,21 +215,7 @@
         }
 
         val authMethod = getAuthenticationMethod()
-        val skipCheck =
-            when {
-                // Lockout is active, the UI layer should not have called this; skip the attempt.
-                repository.lockoutEndTimestamp != null -> true
-                // The input is too short; skip the attempt.
-                input.isTooShort(authMethod) -> true
-                // Auto-confirm attempt when the feature is not enabled; skip the attempt.
-                tryAutoConfirm && !isAutoConfirmEnabled.value -> true
-                // Auto-confirm should skip the attempt if the pin entered is too short.
-                tryAutoConfirm &&
-                    authMethod == AuthenticationMethodModel.Pin &&
-                    input.size < repository.getPinLength() -> true
-                else -> false
-            }
-        if (skipCheck) {
+        if (shouldSkipAuthenticationAttempt(authMethod, tryAutoConfirm, input.size)) {
             return AuthenticationResult.SKIPPED
         }
 
@@ -198,43 +224,85 @@
         val authenticationResult = repository.checkCredential(credential)
         credential.zeroize()
 
-        if (authenticationResult.isSuccessful || !tryAutoConfirm) {
-            repository.reportAuthenticationAttempt(authenticationResult.isSuccessful)
+        if (authenticationResult.isSuccessful) {
+            repository.reportAuthenticationAttempt(isSuccessful = true)
+            _onAuthenticationResult.emit(true)
+            return AuthenticationResult.SUCCEEDED
         }
 
-        // Check if lockout should start and, if so, kick off the countdown:
-        if (!authenticationResult.isSuccessful && authenticationResult.lockoutDurationMs > 0) {
+        // Authentication failed.
+
+        if (tryAutoConfirm) {
+            // Auto-confirm is active, the failed attempt should have no side-effects.
+            return AuthenticationResult.FAILED
+        }
+
+        repository.reportAuthenticationAttempt(isSuccessful = false)
+
+        if (authenticationResult.lockoutDurationMs > 0) {
+            // Lockout has been triggered.
             repository.reportLockoutStarted(authenticationResult.lockoutDurationMs)
         }
 
-        if (authenticationResult.isSuccessful || !tryAutoConfirm) {
-            _onAuthenticationResult.emit(authenticationResult.isSuccessful)
-        }
+        _onAuthenticationResult.emit(false)
+        return AuthenticationResult.FAILED
+    }
 
-        return if (authenticationResult.isSuccessful) {
-            AuthenticationResult.SUCCEEDED
-        } else {
-            AuthenticationResult.FAILED
+    private suspend fun shouldSkipAuthenticationAttempt(
+        authenticationMethod: AuthenticationMethodModel,
+        isAutoConfirmAttempt: Boolean,
+        inputLength: Int,
+    ): Boolean {
+        return when {
+            // Lockout is active, the UI layer should not have called this; skip the attempt.
+            repository.lockoutEndTimestamp != null -> true
+            // Auto-confirm attempt when the feature is not enabled; skip the attempt.
+            isAutoConfirmAttempt && !isAutoConfirmEnabled.value -> true
+            // The pin is too short; skip only if this is an auto-confirm attempt.
+            authenticationMethod == Pin && authenticationMethod.isInputTooShort(inputLength) ->
+                isAutoConfirmAttempt
+            // The input is too short.
+            authenticationMethod.isInputTooShort(inputLength) -> true
+            else -> false
         }
     }
 
-    private fun List<Any>.isTooShort(authMethod: AuthenticationMethodModel): Boolean {
-        return when (authMethod) {
-            AuthenticationMethodModel.Pattern -> size < repository.minPatternLength
-            AuthenticationMethodModel.Password -> size < repository.minPasswordLength
+    private suspend fun AuthenticationMethodModel.isInputTooShort(inputLength: Int): Boolean {
+        return when (this) {
+            Pattern -> inputLength < repository.minPatternLength
+            Password -> inputLength < repository.minPasswordLength
+            Pin -> inputLength < repository.getPinLength()
             else -> false
         }
     }
 
+    /**
+     * @return Whether the current user, managed profile or whole device is next at risk of wipe.
+     */
+    private suspend fun getWipeTarget(): WipeTarget {
+        // Check which profile has the strictest policy for failed authentication attempts.
+        val userToBeWiped = repository.getProfileWithMinFailedUnlockAttemptsForWipe()
+        return when (userToBeWiped) {
+            selectedUserInteractor.getSelectedUserId() ->
+                if (userToBeWiped == UserHandle.USER_SYSTEM) {
+                    WipeTarget.WholeDevice
+                } else {
+                    WipeTarget.User
+                }
+
+            // Shouldn't happen at this stage; this is to maintain legacy behavior.
+            UserHandle.USER_NULL -> WipeTarget.WholeDevice
+            else -> WipeTarget.ManagedProfile
+        }
+    }
+
     private fun AuthenticationMethodModel.createCredential(
         input: List<Any>
     ): LockscreenCredential? {
         return when (this) {
-            is AuthenticationMethodModel.Pin ->
-                LockscreenCredential.createPin(input.joinToString(""))
-            is AuthenticationMethodModel.Password ->
-                LockscreenCredential.createPassword(input.joinToString(""))
-            is AuthenticationMethodModel.Pattern ->
+            is Pin -> LockscreenCredential.createPin(input.joinToString(""))
+            is Password -> LockscreenCredential.createPassword(input.joinToString(""))
+            is Pattern ->
                 LockscreenCredential.createPattern(
                     input
                         .map { it as AuthenticationPatternCoordinate }
diff --git a/packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationMethodModel.kt b/packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationMethodModel.kt
index 3552a19..4e45fcc 100644
--- a/packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationMethodModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationMethodModel.kt
@@ -30,13 +30,13 @@
      * Device doesn't use a secure authentication method. Either there is no lockscreen or the lock
      * screen can be swiped away when displayed.
      */
-    object None : AuthenticationMethodModel(isSecure = false)
+    data object None : AuthenticationMethodModel(isSecure = false)
 
-    object Pin : AuthenticationMethodModel(isSecure = true)
+    data object Pin : AuthenticationMethodModel(isSecure = true)
 
-    object Password : AuthenticationMethodModel(isSecure = true)
+    data object Password : AuthenticationMethodModel(isSecure = true)
 
-    object Pattern : AuthenticationMethodModel(isSecure = true)
+    data object Pattern : AuthenticationMethodModel(isSecure = true)
 
-    object Sim : AuthenticationMethodModel(isSecure = true)
+    data object Sim : AuthenticationMethodModel(isSecure = true)
 }
diff --git a/packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationWipeModel.kt b/packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationWipeModel.kt
new file mode 100644
index 0000000..7a2b83f2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationWipeModel.kt
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.authentication.shared.model
+
+import androidx.annotation.StringRes
+import com.android.systemui.res.R
+
+/**
+ * Some users have a DevicePolicyManager that requests a user/profile to be wiped after N
+ * unsuccessful authentication attempts. This models the pre-wipe state, so that a warning can be
+ * shown.
+ */
+data class AuthenticationWipeModel(
+    /** Indicates what part of the user data will be removed. */
+    val wipeTarget: WipeTarget,
+
+    /** Unsuccessful authentication attempts since the last successful device entry. */
+    val failedAttempts: Int,
+
+    /**
+     * Remaining failed authentication attempts before wipe is triggered. 0 indicates a wipe is
+     * imminent, no more authentication attempts are allowed.
+     */
+    val remainingAttempts: Int,
+) {
+    sealed class WipeTarget(
+        @StringRes val messageIdForAlmostWipe: Int,
+        @StringRes val messageIdForWipe: Int,
+    ) {
+        /** The work profile will be removed, which will delete all profile data. */
+        data object ManagedProfile :
+            WipeTarget(
+                messageIdForAlmostWipe = R.string.kg_failed_attempts_almost_at_erase_profile,
+                messageIdForWipe = R.string.kg_failed_attempts_now_erasing_profile,
+            )
+
+        /** The user will be removed, which will delete all user data. */
+        data object User :
+            WipeTarget(
+                messageIdForAlmostWipe = R.string.kg_failed_attempts_almost_at_erase_user,
+                messageIdForWipe = R.string.kg_failed_attempts_now_erasing_user,
+            )
+
+        /** The device will be reset and all data will be deleted. */
+        data object WholeDevice :
+            WipeTarget(
+                messageIdForAlmostWipe = R.string.kg_failed_attempts_almost_at_wipe,
+                messageIdForWipe = R.string.kg_failed_attempts_now_wiping,
+            )
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/back/domain/interactor/BackActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/back/domain/interactor/BackActionInteractor.kt
index 066cba23..6076f32 100644
--- a/packages/SystemUI/src/com/android/systemui/back/domain/interactor/BackActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/back/domain/interactor/BackActionInteractor.kt
@@ -22,10 +22,9 @@
 import android.window.OnBackInvokedDispatcher
 import android.window.WindowOnBackInvokedDispatcher
 import com.android.systemui.CoreStartable
+import com.android.systemui.Flags.predictiveBackAnimateShade
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor
 import com.android.systemui.shade.QuickSettingsController
@@ -48,14 +47,13 @@
     private val statusBarKeyguardViewManager: StatusBarKeyguardViewManager,
     private val shadeController: ShadeController,
     private val notificationShadeWindowController: NotificationShadeWindowController,
-    private val windowRootViewVisibilityInteractor: WindowRootViewVisibilityInteractor,
-    featureFlags: FeatureFlags,
+    private val windowRootViewVisibilityInteractor: WindowRootViewVisibilityInteractor
 ) : CoreStartable {
 
     private var isCallbackRegistered = false
 
     private val callback =
-        if (featureFlags.isEnabled(Flags.WM_SHADE_ANIMATE_BACK_GESTURE)) {
+        if (predictiveBackAnimateShade()) {
             /**
              * New callback that handles back gesture invoked, cancel, progress and provides
              * feedback via Shade animation.
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index 83d415f..ab23564 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -403,13 +403,6 @@
 
             final BiometricPromptLayout view = (BiometricPromptLayout) layoutInflater.inflate(
                     R.layout.biometric_prompt_layout, null, false);
-            /**
-             * View is only set visible in BiometricViewSizeBinder once PromptSize is determined
-             * that accounts for iconView size, to prevent prompt resizing being visible to the
-             * user.
-             * TODO(b/288175072): May be able to remove this once constraint layout is implemented
-             */
-            view.setVisibility(View.INVISIBLE);
             mBiometricView = BiometricViewBinder.bind(view, viewModel, mPanelController,
                     // TODO(b/201510778): This uses the wrong timeout in some cases
                     getJankListener(view, TRANSIT,
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index 5fba761..26f7646 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -84,6 +84,7 @@
 import com.android.systemui.log.core.LogLevel;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.VibratorHelper;
+import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.util.concurrency.DelayableExecutor;
 import com.android.systemui.util.concurrency.Execution;
 
@@ -105,6 +106,7 @@
 import javax.inject.Provider;
 
 import kotlinx.coroutines.CoroutineScope;
+import kotlinx.coroutines.Job;
 
 /**
  * Receives messages sent from {@link com.android.server.biometrics.BiometricService} and shows the
@@ -114,8 +116,12 @@
  * {@link com.android.keyguard.KeyguardUpdateMonitor}
  */
 @SysUISingleton
-public class AuthController implements CoreStartable, CommandQueue.Callbacks,
-        AuthDialogCallback, DozeReceiver {
+public class AuthController implements
+        CoreStartable,
+        ConfigurationController.ConfigurationListener,
+        CommandQueue.Callbacks,
+        AuthDialogCallback,
+        DozeReceiver {
 
     private static final String TAG = "AuthController";
     private static final boolean DEBUG = true;
@@ -131,6 +137,7 @@
     private final Provider<UdfpsController> mUdfpsControllerFactory;
     private final Provider<SideFpsController> mSidefpsControllerFactory;
     private final CoroutineScope mApplicationCoroutineScope;
+    private Job mBiometricContextListenerJob = null;
 
     // TODO: these should be migrated out once ready
     @NonNull private final Provider<PromptCredentialInteractor> mPromptCredentialInteractor;
@@ -141,10 +148,6 @@
 
     private final Display mDisplay;
     private float mScaleFactor = 1f;
-    // sensor locations without any resolution scaling nor rotation adjustments:
-    @Nullable private final Point mFaceSensorLocationDefault;
-    // cached sensor locations:
-    @Nullable private Point mFaceSensorLocation;
     @Nullable private Point mFingerprintSensorLocation;
     @Nullable private Rect mUdfpsBounds;
     private final Set<Callback> mCallbacks = new HashSet<>();
@@ -615,7 +618,6 @@
         mScaleFactor = mUdfpsUtils.getScaleFactor(mCachedDisplayInfo);
         updateUdfpsLocation();
         updateFingerprintLocation();
-        updateFaceLocation();
     }
     /**
      * @return where the fingerprint sensor exists in pixels in its natural orientation.
@@ -675,31 +677,6 @@
     }
 
     /**
-     * @return where the face sensor exists in pixels in the current device orientation. Returns
-     * null if no face sensor exists.
-     */
-    @Nullable public Point getFaceSensorLocation() {
-        return mFaceSensorLocation;
-    }
-
-    private void updateFaceLocation() {
-        if (mFaceProps == null || mFaceSensorLocationDefault == null) {
-            mFaceSensorLocation = null;
-        } else {
-            mFaceSensorLocation = rotateToCurrentOrientation(
-                    new Point(
-                            (int) (mFaceSensorLocationDefault.x * mScaleFactor),
-                            (int) (mFaceSensorLocationDefault.y * mScaleFactor)),
-                    mCachedDisplayInfo
-            );
-        }
-
-        for (final Callback cb : mCallbacks) {
-            cb.onFaceSensorLocationChanged();
-        }
-    }
-
-    /**
      * @param inOutPoint point on the display in pixels. Going in, represents the point
      *                   in the device's natural orientation. Going out, represents
      *                   the point in the display's current orientation.
@@ -814,17 +791,7 @@
         mWakefulnessLifecycle = wakefulnessLifecycle;
         mPanelInteractionDetector = panelInteractionDetector;
 
-
         mFaceProps = mFaceManager != null ? mFaceManager.getSensorPropertiesInternal() : null;
-        int[] faceAuthLocation = context.getResources().getIntArray(
-                com.android.systemui.res.R.array.config_face_auth_props);
-        if (faceAuthLocation == null || faceAuthLocation.length < 2) {
-            mFaceSensorLocationDefault = null;
-        } else {
-            mFaceSensorLocationDefault = new Point(
-                    faceAuthLocation[0],
-                    faceAuthLocation[1]);
-        }
 
         mDisplay = mContext.getDisplay();
         updateSensorLocations();
@@ -861,7 +828,8 @@
                     mCachedDisplayInfo.getNaturalWidth(),
                     mCachedDisplayInfo.getNaturalHeight(),
                     mScaleFactor,
-                    mCachedDisplayInfo.rotation);
+                    mCachedDisplayInfo.rotation,
+                    udfpsProp.sensorType);
 
             mUdfpsController.updateOverlayParams(udfpsProp, mUdfpsOverlayParams);
             if (!Objects.equals(previousUdfpsBounds, mUdfpsBounds) || !Objects.equals(
@@ -909,7 +877,11 @@
 
     @Override
     public void setBiometricContextListener(IBiometricContextListener listener) {
-        mLogContextInteractor.get().addBiometricContextListener(listener);
+        if (mBiometricContextListenerJob != null) {
+            mBiometricContextListenerJob.cancel(null);
+        }
+        mBiometricContextListenerJob =
+                mLogContextInteractor.get().addBiometricContextListener(listener);
     }
 
     /**
@@ -1155,6 +1127,9 @@
         }
 
         mCurrentDialog.dismissFromSystemServer();
+        for (Callback cb : mCallbacks) {
+            cb.onBiometricPromptDismissed();
+        }
 
         // BiometricService will have already sent the callback to the client in this case.
         // This avoids a round trip to SystemUI. So, just dismiss the dialog and we're done.
@@ -1297,7 +1272,7 @@
     }
 
     @Override
-    public void onConfigurationChanged(Configuration newConfig) {
+    public void onConfigChanged(Configuration newConfig) {
         updateSensorLocations();
 
         // TODO(b/287311775): consider removing this to retain the UI cleanly vs re-creating
@@ -1347,8 +1322,6 @@
         final AuthDialog dialog = mCurrentDialog;
         pw.println("  mCachedDisplayInfo=" + mCachedDisplayInfo);
         pw.println("  mScaleFactor=" + mScaleFactor);
-        pw.println("  faceAuthSensorLocationDefault=" + mFaceSensorLocationDefault);
-        pw.println("  faceAuthSensorLocation=" + getFaceSensorLocation());
         pw.println("  fingerprintSensorLocationInNaturalOrientation="
                 + getFingerprintSensorLocationInNaturalOrientation());
         pw.println("  fingerprintSensorLocation=" + getFingerprintSensorLocation());
@@ -1422,11 +1395,5 @@
          * {@link #onFingerprintLocationChanged}.
          */
         default void onUdfpsLocationChanged(UdfpsOverlayParams udfpsOverlayParams) {}
-
-        /**
-         * Called when the location of the face unlock sensor (typically the front facing camera)
-         * changes. The location in pixels can change due to resolution changes.
-         */
-        default void onFaceSensorLocationChanged() {}
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
index 6345312..86f372a 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
@@ -32,13 +32,14 @@
 import com.android.settingslib.Utils
 import com.android.systemui.CoreStartable
 import com.android.systemui.Flags.lightRevealMigration
-import com.android.systemui.res.R
+import com.android.systemui.biometrics.data.repository.FacePropertyRepository
 import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
 import com.android.systemui.keyguard.WakefulnessLifecycle
 import com.android.systemui.log.core.LogLevel
 import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.res.R
 import com.android.systemui.statusbar.CircleReveal
 import com.android.systemui.statusbar.LiftReveal
 import com.android.systemui.statusbar.LightRevealEffect
@@ -50,6 +51,7 @@
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.statusbar.policy.KeyguardStateController
 import com.android.systemui.util.ViewController
+import kotlinx.coroutines.ExperimentalCoroutinesApi
 import java.io.PrintWriter
 import javax.inject.Inject
 import javax.inject.Provider
@@ -62,6 +64,7 @@
  *
  * The ripple uses the accent color of the current theme.
  */
+@ExperimentalCoroutinesApi
 @SysUISingleton
 class AuthRippleController @Inject constructor(
     private val sysuiContext: Context,
@@ -75,10 +78,10 @@
     private val udfpsControllerProvider: Provider<UdfpsController>,
     private val statusBarStateController: StatusBarStateController,
     private val displayMetrics: DisplayMetrics,
-    private val featureFlags: FeatureFlags,
     private val logger: KeyguardLogger,
     private val biometricUnlockController: BiometricUnlockController,
     private val lightRevealScrim: LightRevealScrim,
+    private val facePropertyRepository: FacePropertyRepository,
     rippleView: AuthRippleView?
 ) :
     ViewController<AuthRippleView>(rippleView),
@@ -262,7 +265,7 @@
 
     fun updateSensorLocation() {
         fingerprintSensorLocation = authController.fingerprintSensorLocation
-        faceSensorLocation = authController.faceSensorLocation
+        faceSensorLocation = facePropertyRepository.sensorLocation.value
     }
 
     private fun updateRippleColor() {
@@ -313,6 +316,18 @@
                 mView.fadeDwellRipple()
             }
         }
+
+        override fun onBiometricDetected(
+                userId: Int,
+                biometricSourceType: BiometricSourceType,
+                isStrongBiometric: Boolean
+        ) {
+            // TODO (b/309804148): add support detect auth ripple for deviceEntryUdfpsRefactor
+            if (!DeviceEntryUdfpsRefactor.isEnabled &&
+                    keyguardUpdateMonitor.getUserCanSkipBouncer(userId)) {
+                showUnlockRipple(biometricSourceType)
+            }
+        }
     }
 
     private val configurationChangedListener =
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/FaceAuthAccessibilityDelegate.kt b/packages/SystemUI/src/com/android/systemui/biometrics/FaceAuthAccessibilityDelegate.kt
index cb75049..3ea1632 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/FaceAuthAccessibilityDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/FaceAuthAccessibilityDelegate.kt
@@ -22,7 +22,7 @@
 import android.view.accessibility.AccessibilityNodeInfo
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
 import com.android.systemui.res.R
 import javax.inject.Inject
 
@@ -35,7 +35,7 @@
 @Inject
 constructor(
     @Main private val resources: Resources,
-    private val faceAuthInteractor: KeyguardFaceAuthInteractor,
+    private val faceAuthInteractor: DeviceEntryFaceAuthInteractor,
 ) : View.AccessibilityDelegate() {
     override fun onInitializeAccessibilityNodeInfo(host: View, info: AccessibilityNodeInfo) {
         super.onInitializeAccessibilityNodeInfo(host, info)
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 2fd13b3..d664637 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -80,11 +80,11 @@
 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor;
 import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor;
 import com.android.systemui.doze.DozeReceiver;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.keyguard.ScreenLifecycle;
-import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor;
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
 import com.android.systemui.log.SessionTracker;
 import com.android.systemui.plugins.FalsingManager;
@@ -149,7 +149,7 @@
     @NonNull private final DumpManager mDumpManager;
     @NonNull private final SystemUIDialogManager mDialogManager;
     @NonNull private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
-    @NonNull private final KeyguardFaceAuthInteractor mKeyguardFaceAuthInteractor;
+    @NonNull private final DeviceEntryFaceAuthInteractor mDeviceEntryFaceAuthInteractor;
     @NonNull private final VibratorHelper mVibrator;
     @NonNull private final FalsingManager mFalsingManager;
     @NonNull private final PowerManager mPowerManager;
@@ -664,7 +664,7 @@
             @NonNull SessionTracker sessionTracker,
             @NonNull AlternateBouncerInteractor alternateBouncerInteractor,
             @NonNull InputManager inputManager,
-            @NonNull KeyguardFaceAuthInteractor keyguardFaceAuthInteractor,
+            @NonNull DeviceEntryFaceAuthInteractor deviceEntryFaceAuthInteractor,
             @NonNull UdfpsKeyguardAccessibilityDelegate udfpsKeyguardAccessibilityDelegate,
             @NonNull SelectedUserInteractor selectedUserInteractor,
             @NonNull FpsUnlockTracker fpsUnlockTracker,
@@ -736,7 +736,7 @@
                     }
                     return Unit.INSTANCE;
                 });
-        mKeyguardFaceAuthInteractor = keyguardFaceAuthInteractor;
+        mDeviceEntryFaceAuthInteractor = deviceEntryFaceAuthInteractor;
 
         final UdfpsOverlayController mUdfpsOverlayController = new UdfpsOverlayController();
         mFingerprintManager.setUdfpsOverlayController(mUdfpsOverlayController);
@@ -1027,7 +1027,7 @@
         if (!mOnFingerDown) {
             playStartHaptic();
 
-            mKeyguardFaceAuthInteractor.onUdfpsSensorTouched();
+            mDeviceEntryFaceAuthInteractor.onUdfpsSensorTouched();
         }
         mOnFingerDown = true;
         mFingerprintManager.onPointerDown(requestId, mSensorProps.sensorId, pointerId, x, y,
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 8ae6f87..307b985 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt
@@ -19,6 +19,7 @@
 import android.content.res.Resources
 import com.android.internal.R
 import com.android.systemui.CoreStartable
+import com.android.systemui.biometrics.AuthController
 import com.android.systemui.biometrics.EllipseOverlapDetectorParams
 import com.android.systemui.biometrics.UdfpsUtils
 import com.android.systemui.biometrics.data.repository.BiometricStatusRepository
@@ -38,18 +39,30 @@
 import com.android.systemui.biometrics.udfps.OverlapDetector
 import com.android.systemui.biometrics.ui.binder.SideFpsOverlayViewBinder
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener
 import com.android.systemui.util.concurrency.ThreadFactory
 import dagger.Binds
 import dagger.Module
 import dagger.Provides
 import dagger.multibindings.ClassKey
 import dagger.multibindings.IntoMap
+import dagger.multibindings.IntoSet
 import java.util.concurrent.Executor
 import javax.inject.Qualifier
 
 /** Dagger module for all things biometric. */
 @Module
 interface BiometricsModule {
+    /** Starts AuthController.  */
+    @Binds
+    @IntoMap
+    @ClassKey(AuthController::class)
+    fun bindAuthControllerStartable(service: AuthController): CoreStartable
+
+    /** Listen to config changes for AuthController. */
+    @Binds
+    @IntoSet
+    fun bindAuthControllerConfigChanges(service: AuthController): ConfigurationListener
 
     @Binds
     @IntoMap
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/DisplayStateRepository.kt b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/DisplayStateRepository.kt
index b0143f5..aaccbc1 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/DisplayStateRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/DisplayStateRepository.kt
@@ -22,6 +22,7 @@
 import android.hardware.display.DisplayManager.DisplayListener
 import android.hardware.display.DisplayManager.EVENT_FLAG_DISPLAY_CHANGED
 import android.os.Handler
+import android.util.Size
 import android.view.DisplayInfo
 import com.android.internal.util.ArrayUtils
 import com.android.systemui.biometrics.shared.model.DisplayRotation
@@ -40,6 +41,7 @@
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.stateIn
 
 /** Repository for the current state of the display */
@@ -58,6 +60,9 @@
 
     /** Provides the current display rotation */
     val currentRotation: StateFlow<DisplayRotation>
+
+    /** Provides the current display size */
+    val currentDisplaySize: StateFlow<Size>
 }
 
 // TODO(b/296211844): This class could directly use DeviceStateRepository and DisplayRepository
@@ -110,17 +115,13 @@
                 initialValue = false,
             )
 
-    private fun getDisplayRotation(): DisplayRotation {
+    private fun getDisplayInfo(): DisplayInfo {
         val cachedDisplayInfo = DisplayInfo()
         context.display?.getDisplayInfo(cachedDisplayInfo)
-        var rotation = cachedDisplayInfo.rotation
-        if (isReverseDefaultRotation) {
-            rotation = (rotation + 1) % 4
-        }
-        return rotation.toDisplayRotation()
+        return cachedDisplayInfo
     }
 
-    override val currentRotation: StateFlow<DisplayRotation> =
+    private val currentDisplayInfo: StateFlow<DisplayInfo> =
         conflatedCallbackFlow {
                 val callback =
                     object : DisplayListener {
@@ -129,11 +130,11 @@
                         override fun onDisplayAdded(displayId: Int) {}
 
                         override fun onDisplayChanged(displayId: Int) {
-                            val rotation = getDisplayRotation()
+                            val displayInfo = getDisplayInfo()
                             trySendWithFailureLogging(
-                                rotation,
+                                displayInfo,
                                 TAG,
-                                "Error sending display rotation to $rotation"
+                                "Error sending displayInfo to $displayInfo"
                             )
                         }
                     }
@@ -148,7 +149,37 @@
             .stateIn(
                 applicationScope,
                 started = SharingStarted.Eagerly,
-                initialValue = getDisplayRotation(),
+                initialValue = getDisplayInfo(),
+            )
+
+    private fun rotationToDisplayRotation(rotation: Int): DisplayRotation {
+        var adjustedRotation = rotation
+        if (isReverseDefaultRotation) {
+            adjustedRotation = (rotation + 1) % 4
+        }
+        return adjustedRotation.toDisplayRotation()
+    }
+
+    override val currentRotation: StateFlow<DisplayRotation> =
+        currentDisplayInfo
+            .map { rotationToDisplayRotation(it.rotation) }
+            .stateIn(
+                applicationScope,
+                started = SharingStarted.WhileSubscribed(),
+                initialValue = rotationToDisplayRotation(currentDisplayInfo.value.rotation)
+            )
+
+    override val currentDisplaySize: StateFlow<Size> =
+        currentDisplayInfo
+            .map { Size(it.naturalWidth, it.naturalHeight) }
+            .stateIn(
+                applicationScope,
+                started = SharingStarted.WhileSubscribed(),
+                initialValue =
+                    Size(
+                        currentDisplayInfo.value.naturalWidth,
+                        currentDisplayInfo.value.naturalHeight
+                    ),
             )
 
     companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/FacePropertyRepository.kt b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/FacePropertyRepository.kt
index 0ae2e16..ae1539e 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/FacePropertyRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/FacePropertyRepository.kt
@@ -17,25 +17,39 @@
 
 package com.android.systemui.biometrics.data.repository
 
+import android.content.Context
+import android.graphics.Point
+import android.hardware.camera2.CameraManager
 import android.hardware.face.FaceManager
 import android.hardware.face.FaceSensorPropertiesInternal
 import android.hardware.face.IFaceAuthenticatorsRegisteredCallback
 import android.util.Log
+import android.util.RotationUtils
+import android.util.Size
+import com.android.systemui.biometrics.shared.model.DisplayRotation
 import com.android.systemui.biometrics.shared.model.LockoutMode
 import com.android.systemui.biometrics.shared.model.SensorStrength
 import com.android.systemui.biometrics.shared.model.toLockoutMode
+import com.android.systemui.biometrics.shared.model.toRotation
 import com.android.systemui.biometrics.shared.model.toSensorStrength
 import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
 import com.android.systemui.common.coroutine.ConflatedCallbackFlow
+import com.android.systemui.common.ui.data.repository.ConfigurationRepository
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.res.R
+import java.util.concurrent.Executor
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.flow.stateIn
 import kotlinx.coroutines.withContext
@@ -47,20 +61,38 @@
 
     /** Get the current lockout mode for the user. This makes a binder based service call. */
     suspend fun getLockoutMode(userId: Int): LockoutMode
+
+    /** The current face sensor location in current device rotation */
+    val sensorLocation: StateFlow<Point?>
 }
 
 /** Describes a biometric sensor */
 data class FaceSensorInfo(val id: Int, val strength: SensorStrength)
 
+/** Data class for camera info */
+private data class CameraInfo(
+    /** The logical id of the camera */
+    val cameraId: String,
+    /** The physical id of the camera */
+    val cameraPhysicalId: String?,
+    /** The center point of the camera in natural orientation */
+    val cameraLocation: Point?,
+)
+
 private const val TAG = "FaceSensorPropertyRepositoryImpl"
 
 @SysUISingleton
 class FacePropertyRepositoryImpl
 @Inject
 constructor(
+    @Application val applicationContext: Context,
+    @Main mainExecutor: Executor,
     @Application private val applicationScope: CoroutineScope,
     @Background private val backgroundDispatcher: CoroutineDispatcher,
     private val faceManager: FaceManager?,
+    private val cameraManager: CameraManager,
+    displayStateRepository: DisplayStateRepository,
+    configurationRepository: ConfigurationRepository,
 ) : FacePropertyRepository {
 
     override val sensorInfo: StateFlow<FaceSensorInfo?> =
@@ -89,10 +121,179 @@
             .onEach { Log.d(TAG, "sensorProps changed: $it") }
             .stateIn(applicationScope, SharingStarted.Eagerly, null)
 
+    private val cameraInfoList: List<CameraInfo> = loadCameraInfoList()
+    private var currentPhysicalCameraId: String? = null
+
+    private val defaultSensorLocation: StateFlow<Point?> =
+        ConflatedCallbackFlow.conflatedCallbackFlow {
+                val callback =
+                    object : CameraManager.AvailabilityCallback() {
+
+                        // This callback will only be called when there is more than one front
+                        // camera on the device (e.g. foldable device with cameras on both outer &
+                        // inner display).
+                        override fun onPhysicalCameraAvailable(
+                            cameraId: String,
+                            physicalCameraId: String
+                        ) {
+                            currentPhysicalCameraId = physicalCameraId
+                            val cameraInfo =
+                                cameraInfoList.firstOrNull {
+                                    physicalCameraId == it.cameraPhysicalId
+                                }
+                            trySendWithFailureLogging(
+                                cameraInfo?.cameraLocation,
+                                TAG,
+                                "Update face sensor location to $cameraInfo."
+                            )
+                        }
+
+                        // This callback will only be called when there is more than one front
+                        // camera on the device (e.g. foldable device with cameras on both outer &
+                        // inner display).
+                        //
+                        // By default, all cameras are available which means there will be no
+                        // onPhysicalCameraAvailable() invoked and depending on the device state
+                        // (Fold or unfold), only the onPhysicalCameraUnavailable() for another
+                        // camera will be invoke. So we need to use this method to decide the
+                        // initial physical ID for foldable devices.
+                        override fun onPhysicalCameraUnavailable(
+                            cameraId: String,
+                            physicalCameraId: String
+                        ) {
+                            if (currentPhysicalCameraId == null) {
+                                val cameraInfo =
+                                    cameraInfoList.firstOrNull {
+                                        physicalCameraId != it.cameraPhysicalId
+                                    }
+                                currentPhysicalCameraId = cameraInfo?.cameraPhysicalId
+                                trySendWithFailureLogging(
+                                    cameraInfo?.cameraLocation,
+                                    TAG,
+                                    "Update face sensor location to $cameraInfo."
+                                )
+                            }
+                        }
+                    }
+                cameraManager.registerAvailabilityCallback(mainExecutor, callback)
+                awaitClose { cameraManager.unregisterAvailabilityCallback(callback) }
+            }
+            .stateIn(
+                applicationScope,
+                started = SharingStarted.WhileSubscribed(),
+                initialValue =
+                    if (cameraInfoList.isNotEmpty()) cameraInfoList[0].cameraLocation else null
+            )
+
+    override val sensorLocation: StateFlow<Point?> =
+        sensorInfo
+            .flatMapLatest { info ->
+                if (info == null) {
+                    flowOf(null)
+                } else {
+                    combine(
+                        defaultSensorLocation,
+                        displayStateRepository.currentRotation,
+                        displayStateRepository.currentDisplaySize,
+                        configurationRepository.scaleForResolution
+                    ) { defaultLocation, displayRotation, displaySize, scaleForResolution ->
+                        computeCurrentFaceLocation(
+                            defaultLocation,
+                            displayRotation,
+                            displaySize,
+                            scaleForResolution
+                        )
+                    }
+                }
+            }
+            .stateIn(
+                applicationScope,
+                started = SharingStarted.WhileSubscribed(),
+                initialValue = null
+            )
+
+    private fun computeCurrentFaceLocation(
+        defaultLocation: Point?,
+        rotation: DisplayRotation,
+        displaySize: Size,
+        scaleForResolution: Float,
+    ): Point? {
+        if (defaultLocation == null) {
+            return null
+        }
+
+        return rotateToCurrentOrientation(
+            Point(
+                (defaultLocation.x * scaleForResolution).toInt(),
+                (defaultLocation.y * scaleForResolution).toInt()
+            ),
+            rotation,
+            displaySize
+        )
+    }
+
+    private fun rotateToCurrentOrientation(
+        inOutPoint: Point,
+        rotation: DisplayRotation,
+        displaySize: Size
+    ): Point {
+        RotationUtils.rotatePoint(
+            inOutPoint,
+            rotation.toRotation(),
+            displaySize.width,
+            displaySize.height
+        )
+        return inOutPoint
+    }
     override suspend fun getLockoutMode(userId: Int): LockoutMode {
         if (sensorInfo.value == null || faceManager == null) {
             return LockoutMode.NONE
         }
         return faceManager.getLockoutModeForUser(sensorInfo.value!!.id, userId).toLockoutMode()
     }
+
+    private fun loadCameraInfoList(): List<CameraInfo> {
+        val list = mutableListOf<CameraInfo>()
+
+        val outer =
+            loadCameraInfo(
+                R.string.config_protectedCameraId,
+                R.string.config_protectedPhysicalCameraId,
+                R.array.config_face_auth_props
+            )
+        if (outer != null) {
+            list.add(outer)
+        }
+
+        val inner =
+            loadCameraInfo(
+                R.string.config_protectedInnerCameraId,
+                R.string.config_protectedInnerPhysicalCameraId,
+                R.array.config_inner_face_auth_props
+            )
+        if (inner != null) {
+            list.add(inner)
+        }
+        return list
+    }
+
+    private fun loadCameraInfo(
+        cameraIdRes: Int,
+        cameraPhysicalIdRes: Int,
+        cameraLocationRes: Int
+    ): CameraInfo? {
+        val cameraId = applicationContext.getString(cameraIdRes)
+        if (cameraId.isNullOrEmpty()) {
+            return null
+        }
+        val physicalCameraId = applicationContext.getString(cameraPhysicalIdRes)
+        val cameraLocation: IntArray = applicationContext.resources.getIntArray(cameraLocationRes)
+        val location: Point?
+        if (cameraLocation.size < 2) {
+            location = null
+        } else {
+            location = Point(cameraLocation[0], cameraLocation[1])
+        }
+        return CameraInfo(cameraId, physicalCameraId, location)
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractor.kt
index f4231ac..348b54e 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractor.kt
@@ -26,6 +26,8 @@
 import com.android.systemui.biometrics.shared.model.FingerprintSensorType
 import com.android.systemui.biometrics.shared.model.isDefaultOrientation
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.log.SideFpsLogger
 import com.android.systemui.res.R
 import java.util.Optional
@@ -47,9 +49,13 @@
     windowManager: WindowManager,
     displayStateInteractor: DisplayStateInteractor,
     fingerprintInteractiveToAuthProvider: Optional<FingerprintInteractiveToAuthProvider>,
+    keyguardTransitionInteractor: KeyguardTransitionInteractor,
     private val logger: SideFpsLogger,
 ) {
 
+    private val isProlongedTouchEnabledForDevice =
+        context.resources.getBoolean(R.bool.config_restToUnlockSupported)
+
     private val sensorLocationForCurrentDisplay =
         combine(
                 displayStateInteractor.displayChanges,
@@ -62,11 +68,24 @@
     val isAvailable: Flow<Boolean> =
         fingerprintPropertyRepository.sensorType.map { it == FingerprintSensorType.POWER_BUTTON }
 
-    val authenticationDuration: Long =
-        context.resources?.getInteger(R.integer.config_restToUnlockDuration)?.toLong() ?: 0L
+    val authenticationDuration: Flow<Long> =
+        keyguardTransitionInteractor
+            .isFinishedInStateWhere { it == KeyguardState.OFF || it == KeyguardState.DOZING }
+            .map {
+                if (it)
+                    context.resources
+                        ?.getInteger(R.integer.config_restToUnlockDurationScreenOff)
+                        ?.toLong()
+                else
+                    context.resources
+                        ?.getInteger(R.integer.config_restToUnlockDurationDefault)
+                        ?.toLong()
+            }
+            .map { it ?: 0L }
+            .onEach { logger.authDurationChanged(it) }
 
     val isProlongedTouchRequiredForAuthentication: Flow<Boolean> =
-        if (fingerprintInteractiveToAuthProvider.isEmpty) {
+        if (fingerprintInteractiveToAuthProvider.isEmpty || !isProlongedTouchEnabledForDevice) {
             flowOf(false)
         } else {
             combine(
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
index a7fb6f7..7b8cb82 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
@@ -77,6 +77,13 @@
         applicationScope: CoroutineScope,
         vibratorHelper: VibratorHelper,
     ): Spaghetti {
+        /**
+         * View is only set visible in BiometricViewSizeBinder once PromptSize is determined that
+         * accounts for iconView size, to prevent prompt resizing being visible to the user.
+         *
+         * TODO(b/288175072): May be able to remove this once constraint layout is implemented
+         */
+        view.visibility = View.INVISIBLE
         val accessibilityManager = view.context.getSystemService(AccessibilityManager::class.java)!!
 
         val textColorError =
@@ -97,18 +104,12 @@
 
         val iconOverlayView = view.requireViewById<LottieAnimationView>(R.id.biometric_icon_overlay)
         val iconView = view.requireViewById<LottieAnimationView>(R.id.biometric_icon)
-        /**
-         * View is only set visible in BiometricViewSizeBinder once PromptSize is determined that
-         * accounts for iconView size, to prevent prompt resizing being visible to the user.
-         *
-         * TODO(b/288175072): May be able to remove this once constraint layout is implemented
-         */
-        iconView.addLottieOnCompositionLoadedListener { viewModel.setIsIconViewLoaded(true) }
+
         PromptIconViewBinder.bind(
             iconView,
             iconOverlayView,
             view.getUpdatedFingerprintAffordanceSize(),
-            viewModel.iconViewModel
+            viewModel
         )
 
         val indicatorMessageView = view.requireViewById<TextView>(R.id.indicator)
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt
index f340bd8..1a7b6c9 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt
@@ -93,7 +93,6 @@
             // TODO(b/251476085): migrate the legacy panel controller and simplify this
             view.repeatWhenAttached {
                 var currentSize: PromptSize? = null
-
                 lifecycleScope.launch {
                     /**
                      * View is only set visible in BiometricViewSizeBinder once PromptSize is
@@ -211,6 +210,7 @@
                                     }
                                 }
                             }
+
                             currentSize = size
                             view.visibility = View.VISIBLE
                             viewModel.setIsIconViewLoaded(false)
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PromptIconViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PromptIconViewBinder.kt
index 475ef18..6e3bcf5 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PromptIconViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PromptIconViewBinder.kt
@@ -26,6 +26,7 @@
 import com.android.settingslib.widget.LottieColorUtils
 import com.android.systemui.biometrics.ui.viewmodel.PromptIconViewModel
 import com.android.systemui.biometrics.ui.viewmodel.PromptIconViewModel.AuthType
+import com.android.systemui.biometrics.ui.viewmodel.PromptViewModel
 import com.android.systemui.lifecycle.repeatWhenAttached
 import com.android.systemui.util.kotlin.Utils.Companion.toQuad
 import com.android.systemui.util.kotlin.Utils.Companion.toQuint
@@ -45,8 +46,9 @@
         iconView: LottieAnimationView,
         iconOverlayView: LottieAnimationView,
         iconViewLayoutParamSizeOverride: Pair<Int, Int>?,
-        viewModel: PromptIconViewModel
+        promptViewModel: PromptViewModel
     ) {
+        val viewModel = promptViewModel.iconViewModel
         iconView.repeatWhenAttached {
             repeatOnLifecycle(Lifecycle.State.STARTED) {
                 viewModel.onConfigurationChanged(iconView.context.resources.configuration)
@@ -71,25 +73,45 @@
                     }
 
                 launch {
+                    var width: Int
+                    var height: Int
                     viewModel.activeAuthType.collect { activeAuthType ->
-                        if (iconViewLayoutParamSizeOverride == null) {
-                            val width: Int
-                            val height: Int
-                            when (activeAuthType) {
-                                AuthType.Fingerprint,
-                                AuthType.Coex -> {
-                                    width = viewModel.fingerprintIconWidth
-                                    height = viewModel.fingerprintIconHeight
-                                }
-                                AuthType.Face -> {
-                                    width = viewModel.faceIconWidth
-                                    height = viewModel.faceIconHeight
+                        when (activeAuthType) {
+                            AuthType.Fingerprint,
+                            AuthType.Coex -> {
+                                width = viewModel.fingerprintIconWidth
+                                height = viewModel.fingerprintIconHeight
+
+                                /**
+                                 * View is only set visible in BiometricViewSizeBinder once
+                                 * PromptSize is determined that accounts for iconView size, to
+                                 * prevent prompt resizing being visible to the user.
+                                 *
+                                 * TODO(b/288175072): May be able to remove this once constraint
+                                 *   layout is implemented
+                                 */
+                                iconView.removeAllLottieOnCompositionLoadedListener()
+                                iconView.addLottieOnCompositionLoadedListener {
+                                    promptViewModel.setIsIconViewLoaded(true)
                                 }
                             }
+                            AuthType.Face -> {
+                                width = viewModel.faceIconWidth
+                                height = viewModel.faceIconHeight
+                                /**
+                                 * Set to true by default since face icon is a drawable, which
+                                 * doesn't have a LottieOnCompositionLoadedListener equivalent.
+                                 *
+                                 * TODO(b/318569643): To be updated once face assets are updated
+                                 *   from drawables
+                                 */
+                                promptViewModel.setIsIconViewLoaded(true)
+                            }
+                        }
 
+                        if (iconViewLayoutParamSizeOverride == null) {
                             iconView.layoutParams.width = width
                             iconView.layoutParams.height = height
-
                             iconOverlayView.layoutParams.width = width
                             iconOverlayView.layoutParams.height = height
                         }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinder.kt
index a8c9446..c36e0e2 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinder.kt
@@ -47,6 +47,7 @@
 import com.android.systemui.lifecycle.repeatWhenAttached
 import com.android.systemui.res.R
 import com.android.systemui.util.kotlin.sample
+import dagger.Lazy
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.combine
@@ -59,49 +60,56 @@
 constructor(
     @Application private val applicationScope: CoroutineScope,
     @Application private val applicationContext: Context,
-    private val biometricStatusInteractor: BiometricStatusInteractor,
-    private val displayStateInteractor: DisplayStateInteractor,
-    private val deviceEntrySideFpsOverlayInteractor: DeviceEntrySideFpsOverlayInteractor,
-    private val fpsUnlockTracker: FpsUnlockTracker,
-    private val layoutInflater: LayoutInflater,
-    private val sideFpsProgressBarViewModel: SideFpsProgressBarViewModel,
-    private val sfpsSensorInteractor: SideFpsSensorInteractor,
-    private val windowManager: WindowManager
+    private val biometricStatusInteractor: Lazy<BiometricStatusInteractor>,
+    private val displayStateInteractor: Lazy<DisplayStateInteractor>,
+    private val deviceEntrySideFpsOverlayInteractor: Lazy<DeviceEntrySideFpsOverlayInteractor>,
+    private val fpsUnlockTracker: Lazy<FpsUnlockTracker>,
+    private val layoutInflater: Lazy<LayoutInflater>,
+    private val sideFpsProgressBarViewModel: Lazy<SideFpsProgressBarViewModel>,
+    private val sfpsSensorInteractor: Lazy<SideFpsSensorInteractor>,
+    private val windowManager: Lazy<WindowManager>
 ) : CoreStartable {
 
     override fun start() {
         if (!SideFpsControllerRefactor.isEnabled) {
             return
         }
+
         applicationScope
             .launch {
-                combine(
-                        biometricStatusInteractor.sfpsAuthenticationReason,
-                        deviceEntrySideFpsOverlayInteractor.showIndicatorForDeviceEntry,
-                        sideFpsProgressBarViewModel.isVisible,
-                        ::Triple
-                    )
-                    .sample(displayStateInteractor.isInRearDisplayMode, ::Pair)
-                    .collect { (combinedFlows, isInRearDisplayMode: Boolean) ->
-                        val (
-                            systemServerAuthReason,
-                            showIndicatorForDeviceEntry,
-                            progressBarIsVisible) =
-                            combinedFlows
-                        if (!isInRearDisplayMode) {
-                            if (progressBarIsVisible) {
-                                hide()
-                            } else if (systemServerAuthReason != NotRunning) {
-                                show()
-                            } else if (showIndicatorForDeviceEntry) {
-                                show()
-                            } else {
-                                hide()
+                sfpsSensorInteractor.get().isAvailable.collect { isSfpsAvailable ->
+                    if (isSfpsAvailable) {
+                        combine(
+                                biometricStatusInteractor.get().sfpsAuthenticationReason,
+                                deviceEntrySideFpsOverlayInteractor
+                                    .get()
+                                    .showIndicatorForDeviceEntry,
+                                sideFpsProgressBarViewModel.get().isVisible,
+                                ::Triple
+                            )
+                            .sample(displayStateInteractor.get().isInRearDisplayMode, ::Pair)
+                            .collect { (combinedFlows, isInRearDisplayMode: Boolean) ->
+                                val (
+                                    systemServerAuthReason,
+                                    showIndicatorForDeviceEntry,
+                                    progressBarIsVisible) =
+                                    combinedFlows
+                                if (!isInRearDisplayMode) {
+                                    if (progressBarIsVisible) {
+                                        hide()
+                                    } else if (systemServerAuthReason != NotRunning) {
+                                        show()
+                                    } else if (showIndicatorForDeviceEntry) {
+                                        show()
+                                    } else {
+                                        hide()
+                                    }
+                                }
                             }
-                        }
                     }
+                }
             }
-            .invokeOnCompletion { fpsUnlockTracker.stopTracking() }
+            .invokeOnCompletion { fpsUnlockTracker.get().stopTracking() }
     }
 
     private var overlayView: View? = null
@@ -113,29 +121,29 @@
             if (it.isAttachedToWindow) {
                 lottie = it.requireViewById<LottieAnimationView>(R.id.sidefps_animation)
                 lottie?.pauseAnimation()
-                windowManager.removeView(it)
+                windowManager.get().removeView(it)
             }
         }
 
-        overlayView = layoutInflater.inflate(R.layout.sidefps_view, null, false)
+        overlayView = layoutInflater.get().inflate(R.layout.sidefps_view, null, false)
         val overlayViewModel =
             SideFpsOverlayViewModel(
                 applicationContext,
-                biometricStatusInteractor,
-                deviceEntrySideFpsOverlayInteractor,
-                displayStateInteractor,
-                sfpsSensorInteractor,
-                sideFpsProgressBarViewModel
+                biometricStatusInteractor.get(),
+                deviceEntrySideFpsOverlayInteractor.get(),
+                displayStateInteractor.get(),
+                sfpsSensorInteractor.get(),
+                sideFpsProgressBarViewModel.get()
             )
-        bind(overlayView!!, overlayViewModel, fpsUnlockTracker, windowManager)
+        bind(overlayView!!, overlayViewModel, fpsUnlockTracker.get(), windowManager.get())
         overlayView!!.visibility = View.INVISIBLE
-        windowManager.addView(overlayView, overlayViewModel.defaultOverlayViewParams)
+        windowManager.get().addView(overlayView, overlayViewModel.defaultOverlayViewParams)
     }
 
     /** Hide the side fingerprint sensor indicator */
     private fun hide() {
         if (overlayView != null) {
-            windowManager.removeView(overlayView)
+            windowManager.get().removeView(overlayView)
             overlayView = null
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
index 1095abe..c8ce245 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
@@ -20,12 +20,16 @@
 import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
 import com.android.systemui.authentication.domain.interactor.AuthenticationResult
 import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel.Password
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel.Pattern
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel.Pin
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel.Sim
 import com.android.systemui.bouncer.data.repository.BouncerRepository
 import com.android.systemui.classifier.FalsingClassifier
 import com.android.systemui.classifier.domain.interactor.FalsingInteractor
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
 import com.android.systemui.power.domain.interactor.PowerInteractor
 import com.android.systemui.res.R
 import javax.inject.Inject
@@ -48,7 +52,7 @@
     @Application private val applicationContext: Context,
     private val repository: BouncerRepository,
     private val authenticationInteractor: AuthenticationInteractor,
-    private val keyguardFaceAuthInteractor: KeyguardFaceAuthInteractor,
+    private val deviceEntryFaceAuthInteractor: DeviceEntryFaceAuthInteractor,
     private val falsingInteractor: FalsingInteractor,
     private val powerInteractor: PowerInteractor,
     private val simBouncerInteractor: SimBouncerInteractor,
@@ -96,7 +100,7 @@
      * user's pocket or by the user's face while holding their device up to their ear.
      */
     fun onIntentionalUserInput() {
-        keyguardFaceAuthInteractor.onPrimaryBouncerUserInput()
+        deviceEntryFaceAuthInteractor.onPrimaryBouncerUserInput()
         powerInteractor.onUserTouch()
         falsingInteractor.updateFalseConfidence(FalsingClassifier.Result.passed(0.6))
     }
@@ -158,7 +162,7 @@
             return AuthenticationResult.SKIPPED
         }
 
-        if (authenticationInteractor.getAuthenticationMethod() == AuthenticationMethodModel.Sim) {
+        if (authenticationInteractor.getAuthenticationMethod() == Sim) {
             // SIM is authenticated in SimBouncerInteractor.
             return AuthenticationResult.SKIPPED
         }
@@ -178,21 +182,21 @@
             authResult == AuthenticationResult.FAILED ||
                 (authResult == AuthenticationResult.SKIPPED && !tryAutoConfirm)
         ) {
-            showErrorMessage()
+            showWrongInputMessage()
         }
         return authResult
     }
 
     /**
-     * Shows the error message.
+     * Shows the a message notifying the user that their credentials input is wrong.
      *
      * Callers should use this instead of [authenticate] when they know ahead of time that an auth
      * attempt will fail but aren't interested in the other side effects like triggering lockout.
      * For example, if the user entered a pattern that's too short, the system can show the error
      * message without having the attempt trigger lockout.
      */
-    private suspend fun showErrorMessage() {
-        repository.setMessage(errorMessage(authenticationInteractor.getAuthenticationMethod()))
+    private suspend fun showWrongInputMessage() {
+        repository.setMessage(wrongInputMessage(authenticationInteractor.getAuthenticationMethod()))
     }
 
     /** Notifies that the input method editor (software keyboard) has been hidden by the user. */
@@ -202,24 +206,19 @@
 
     private fun promptMessage(authMethod: AuthenticationMethodModel): String {
         return when (authMethod) {
-            is AuthenticationMethodModel.Sim -> simBouncerInteractor.getDefaultMessage()
-            is AuthenticationMethodModel.Pin ->
-                applicationContext.getString(R.string.keyguard_enter_your_pin)
-            is AuthenticationMethodModel.Password ->
-                applicationContext.getString(R.string.keyguard_enter_your_password)
-            is AuthenticationMethodModel.Pattern ->
-                applicationContext.getString(R.string.keyguard_enter_your_pattern)
+            is Sim -> simBouncerInteractor.getDefaultMessage()
+            is Pin -> applicationContext.getString(R.string.keyguard_enter_your_pin)
+            is Password -> applicationContext.getString(R.string.keyguard_enter_your_password)
+            is Pattern -> applicationContext.getString(R.string.keyguard_enter_your_pattern)
             else -> ""
         }
     }
 
-    private fun errorMessage(authMethod: AuthenticationMethodModel): String {
+    private fun wrongInputMessage(authMethod: AuthenticationMethodModel): String {
         return when (authMethod) {
-            is AuthenticationMethodModel.Pin -> applicationContext.getString(R.string.kg_wrong_pin)
-            is AuthenticationMethodModel.Password ->
-                applicationContext.getString(R.string.kg_wrong_password)
-            is AuthenticationMethodModel.Pattern ->
-                applicationContext.getString(R.string.kg_wrong_pattern)
+            is Pin -> applicationContext.getString(R.string.kg_wrong_pin)
+            is Password -> applicationContext.getString(R.string.kg_wrong_password)
+            is Pattern -> applicationContext.getString(R.string.kg_wrong_pattern)
             else -> ""
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractor.kt
index f612f9a..ef4554c 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractor.kt
@@ -22,6 +22,7 @@
 import com.android.keyguard.KeyguardSecurityModel.SecurityMode
 import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.keyguard.KeyguardUpdateMonitorCallback
+import com.android.systemui.Flags
 import com.android.systemui.biometrics.data.repository.FacePropertyRepository
 import com.android.systemui.biometrics.shared.model.SensorStrength
 import com.android.systemui.bouncer.data.repository.BouncerMessageRepository
@@ -29,11 +30,9 @@
 import com.android.systemui.bouncer.shared.model.Message
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags.REVAMPED_BOUNCER_MESSAGES
+import com.android.systemui.deviceentry.data.repository.DeviceEntryFaceAuthRepository
 import com.android.systemui.flags.SystemPropertiesHelper
 import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
-import com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepository
 import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository
 import com.android.systemui.keyguard.data.repository.TrustRepository
 import com.android.systemui.res.R.string.bouncer_face_not_recognized
@@ -100,7 +99,6 @@
     private val repository: BouncerMessageRepository,
     private val userRepository: UserRepository,
     private val countDownTimerUtil: CountDownTimerUtil,
-    private val featureFlags: FeatureFlags,
     private val updateMonitor: KeyguardUpdateMonitor,
     trustRepository: TrustRepository,
     biometricSettingsRepository: BiometricSettingsRepository,
@@ -229,7 +227,7 @@
             }
 
     fun onPrimaryAuthLockedOut(secondsBeforeLockoutReset: Long) {
-        if (!featureFlags.isEnabled(REVAMPED_BOUNCER_MESSAGES)) return
+        if (!Flags.revampedBouncerMessages()) return
 
         val callback =
             object : CountDownTimerCallback {
@@ -250,7 +248,7 @@
     }
 
     fun onPrimaryAuthIncorrectAttempt() {
-        if (!featureFlags.isEnabled(REVAMPED_BOUNCER_MESSAGES)) return
+        if (!Flags.revampedBouncerMessages()) return
 
         repository.setMessage(
             incorrectSecurityInput(currentSecurityMode, isFingerprintAuthCurrentlyAllowed.value)
@@ -258,21 +256,21 @@
     }
 
     fun setFingerprintAcquisitionMessage(value: String?) {
-        if (!featureFlags.isEnabled(REVAMPED_BOUNCER_MESSAGES)) return
+        if (!Flags.revampedBouncerMessages()) return
         repository.setMessage(
             defaultMessage(currentSecurityMode, value, isFingerprintAuthCurrentlyAllowed.value)
         )
     }
 
     fun setFaceAcquisitionMessage(value: String?) {
-        if (!featureFlags.isEnabled(REVAMPED_BOUNCER_MESSAGES)) return
+        if (!Flags.revampedBouncerMessages()) return
         repository.setMessage(
             defaultMessage(currentSecurityMode, value, isFingerprintAuthCurrentlyAllowed.value)
         )
     }
 
     fun setCustomMessage(value: String?) {
-        if (!featureFlags.isEnabled(REVAMPED_BOUNCER_MESSAGES)) return
+        if (!Flags.revampedBouncerMessages()) return
 
         repository.setMessage(
             defaultMessage(currentSecurityMode, value, isFingerprintAuthCurrentlyAllowed.value)
@@ -283,7 +281,7 @@
         get() = defaultMessage(currentSecurityMode, isFingerprintAuthCurrentlyAllowed.value)
 
     fun onPrimaryBouncerUserInput() {
-        if (!featureFlags.isEnabled(REVAMPED_BOUNCER_MESSAGES)) return
+        if (!Flags.revampedBouncerMessages()) return
         repository.setMessage(defaultMessage)
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt
index 621ca5d..654fa22 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt
@@ -38,9 +38,9 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
 import com.android.systemui.keyguard.DismissCallbackRegistry
 import com.android.systemui.keyguard.data.repository.TrustRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.res.R
 import com.android.systemui.shared.system.SysUiStatsLog
@@ -77,7 +77,7 @@
     private val trustRepository: TrustRepository,
     @Application private val applicationScope: CoroutineScope,
     private val selectedUserInteractor: SelectedUserInteractor,
-    private val keyguardFaceAuthInteractor: KeyguardFaceAuthInteractor,
+    private val deviceEntryFaceAuthInteractor: DeviceEntryFaceAuthInteractor,
 ) {
     private val passiveAuthBouncerDelay =
         context.resources.getInteger(R.integer.primary_bouncer_passive_auth_delay).toLong()
@@ -429,7 +429,7 @@
                 keyguardUpdateMonitor.canTriggerActiveUnlockBasedOnDeviceState()
 
         return !needsFullscreenBouncer() &&
-            (keyguardFaceAuthInteractor.canFaceAuthRun() || canRunActiveUnlock)
+            (deviceEntryFaceAuthInteractor.canFaceAuthRun() || canRunActiveUnlock)
     }
 
     companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/BouncerMessageViewBinder.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/BouncerMessageViewBinder.kt
index 5a59012..b8e6ad6 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/BouncerMessageViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/BouncerMessageViewBinder.kt
@@ -23,11 +23,10 @@
 import com.android.keyguard.BouncerKeyguardMessageArea
 import com.android.keyguard.KeyguardMessageArea
 import com.android.keyguard.KeyguardMessageAreaController
+import com.android.systemui.Flags
 import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor
 import com.android.systemui.bouncer.shared.model.Message
 import com.android.systemui.bouncer.ui.BouncerMessageView
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
 import com.android.systemui.lifecycle.repeatWhenAttached
 import com.android.systemui.log.BouncerLogger
 import kotlinx.coroutines.launch
@@ -39,10 +38,9 @@
         interactor: BouncerMessageInteractor,
         factory: KeyguardMessageAreaController.Factory,
         bouncerLogger: BouncerLogger,
-        featureFlags: FeatureFlags
     ) {
         view.repeatWhenAttached {
-            if (!featureFlags.isEnabled(Flags.REVAMPED_BOUNCER_MESSAGES)) {
+            if (!Flags.revampedBouncerMessages()) {
                 view.primaryMessageView?.disable()
                 view.secondaryMessageView?.disable()
                 return@repeatWhenAttached
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/KeyguardBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/KeyguardBouncerViewBinder.kt
index 5dcd661..cc387e9 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/KeyguardBouncerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/KeyguardBouncerViewBinder.kt
@@ -32,7 +32,6 @@
 import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants.EXPANSION_VISIBLE
 import com.android.systemui.bouncer.ui.BouncerViewDelegate
 import com.android.systemui.bouncer.ui.viewmodel.KeyguardBouncerViewModel
-import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel
 import com.android.systemui.lifecycle.repeatWhenAttached
 import com.android.systemui.log.BouncerLogger
@@ -53,7 +52,6 @@
         messageAreaControllerFactory: KeyguardMessageAreaController.Factory,
         bouncerMessageInteractor: BouncerMessageInteractor,
         bouncerLogger: BouncerLogger,
-        featureFlags: FeatureFlags,
         selectedUserInteractor: SelectedUserInteractor,
     ) {
         // Builds the KeyguardSecurityContainerController from bouncer view group.
@@ -141,8 +139,7 @@
                                     it.bindMessageView(
                                         bouncerMessageInteractor,
                                         messageAreaControllerFactory,
-                                        bouncerLogger,
-                                        featureFlags
+                                        bouncerLogger
                                     )
                                 }
                             } else {
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt
index be6cf85..4d686a1 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt
@@ -16,12 +16,15 @@
 
 package com.android.systemui.bouncer.ui.viewmodel
 
+import android.app.admin.DevicePolicyManager
+import android.app.admin.DevicePolicyResources
 import android.content.Context
 import android.graphics.Bitmap
 import androidx.core.graphics.drawable.toBitmap
 import com.android.internal.R
 import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
 import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
+import com.android.systemui.authentication.shared.model.AuthenticationWipeModel
 import com.android.systemui.bouncer.domain.interactor.BouncerActionButtonInteractor
 import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
 import com.android.systemui.bouncer.domain.interactor.SimBouncerInteractor
@@ -51,7 +54,6 @@
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.asStateFlow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.stateIn
@@ -64,14 +66,15 @@
     @Application private val applicationScope: CoroutineScope,
     @Main private val mainDispatcher: CoroutineDispatcher,
     private val bouncerInteractor: BouncerInteractor,
+    private val simBouncerInteractor: SimBouncerInteractor,
     private val authenticationInteractor: AuthenticationInteractor,
     flags: SceneContainerFlags,
     selectedUser: Flow<UserViewModel>,
     users: Flow<List<UserViewModel>>,
     userSwitcherMenu: Flow<List<UserActionViewModel>>,
-    actionButtonInteractor: BouncerActionButtonInteractor,
-    private val simBouncerInteractor: SimBouncerInteractor,
+    actionButton: Flow<BouncerActionButtonModel?>,
     private val clock: SystemClock,
+    private val devicePolicyManager: DevicePolicyManager,
 ) {
     val selectedUserImage: StateFlow<Bitmap?> =
         selectedUser
@@ -113,7 +116,6 @@
 
     // Handle to the scope of the child ViewModel (stored in [authMethod]).
     private var childViewModelScope: CoroutineScope? = null
-    private val _dialogMessage = MutableStateFlow<String?>(null)
 
     /** View-model for the current UI, based on the current authentication method. */
     val authMethodViewModel: StateFlow<AuthMethodBouncerViewModel?> =
@@ -129,12 +131,32 @@
      * A message for a dialog to show when the user has attempted the wrong credential too many
      * times and now must wait a while before attempting again.
      *
-     * If `null`, no dialog should be shown.
-     *
-     * Once the dialog is shown, the UI should call [onDialogDismissed] when the user dismisses this
-     * dialog.
+     * If `null`, the lockout dialog should not be shown.
      */
-    val dialogMessage: StateFlow<String?> = _dialogMessage.asStateFlow()
+    private val lockoutDialogMessage = MutableStateFlow<String?>(null)
+
+    /**
+     * A message for a dialog to show when the user has attempted the wrong credential too many
+     * times and their user/profile/device data is at risk of being wiped due to a Device Manager
+     * policy.
+     *
+     * If `null`, the wipe dialog should not be shown.
+     */
+    private val wipeDialogMessage = MutableStateFlow<String?>(null)
+
+    /**
+     * Models the dialog to be shown to the user, or `null` if no dialog should be shown.
+     *
+     * Once the dialog is shown, the UI should call [DialogViewModel.onDismiss] when the user
+     * dismisses this dialog.
+     */
+    val dialogViewModel: StateFlow<DialogViewModel?> =
+        combine(wipeDialogMessage, lockoutDialogMessage) { _, _ -> createDialogViewModel() }
+            .stateIn(
+                scope = applicationScope,
+                started = SharingStarted.WhileSubscribed(),
+                initialValue = createDialogViewModel(),
+            )
 
     /**
      * A message shown when the user has attempted the wrong credential too many times and now must
@@ -159,7 +181,7 @@
      * be shown.
      */
     val actionButton: StateFlow<BouncerActionButtonModel?> =
-        actionButtonInteractor.actionButton.stateIn(
+        actionButton.stateIn(
             scope = applicationScope,
             started = SharingStarted.WhileSubscribed(),
             initialValue = null
@@ -208,6 +230,7 @@
 
     init {
         if (flags.isEnabled()) {
+            // Keeps the lockout dialog up-to-date.
             applicationScope.launch {
                 bouncerInteractor.onLockoutStarted.collect {
                     showLockoutDialog()
@@ -219,18 +242,20 @@
                 // Update the lockout countdown whenever the selected user is switched.
                 selectedUser.collect { startLockoutCountdown() }
             }
-        }
-    }
 
-    /** Notifies that the dialog has been dismissed by the user. */
-    fun onDialogDismissed() {
-        _dialogMessage.value = null
+            // Keeps the upcoming wipe dialog up-to-date.
+            applicationScope.launch {
+                authenticationInteractor.upcomingWipe.collect { wipeModel ->
+                    wipeDialogMessage.value = wipeModel?.message
+                }
+            }
+        }
     }
 
     private fun showLockoutDialog() {
         applicationScope.launch {
             val failedAttempts = authenticationInteractor.failedAuthenticationAttempts.value
-            _dialogMessage.value =
+            lockoutDialogMessage.value =
                 authMethodViewModel.value?.lockoutMessageId?.let { messageId ->
                     applicationContext.getString(
                         messageId,
@@ -341,6 +366,71 @@
         )
     }
 
+    /**
+     * @return A message warning the user that the user/profile/device will be wiped upon a further
+     *   [AuthenticationWipeModel.remainingAttempts] unsuccessful authentication attempts.
+     */
+    private fun AuthenticationWipeModel.getAlmostAtWipeMessage(): String {
+        val message =
+            applicationContext.getString(
+                wipeTarget.messageIdForAlmostWipe,
+                failedAttempts,
+                remainingAttempts,
+            )
+        return if (wipeTarget == AuthenticationWipeModel.WipeTarget.ManagedProfile) {
+            devicePolicyManager.resources.getString(
+                DevicePolicyResources.Strings.SystemUi
+                    .KEYGUARD_DIALOG_FAILED_ATTEMPTS_ALMOST_ERASING_PROFILE,
+                { message },
+                failedAttempts,
+                remainingAttempts,
+            )
+                ?: message
+        } else {
+            message
+        }
+    }
+
+    /**
+     * @return A message informing the user that their user/profile/device will be wiped promptly.
+     */
+    private fun AuthenticationWipeModel.getWipeMessage(): String {
+        val message = applicationContext.getString(wipeTarget.messageIdForWipe, failedAttempts)
+        return if (wipeTarget == AuthenticationWipeModel.WipeTarget.ManagedProfile) {
+            devicePolicyManager.resources.getString(
+                DevicePolicyResources.Strings.SystemUi
+                    .KEYGUARD_DIALOG_FAILED_ATTEMPTS_ERASING_PROFILE,
+                { message },
+                failedAttempts,
+            )
+                ?: message
+        } else {
+            message
+        }
+    }
+
+    private val AuthenticationWipeModel.message: String
+        get() = if (remainingAttempts > 0) getAlmostAtWipeMessage() else getWipeMessage()
+
+    private fun createDialogViewModel(): DialogViewModel? {
+        val wipeText = wipeDialogMessage.value
+        val lockoutText = lockoutDialogMessage.value
+        return when {
+            // The wipe dialog takes priority over the lockout dialog.
+            wipeText != null ->
+                DialogViewModel(
+                    text = wipeText,
+                    onDismiss = { wipeDialogMessage.value = null },
+                )
+            lockoutText != null ->
+                DialogViewModel(
+                    text = lockoutText,
+                    onDismiss = { lockoutDialogMessage.value = null },
+                )
+            else -> null // No dialog to show.
+        }
+    }
+
     data class MessageViewModel(
         val text: String,
 
@@ -353,6 +443,13 @@
         val isUpdateAnimated: Boolean,
     )
 
+    data class DialogViewModel(
+        val text: String,
+
+        /** Callback to run after the dialog has been dismissed by the user. */
+        val onDismiss: () -> Unit,
+    )
+
     data class UserSwitcherDropdownItemViewModel(
         val icon: Icon,
         val text: Text,
@@ -370,26 +467,28 @@
         @Application applicationScope: CoroutineScope,
         @Main mainDispatcher: CoroutineDispatcher,
         bouncerInteractor: BouncerInteractor,
+        simBouncerInteractor: SimBouncerInteractor,
         authenticationInteractor: AuthenticationInteractor,
         flags: SceneContainerFlags,
         userSwitcherViewModel: UserSwitcherViewModel,
         actionButtonInteractor: BouncerActionButtonInteractor,
-        simBouncerInteractor: SimBouncerInteractor,
         clock: SystemClock,
+        devicePolicyManager: DevicePolicyManager,
     ): BouncerViewModel {
         return BouncerViewModel(
             applicationContext = applicationContext,
             applicationScope = applicationScope,
             mainDispatcher = mainDispatcher,
             bouncerInteractor = bouncerInteractor,
+            simBouncerInteractor = simBouncerInteractor,
             authenticationInteractor = authenticationInteractor,
             flags = flags,
             selectedUser = userSwitcherViewModel.selectedUser,
             users = userSwitcherViewModel.users,
             userSwitcherMenu = userSwitcherViewModel.menu,
-            actionButtonInteractor = actionButtonInteractor,
-            simBouncerInteractor = simBouncerInteractor,
+            actionButton = actionButtonInteractor.actionButton,
             clock = clock,
+            devicePolicyManager = devicePolicyManager,
         )
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/common/data/CommonDataLayerModule.kt b/packages/SystemUI/src/com/android/systemui/common/data/CommonDataLayerModule.kt
index 27c9b3f..d1c728c 100644
--- a/packages/SystemUI/src/com/android/systemui/common/data/CommonDataLayerModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/data/CommonDataLayerModule.kt
@@ -16,6 +16,8 @@
 
 package com.android.systemui.common.data
 
+import com.android.systemui.common.data.repository.PackageChangeRepository
+import com.android.systemui.common.data.repository.PackageChangeRepositoryImpl
 import com.android.systemui.common.ui.data.repository.ConfigurationRepository
 import com.android.systemui.common.ui.data.repository.ConfigurationRepositoryImpl
 import dagger.Binds
@@ -23,5 +25,13 @@
 
 @Module
 abstract class CommonDataLayerModule {
-    @Binds abstract fun bindRepository(impl: ConfigurationRepositoryImpl): ConfigurationRepository
+    @Binds
+    abstract fun bindConfigurationRepository(
+        impl: ConfigurationRepositoryImpl
+    ): ConfigurationRepository
+
+    @Binds
+    abstract fun bindPackageChangeRepository(
+        impl: PackageChangeRepositoryImpl
+    ): PackageChangeRepository
 }
diff --git a/packages/SystemUI/src/com/android/systemui/common/data/repository/PackageChangeRepository.kt b/packages/SystemUI/src/com/android/systemui/common/data/repository/PackageChangeRepository.kt
new file mode 100644
index 0000000..7c7b3db
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/common/data/repository/PackageChangeRepository.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.common.data.repository
+
+import android.os.UserHandle
+import com.android.systemui.common.data.shared.model.PackageChangeModel
+import kotlinx.coroutines.flow.Flow
+
+interface PackageChangeRepository {
+    /**
+     * Emits values when packages for the specified user are changed. See supported modifications in
+     * [PackageChangeModel]
+     *
+     * [UserHandle.USER_ALL] may be used to listen to all users.
+     */
+    fun packageChanged(user: UserHandle): Flow<PackageChangeModel>
+}
diff --git a/packages/SystemUI/src/com/android/systemui/common/data/repository/PackageChangeRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/common/data/repository/PackageChangeRepositoryImpl.kt
new file mode 100644
index 0000000..b1b348c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/common/data/repository/PackageChangeRepositoryImpl.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.common.data.repository
+
+import android.os.UserHandle
+import com.android.systemui.common.data.shared.model.PackageChangeModel
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.filter
+
+@SysUISingleton
+class PackageChangeRepositoryImpl
+@Inject
+constructor(
+    private val monitorFactory: PackageUpdateMonitor.Factory,
+) : PackageChangeRepository {
+    /**
+     * A [PackageUpdateMonitor] which monitors package updates for all users. The per-user filtering
+     * is done by [packageChanged].
+     */
+    private val monitor by lazy { monitorFactory.create(UserHandle.ALL) }
+
+    override fun packageChanged(user: UserHandle): Flow<PackageChangeModel> =
+        monitor.packageChanged.filter {
+            user == UserHandle.ALL || user == UserHandle.getUserHandleForUid(it.packageUid)
+        }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/common/data/repository/PackageUpdateLogger.kt b/packages/SystemUI/src/com/android/systemui/common/data/repository/PackageUpdateLogger.kt
new file mode 100644
index 0000000..adbb37c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/common/data/repository/PackageUpdateLogger.kt
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.common.data.repository
+
+import android.os.UserHandle
+import com.android.systemui.common.data.shared.model.PackageChangeModel
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.core.LogLevel
+import com.android.systemui.log.dagger.PackageChangeRepoLog
+import javax.inject.Inject
+
+private fun getChangeString(model: PackageChangeModel) =
+    when (model) {
+        is PackageChangeModel.Installed -> "installed"
+        is PackageChangeModel.Uninstalled -> "uninstalled"
+        is PackageChangeModel.UpdateStarted -> "started updating"
+        is PackageChangeModel.UpdateFinished -> "finished updating"
+        is PackageChangeModel.Changed -> "changed"
+    }
+
+/** A debug logger for [PackageChangeRepository]. */
+@SysUISingleton
+class PackageUpdateLogger @Inject constructor(@PackageChangeRepoLog private val buffer: LogBuffer) {
+
+    fun logChange(model: PackageChangeModel) {
+        buffer.log(
+            TAG,
+            LogLevel.DEBUG,
+            {
+                str1 = model.packageName
+                str2 = getChangeString(model)
+                int1 = model.packageUid
+            },
+            {
+                val user = UserHandle.getUserHandleForUid(int1)
+                "Package $str1 ($int1) $str2 on user $user"
+            }
+        )
+    }
+}
+
+private const val TAG = "PackageChangeRepoLog"
diff --git a/packages/SystemUI/src/com/android/systemui/common/data/repository/PackageUpdateMonitor.kt b/packages/SystemUI/src/com/android/systemui/common/data/repository/PackageUpdateMonitor.kt
new file mode 100644
index 0000000..f7cc344
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/common/data/repository/PackageUpdateMonitor.kt
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.common.data.repository
+
+import android.content.Context
+import android.os.Handler
+import android.os.UserHandle
+import com.android.internal.content.PackageMonitor
+import com.android.systemui.common.data.shared.model.PackageChangeModel
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onEach
+
+/**
+ * A wrapper around [PackageMonitor] which exposes package updates as a flow.
+ *
+ * External clients should use [PackageChangeRepository] instead to ensure only a single callback is
+ * registered for all of SystemUI.
+ */
+class PackageUpdateMonitor
+@AssistedInject
+constructor(
+    @Assisted private val user: UserHandle,
+    @Background private val bgDispatcher: CoroutineDispatcher,
+    @Background private val bgHandler: Handler,
+    @Application private val context: Context,
+    @Application private val scope: CoroutineScope,
+    private val logger: PackageUpdateLogger,
+) : PackageMonitor() {
+
+    @AssistedFactory
+    fun interface Factory {
+        fun create(user: UserHandle): PackageUpdateMonitor
+    }
+
+    var isActive = false
+        private set
+
+    private val _packageChanged =
+        MutableSharedFlow<PackageChangeModel>(replay = 0, extraBufferCapacity = BUFFER_CAPACITY)
+            .apply {
+                // Automatically register/unregister as needed, depending on whether
+                // there are subscribers to this flow.
+                subscriptionCount
+                    .map { it > 0 }
+                    .distinctUntilChanged()
+                    .onEach { active ->
+                        if (active) {
+                            register(context, user, bgHandler)
+                        } else if (isActive) {
+                            // Avoid calling unregister if we were not previously active, as this
+                            // will cause an IllegalStateException.
+                            unregister()
+                        }
+                        isActive = active
+                    }
+                    .flowOn(bgDispatcher)
+                    .launchIn(scope)
+            }
+
+    val packageChanged: Flow<PackageChangeModel>
+        get() = _packageChanged.onEach(logger::logChange)
+
+    override fun onPackageAdded(packageName: String, uid: Int) {
+        super.onPackageAdded(packageName, uid)
+        _packageChanged.tryEmit(PackageChangeModel.Installed(packageName, uid))
+    }
+
+    override fun onPackageRemoved(packageName: String, uid: Int) {
+        super.onPackageRemoved(packageName, uid)
+        _packageChanged.tryEmit(PackageChangeModel.Uninstalled(packageName, uid))
+    }
+
+    override fun onPackageChanged(
+        packageName: String,
+        uid: Int,
+        components: Array<out String>
+    ): Boolean {
+        super.onPackageChanged(packageName, uid, components)
+        _packageChanged.tryEmit(PackageChangeModel.Changed(packageName, uid))
+        return false
+    }
+
+    override fun onPackageUpdateStarted(packageName: String, uid: Int) {
+        super.onPackageUpdateStarted(packageName, uid)
+        _packageChanged.tryEmit(PackageChangeModel.UpdateStarted(packageName, uid))
+    }
+
+    override fun onPackageUpdateFinished(packageName: String, uid: Int) {
+        super.onPackageUpdateFinished(packageName, uid)
+        _packageChanged.tryEmit(PackageChangeModel.UpdateFinished(packageName, uid))
+    }
+
+    private companion object {
+        // This capacity is the number of package changes that we will keep buffered in the shared
+        // flow. It is unlikely that at any given time there would be this many changes being
+        // processed by consumers, but this is done just in case that many packages are changed at
+        // the same time and there is backflow due to consumers processing the changes more slowly
+        // than they are being emitted.
+        const val BUFFER_CAPACITY = 100
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/common/data/shared/model/PackageChangeModel.kt b/packages/SystemUI/src/com/android/systemui/common/data/shared/model/PackageChangeModel.kt
new file mode 100644
index 0000000..853eff7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/common/data/shared/model/PackageChangeModel.kt
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.common.data.shared.model
+
+import android.content.Intent
+
+/** Represents changes to an installed package. */
+sealed interface PackageChangeModel {
+    val packageName: String
+    val packageUid: Int
+
+    /**
+     * An existing application package was uninstalled.
+     *
+     * Equivalent to receiving the [Intent.ACTION_PACKAGE_REMOVED] broadcast with
+     * [Intent.EXTRA_REPLACING] set to false.
+     */
+    data class Uninstalled(override val packageName: String, override val packageUid: Int) :
+        PackageChangeModel
+
+    /**
+     * A new version of an existing application is going to be installed.
+     *
+     * Equivalent to receiving the [Intent.ACTION_PACKAGE_REMOVED] broadcast with
+     * [Intent.EXTRA_REPLACING] set to true.
+     */
+    data class UpdateStarted(override val packageName: String, override val packageUid: Int) :
+        PackageChangeModel
+
+    /**
+     * A new version of an existing application package has been installed, replacing the old
+     * version.
+     *
+     * Equivalent to receiving the [Intent.ACTION_PACKAGE_ADDED] broadcast with
+     * [Intent.EXTRA_REPLACING] set to true.
+     */
+    data class UpdateFinished(override val packageName: String, override val packageUid: Int) :
+        PackageChangeModel
+
+    /**
+     * A new application package has been installed.
+     *
+     * Equivalent to receiving the [Intent.ACTION_PACKAGE_ADDED] broadcast with
+     * [Intent.EXTRA_REPLACING] set to false.
+     */
+    data class Installed(override val packageName: String, override val packageUid: Int) :
+        PackageChangeModel
+
+    /**
+     * An existing application package has been changed (for example, a component has been enabled
+     * or disabled).
+     *
+     * Equivalent to receiving the [Intent.ACTION_PACKAGE_CHANGED] broadcast.
+     */
+    data class Changed(override val packageName: String, override val packageUid: Int) :
+        PackageChangeModel
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt
index 847b98e..10768ea 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt
@@ -16,7 +16,6 @@
 
 package com.android.systemui.communal.dagger
 
-import android.content.Context
 import com.android.systemui.communal.data.db.CommunalDatabaseModule
 import com.android.systemui.communal.data.repository.CommunalMediaRepositoryModule
 import com.android.systemui.communal.data.repository.CommunalRepositoryModule
@@ -24,9 +23,8 @@
 import com.android.systemui.communal.data.repository.CommunalWidgetRepositoryModule
 import com.android.systemui.communal.widgets.EditWidgetsActivityStarter
 import com.android.systemui.communal.widgets.EditWidgetsActivityStarterImpl
-import com.android.systemui.dagger.qualifiers.Application
+import dagger.Binds
 import dagger.Module
-import dagger.Provides
 
 @Module(
     includes =
@@ -38,11 +36,9 @@
             CommunalDatabaseModule::class,
         ]
 )
-class CommunalModule {
-    @Provides
-    fun provideEditWidgetsActivityStarter(
-        @Application context: Context
-    ): EditWidgetsActivityStarter {
-        return EditWidgetsActivityStarterImpl(context)
-    }
+interface CommunalModule {
+    @Binds
+    fun bindEditWidgetsActivityStarter(
+        starter: EditWidgetsActivityStarterImpl
+    ): EditWidgetsActivityStarter
 }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/model/CommunalMediaModel.kt b/packages/SystemUI/src/com/android/systemui/communal/data/model/CommunalMediaModel.kt
new file mode 100644
index 0000000..cf2e33c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/model/CommunalMediaModel.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.data.model
+
+/** Data model of media on the communal hub. */
+data class CommunalMediaModel(
+    val hasAnyMediaOrRecommendation: Boolean,
+    val createdTimestampMillis: Long = 0L,
+) {
+    companion object {
+        val INACTIVE =
+            CommunalMediaModel(
+                hasAnyMediaOrRecommendation = false,
+            )
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalMediaRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalMediaRepository.kt
index e41c322..e8a561b 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalMediaRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalMediaRepository.kt
@@ -16,18 +16,17 @@
 
 package com.android.systemui.communal.data.repository
 
+import com.android.systemui.communal.data.model.CommunalMediaModel
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.media.controls.models.player.MediaData
 import com.android.systemui.media.controls.pipeline.MediaDataManager
 import javax.inject.Inject
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.onCompletion
-import kotlinx.coroutines.flow.onStart
 
 /** Encapsulates the state of smartspace in communal. */
 interface CommunalMediaRepository {
-    val mediaPlaying: Flow<Boolean>
+    val mediaModel: Flow<CommunalMediaModel>
 }
 
 @SysUISingleton
@@ -47,27 +46,32 @@
                 receivedSmartspaceCardLatency: Int,
                 isSsReactivated: Boolean
             ) {
-                if (!mediaDataManager.hasAnyMediaOrRecommendation()) {
-                    return
-                }
-                _mediaPlaying.value = true
+                updateMediaModel(data)
             }
 
             override fun onMediaDataRemoved(key: String) {
-                if (mediaDataManager.hasAnyMediaOrRecommendation()) {
-                    return
-                }
-                _mediaPlaying.value = false
+                updateMediaModel()
             }
         }
 
-    private val _mediaPlaying: MutableStateFlow<Boolean> = MutableStateFlow(false)
+    init {
+        mediaDataManager.addListener(mediaDataListener)
+    }
 
-    override val mediaPlaying: Flow<Boolean> =
-        _mediaPlaying
-            .onStart {
-                mediaDataManager.addListener(mediaDataListener)
-                _mediaPlaying.value = mediaDataManager.hasAnyMediaOrRecommendation()
-            }
-            .onCompletion { mediaDataManager.removeListener(mediaDataListener) }
+    private val _mediaModel: MutableStateFlow<CommunalMediaModel> =
+        MutableStateFlow(CommunalMediaModel.INACTIVE)
+
+    override val mediaModel: Flow<CommunalMediaModel> = _mediaModel
+
+    private fun updateMediaModel(data: MediaData? = null) {
+        if (mediaDataManager.hasAnyMediaOrRecommendation()) {
+            _mediaModel.value =
+                CommunalMediaModel(
+                    hasAnyMediaOrRecommendation = true,
+                    createdTimestampMillis = data?.createdTimestampMillis ?: 0L,
+                )
+        } else {
+            _mediaModel.value = CommunalMediaModel.INACTIVE
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt
index 1f4be40..553b3eb 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt
@@ -56,6 +56,9 @@
     /** Exposes the transition state of the communal [SceneTransitionLayout]. */
     val transitionState: StateFlow<ObservableCommunalTransitionState>
 
+    /** Whether the CTA tile is visible in the hub under view mode. */
+    val isCtaTileInViewModeVisible: Flow<Boolean>
+
     /** Updates the requested scene. */
     fun setDesiredScene(desiredScene: CommunalSceneKey)
 
@@ -65,6 +68,9 @@
      * Note that you must call is with `null` when the UI is done or risk a memory leak.
      */
     fun setTransitionState(transitionState: Flow<ObservableCommunalTransitionState>?)
+
+    /** Updates whether to display the CTA tile in the hub under view mode. */
+    fun setCtaTileInViewModeVisibility(isVisible: Boolean)
 }
 
 @OptIn(ExperimentalCoroutinesApi::class)
@@ -96,6 +102,16 @@
                 initialValue = defaultTransitionState,
             )
 
+    // TODO(b/313462210) - persist the value in local storage, so the tile won't show up again
+    //  once dismissed.
+    private val _isCtaTileInViewModeVisible: MutableStateFlow<Boolean> = MutableStateFlow(true)
+    override val isCtaTileInViewModeVisible: Flow<Boolean> =
+        _isCtaTileInViewModeVisible.asStateFlow()
+
+    override fun setCtaTileInViewModeVisibility(isVisible: Boolean) {
+        _isCtaTileInViewModeVisible.value = isVisible
+    }
+
     override fun setDesiredScene(desiredScene: CommunalSceneKey) {
         _desiredScene.value = desiredScene
     }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt
index d1bbe57..e6816e9 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt
@@ -18,14 +18,12 @@
 
 import android.appwidget.AppWidgetHost
 import android.appwidget.AppWidgetManager
-import android.content.BroadcastReceiver
 import android.content.ComponentName
-import android.content.Context
 import android.content.Intent
 import android.content.IntentFilter
 import android.os.UserManager
+import androidx.annotation.WorkerThread
 import com.android.systemui.broadcast.BroadcastDispatcher
-import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
 import com.android.systemui.communal.data.db.CommunalItemRank
 import com.android.systemui.communal.data.db.CommunalWidgetDao
 import com.android.systemui.communal.data.db.CommunalWidgetItem
@@ -38,18 +36,23 @@
 import com.android.systemui.log.core.Logger
 import com.android.systemui.log.dagger.CommunalLog
 import com.android.systemui.settings.UserTracker
+import java.util.Optional
 import javax.inject.Inject
+import kotlin.coroutines.cancellation.CancellationException
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.callbackFlow
 import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.emptyFlow
 import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.flowOn
 import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.mapLatest
+import kotlinx.coroutines.flow.onStart
 import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
 
 /** Encapsulates the state of widgets for communal mode. */
 interface CommunalWidgetRepository {
@@ -57,7 +60,11 @@
     val communalWidgets: Flow<List<CommunalWidgetContentModel>>
 
     /** Add a widget at the specified position in the app widget service and the database. */
-    fun addWidget(provider: ComponentName, priority: Int) {}
+    fun addWidget(
+        provider: ComponentName,
+        priority: Int,
+        configureWidget: suspend (id: Int) -> Boolean
+    ) {}
 
     /** Delete a widget by id from app widget service and the database. */
     fun deleteWidget(widgetId: Int) {}
@@ -75,7 +82,7 @@
 class CommunalWidgetRepositoryImpl
 @Inject
 constructor(
-    private val appWidgetManager: AppWidgetManager,
+    private val appWidgetManager: Optional<AppWidgetManager>,
     private val appWidgetHost: AppWidgetHost,
     @Application private val applicationScope: CoroutineScope,
     @Background private val bgDispatcher: CoroutineDispatcher,
@@ -96,37 +103,22 @@
     // Whether the [AppWidgetHost] is listening for updates.
     private var isHostListening = false
 
+    private suspend fun isUserUnlockingOrUnlocked(): Boolean =
+        withContext(bgDispatcher) { userManager.isUserUnlockingOrUnlocked(userTracker.userHandle) }
+
     private val isUserUnlocked: Flow<Boolean> =
-        callbackFlow {
-                if (!communalRepository.isCommunalEnabled) {
-                    awaitClose()
-                }
-
-                fun isUserUnlockingOrUnlocked(): Boolean {
-                    return userManager.isUserUnlockingOrUnlocked(userTracker.userHandle)
-                }
-
-                fun send() {
-                    trySendWithFailureLogging(isUserUnlockingOrUnlocked(), TAG)
-                }
-
-                if (isUserUnlockingOrUnlocked()) {
-                    send()
-                    awaitClose()
+        flowOf(communalRepository.isCommunalEnabled)
+            .flatMapLatest { enabled ->
+                if (enabled) {
+                    broadcastDispatcher
+                        .broadcastFlow(
+                            filter = IntentFilter(Intent.ACTION_USER_UNLOCKED),
+                            user = userTracker.userHandle
+                        )
+                        .onStart { emit(Unit) }
+                        .mapLatest { isUserUnlockingOrUnlocked() }
                 } else {
-                    val receiver =
-                        object : BroadcastReceiver() {
-                            override fun onReceive(context: Context?, intent: Intent?) {
-                                send()
-                            }
-                        }
-
-                    broadcastDispatcher.registerReceiver(
-                        receiver,
-                        IntentFilter(Intent.ACTION_USER_UNLOCKED),
-                    )
-
-                    awaitClose { broadcastDispatcher.unregisterReceiver(receiver) }
+                    emptyFlow()
                 }
             }
             .distinctUntilChanged()
@@ -144,21 +136,55 @@
 
     override val communalWidgets: Flow<List<CommunalWidgetContentModel>> =
         isHostActive.flatMapLatest { isHostActive ->
-            if (!isHostActive) {
+            if (!isHostActive || !appWidgetManager.isPresent) {
                 return@flatMapLatest flowOf(emptyList())
             }
-            communalWidgetDao.getWidgets().map { it.map(::mapToContentModel) }
+            communalWidgetDao
+                .getWidgets()
+                .map { it.map(::mapToContentModel) }
+                // As this reads from a database and triggers IPCs to AppWidgetManager,
+                // it should be executed in the background.
+                .flowOn(bgDispatcher)
         }
 
-    override fun addWidget(provider: ComponentName, priority: Int) {
+    override fun addWidget(
+        provider: ComponentName,
+        priority: Int,
+        configureWidget: suspend (id: Int) -> Boolean
+    ) {
         applicationScope.launch(bgDispatcher) {
             val id = communalWidgetHost.allocateIdAndBindWidget(provider)
-            id?.let {
-                communalWidgetDao.addWidget(
-                    widgetId = it,
-                    provider = provider,
-                    priority = priority,
-                )
+            if (id != null) {
+                val configured =
+                    if (communalWidgetHost.requiresConfiguration(id)) {
+                        logger.i("Widget ${provider.flattenToString()} requires configuration.")
+                        try {
+                            configureWidget.invoke(id)
+                        } catch (ex: Exception) {
+                            // Cleanup the app widget id if an error happens during configuration.
+                            logger.e("Error during widget configuration, cleaning up id $id", ex)
+                            if (ex is CancellationException) {
+                                appWidgetHost.deleteAppWidgetId(id)
+                                // Re-throw cancellation to ensure the parent coroutine also gets
+                                // cancelled.
+                                throw ex
+                            } else {
+                                false
+                            }
+                        }
+                    } else {
+                        logger.i("Skipping configuration for ${provider.flattenToString()}")
+                        true
+                    }
+                if (configured) {
+                    communalWidgetDao.addWidget(
+                        widgetId = id,
+                        provider = provider,
+                        priority = priority,
+                    )
+                } else {
+                    appWidgetHost.deleteAppWidgetId(id)
+                }
             }
             logger.i("Added widget ${provider.flattenToString()} at position $priority.")
         }
@@ -181,13 +207,14 @@
         }
     }
 
+    @WorkerThread
     private fun mapToContentModel(
         entry: Map.Entry<CommunalItemRank, CommunalWidgetItem>
     ): CommunalWidgetContentModel {
         val (_, widgetId) = entry.value
         return CommunalWidgetContentModel(
             appWidgetId = widgetId,
-            providerInfo = appWidgetManager.getAppWidgetInfo(widgetId),
+            providerInfo = appWidgetManager.get().getAppWidgetInfo(widgetId),
             priority = entry.key.rank,
         )
     }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryModule.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryModule.kt
index 5793f10..d0d9e3f 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryModule.kt
@@ -31,6 +31,7 @@
 import dagger.Binds
 import dagger.Module
 import dagger.Provides
+import java.util.Optional
 import javax.inject.Named
 
 @Module
@@ -41,8 +42,8 @@
 
         @SysUISingleton
         @Provides
-        fun provideAppWidgetManager(@Application context: Context): AppWidgetManager {
-            return AppWidgetManager.getInstance(context)
+        fun provideAppWidgetManager(@Application context: Context): Optional<AppWidgetManager> {
+            return Optional.ofNullable(AppWidgetManager.getInstance(context))
         }
 
         @SysUISingleton
@@ -54,7 +55,7 @@
         @SysUISingleton
         @Provides
         fun provideCommunalWidgetHost(
-            appWidgetManager: AppWidgetManager,
+            appWidgetManager: Optional<AppWidgetManager>,
             appWidgetHost: AppWidgetHost,
             @CommunalLog logBuffer: LogBuffer,
         ): CommunalWidgetHost {
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
index 0f4e583..24d4c6c 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
@@ -24,6 +24,9 @@
 import com.android.systemui.communal.data.repository.CommunalWidgetRepository
 import com.android.systemui.communal.domain.model.CommunalContentModel
 import com.android.systemui.communal.shared.model.CommunalContentSize
+import com.android.systemui.communal.shared.model.CommunalContentSize.FULL
+import com.android.systemui.communal.shared.model.CommunalContentSize.HALF
+import com.android.systemui.communal.shared.model.CommunalContentSize.THIRD
 import com.android.systemui.communal.shared.model.CommunalSceneKey
 import com.android.systemui.communal.shared.model.ObservableCommunalTransitionState
 import com.android.systemui.communal.widgets.EditWidgetsActivityStarter
@@ -31,15 +34,13 @@
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.smartspace.data.repository.SmartspaceRepository
 import javax.inject.Inject
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.map
 
 /** Encapsulates business-logic related to communal mode. */
-@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class CommunalInteractor
 @Inject
@@ -95,9 +96,20 @@
         editWidgetsActivityStarter.startActivity()
     }
 
-    /** Add a widget at the specified position. */
-    fun addWidget(componentName: ComponentName, priority: Int) =
-        widgetRepository.addWidget(componentName, priority)
+    /** Dismiss the CTA tile from the hub in view mode. */
+    fun dismissCtaTile() = communalRepository.setCtaTileInViewModeVisibility(isVisible = false)
+
+    /**
+     * Add a widget at the specified position.
+     *
+     * @param configureWidget The callback to trigger if widget configuration is needed. Should
+     *   return whether configuration was successful.
+     */
+    fun addWidget(
+        componentName: ComponentName,
+        priority: Int,
+        configureWidget: suspend (id: Int) -> Boolean
+    ) = widgetRepository.addWidget(componentName, priority, configureWidget)
 
     /** Delete a widget by id. */
     fun deleteWidget(id: Int) = widgetRepository.deleteWidget(id)
@@ -122,48 +134,101 @@
             }
         }
 
-    /** A flow of available smartspace content. Currently only showing timer targets. */
-    val smartspaceContent: Flow<List<CommunalContentModel.Smartspace>> =
+    /** A flow of available smartspace targets. Currently only showing timers. */
+    private val smartspaceTargets: Flow<List<SmartspaceTarget>> =
         if (!smartspaceRepository.isSmartspaceRemoteViewsEnabled) {
             flowOf(emptyList())
         } else {
             smartspaceRepository.communalSmartspaceTargets.map { targets ->
-                targets
-                    .filter { target ->
-                        target.featureType == SmartspaceTarget.FEATURE_TIMER &&
-                            target.remoteViews != null
-                    }
-                    .map Target@{ target ->
-                        return@Target CommunalContentModel.Smartspace(
-                            smartspaceTargetId = target.smartspaceTargetId,
-                            remoteViews = target.remoteViews!!,
-                            // Smartspace always as HALF for now.
-                            size = CommunalContentSize.HALF,
-                        )
-                    }
+                targets.filter { target ->
+                    target.featureType == SmartspaceTarget.FEATURE_TIMER &&
+                        target.remoteViews != null
+                }
             }
         }
 
+    /** CTA tile to be displayed in the glanceable hub (view mode). */
+    val ctaTileContent: Flow<List<CommunalContentModel.CtaTileInViewMode>> =
+        communalRepository.isCtaTileInViewModeVisible.map { visible ->
+            if (visible) listOf(CommunalContentModel.CtaTileInViewMode()) else emptyList()
+        }
+
     /** A list of tutorial content to be displayed in the communal hub in tutorial mode. */
     val tutorialContent: List<CommunalContentModel.Tutorial> =
         listOf(
-            CommunalContentModel.Tutorial(id = 0, CommunalContentSize.FULL),
-            CommunalContentModel.Tutorial(id = 1, CommunalContentSize.THIRD),
-            CommunalContentModel.Tutorial(id = 2, CommunalContentSize.THIRD),
-            CommunalContentModel.Tutorial(id = 3, CommunalContentSize.THIRD),
-            CommunalContentModel.Tutorial(id = 4, CommunalContentSize.HALF),
-            CommunalContentModel.Tutorial(id = 5, CommunalContentSize.HALF),
-            CommunalContentModel.Tutorial(id = 6, CommunalContentSize.HALF),
-            CommunalContentModel.Tutorial(id = 7, CommunalContentSize.HALF),
+            CommunalContentModel.Tutorial(id = 0, FULL),
+            CommunalContentModel.Tutorial(id = 1, THIRD),
+            CommunalContentModel.Tutorial(id = 2, THIRD),
+            CommunalContentModel.Tutorial(id = 3, THIRD),
+            CommunalContentModel.Tutorial(id = 4, HALF),
+            CommunalContentModel.Tutorial(id = 5, HALF),
+            CommunalContentModel.Tutorial(id = 6, HALF),
+            CommunalContentModel.Tutorial(id = 7, HALF),
         )
 
-    val umoContent: Flow<List<CommunalContentModel.Umo>> =
-        mediaRepository.mediaPlaying.flatMapLatest { mediaPlaying ->
-            if (mediaPlaying) {
-                // TODO(b/310254801): support HALF and FULL layouts
-                flowOf(listOf(CommunalContentModel.Umo(CommunalContentSize.THIRD)))
-            } else {
-                flowOf(emptyList())
+    /**
+     * A flow of ongoing content, including smartspace timers and umo, ordered by creation time and
+     * sized dynamically.
+     */
+    val ongoingContent: Flow<List<CommunalContentModel.Ongoing>> =
+        combine(smartspaceTargets, mediaRepository.mediaModel) { smartspace, media ->
+            val ongoingContent = mutableListOf<CommunalContentModel.Ongoing>()
+
+            // Add smartspace
+            ongoingContent.addAll(
+                smartspace.map { target ->
+                    CommunalContentModel.Smartspace(
+                        smartspaceTargetId = target.smartspaceTargetId,
+                        remoteViews = target.remoteViews!!,
+                        createdTimestampMillis = target.creationTimeMillis,
+                    )
+                }
+            )
+
+            // Add UMO
+            if (media.hasAnyMediaOrRecommendation) {
+                ongoingContent.add(
+                    CommunalContentModel.Umo(
+                        createdTimestampMillis = media.createdTimestampMillis,
+                    )
+                )
             }
+
+            // Order by creation time descending
+            ongoingContent.sortByDescending { it.createdTimestampMillis }
+
+            // Dynamic sizing
+            ongoingContent.forEachIndexed { index, model ->
+                model.size = dynamicContentSize(ongoingContent.size, index)
+            }
+
+            return@combine ongoingContent
         }
+
+    companion object {
+        /**
+         * Calculates the content size dynamically based on the total number of contents of that
+         * type.
+         *
+         * Contents with the same type are expected to fill each column evenly. Currently there are
+         * three possible sizes. When the total number is 1, size for that content is [FULL], when
+         * the total number is 2, size for each is [HALF], and 3, size for each is [THIRD].
+         *
+         * When dynamic contents fill in multiple columns, the first column follows the algorithm
+         * above, and the remaining contents are packed in [THIRD]s. For example, when the total
+         * number if 4, the first one is [FULL], filling the column, and the remaining 3 are
+         * [THIRD].
+         *
+         * @param size The total number of contents of this type.
+         * @param index The index of the current content of this type.
+         */
+        private fun dynamicContentSize(size: Int, index: Int): CommunalContentSize {
+            val remainder = size % CommunalContentSize.entries.size
+            return CommunalContentSize.toSize(
+                span =
+                    FULL.span /
+                        if (index > remainder - 1) CommunalContentSize.entries.size else remainder
+            )
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/model/CommunalContentModel.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/model/CommunalContentModel.kt
index 3ae5229..46f957f 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/model/CommunalContentModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/model/CommunalContentModel.kt
@@ -30,46 +30,95 @@
     /** Size to be rendered in the grid. */
     val size: CommunalContentSize
 
+    /**
+     * A type of communal content is ongoing / live / ephemeral, and can be sized and ordered
+     * dynamically.
+     */
+    sealed interface Ongoing : CommunalContentModel {
+        override var size: CommunalContentSize
+
+        /** Timestamp in milliseconds of when the content was created. */
+        val createdTimestampMillis: Long
+    }
+
     class Widget(
         val appWidgetId: Int,
         val providerInfo: AppWidgetProviderInfo,
         val appWidgetHost: AppWidgetHost,
     ) : CommunalContentModel {
-        override val key = "widget_$appWidgetId"
+        override val key = KEY.widget(appWidgetId)
         // Widget size is always half.
         override val size = CommunalContentSize.HALF
     }
 
     /** A placeholder item representing a new widget being added */
     class WidgetPlaceholder : CommunalContentModel {
-        override val key: String = "widget_placeholder_${UUID.randomUUID()}"
+        override val key: String = KEY.widgetPlaceholder()
+        // Same as widget size.
+        override val size = CommunalContentSize.HALF
+    }
+
+    /** A CTA tile in the glanceable hub view mode which can be dismissed. */
+    class CtaTileInViewMode : CommunalContentModel {
+        override val key: String = KEY.CTA_TILE_IN_VIEW_MODE_KEY
+        // Same as widget size.
+        override val size = CommunalContentSize.HALF
+    }
+
+    /** A CTA tile in the glanceable hub edit model which remains visible in the grid. */
+    class CtaTileInEditMode : CommunalContentModel {
+        override val key: String = KEY.CTA_TILE_IN_EDIT_MODE_KEY
         // Same as widget size.
         override val size = CommunalContentSize.HALF
     }
 
     class Tutorial(
         id: Int,
-        override val size: CommunalContentSize,
+        override var size: CommunalContentSize,
     ) : CommunalContentModel {
-        override val key = "tutorial_$id"
+        override val key = KEY.tutorial(id)
     }
 
     class Smartspace(
         smartspaceTargetId: String,
         val remoteViews: RemoteViews,
-        override val size: CommunalContentSize,
-    ) : CommunalContentModel {
-        override val key = "smartspace_$smartspaceTargetId"
+        override val createdTimestampMillis: Long,
+        override var size: CommunalContentSize = CommunalContentSize.HALF,
+    ) : Ongoing {
+        override val key = KEY.smartspace(smartspaceTargetId)
     }
 
     class Umo(
-        override val size: CommunalContentSize,
-    ) : CommunalContentModel {
-        override val key = UMO_KEY
+        override val createdTimestampMillis: Long,
+        override var size: CommunalContentSize = CommunalContentSize.HALF,
+    ) : Ongoing {
+        override val key = KEY.umo()
     }
 
-    companion object {
-        /** Key for the [Umo] in CommunalContentModel. There should only ever be one UMO. */
-        const val UMO_KEY = "umo"
+    class KEY {
+        companion object {
+            const val CTA_TILE_IN_VIEW_MODE_KEY = "cta_tile_in_view_mode"
+            const val CTA_TILE_IN_EDIT_MODE_KEY = "cta_tile_in_edit_mode"
+
+            fun widget(id: Int): String {
+                return "widget_$id"
+            }
+
+            fun widgetPlaceholder(): String {
+                return "widget_placeholder_${UUID.randomUUID()}"
+            }
+
+            fun tutorial(id: Int): String {
+                return "tutorial_$id"
+            }
+
+            fun smartspace(id: String): String {
+                return "smartspace_$id"
+            }
+
+            fun umo(): String {
+                return "umo"
+            }
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/log/CommunalLoggerStartable.kt b/packages/SystemUI/src/com/android/systemui/communal/log/CommunalLoggerStartable.kt
new file mode 100644
index 0000000..889023e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/log/CommunalLoggerStartable.kt
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.log
+
+import com.android.internal.logging.UiEventLogger
+import com.android.systemui.CoreStartable
+import com.android.systemui.communal.domain.interactor.CommunalInteractor
+import com.android.systemui.communal.shared.log.CommunalUiEvent
+import com.android.systemui.communal.shared.model.CommunalSceneKey
+import com.android.systemui.communal.shared.model.ObservableCommunalTransitionState
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.util.kotlin.pairwise
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.drop
+import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onEach
+
+/** A [CoreStartable] responsible for logging metrics for the communal hub. */
+@SysUISingleton
+class CommunalLoggerStartable
+@Inject
+constructor(
+    @Background private val backgroundScope: CoroutineScope,
+    private val communalInteractor: CommunalInteractor,
+    private val uiEventLogger: UiEventLogger,
+) : CoreStartable {
+
+    override fun start() {
+        communalInteractor.transitionState
+            .map { state ->
+                when {
+                    state.isOnCommunal() -> CommunalUiEvent.COMMUNAL_HUB_SHOWN
+                    state.isNotOnCommunal() -> CommunalUiEvent.COMMUNAL_HUB_GONE
+                    else -> null
+                }
+            }
+            .filterNotNull()
+            .distinctUntilChanged()
+            // Drop the default value.
+            .drop(1)
+            .onEach { uiEvent -> uiEventLogger.log(uiEvent) }
+            .launchIn(backgroundScope)
+
+        communalInteractor.transitionState
+            .pairwise()
+            .map { (old, new) ->
+                when {
+                    new.isOnCommunal() && old.isSwipingToCommunal() ->
+                        CommunalUiEvent.COMMUNAL_HUB_SWIPE_TO_ENTER_FINISH
+                    new.isOnCommunal() && old.isSwipingFromCommunal() ->
+                        CommunalUiEvent.COMMUNAL_HUB_SWIPE_TO_EXIT_CANCEL
+                    new.isNotOnCommunal() && old.isSwipingFromCommunal() ->
+                        CommunalUiEvent.COMMUNAL_HUB_SWIPE_TO_EXIT_FINISH
+                    new.isNotOnCommunal() && old.isSwipingToCommunal() ->
+                        CommunalUiEvent.COMMUNAL_HUB_SWIPE_TO_ENTER_CANCEL
+                    new.isSwipingToCommunal() && old.isNotOnCommunal() ->
+                        CommunalUiEvent.COMMUNAL_HUB_SWIPE_TO_ENTER_START
+                    new.isSwipingFromCommunal() && old.isOnCommunal() ->
+                        CommunalUiEvent.COMMUNAL_HUB_SWIPE_TO_EXIT_START
+                    else -> null
+                }
+            }
+            .filterNotNull()
+            .distinctUntilChanged()
+            .onEach { uiEvent -> uiEventLogger.log(uiEvent) }
+            .launchIn(backgroundScope)
+    }
+}
+
+/** Whether currently in communal scene. */
+private fun ObservableCommunalTransitionState.isOnCommunal(): Boolean {
+    return this is ObservableCommunalTransitionState.Idle && scene == CommunalSceneKey.Communal
+}
+
+/** Whether currently in a scene other than communal. */
+private fun ObservableCommunalTransitionState.isNotOnCommunal(): Boolean {
+    return this is ObservableCommunalTransitionState.Idle && scene != CommunalSceneKey.Communal
+}
+
+/** Whether currently transitioning from another scene to communal. */
+private fun ObservableCommunalTransitionState.isSwipingToCommunal(): Boolean {
+    return this is ObservableCommunalTransitionState.Transition &&
+        toScene == CommunalSceneKey.Communal &&
+        isInitiatedByUserInput
+}
+
+/** Whether currently transitioning from communal to another scene. */
+private fun ObservableCommunalTransitionState.isSwipingFromCommunal(): Boolean {
+    return this is ObservableCommunalTransitionState.Transition &&
+        fromScene == CommunalSceneKey.Communal &&
+        isInitiatedByUserInput
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/shared/CommunalWidgetHost.kt b/packages/SystemUI/src/com/android/systemui/communal/shared/CommunalWidgetHost.kt
index 086d729..41f9cb4 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/shared/CommunalWidgetHost.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/shared/CommunalWidgetHost.kt
@@ -18,10 +18,13 @@
 
 import android.appwidget.AppWidgetHost
 import android.appwidget.AppWidgetManager
+import android.appwidget.AppWidgetProviderInfo.WIDGET_FEATURE_CONFIGURATION_OPTIONAL
+import android.appwidget.AppWidgetProviderInfo.WIDGET_FEATURE_RECONFIGURABLE
 import android.content.ComponentName
 import com.android.systemui.log.LogBuffer
 import com.android.systemui.log.core.Logger
 import com.android.systemui.log.dagger.CommunalLog
+import java.util.Optional
 import javax.inject.Inject
 
 /**
@@ -31,7 +34,7 @@
 class CommunalWidgetHost
 @Inject
 constructor(
-    private val appWidgetManager: AppWidgetManager,
+    private val appWidgetManager: Optional<AppWidgetManager>,
     private val appWidgetHost: AppWidgetHost,
     @CommunalLog logBuffer: LogBuffer,
 ) {
@@ -56,6 +59,29 @@
         return null
     }
 
-    private fun bindWidget(widgetId: Int, provider: ComponentName): Boolean =
-        appWidgetManager.bindAppWidgetIdIfAllowed(widgetId, provider)
+    private fun bindWidget(widgetId: Int, provider: ComponentName): Boolean {
+        if (appWidgetManager.isPresent) {
+            return appWidgetManager.get().bindAppWidgetIdIfAllowed(widgetId, provider)
+        }
+        return false
+    }
+
+    /**
+     * Returns whether a particular widget requires configuration when it is first added.
+     *
+     * Must be called after the widget id has been bound.
+     */
+    fun requiresConfiguration(widgetId: Int): Boolean {
+        if (appWidgetManager.isPresent) {
+            val widgetInfo = appWidgetManager.get().getAppWidgetInfo(widgetId)
+            val featureFlags: Int = widgetInfo.widgetFeatures
+            // A widget's configuration is optional only if it's configuration is marked as optional
+            // AND it can be reconfigured later.
+            val configurationOptional =
+                (featureFlags and WIDGET_FEATURE_CONFIGURATION_OPTIONAL != 0 &&
+                    featureFlags and WIDGET_FEATURE_RECONFIGURABLE != 0)
+            return widgetInfo.configure != null && !configurationOptional
+        }
+        return false
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/shared/log/CommunalUiEvent.kt b/packages/SystemUI/src/com/android/systemui/communal/shared/log/CommunalUiEvent.kt
new file mode 100644
index 0000000..b64c195
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/shared/log/CommunalUiEvent.kt
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.shared.log
+
+import com.android.internal.logging.UiEvent
+import com.android.internal.logging.UiEventLogger.UiEventEnum
+
+/** UI events for the Communal Hub. */
+enum class CommunalUiEvent(private val id: Int) : UiEventEnum {
+    @UiEvent(doc = "Communal Hub is fully shown") COMMUNAL_HUB_SHOWN(1566),
+    @UiEvent(doc = "Communal Hub is fully gone") COMMUNAL_HUB_GONE(1577),
+    @UiEvent(doc = "Communal Hub times out") COMMUNAL_HUB_TIMEOUT(1578),
+    @UiEvent(doc = "The visible content in the Communal Hub is fully loaded and rendered")
+    COMMUNAL_HUB_LOADED(1579),
+    @UiEvent(doc = "User starts the swipe gesture to enter the Communal Hub")
+    COMMUNAL_HUB_SWIPE_TO_ENTER_START(1580),
+    @UiEvent(doc = "User finishes the swipe gesture to enter the Communal Hub")
+    COMMUNAL_HUB_SWIPE_TO_ENTER_FINISH(1581),
+    @UiEvent(doc = "User cancels the swipe gesture to enter the Communal Hub")
+    COMMUNAL_HUB_SWIPE_TO_ENTER_CANCEL(1582),
+    @UiEvent(doc = "User starts the swipe gesture to exit the Communal Hub")
+    COMMUNAL_HUB_SWIPE_TO_EXIT_START(1583),
+    @UiEvent(doc = "User finishes the swipe gesture to exit the Communal Hub")
+    COMMUNAL_HUB_SWIPE_TO_EXIT_FINISH(1584),
+    @UiEvent(doc = "User cancels the swipe gesture to exit the Communal Hub")
+    COMMUNAL_HUB_SWIPE_TO_EXIT_CANCEL(1585),
+    @UiEvent(doc = "User starts the drag gesture to reorder a widget")
+    COMMUNAL_HUB_REORDER_WIDGET_START(1586),
+    @UiEvent(doc = "User finishes the drag gesture to reorder a widget")
+    COMMUNAL_HUB_REORDER_WIDGET_FINISH(1587),
+    @UiEvent(doc = "User cancels the drag gesture to reorder a widget")
+    COMMUNAL_HUB_REORDER_WIDGET_CANCEL(1588),
+    @UiEvent(doc = "Edit mode for the Communal Hub is shown") COMMUNAL_HUB_EDIT_MODE_SHOWN(1569),
+    @UiEvent(doc = "Edit mode for the Communal Hub is gone") COMMUNAL_HUB_EDIT_MODE_GONE(1589),
+    @UiEvent(doc = "Widget picker for the Communal Hub is shown")
+    COMMUNAL_HUB_WIDGET_PICKER_SHOWN(1590),
+    @UiEvent(doc = "Widget picker for the Communal Hub is gone")
+    COMMUNAL_HUB_WIDGET_PICKER_GONE(1591),
+    @UiEvent(doc = "User performs a swipe up gesture from bottom to enter bouncer")
+    COMMUNAL_HUB_SWIPE_UP_TO_BOUNCER(1573),
+    @UiEvent(doc = "User performs a swipe down gesture from top to enter shade")
+    COMMUNAL_HUB_SWIPE_DOWN_TO_SHADE(1574);
+
+    override fun getId(): Int {
+        return id
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalContentSize.kt b/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalContentSize.kt
index c903709..572794d 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalContentSize.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalContentSize.kt
@@ -30,5 +30,13 @@
     HALF(3),
 
     /** Content takes a third of the height of the column. */
-    THIRD(2),
+    THIRD(2);
+
+    companion object {
+        /** Converts from span to communal content size. */
+        fun toSize(span: Int): CommunalContentSize {
+            return entries.find { it.span == span }
+                ?: throw Exception("Invalid span for communal content size")
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
index 577e404..84708a4 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
@@ -17,24 +17,19 @@
 package com.android.systemui.communal.ui.viewmodel
 
 import android.content.ComponentName
-import android.os.PowerManager
-import android.os.SystemClock
-import android.view.MotionEvent
+import android.widget.RemoteViews
 import com.android.systemui.communal.domain.interactor.CommunalInteractor
 import com.android.systemui.communal.domain.model.CommunalContentModel
 import com.android.systemui.communal.shared.model.CommunalSceneKey
 import com.android.systemui.communal.shared.model.ObservableCommunalTransitionState
 import com.android.systemui.media.controls.ui.MediaHost
-import com.android.systemui.shade.ShadeViewController
-import javax.inject.Provider
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.flowOf
 
 /** The base view model for the communal hub. */
 abstract class BaseCommunalViewModel(
     private val communalInteractor: CommunalInteractor,
-    private val shadeViewController: Provider<ShadeViewController>,
-    private val powerManager: PowerManager,
     val mediaHost: MediaHost,
 ) {
     val isKeyguardVisible: Flow<Boolean> = communalInteractor.isKeyguardVisible
@@ -57,28 +52,16 @@
     /**
      * Called when a widget is added via drag and drop from the widget picker into the communal hub.
      */
-    fun onAddWidget(componentName: ComponentName, priority: Int) {
-        communalInteractor.addWidget(componentName, priority)
+    open fun onAddWidget(componentName: ComponentName, priority: Int) {
+        communalInteractor.addWidget(componentName, priority, ::configureWidget)
     }
 
-    // TODO(b/308813166): remove once CommunalContainer is moved lower in z-order and doesn't block
-    //  touches anymore.
-    /** Called when a touch is received outside the edge swipe area when hub mode is closed. */
-    fun onOuterTouch(motionEvent: MotionEvent) {
-        // Forward the touch to the shade so that basic gestures like swipe up/down for
-        // shade/bouncer work.
-        shadeViewController.get().handleExternalTouch(motionEvent)
-    }
-
-    // TODO(b/308813166): remove once CommunalContainer is moved lower in z-order and doesn't block
-    //  touches anymore.
-    /** Called to refresh the screen timeout when a user touch is received. */
-    fun onUserActivity() {
-        powerManager.userActivity(
-            SystemClock.uptimeMillis(),
-            PowerManager.USER_ACTIVITY_EVENT_TOUCH,
-            0
-        )
+    /**
+     * Called when a widget needs to be configured, with the id of the widget. The return value
+     * should represent whether configuring the widget was successful.
+     */
+    protected open suspend fun configureWidget(widgetId: Int): Boolean {
+        return true
     }
 
     /** A list of all the communal content to be displayed in the communal hub. */
@@ -87,6 +70,12 @@
     /** Whether in edit mode for the communal hub. */
     open val isEditMode = false
 
+    /** Whether the popup message triggered by dismissing the CTA tile is showing. */
+    open val isPopupOnDismissCtaShowing: Flow<Boolean> = flowOf(false)
+
+    /** Hide the popup message triggered by dismissing the CTA tile. */
+    open fun onHidePopupAfterDismissCta() {}
+
     /** Called as the UI requests deleting a widget. */
     open fun onDeleteWidget(id: Int) {}
 
@@ -101,4 +90,19 @@
 
     /** Called as the UI requests opening the widget editor. */
     open fun onOpenWidgetEditor() {}
+
+    /** Called as the UI requests to dismiss the CTA tile. */
+    open fun onDismissCtaTile() {}
+
+    /** Gets the interaction handler used to handle taps on a remote view */
+    abstract fun getInteractionHandler(): RemoteViews.InteractionHandler
+
+    /** Called as the user starts dragging a widget to reorder. */
+    open fun onReorderWidgetStart() {}
+
+    /** Called as the user finishes dragging a widget to reorder. */
+    open fun onReorderWidgetEnd() {}
+
+    /** Called as the user cancels dragging a widget to reorder. */
+    open fun onReorderWidgetCancel() {}
 }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt
index 368df9e..7faf653 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt
@@ -16,17 +16,28 @@
 
 package com.android.systemui.communal.ui.viewmodel
 
-import android.os.PowerManager
+import android.app.Activity
+import android.app.Activity.RESULT_CANCELED
+import android.app.Activity.RESULT_OK
+import android.app.ActivityOptions
+import android.appwidget.AppWidgetHost
+import android.content.ActivityNotFoundException
+import android.content.ComponentName
+import android.widget.RemoteViews
+import com.android.internal.logging.UiEventLogger
 import com.android.systemui.communal.domain.interactor.CommunalInteractor
 import com.android.systemui.communal.domain.model.CommunalContentModel
+import com.android.systemui.communal.shared.log.CommunalUiEvent
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.media.controls.ui.MediaHost
 import com.android.systemui.media.dagger.MediaModule
-import com.android.systemui.shade.ShadeViewController
+import com.android.systemui.util.nullableAtomicReference
 import javax.inject.Inject
 import javax.inject.Named
-import javax.inject.Provider
+import kotlinx.coroutines.CompletableDeferred
 import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.map
 
 /** The view model for communal hub in edit mode. */
 @SysUISingleton
@@ -34,19 +45,104 @@
 @Inject
 constructor(
     private val communalInteractor: CommunalInteractor,
-    shadeViewController: Provider<ShadeViewController>,
-    powerManager: PowerManager,
+    private val appWidgetHost: AppWidgetHost,
     @Named(MediaModule.COMMUNAL_HUB) mediaHost: MediaHost,
-) : BaseCommunalViewModel(communalInteractor, shadeViewController, powerManager, mediaHost) {
+    private val uiEventLogger: UiEventLogger,
+) : BaseCommunalViewModel(communalInteractor, mediaHost) {
+
+    private companion object {
+        private const val KEY_SPLASH_SCREEN_STYLE = "android.activity.splashScreenStyle"
+        private const val SPLASH_SCREEN_STYLE_EMPTY = 0
+    }
+
+    private val _widgetsToConfigure = MutableSharedFlow<Int>()
+
+    /**
+     * Flow emitting ids of widgets which need to be configured. The consumer of this flow should
+     * trigger [startConfigurationActivity] to initiate configuration.
+     */
+    val widgetsToConfigure: Flow<Int> = _widgetsToConfigure
+
+    private var pendingConfiguration: CompletableDeferred<Int>? by nullableAtomicReference()
 
     override val isEditMode = true
 
-    // Only widgets are editable.
+    // Only widgets are editable. The CTA tile comes last in the list and remains visible.
     override val communalContent: Flow<List<CommunalContentModel>> =
-        communalInteractor.widgetContent
+        communalInteractor.widgetContent.map { widgets ->
+            widgets + listOf(CommunalContentModel.CtaTileInEditMode())
+        }
 
     override fun onDeleteWidget(id: Int) = communalInteractor.deleteWidget(id)
 
     override fun onReorderWidgets(widgetIdToPriorityMap: Map<Int, Int>) =
         communalInteractor.updateWidgetOrder(widgetIdToPriorityMap)
+
+    override fun getInteractionHandler(): RemoteViews.InteractionHandler {
+        // Ignore all interactions in edit mode.
+        return RemoteViews.InteractionHandler { _, _, _ -> false }
+    }
+
+    override fun onAddWidget(componentName: ComponentName, priority: Int) {
+        if (pendingConfiguration != null) {
+            throw IllegalStateException(
+                "Cannot add $componentName widget while widget configuration is pending"
+            )
+        }
+        super.onAddWidget(componentName, priority)
+    }
+
+    fun startConfigurationActivity(activity: Activity, widgetId: Int, requestCode: Int) {
+        val options =
+            ActivityOptions.makeBasic().apply {
+                setPendingIntentBackgroundActivityStartMode(
+                    ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED
+                )
+            }
+        val bundle = options.toBundle()
+        bundle.putInt(KEY_SPLASH_SCREEN_STYLE, SPLASH_SCREEN_STYLE_EMPTY)
+        try {
+            appWidgetHost.startAppWidgetConfigureActivityForResult(
+                activity,
+                widgetId,
+                0,
+                // Use the widget id as the request code.
+                requestCode,
+                bundle
+            )
+        } catch (e: ActivityNotFoundException) {
+            setConfigurationResult(RESULT_CANCELED)
+        }
+    }
+
+    override suspend fun configureWidget(widgetId: Int): Boolean {
+        if (pendingConfiguration != null) {
+            throw IllegalStateException(
+                "Attempting to configure $widgetId while another configuration is already active"
+            )
+        }
+        pendingConfiguration = CompletableDeferred()
+        _widgetsToConfigure.emit(widgetId)
+        val resultCode = pendingConfiguration?.await() ?: RESULT_CANCELED
+        pendingConfiguration = null
+        return resultCode == RESULT_OK
+    }
+
+    /** Sets the result of widget configuration. */
+    fun setConfigurationResult(resultCode: Int) {
+        pendingConfiguration?.complete(resultCode)
+            ?: throw IllegalStateException("No widget pending configuration")
+    }
+
+    override fun onReorderWidgetStart() {
+        uiEventLogger.log(CommunalUiEvent.COMMUNAL_HUB_REORDER_WIDGET_START)
+    }
+
+    override fun onReorderWidgetEnd() {
+        uiEventLogger.log(CommunalUiEvent.COMMUNAL_HUB_REORDER_WIDGET_FINISH)
+    }
+
+    override fun onReorderWidgetCancel() {
+        uiEventLogger.log(CommunalUiEvent.COMMUNAL_HUB_REORDER_WIDGET_CANCEL)
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
index abf1986..7a96fab 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
@@ -16,34 +16,40 @@
 
 package com.android.systemui.communal.ui.viewmodel
 
-import android.os.PowerManager
+import android.widget.RemoteViews
 import com.android.systemui.communal.domain.interactor.CommunalInteractor
 import com.android.systemui.communal.domain.interactor.CommunalTutorialInteractor
 import com.android.systemui.communal.domain.model.CommunalContentModel
+import com.android.systemui.communal.widgets.WidgetInteractionHandler
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.media.controls.ui.MediaHost
 import com.android.systemui.media.dagger.MediaModule
-import com.android.systemui.shade.ShadeViewController
 import javax.inject.Inject
 import javax.inject.Named
-import javax.inject.Provider
+import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.delay
 import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.launch
 
 /** The default view model used for showing the communal hub. */
 @SysUISingleton
 class CommunalViewModel
 @Inject
 constructor(
+    @Application private val scope: CoroutineScope,
     private val communalInteractor: CommunalInteractor,
+    private val interactionHandler: WidgetInteractionHandler,
     tutorialInteractor: CommunalTutorialInteractor,
-    shadeViewController: Provider<ShadeViewController>,
-    powerManager: PowerManager,
     @Named(MediaModule.COMMUNAL_HUB) mediaHost: MediaHost,
-) : BaseCommunalViewModel(communalInteractor, shadeViewController, powerManager, mediaHost) {
+) : BaseCommunalViewModel(communalInteractor, mediaHost) {
     @OptIn(ExperimentalCoroutinesApi::class)
     override val communalContent: Flow<List<CommunalContentModel>> =
         tutorialInteractor.isTutorialAvailable.flatMapLatest { isTutorialMode ->
@@ -51,13 +57,54 @@
                 return@flatMapLatest flowOf(communalInteractor.tutorialContent)
             }
             combine(
-                communalInteractor.smartspaceContent,
-                communalInteractor.umoContent,
+                communalInteractor.ongoingContent,
                 communalInteractor.widgetContent,
-            ) { smartspace, umo, widgets ->
-                smartspace + umo + widgets
+                communalInteractor.ctaTileContent,
+            ) { ongoing, widgets, ctaTile,
+                ->
+                ongoing + widgets + ctaTile
             }
         }
 
+    private val _isPopupOnDismissCtaShowing: MutableStateFlow<Boolean> = MutableStateFlow(false)
+    override val isPopupOnDismissCtaShowing: Flow<Boolean> =
+        _isPopupOnDismissCtaShowing.asStateFlow()
+
     override fun onOpenWidgetEditor() = communalInteractor.showWidgetEditor()
+
+    override fun onDismissCtaTile() {
+        communalInteractor.dismissCtaTile()
+        setPopupOnDismissCtaVisibility(true)
+        schedulePopupHiding()
+    }
+
+    override fun getInteractionHandler(): RemoteViews.InteractionHandler = interactionHandler
+
+    override fun onHidePopupAfterDismissCta() {
+        cancelDelayedPopupHiding()
+        setPopupOnDismissCtaVisibility(false)
+    }
+
+    private fun setPopupOnDismissCtaVisibility(isVisible: Boolean) {
+        _isPopupOnDismissCtaShowing.value = isVisible
+    }
+
+    private var delayedHidePopupJob: Job? = null
+    private fun schedulePopupHiding() {
+        cancelDelayedPopupHiding()
+        delayedHidePopupJob =
+            scope.launch {
+                delay(POPUP_AUTO_HIDE_TIMEOUT_MS)
+                onHidePopupAfterDismissCta()
+            }
+    }
+
+    private fun cancelDelayedPopupHiding() {
+        delayedHidePopupJob?.cancel()
+        delayedHidePopupJob = null
+    }
+
+    companion object {
+        const val POPUP_AUTO_HIDE_TIMEOUT_MS = 12000L
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
index c936c63..380ed61 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
@@ -23,26 +23,33 @@
 import android.os.RemoteException
 import android.util.Log
 import android.view.IWindowManager
+import android.view.WindowInsets
 import androidx.activity.ComponentActivity
 import androidx.activity.result.ActivityResultLauncher
 import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult
-import com.android.systemui.communal.domain.interactor.CommunalInteractor
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.lifecycleScope
+import androidx.lifecycle.repeatOnLifecycle
+import com.android.internal.logging.UiEventLogger
+import com.android.systemui.communal.shared.log.CommunalUiEvent
 import com.android.systemui.communal.ui.viewmodel.CommunalEditModeViewModel
 import com.android.systemui.compose.ComposeFacade.setCommunalEditWidgetActivityContent
 import javax.inject.Inject
+import kotlinx.coroutines.launch
 
 /** An Activity for editing the widgets that appear in hub mode. */
 class EditWidgetsActivity
 @Inject
 constructor(
     private val communalViewModel: CommunalEditModeViewModel,
-    private val communalInteractor: CommunalInteractor,
     private var windowManagerService: IWindowManager? = null,
+    private val uiEventLogger: UiEventLogger,
 ) : ComponentActivity() {
     companion object {
         private const val EXTRA_IS_PENDING_WIDGET_DRAG = "is_pending_widget_drag"
         private const val EXTRA_FILTER_STRATEGY = "filter_strategy"
         private const val FILTER_STRATEGY_GLANCEABLE_HUB = 1
+        private const val REQUEST_CODE_CONFIGURE_WIDGET = 1
         private const val TAG = "EditWidgetsActivity"
     }
 
@@ -50,6 +57,8 @@
         registerForActivityResult(StartActivityForResult()) { result ->
             when (result.resultCode) {
                 RESULT_OK -> {
+                    uiEventLogger.log(CommunalUiEvent.COMMUNAL_HUB_WIDGET_PICKER_SHOWN)
+
                     result.data?.let { intent ->
                         val isPendingWidgetDrag =
                             intent.getBooleanExtra(EXTRA_IS_PENDING_WIDGET_DRAG, false)
@@ -62,7 +71,7 @@
                                     Intent.EXTRA_COMPONENT_NAME,
                                     ComponentName::class.java
                                 )
-                                ?.let { communalInteractor.addWidget(it, 0) }
+                                ?.let { communalViewModel.onAddWidget(it, 0) }
                                 ?: run { Log.w(TAG, "No AppWidgetProviderInfo found in result.") }
                         }
                     }
@@ -79,14 +88,30 @@
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
 
+        val windowInsetsController = window.decorView.windowInsetsController
+        windowInsetsController?.hide(WindowInsets.Type.systemBars())
+        window.setDecorFitsSystemWindows(false)
+
+        lifecycleScope.launch {
+            repeatOnLifecycle(Lifecycle.State.STARTED) {
+                // Start the configuration activity when new widgets are added.
+                communalViewModel.widgetsToConfigure.collect { widgetId ->
+                    communalViewModel.startConfigurationActivity(
+                        activity = this@EditWidgetsActivity,
+                        widgetId = widgetId,
+                        requestCode = REQUEST_CODE_CONFIGURE_WIDGET
+                    )
+                }
+            }
+        }
+
         setCommunalEditWidgetActivityContent(
             activity = this,
             viewModel = communalViewModel,
             onOpenWidgetPicker = {
-                val localPackageManager: PackageManager = getPackageManager()
                 val intent =
                     Intent(Intent.ACTION_MAIN).also { it.addCategory(Intent.CATEGORY_HOME) }
-                localPackageManager
+                packageManager
                     .resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY)
                     ?.activityInfo
                     ?.packageName
@@ -117,4 +142,23 @@
             }
         )
     }
+
+    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
+        super.onActivityResult(requestCode, resultCode, data)
+        if (requestCode == REQUEST_CODE_CONFIGURE_WIDGET) {
+            communalViewModel.setConfigurationResult(resultCode)
+        }
+    }
+
+    override fun onStart() {
+        super.onStart()
+
+        uiEventLogger.log(CommunalUiEvent.COMMUNAL_HUB_EDIT_MODE_SHOWN)
+    }
+
+    override fun onStop() {
+        super.onStop()
+
+        uiEventLogger.log(CommunalUiEvent.COMMUNAL_HUB_EDIT_MODE_GONE)
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivityStarter.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivityStarter.kt
index 846e300..55acad0 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivityStarter.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivityStarter.kt
@@ -19,17 +19,26 @@
 import android.content.Context
 import android.content.Intent
 import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.plugins.ActivityStarter
+import javax.inject.Inject
 
 interface EditWidgetsActivityStarter {
     fun startActivity()
 }
 
-class EditWidgetsActivityStarterImpl(@Application private val applicationContext: Context) :
-    EditWidgetsActivityStarter {
+class EditWidgetsActivityStarterImpl
+@Inject
+constructor(
+    @Application private val applicationContext: Context,
+    private val activityStarter: ActivityStarter,
+) : EditWidgetsActivityStarter {
+
     override fun startActivity() {
-        applicationContext.startActivity(
+        activityStarter.startActivityDismissingKeyguard(
             Intent(applicationContext, EditWidgetsActivity::class.java)
-                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
+                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK),
+            /* onlyProvisioned = */ true,
+            /* dismissShade = */ true,
         )
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetInteractionHandler.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetInteractionHandler.kt
new file mode 100644
index 0000000..c8db70b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetInteractionHandler.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.widgets
+
+import android.app.PendingIntent
+import android.view.View
+import android.widget.RemoteViews
+import com.android.systemui.plugins.ActivityStarter
+import javax.inject.Inject
+
+class WidgetInteractionHandler
+@Inject
+constructor(
+    private val activityStarter: ActivityStarter,
+) : RemoteViews.InteractionHandler {
+    override fun onInteraction(
+        view: View,
+        pendingIntent: PendingIntent,
+        response: RemoteViews.RemoteResponse
+    ): Boolean =
+        when {
+            pendingIntent.isActivity -> startActivity(pendingIntent)
+            else ->
+                RemoteViews.startPendingIntent(view, pendingIntent, response.getLaunchOptions(view))
+        }
+
+    private fun startActivity(pendingIntent: PendingIntent): Boolean {
+        activityStarter.startPendingIntentMaybeDismissingKeyguard(
+            /* intent = */ pendingIntent,
+            /* intentSentUiThreadCallback = */ null,
+            // TODO(b/318758390): Properly animate activities started from widgets.
+            /* animationController = */ null
+        )
+        return true
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/PanelConfirmationDialogFactory.kt b/packages/SystemUI/src/com/android/systemui/controls/management/PanelConfirmationDialogFactory.kt
index 5df26b3..a6b4320 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/PanelConfirmationDialogFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/PanelConfirmationDialogFactory.kt
@@ -28,27 +28,26 @@
 /**
  * Factory to create dialogs for consenting to show app panels for specific apps.
  *
- * [internalDialogFactory] is for facilitating testing.
+ * [dialogFactory] is for facilitating testing.
  */
-class PanelConfirmationDialogFactory(
-    private val internalDialogFactory: (Context) -> SystemUIDialog
+class PanelConfirmationDialogFactory @Inject constructor(
+        private val dialogFactory: SystemUIDialog.Factory
 ) {
-    @Inject constructor() : this({ SystemUIDialog(it) })
 
     /**
      * Creates a dialog to show to the user. [response] will be true if an only if the user responds
      * affirmatively.
      */
     fun createConfirmationDialog(
-        context: Context,
-        appName: CharSequence,
-        response: Consumer<Boolean>
+            context: Context,
+            appName: CharSequence,
+            response: Consumer<Boolean>
     ): Dialog {
         val listener =
             DialogInterface.OnClickListener { _, which ->
                 response.accept(which == DialogInterface.BUTTON_POSITIVE)
             }
-        return internalDialogFactory(context).apply {
+        return dialogFactory.create(context).apply {
             setTitle(this.context.getString(R.string.controls_panel_authorization_title, appName))
             setMessage(this.context.getString(R.string.controls_panel_authorization, appName))
             setCanceledOnTouchOutside(true)
diff --git a/packages/SystemUI/src/com/android/systemui/controls/start/ControlsStartable.kt b/packages/SystemUI/src/com/android/systemui/controls/start/ControlsStartable.kt
index 0218f45..20bfbc9 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/start/ControlsStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/start/ControlsStartable.kt
@@ -26,6 +26,8 @@
 import androidx.annotation.WorkerThread
 import com.android.systemui.CoreStartable
 import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.common.data.shared.model.PackageChangeModel
+import com.android.systemui.common.data.repository.PackageChangeRepository
 import com.android.systemui.controls.controller.ControlsController
 import com.android.systemui.controls.dagger.ControlsComponent
 import com.android.systemui.controls.management.ControlsListingController
@@ -33,8 +35,16 @@
 import com.android.systemui.controls.panels.SelectedComponentRepository
 import com.android.systemui.controls.ui.SelectedItem
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.settings.UserTracker
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
 import java.util.concurrent.Executor
 import javax.inject.Inject
 
@@ -53,13 +63,16 @@
 class ControlsStartable
 @Inject
 constructor(
-        @Background private val executor: Executor,
-        private val controlsComponent: ControlsComponent,
-        private val userTracker: UserTracker,
-        private val authorizedPanelsRepository: AuthorizedPanelsRepository,
-        private val selectedComponentRepository: SelectedComponentRepository,
-        private val userManager: UserManager,
-        private val broadcastDispatcher: BroadcastDispatcher,
+    @Application private val scope: CoroutineScope,
+    @Background private val bgDispatcher: CoroutineDispatcher,
+    @Background private val executor: Executor,
+    private val controlsComponent: ControlsComponent,
+    private val userTracker: UserTracker,
+    private val authorizedPanelsRepository: AuthorizedPanelsRepository,
+    private val selectedComponentRepository: SelectedComponentRepository,
+    private val packageChangeRepository: PackageChangeRepository,
+    private val userManager: UserManager,
+    private val broadcastDispatcher: BroadcastDispatcher,
 ) : CoreStartable {
 
     // These two controllers can only be accessed after `start` method once we've checked if the
@@ -78,6 +91,8 @@
             }
         }
 
+    private var packageJob: Job? = null
+
     override fun start() {}
 
     override fun onBootCompleted() {
@@ -94,6 +109,21 @@
         controlsListingController.forceReload()
         selectDefaultPanelIfNecessary()
         bindToPanel()
+        monitorPackageUninstall()
+    }
+
+    private fun monitorPackageUninstall() {
+        packageJob?.cancel()
+        packageJob = packageChangeRepository.packageChanged(userTracker.userHandle)
+            .filter {
+                val selectedPackage =
+                    selectedComponentRepository.getSelectedComponent()?.componentName?.packageName
+                // Selected package was uninstalled
+                (it is PackageChangeModel.Uninstalled) && (it.packageName == selectedPackage)
+            }
+            .onEach { selectedComponentRepository.removeSelectedComponent() }
+            .flowOn(bgDispatcher)
+            .launchIn(scope)
     }
 
     private fun selectDefaultPanelIfNecessary() {
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsDialogsFactory.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsDialogsFactory.kt
index 2ad6014..e42a4a6 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsDialogsFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsDialogsFactory.kt
@@ -25,20 +25,21 @@
 import java.util.function.Consumer
 import javax.inject.Inject
 
-class ControlsDialogsFactory(private val internalDialogFactory: (Context) -> SystemUIDialog) {
+class ControlsDialogsFactory @Inject constructor(
+        private val dialogFactory: SystemUIDialog.Factory
+) {
 
-    @Inject constructor() : this({ SystemUIDialog(it) })
 
     fun createRemoveAppDialog(
-        context: Context,
-        appName: CharSequence,
-        response: Consumer<Boolean>
+            context: Context,
+            appName: CharSequence,
+            response: Consumer<Boolean>
     ): Dialog {
         val listener =
             DialogInterface.OnClickListener { _, which ->
                 response.accept(which == DialogInterface.BUTTON_POSITIVE)
             }
-        return internalDialogFactory(context).apply {
+        return dialogFactory.create(context).apply {
             setTitle(context.getString(R.string.controls_panel_remove_app_authorization, appName))
             setCanceledOnTouchOutside(true)
             setOnCancelListener { response.accept(false) }
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
index e71007b..8831dc6 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
@@ -484,7 +484,9 @@
                     }
                 },
                 PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT,
-                null,
+                ActivityOptions.makeBasic()
+                    .setPendingIntentCreatorBackgroundActivityStartMode(
+                        ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED).toBundle(),
                 userTracker.userHandle
         )
 
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
index 8b992fc..b2d7052 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
@@ -91,6 +91,7 @@
 import android.telephony.CarrierConfigManager;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
+import android.telephony.satellite.SatelliteManager;
 import android.view.Choreographer;
 import android.view.CrossWindowBlurListeners;
 import android.view.IWindowManager;
@@ -712,4 +713,10 @@
                 ServiceManager.getService(Context.URI_GRANTS_SERVICE)
         );
     }
+
+    @Provides
+    @Singleton
+    static Optional<SatelliteManager> provideSatelliteManager(Context context) {
+        return Optional.ofNullable(context.getSystemService(SatelliteManager.class));
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/GlobalRootComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/GlobalRootComponent.java
index 9e8c0ec..e78ce3b 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/GlobalRootComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/GlobalRootComponent.java
@@ -17,8 +17,12 @@
 package com.android.systemui.dagger;
 
 import android.content.Context;
+import android.os.Looper;
 
 import com.android.systemui.dagger.qualifiers.InstrumentationTest;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.flags.SystemPropertiesHelper;
+import com.android.systemui.process.ProcessWrapper;
 import com.android.systemui.util.InitializationChecker;
 
 import dagger.BindsInstance;
@@ -58,4 +62,20 @@
      * Returns an {@link InitializationChecker}.
      */
     InitializationChecker getInitializationChecker();
+
+    /**
+     * Returns the main looper for this process.
+     */
+    @Main
+    Looper getMainLooper();
+
+    /**
+     * Returns a {@link SystemPropertiesHelper}.
+     */
+    SystemPropertiesHelper getSystemPropertiesHelper();
+
+    /**
+     * Returns a {@link ProcessWrapper}
+     */
+    ProcessWrapper getProcessWrapper();
 }
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
index 236c5b8..50f861f 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
@@ -23,6 +23,8 @@
 import android.hardware.SensorPrivacyManager;
 
 import com.android.keyguard.KeyguardViewController;
+import com.android.systemui.ScreenDecorationsModule;
+import com.android.systemui.accessibility.SystemActionsModule;
 import com.android.systemui.battery.BatterySaverModule;
 import com.android.systemui.dock.DockManager;
 import com.android.systemui.dock.DockManagerImpl;
@@ -34,6 +36,7 @@
 import com.android.systemui.power.dagger.PowerModule;
 import com.android.systemui.qs.dagger.QSModule;
 import com.android.systemui.qs.tileimpl.QSFactoryImpl;
+import com.android.systemui.reardisplay.RearDisplayModule;
 import com.android.systemui.recents.Recents;
 import com.android.systemui.recents.RecentsImplementation;
 import com.android.systemui.rotationlock.RotationLockModule;
@@ -59,6 +62,7 @@
 import com.android.systemui.statusbar.policy.IndividualSensorPrivacyControllerImpl;
 import com.android.systemui.statusbar.policy.SensorPrivacyController;
 import com.android.systemui.statusbar.policy.SensorPrivacyControllerImpl;
+import com.android.systemui.toast.ToastModule;
 import com.android.systemui.volume.dagger.VolumeModule;
 import com.android.systemui.wallpapers.dagger.WallpaperModule;
 
@@ -89,19 +93,23 @@
         CollapsedStatusBarFragmentStartableModule.class,
         GestureModule.class,
         HeadsUpModule.class,
+        KeyboardShortcutsModule.class,
         MediaModule.class,
         MultiUserUtilsModule.class,
         NavigationBarControllerModule.class,
         PowerModule.class,
         QSModule.class,
-        ShadeModule.class,
+        RearDisplayModule.class,
         ReferenceScreenshotModule.class,
         RotationLockModule.class,
-        SceneContainerFrameworkModule.class,
+        ScreenDecorationsModule.class,
+        SystemActionsModule.class,
+        ShadeModule.class,
         StartCentralSurfacesModule.class,
+        SceneContainerFrameworkModule.class,
+        ToastModule.class,
         VolumeModule.class,
-        WallpaperModule.class,
-        KeyboardShortcutsModule.class
+        WallpaperModule.class
 })
 public abstract class ReferenceSystemUIModule {
 
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
index d041acb..8d82b55 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
@@ -19,14 +19,12 @@
 import com.android.keyguard.KeyguardBiometricLockoutLogger
 import com.android.systemui.CoreStartable
 import com.android.systemui.LatencyTester
-import com.android.systemui.ScreenDecorations
 import com.android.systemui.SliceBroadcastRelayHandler
-import com.android.systemui.accessibility.SystemActions
 import com.android.systemui.accessibility.Magnification
 import com.android.systemui.back.domain.interactor.BackActionInteractor
-import com.android.systemui.biometrics.AuthController
 import com.android.systemui.biometrics.BiometricNotificationService
 import com.android.systemui.clipboardoverlay.ClipboardListener
+import com.android.systemui.communal.log.CommunalLoggerStartable
 import com.android.systemui.controls.dagger.StartControlsStartableModule
 import com.android.systemui.dagger.qualifiers.PerUser
 import com.android.systemui.dreams.AssistantAttentionMonitor
@@ -46,10 +44,6 @@
 import com.android.systemui.media.taptotransfer.receiver.MediaTttChipControllerReceiver
 import com.android.systemui.media.taptotransfer.sender.MediaTttSenderCoordinator
 import com.android.systemui.mediaprojection.taskswitcher.MediaProjectionTaskSwitcherCoreStartable
-import com.android.systemui.power.PowerUI
-import com.android.systemui.reardisplay.RearDisplayDialogController
-import com.android.systemui.recents.Recents
-import com.android.systemui.recents.ScreenPinningRequest
 import com.android.systemui.settings.dagger.MultiUserUtilsModule
 import com.android.systemui.shortcut.ShortcutKeyDispatcher
 import com.android.systemui.statusbar.ImmersiveModeConfirmation
@@ -61,11 +55,10 @@
 import com.android.systemui.stylus.StylusUsiPowerStartable
 import com.android.systemui.temporarydisplay.chipbar.ChipbarCoordinator
 import com.android.systemui.theme.ThemeOverlayController
-import com.android.systemui.toast.ToastUI
+import com.android.systemui.unfold.DisplaySwitchLatencyTracker
 import com.android.systemui.usb.StorageNotification
 import com.android.systemui.util.NotificationChannels
 import com.android.systemui.util.StartBinderLoggerModule
-import com.android.systemui.volume.VolumeUI
 import com.android.systemui.wallpapers.dagger.WallpaperModule
 import com.android.systemui.wmshell.WMShell
 import dagger.Binds
@@ -74,7 +67,12 @@
 import dagger.multibindings.IntoMap
 
 /**
- * Collection of {@link CoreStartable}s that should be run on AOSP.
+ * DEPRECATED: DO NOT ADD THINGS TO THIS FILE.
+ *
+ * Add a feature specific daggger module for what you are working on. Bind your CoreStartable there.
+ * Include that module where it is needed.
+ *
+ * @deprecated
  */
 @Module(
     includes = [
@@ -85,12 +83,6 @@
     ]
 )
 abstract class SystemUICoreStartableModule {
-    /** Inject into AuthController.  */
-    @Binds
-    @IntoMap
-    @ClassKey(AuthController::class)
-    abstract fun bindAuthController(service: AuthController): CoreStartable
-
     /** Inject into BiometricNotificationService */
     @Binds
     @IntoMap
@@ -151,6 +143,12 @@
     @ClassKey(LatencyTester::class)
     abstract fun bindLatencyTester(sysui: LatencyTester): CoreStartable
 
+    /** Inject into DisplaySwitchLatencyTracker.  */
+    @Binds
+    @IntoMap
+    @ClassKey(DisplaySwitchLatencyTracker::class)
+    abstract fun bindDisplaySwitchLatencyTracker(sysui: DisplaySwitchLatencyTracker): CoreStartable
+
     /** Inject into NotificationChannels.  */
     @Binds
     @IntoMap
@@ -158,18 +156,6 @@
     @PerUser
     abstract fun bindNotificationChannels(sysui: NotificationChannels): CoreStartable
 
-    /** Inject into PowerUI.  */
-    @Binds
-    @IntoMap
-    @ClassKey(PowerUI::class)
-    abstract fun bindPowerUI(sysui: PowerUI): CoreStartable
-
-    /** Inject into Recents.  */
-    @Binds
-    @IntoMap
-    @ClassKey(Recents::class)
-    abstract fun bindRecents(sysui: Recents): CoreStartable
-
     /** Inject into ImmersiveModeConfirmation.  */
     @Binds
     @IntoMap
@@ -182,12 +168,6 @@
     @ClassKey(RingtonePlayer::class)
     abstract fun bind(sysui: RingtonePlayer): CoreStartable
 
-    /** Inject into ScreenDecorations.  */
-    @Binds
-    @IntoMap
-    @ClassKey(ScreenDecorations::class)
-    abstract fun bindScreenDecorations(sysui: ScreenDecorations): CoreStartable
-
     /** Inject into GesturePointerEventHandler. */
     @Binds
     @IntoMap
@@ -218,23 +198,12 @@
     @ClassKey(StorageNotification::class)
     abstract fun bindStorageNotification(sysui: StorageNotification): CoreStartable
 
-    /** Inject into SystemActions.  */
-    @Binds
-    @IntoMap
-    @ClassKey(SystemActions::class)
-    abstract fun bindSystemActions(sysui: SystemActions): CoreStartable
-
     /** Inject into ThemeOverlayController.  */
     @Binds
     @IntoMap
     @ClassKey(ThemeOverlayController::class)
     abstract fun bindThemeOverlayController(sysui: ThemeOverlayController): CoreStartable
 
-    /** Inject into ToastUI.  */
-    @Binds
-    @IntoMap
-    @ClassKey(ToastUI::class)
-    abstract fun bindToastUI(service: ToastUI): CoreStartable
 
     /** Inject into MediaOutputSwitcherDialogUI.  */
     @Binds
@@ -242,12 +211,6 @@
     @ClassKey(MediaOutputSwitcherDialogUI::class)
     abstract fun MediaOutputSwitcherDialogUI(sysui: MediaOutputSwitcherDialogUI): CoreStartable
 
-    /** Inject into VolumeUI.  */
-    @Binds
-    @IntoMap
-    @ClassKey(VolumeUI::class)
-    abstract fun bindVolumeUI(sysui: VolumeUI): CoreStartable
-
     /** Inject into Magnification.  */
     @Binds
     @IntoMap
@@ -293,11 +256,6 @@
     abstract fun bindChipbarController(sysui: ChipbarCoordinator): CoreStartable
 
 
-    /** Inject into RearDisplayDialogController) */
-    @Binds
-    @IntoMap
-    @ClassKey(RearDisplayDialogController::class)
-    abstract fun bindRearDisplayDialogController(sysui: RearDisplayDialogController): CoreStartable
 
     /** Inject into StylusUsiPowerStartable) */
     @Binds
@@ -364,6 +322,6 @@
 
     @Binds
     @IntoMap
-    @ClassKey(ScreenPinningRequest::class)
-    abstract fun bindScreenPinningRequest(impl: ScreenPinningRequest): CoreStartable
+    @ClassKey(CommunalLoggerStartable::class)
+    abstract fun bindCommunalLoggerStartable(impl: CommunalLoggerStartable): CoreStartable
 }
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index a25c788..92300ef 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -112,6 +112,7 @@
 import com.android.systemui.statusbar.notification.row.dagger.ExpandableNotificationRowComponent;
 import com.android.systemui.statusbar.notification.row.dagger.NotificationRowComponent;
 import com.android.systemui.statusbar.phone.CentralSurfaces;
+import com.android.systemui.statusbar.phone.ConfigurationControllerModule;
 import com.android.systemui.statusbar.phone.LetterboxModule;
 import com.android.systemui.statusbar.phone.NotificationIconAreaControllerModule;
 import com.android.systemui.statusbar.pipeline.dagger.StatusBarPipelineModule;
@@ -178,6 +179,7 @@
         ClockRegistryModule.class,
         CommunalModule.class,
         CommonDataLayerModule.class,
+        ConfigurationControllerModule.class,
         ConnectivityModule.class,
         ControlsModule.class,
         CoroutinesModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/decor/FaceScanningProviderFactory.kt b/packages/SystemUI/src/com/android/systemui/decor/FaceScanningProviderFactory.kt
index 615b503..3bc4f34 100644
--- a/packages/SystemUI/src/com/android/systemui/decor/FaceScanningProviderFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/decor/FaceScanningProviderFactory.kt
@@ -32,6 +32,7 @@
 import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.systemui.FaceScanningOverlay
 import com.android.systemui.biometrics.AuthController
+import com.android.systemui.biometrics.data.repository.FacePropertyRepository
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.log.ScreenDecorationsLogger
@@ -41,19 +42,20 @@
 
 @SysUISingleton
 class FaceScanningProviderFactory @Inject constructor(
-    private val authController: AuthController,
-    private val context: Context,
-    private val statusBarStateController: StatusBarStateController,
-    private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
-    @Main private val mainExecutor: Executor,
-    private val logger: ScreenDecorationsLogger,
+        private val authController: AuthController,
+        private val context: Context,
+        private val statusBarStateController: StatusBarStateController,
+        private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
+        @Main private val mainExecutor: Executor,
+        private val logger: ScreenDecorationsLogger,
+        private val facePropertyRepository: FacePropertyRepository,
 ) : DecorProviderFactory() {
     private val display = context.display
     private val displayInfo = DisplayInfo()
 
     override val hasProviders: Boolean
         get() {
-            if (authController.faceSensorLocation == null) {
+            if (facePropertyRepository.sensorLocation.value == null) {
                 return false
             }
 
@@ -86,6 +88,7 @@
                                         keyguardUpdateMonitor,
                                         mainExecutor,
                                         logger,
+                                        facePropertyRepository,
                                 )
                         )
                     }
@@ -104,12 +107,13 @@
 }
 
 class FaceScanningOverlayProviderImpl(
-    override val alignedBound: Int,
-    private val authController: AuthController,
-    private val statusBarStateController: StatusBarStateController,
-    private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
-    private val mainExecutor: Executor,
-    private val logger: ScreenDecorationsLogger,
+        override val alignedBound: Int,
+        private val authController: AuthController,
+        private val statusBarStateController: StatusBarStateController,
+        private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
+        private val mainExecutor: Executor,
+        private val logger: ScreenDecorationsLogger,
+        private val facePropertyRepository: FacePropertyRepository,
 ) : BoundDecorProvider() {
     override val viewId: Int = com.android.systemui.res.R.id.face_scanning_anim
 
@@ -162,8 +166,9 @@
         layoutParams.let { lp ->
             lp.width = ViewGroup.LayoutParams.MATCH_PARENT
             lp.height = ViewGroup.LayoutParams.MATCH_PARENT
-            logger.faceSensorLocation(authController.faceSensorLocation)
-            authController.faceSensorLocation?.y?.let { faceAuthSensorHeight ->
+            logger.faceSensorLocation(facePropertyRepository.sensorLocation.value)
+            facePropertyRepository.sensorLocation.value?.y?.let {
+                faceAuthSensorHeight ->
                 val faceScanningHeight = (faceAuthSensorHeight * 2)
                 when (rotation) {
                     Surface.ROTATION_0, Surface.ROTATION_180 ->
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/DeviceEntryModule.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/DeviceEntryModule.kt
index b915418..71b5ab2 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/DeviceEntryModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/DeviceEntryModule.kt
@@ -1,6 +1,7 @@
 package com.android.systemui.deviceentry
 
 import com.android.systemui.deviceentry.data.repository.DeviceEntryRepositoryModule
+import com.android.systemui.deviceentry.data.repository.FaceWakeUpTriggersConfigModule
 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
 import dagger.Module
 import dagger.multibindings.Multibinds
@@ -9,6 +10,7 @@
     includes =
         [
             DeviceEntryRepositoryModule::class,
+            FaceWakeUpTriggersConfigModule::class,
         ],
 )
 abstract class DeviceEntryModule {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt
similarity index 94%
rename from packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt
rename to packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt
index 4d60dd0..7a70c4a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.keyguard.data.repository
+package com.android.systemui.deviceentry.data.repository
 
 import android.app.StatusBarManager
 import android.content.Context
@@ -22,7 +22,6 @@
 import android.os.CancellationSignal
 import com.android.internal.logging.InstanceId
 import com.android.internal.logging.UiEventLogger
-import com.android.keyguard.FaceAuthUiEvent
 import com.android.systemui.Dumpable
 import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor
 import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
@@ -32,19 +31,27 @@
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.deviceentry.shared.FaceAuthUiEvent
+import com.android.systemui.deviceentry.shared.model.AcquiredFaceAuthenticationStatus
+import com.android.systemui.deviceentry.shared.model.ErrorFaceAuthenticationStatus
+import com.android.systemui.deviceentry.shared.model.FaceAuthenticationStatus
+import com.android.systemui.deviceentry.shared.model.FaceDetectionStatus
+import com.android.systemui.deviceentry.shared.model.FailedFaceAuthenticationStatus
+import com.android.systemui.deviceentry.shared.model.HelpFaceAuthenticationStatus
+import com.android.systemui.deviceentry.shared.model.SuccessFaceAuthenticationStatus
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
+import com.android.systemui.keyguard.data.repository.BiometricType
+import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository
+import com.android.systemui.keyguard.data.repository.FaceAuthTableLog
+import com.android.systemui.keyguard.data.repository.FaceDetectTableLog
+import com.android.systemui.keyguard.data.repository.KeyguardRepository
+import com.android.systemui.keyguard.data.repository.TrustRepository
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
-import com.android.systemui.keyguard.shared.model.AcquiredFaceAuthenticationStatus
-import com.android.systemui.keyguard.shared.model.ErrorFaceAuthenticationStatus
-import com.android.systemui.keyguard.shared.model.FaceAuthenticationStatus
-import com.android.systemui.keyguard.shared.model.FaceDetectionStatus
-import com.android.systemui.keyguard.shared.model.FailedFaceAuthenticationStatus
-import com.android.systemui.keyguard.shared.model.HelpFaceAuthenticationStatus
 import com.android.systemui.keyguard.shared.model.KeyguardState
-import com.android.systemui.keyguard.shared.model.SuccessFaceAuthenticationStatus
 import com.android.systemui.keyguard.shared.model.SysUiFaceAuthenticateOptions
 import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.log.FaceAuthenticationLogger
@@ -626,17 +633,19 @@
             faceAuthLogger.skippingDetection(_isAuthRunning.value, detectCancellationSignal != null)
             return
         }
-        detectCancellationSignal?.cancel()
-        detectCancellationSignal = CancellationSignal()
         withContext(mainDispatcher) {
             // We always want to invoke face detect in the main thread.
             faceAuthLogger.faceDetectionStarted()
-            faceManager?.detectFace(
-                checkNotNull(detectCancellationSignal),
-                detectionCallback,
-                SysUiFaceAuthenticateOptions(currentUserId, uiEvent, uiEvent.extraInfo)
-                    .toFaceAuthenticateOptions()
-            )
+            detectCancellationSignal?.cancel()
+            detectCancellationSignal = CancellationSignal()
+            detectCancellationSignal?.let {
+                faceManager?.detectFace(
+                    it,
+                    detectionCallback,
+                    SysUiFaceAuthenticateOptions(currentUserId, uiEvent, uiEvent.extraInfo)
+                        .toFaceAuthenticateOptions()
+                )
+            }
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/keyguard/FaceWakeUpTriggersConfig.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/FaceWakeUpTriggersConfig.kt
similarity index 83%
rename from packages/SystemUI/src/com/android/keyguard/FaceWakeUpTriggersConfig.kt
rename to packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/FaceWakeUpTriggersConfig.kt
index 84a6b09..9d6718b 100644
--- a/packages/SystemUI/src/com/android/keyguard/FaceWakeUpTriggersConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/FaceWakeUpTriggersConfig.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,28 +14,35 @@
  * limitations under the License.
  */
 
-package com.android.keyguard
+package com.android.systemui.deviceentry.data.repository
 
 import android.content.res.Resources
 import android.os.Build
 import android.os.PowerManager
 import com.android.systemui.Dumpable
-import com.android.systemui.res.R
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.power.shared.model.WakeSleepReason
+import com.android.systemui.res.R
 import com.android.systemui.util.settings.GlobalSettings
+import dagger.Binds
+import dagger.Module
 import java.io.PrintWriter
 import java.util.stream.Collectors
 import javax.inject.Inject
 
 /** Determines which device wake-ups should trigger passive authentication. */
+interface FaceWakeUpTriggersConfig {
+    fun shouldTriggerFaceAuthOnWakeUpFrom(@PowerManager.WakeReason pmWakeReason: Int): Boolean
+    fun shouldTriggerFaceAuthOnWakeUpFrom(wakeReason: WakeSleepReason): Boolean
+}
+
 @SysUISingleton
-class FaceWakeUpTriggersConfig
+class FaceWakeUpTriggersConfigImpl
 @Inject
 constructor(@Main resources: Resources, globalSettings: GlobalSettings, dumpManager: DumpManager) :
-    Dumpable {
+    Dumpable, FaceWakeUpTriggersConfig {
     private val defaultTriggerFaceAuthOnWakeUpFrom: Set<Int> =
         resources.getIntArray(R.array.config_face_auth_wake_up_triggers).toSet()
     private val triggerFaceAuthOnWakeUpFrom: Set<Int>
@@ -65,11 +72,13 @@
         dumpManager.registerDumpable(this)
     }
 
-    fun shouldTriggerFaceAuthOnWakeUpFrom(@PowerManager.WakeReason pmWakeReason: Int): Boolean {
+    override fun shouldTriggerFaceAuthOnWakeUpFrom(
+        @PowerManager.WakeReason pmWakeReason: Int
+    ): Boolean {
         return triggerFaceAuthOnWakeUpFrom.contains(pmWakeReason)
     }
 
-    fun shouldTriggerFaceAuthOnWakeUpFrom(wakeReason: WakeSleepReason): Boolean =
+    override fun shouldTriggerFaceAuthOnWakeUpFrom(wakeReason: WakeSleepReason): Boolean =
         wakeSleepReasonsToTriggerFaceAuth.contains(wakeReason)
 
     override fun dump(pw: PrintWriter, args: Array<out String>) {
@@ -87,3 +96,8 @@
             ?: default
     }
 }
+
+@Module
+interface FaceWakeUpTriggersConfigModule {
+    @Binds fun repository(impl: FaceWakeUpTriggersConfigImpl): FaceWakeUpTriggersConfig
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/NoopDeviceEntryFaceAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/NoopDeviceEntryFaceAuthRepository.kt
similarity index 68%
rename from packages/SystemUI/src/com/android/systemui/keyguard/data/repository/NoopDeviceEntryFaceAuthRepository.kt
rename to packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/NoopDeviceEntryFaceAuthRepository.kt
index f4a74f0..6695b182 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/NoopDeviceEntryFaceAuthRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/NoopDeviceEntryFaceAuthRepository.kt
@@ -1,25 +1,25 @@
 /*
- *   Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
  *
- *   Licensed under the Apache License, Version 2.0 (the "License");
- *   you may not use this file except in compliance with the License.
- *   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.data.repository
+package com.android.systemui.deviceentry.data.repository
 
-import com.android.keyguard.FaceAuthUiEvent
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.shared.model.FaceAuthenticationStatus
-import com.android.systemui.keyguard.shared.model.FaceDetectionStatus
+import com.android.systemui.deviceentry.shared.FaceAuthUiEvent
+import com.android.systemui.deviceentry.shared.model.FaceAuthenticationStatus
+import com.android.systemui.deviceentry.shared.model.FaceDetectionStatus
 import javax.inject.Inject
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricAuthInteractor.kt
index 1a6bd04..5699176 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricAuthInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricAuthInteractor.kt
@@ -18,13 +18,14 @@
 
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.deviceentry.shared.DeviceEntryBiometricMode
+import com.android.systemui.deviceentry.shared.model.FailedFaceAuthenticationStatus
 import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
-import com.android.systemui.keyguard.shared.model.FailedFaceAuthenticationStatus
 import javax.inject.Inject
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.emptyFlow
+import kotlinx.coroutines.flow.filterIsInstance
 import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.map
 
@@ -62,7 +63,9 @@
     val faceOnlyFaceFailure: Flow<FailedFaceAuthenticationStatus> =
         faceOnly.flatMapLatest { faceOnly ->
             if (faceOnly) {
-                deviceEntryFaceAuthInteractor.faceFailure
+                deviceEntryFaceAuthInteractor.authenticationStatus.filterIsInstance<
+                    FailedFaceAuthenticationStatus
+                >()
             } else {
                 emptyFlow()
             }
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractor.kt
index 70716c6..99bd25b 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractor.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,19 +16,80 @@
 
 package com.android.systemui.deviceentry.domain.interactor
 
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepository
-import com.android.systemui.keyguard.shared.model.FailedFaceAuthenticationStatus
-import javax.inject.Inject
+import com.android.systemui.deviceentry.shared.model.FaceAuthenticationStatus
+import com.android.systemui.deviceentry.shared.model.FaceDetectionStatus
 import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.filterIsInstance
 
-@SysUISingleton
-class DeviceEntryFaceAuthInteractor
-@Inject
-constructor(
-    repository: DeviceEntryFaceAuthRepository,
-) {
-    val faceFailure: Flow<FailedFaceAuthenticationStatus> =
-        repository.authenticationStatus.filterIsInstance<FailedFaceAuthenticationStatus>()
+/**
+ * Interactor that exposes API to get the face authentication status and handle any events that can
+ * cause face authentication to run for device entry.
+ */
+interface DeviceEntryFaceAuthInteractor {
+
+    /** Current authentication status */
+    val authenticationStatus: Flow<FaceAuthenticationStatus>
+
+    /** Current detection status */
+    val detectionStatus: Flow<FaceDetectionStatus>
+
+    /** Can face auth be run right now */
+    fun canFaceAuthRun(): Boolean
+
+    /** Whether face auth is currently running or not. */
+    fun isRunning(): Boolean
+
+    /** Whether face auth is in lock out state. */
+    fun isLockedOut(): Boolean
+
+    /** Whether face auth is enrolled and enabled for the current user */
+    fun isFaceAuthEnabledAndEnrolled(): Boolean
+
+    /** Whether the current user is authenticated successfully with face auth */
+    fun isAuthenticated(): Boolean
+    /**
+     * Register listener for use from code that cannot use [authenticationStatus] or
+     * [detectionStatus]
+     */
+    fun registerListener(listener: FaceAuthenticationListener)
+
+    /** Unregister previously registered listener */
+    fun unregisterListener(listener: FaceAuthenticationListener)
+
+    fun onUdfpsSensorTouched()
+    fun onAssistantTriggeredOnLockScreen()
+    fun onDeviceLifted()
+    fun onQsExpansionStared()
+    fun onNotificationPanelClicked()
+    fun onSwipeUpOnBouncer()
+    fun onPrimaryBouncerUserInput()
+    fun onAccessibilityAction()
+    fun onWalletLaunched()
+
+    /** Whether face auth is considered class 3 */
+    fun isFaceAuthStrong(): Boolean
+}
+
+/**
+ * Listener that can be registered with the [DeviceEntryFaceAuthInteractor] to receive updates about
+ * face authentication & detection updates.
+ *
+ * This is present to make it easier for use the new face auth API for code that cannot use
+ * [DeviceEntryFaceAuthInteractor.authenticationStatus] or
+ * [DeviceEntryFaceAuthInteractor.detectionStatus] flows.
+ */
+interface FaceAuthenticationListener {
+    /** Receive face isAuthenticated updates */
+    fun onAuthenticatedChanged(isAuthenticated: Boolean)
+
+    /** Receive face authentication status updates */
+    fun onAuthenticationStatusChanged(status: FaceAuthenticationStatus)
+
+    /** Receive status updates whenever face detection runs */
+    fun onDetectionStatusChanged(status: FaceDetectionStatus)
+
+    fun onLockoutStateChanged(isLockedOut: Boolean)
+
+    fun onRunningStateChanged(isRunning: Boolean)
+
+    fun onAuthEnrollmentStateChanged(enrolled: Boolean)
 }
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFingerprintAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFingerprintAuthInteractor.kt
index efa1c0a..684627b 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFingerprintAuthInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFingerprintAuthInteractor.kt
@@ -19,6 +19,7 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository
 import com.android.systemui.keyguard.shared.model.FailFingerprintAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.FingerprintAuthenticationStatus
 import javax.inject.Inject
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.filterIsInstance
@@ -31,4 +32,11 @@
 ) {
     val fingerprintFailure: Flow<FailFingerprintAuthenticationStatus> =
         repository.authenticationStatus.filterIsInstance<FailFingerprintAuthenticationStatus>()
+
+    /** Whether fingerprint authentication is currently running or not */
+    val isRunning: Flow<Boolean> = repository.isRunning
+
+    /** Provide the current status of fingerprint authentication. */
+    val authenticationStatus: Flow<FingerprintAuthenticationStatus> =
+        repository.authenticationStatus
 }
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt
index f6a9570..2680328 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt
@@ -20,8 +20,8 @@
 import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.deviceentry.data.repository.DeviceEntryFaceAuthRepository
 import com.android.systemui.deviceentry.data.repository.DeviceEntryRepository
-import com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepository
 import com.android.systemui.keyguard.data.repository.TrustRepository
 import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
 import com.android.systemui.scene.domain.interactor.SceneInteractor
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/NoopDeviceEntryFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/NoopDeviceEntryFaceAuthInteractor.kt
new file mode 100644
index 0000000..3b94166
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/NoopDeviceEntryFaceAuthInteractor.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.deviceentry.domain.interactor
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.deviceentry.shared.model.FaceAuthenticationStatus
+import com.android.systemui.deviceentry.shared.model.FaceDetectionStatus
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.emptyFlow
+
+/**
+ * Implementation of the interactor that noops all face auth operations.
+ *
+ * This is required for SystemUI variants that do not support face authentication but still inject
+ * other SysUI components that depend on [DeviceEntryFaceAuthInteractor]
+ */
+@SysUISingleton
+class NoopDeviceEntryFaceAuthInteractor @Inject constructor() : DeviceEntryFaceAuthInteractor {
+    override val authenticationStatus: Flow<FaceAuthenticationStatus>
+        get() = emptyFlow()
+    override val detectionStatus: Flow<FaceDetectionStatus>
+        get() = emptyFlow()
+
+    override fun canFaceAuthRun(): Boolean = false
+
+    override fun isRunning(): Boolean = false
+
+    override fun isLockedOut(): Boolean = false
+
+    override fun isFaceAuthEnabledAndEnrolled(): Boolean = false
+
+    override fun isFaceAuthStrong(): Boolean = false
+
+    override fun isAuthenticated(): Boolean = false
+
+    override fun registerListener(listener: FaceAuthenticationListener) {}
+
+    override fun unregisterListener(listener: FaceAuthenticationListener) {}
+
+    override fun onUdfpsSensorTouched() {}
+
+    override fun onAssistantTriggeredOnLockScreen() {}
+
+    override fun onDeviceLifted() {}
+
+    override fun onQsExpansionStared() {}
+
+    override fun onNotificationPanelClicked() {}
+
+    override fun onSwipeUpOnBouncer() {}
+    override fun onPrimaryBouncerUserInput() {}
+    override fun onAccessibilityAction() {}
+    override fun onWalletLaunched() = Unit
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/SystemUIDeviceEntryFaceAuthInteractor.kt
similarity index 94%
rename from packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt
rename to packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/SystemUIDeviceEntryFaceAuthInteractor.kt
index e3f4739..98130eb 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/SystemUIDeviceEntryFaceAuthInteractor.kt
@@ -14,14 +14,12 @@
  *   limitations under the License.
  */
 
-package com.android.systemui.keyguard.domain.interactor
+package com.android.systemui.deviceentry.domain.interactor
 
 import android.app.trust.TrustManager
 import android.content.Context
 import android.hardware.biometrics.BiometricFaceConstants
 import android.hardware.biometrics.BiometricSourceType
-import com.android.keyguard.FaceAuthUiEvent
-import com.android.keyguard.FaceWakeUpTriggersConfig
 import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.systemui.CoreStartable
 import com.android.systemui.biometrics.data.repository.FacePropertyRepository
@@ -32,11 +30,14 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.deviceentry.data.repository.DeviceEntryFaceAuthRepository
+import com.android.systemui.deviceentry.data.repository.FaceWakeUpTriggersConfig
+import com.android.systemui.deviceentry.shared.FaceAuthUiEvent
+import com.android.systemui.deviceentry.shared.model.ErrorFaceAuthenticationStatus
+import com.android.systemui.deviceentry.shared.model.FaceAuthenticationStatus
 import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
-import com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepository
 import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository
-import com.android.systemui.keyguard.shared.model.ErrorFaceAuthenticationStatus
-import com.android.systemui.keyguard.shared.model.FaceAuthenticationStatus
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
 import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.log.FaceAuthenticationLogger
 import com.android.systemui.power.domain.interactor.PowerInteractor
@@ -65,7 +66,7 @@
  * SystemUI Keyguard.
  */
 @SysUISingleton
-class SystemUIKeyguardFaceAuthInteractor
+class SystemUIDeviceEntryFaceAuthInteractor
 @Inject
 constructor(
     private val context: Context,
@@ -84,7 +85,7 @@
     private val powerInteractor: PowerInteractor,
     private val biometricSettingsRepository: BiometricSettingsRepository,
     private val trustManager: TrustManager,
-) : CoreStartable, KeyguardFaceAuthInteractor {
+) : CoreStartable, DeviceEntryFaceAuthInteractor {
 
     private val listeners: MutableList<FaceAuthenticationListener> = mutableListOf()
 
@@ -310,7 +311,7 @@
     }
 
     companion object {
-        const val TAG = "KeyguardFaceAuthInteractor"
+        const val TAG = "DeviceEntryFaceAuthInteractor"
     }
 }
 
diff --git a/packages/SystemUI/src/com/android/keyguard/FaceAuthReason.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/shared/FaceAuthReason.kt
similarity index 73%
rename from packages/SystemUI/src/com/android/keyguard/FaceAuthReason.kt
rename to packages/SystemUI/src/com/android/systemui/deviceentry/shared/FaceAuthReason.kt
index 2abb7a4..ee220d5 100644
--- a/packages/SystemUI/src/com/android/keyguard/FaceAuthReason.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/shared/FaceAuthReason.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,58 +14,55 @@
  * limitations under the License.
  */
 
-package com.android.keyguard
+package com.android.systemui.deviceentry.shared
 
 import android.annotation.StringDef
 import android.os.PowerManager
 import com.android.internal.logging.UiEvent
 import com.android.internal.logging.UiEventLogger
-import com.android.keyguard.FaceAuthApiRequestReason.Companion.ACCESSIBILITY_ACTION
-import com.android.keyguard.FaceAuthApiRequestReason.Companion.NOTIFICATION_PANEL_CLICKED
-import com.android.keyguard.FaceAuthApiRequestReason.Companion.PICK_UP_GESTURE_TRIGGERED
-import com.android.keyguard.FaceAuthApiRequestReason.Companion.QS_EXPANDED
-import com.android.keyguard.FaceAuthApiRequestReason.Companion.SWIPE_UP_ON_BOUNCER
-import com.android.keyguard.FaceAuthApiRequestReason.Companion.UDFPS_POINTER_DOWN
-import com.android.keyguard.InternalFaceAuthReasons.ALL_AUTHENTICATORS_REGISTERED
-import com.android.keyguard.InternalFaceAuthReasons.ALTERNATE_BIOMETRIC_BOUNCER_SHOWN
-import com.android.keyguard.InternalFaceAuthReasons.ASSISTANT_VISIBILITY_CHANGED
-import com.android.keyguard.InternalFaceAuthReasons.AUTH_REQUEST_DURING_CANCELLATION
-import com.android.keyguard.InternalFaceAuthReasons.BIOMETRIC_ENABLED
-import com.android.keyguard.InternalFaceAuthReasons.CAMERA_LAUNCHED
-import com.android.keyguard.InternalFaceAuthReasons.DEVICE_WOKEN_UP_ON_REACH_GESTURE
-import com.android.keyguard.InternalFaceAuthReasons.DISPLAY_OFF
-import com.android.keyguard.InternalFaceAuthReasons.DREAM_STARTED
-import com.android.keyguard.InternalFaceAuthReasons.DREAM_STOPPED
-import com.android.keyguard.InternalFaceAuthReasons.ENROLLMENTS_CHANGED
-import com.android.keyguard.InternalFaceAuthReasons.FACE_AUTHENTICATED
-import com.android.keyguard.InternalFaceAuthReasons.FACE_AUTH_STOPPED_ON_USER_INPUT
-import com.android.keyguard.InternalFaceAuthReasons.FACE_CANCEL_NOT_RECEIVED
-import com.android.keyguard.InternalFaceAuthReasons.FACE_LOCKOUT_RESET
-import com.android.keyguard.InternalFaceAuthReasons.FINISHED_GOING_TO_SLEEP
-import com.android.keyguard.InternalFaceAuthReasons.FP_AUTHENTICATED
-import com.android.keyguard.InternalFaceAuthReasons.FP_LOCKED_OUT
-import com.android.keyguard.InternalFaceAuthReasons.GOING_TO_SLEEP
-import com.android.keyguard.InternalFaceAuthReasons.KEYGUARD_GOING_AWAY
-import com.android.keyguard.InternalFaceAuthReasons.KEYGUARD_INIT
-import com.android.keyguard.InternalFaceAuthReasons.KEYGUARD_OCCLUSION_CHANGED
-import com.android.keyguard.InternalFaceAuthReasons.KEYGUARD_RESET
-import com.android.keyguard.InternalFaceAuthReasons.KEYGUARD_VISIBILITY_CHANGED
-import com.android.keyguard.InternalFaceAuthReasons.NON_STRONG_BIOMETRIC_ALLOWED_CHANGED
-import com.android.keyguard.InternalFaceAuthReasons.OCCLUDING_APP_REQUESTED
-import com.android.keyguard.InternalFaceAuthReasons.POSTURE_CHANGED
-import com.android.keyguard.InternalFaceAuthReasons.PRIMARY_BOUNCER_SHOWN
-import com.android.keyguard.InternalFaceAuthReasons.PRIMARY_BOUNCER_SHOWN_OR_WILL_BE_SHOWN
-import com.android.keyguard.InternalFaceAuthReasons.RETRY_AFTER_HW_UNAVAILABLE
-import com.android.keyguard.InternalFaceAuthReasons.STARTED_WAKING_UP
-import com.android.keyguard.InternalFaceAuthReasons.STRONG_AUTH_ALLOWED_CHANGED
-import com.android.keyguard.InternalFaceAuthReasons.TRUST_DISABLED
-import com.android.keyguard.InternalFaceAuthReasons.TRUST_ENABLED
-import com.android.keyguard.InternalFaceAuthReasons.USER_SWITCHING
+import com.android.systemui.deviceentry.shared.FaceAuthApiRequestReason.Companion.ACCESSIBILITY_ACTION
+import com.android.systemui.deviceentry.shared.FaceAuthApiRequestReason.Companion.NOTIFICATION_PANEL_CLICKED
+import com.android.systemui.deviceentry.shared.FaceAuthApiRequestReason.Companion.PICK_UP_GESTURE_TRIGGERED
+import com.android.systemui.deviceentry.shared.FaceAuthApiRequestReason.Companion.QS_EXPANDED
+import com.android.systemui.deviceentry.shared.FaceAuthApiRequestReason.Companion.SWIPE_UP_ON_BOUNCER
+import com.android.systemui.deviceentry.shared.FaceAuthApiRequestReason.Companion.UDFPS_POINTER_DOWN
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.ALL_AUTHENTICATORS_REGISTERED
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.ALTERNATE_BIOMETRIC_BOUNCER_SHOWN
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.ASSISTANT_VISIBILITY_CHANGED
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.AUTH_REQUEST_DURING_CANCELLATION
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.BIOMETRIC_ENABLED
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.CAMERA_LAUNCHED
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.DEVICE_WOKEN_UP_ON_REACH_GESTURE
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.DISPLAY_OFF
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.DREAM_STARTED
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.DREAM_STOPPED
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.ENROLLMENTS_CHANGED
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.FACE_AUTHENTICATED
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.FACE_AUTH_STOPPED_ON_USER_INPUT
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.FACE_CANCEL_NOT_RECEIVED
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.FACE_LOCKOUT_RESET
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.FINISHED_GOING_TO_SLEEP
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.FP_AUTHENTICATED
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.FP_LOCKED_OUT
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.GOING_TO_SLEEP
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.KEYGUARD_GOING_AWAY
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.KEYGUARD_INIT
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.KEYGUARD_OCCLUSION_CHANGED
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.KEYGUARD_RESET
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.KEYGUARD_VISIBILITY_CHANGED
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.NON_STRONG_BIOMETRIC_ALLOWED_CHANGED
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.OCCLUDING_APP_REQUESTED
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.POSTURE_CHANGED
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.PRIMARY_BOUNCER_SHOWN
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.PRIMARY_BOUNCER_SHOWN_OR_WILL_BE_SHOWN
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.RETRY_AFTER_HW_UNAVAILABLE
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.STARTED_WAKING_UP
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.STRONG_AUTH_ALLOWED_CHANGED
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.TRUST_DISABLED
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.TRUST_ENABLED
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.USER_SWITCHING
 
-/**
- * List of reasons why face auth is requested by clients through
- * [KeyguardUpdateMonitor.requestFaceAuth].
- */
+/** List of reasons why face auth is requested by clients. */
 @Retention(AnnotationRetention.SOURCE)
 @StringDef(
     SWIPE_UP_ON_BOUNCER,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FaceAuthenticationModels.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/shared/model/FaceAuthenticationModels.kt
similarity index 95%
rename from packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FaceAuthenticationModels.kt
rename to packages/SystemUI/src/com/android/systemui/deviceentry/shared/model/FaceAuthenticationModels.kt
index 3de3666..f006b34 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FaceAuthenticationModels.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/shared/model/FaceAuthenticationModels.kt
@@ -14,14 +14,14 @@
  * limitations under the License.
  */
 
-package com.android.systemui.keyguard.shared.model
+package com.android.systemui.deviceentry.shared.model
 
 import android.hardware.face.FaceManager
 import android.os.SystemClock.elapsedRealtime
 
 /**
  * Authentication status provided by
- * [com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepository]
+ * [com.android.systemui.deviceentry.data.repository.DeviceEntryFaceAuthRepository]
  */
 sealed class FaceAuthenticationStatus
 
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/ui/viewmodel/AlternateBouncerUdfpsAccessibilityOverlayViewModel.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/ui/viewmodel/AlternateBouncerUdfpsAccessibilityOverlayViewModel.kt
new file mode 100644
index 0000000..eeac527
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/ui/viewmodel/AlternateBouncerUdfpsAccessibilityOverlayViewModel.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.deviceentry.ui.viewmodel
+
+import com.android.systemui.accessibility.domain.interactor.AccessibilityInteractor
+import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
+import javax.inject.Inject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flowOf
+
+/** Models the UI state for the alternate bouncer UDFPS accessibility overlay */
+@ExperimentalCoroutinesApi
+class AlternateBouncerUdfpsAccessibilityOverlayViewModel
+@Inject
+constructor(
+    udfpsOverlayInteractor: UdfpsOverlayInteractor,
+    accessibilityInteractor: AccessibilityInteractor,
+) :
+    UdfpsAccessibilityOverlayViewModel(
+        udfpsOverlayInteractor,
+        accessibilityInteractor,
+    ) {
+    /** Overlay is always visible if touch exploration is enabled on the alternate bouncer. */
+    override fun isVisibleWhenTouchExplorationEnabled(): Flow<Boolean> = flowOf(true)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/ui/viewmodel/DeviceEntryUdfpsAccessibilityOverlayViewModel.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/ui/viewmodel/DeviceEntryUdfpsAccessibilityOverlayViewModel.kt
new file mode 100644
index 0000000..af51576
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/ui/viewmodel/DeviceEntryUdfpsAccessibilityOverlayViewModel.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.deviceentry.ui.viewmodel
+
+import com.android.systemui.accessibility.domain.interactor.AccessibilityInteractor
+import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
+import com.android.systemui.keyguard.ui.view.DeviceEntryIconView
+import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryForegroundViewModel
+import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryIconViewModel
+import javax.inject.Inject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+
+/** Models the UI state for the non-alternate bouncer UDFPS accessibility overlay */
+@ExperimentalCoroutinesApi
+class DeviceEntryUdfpsAccessibilityOverlayViewModel
+@Inject
+constructor(
+    udfpsOverlayInteractor: UdfpsOverlayInteractor,
+    accessibilityInteractor: AccessibilityInteractor,
+    private val deviceEntryIconViewModel: DeviceEntryIconViewModel,
+    private val deviceEntryFgIconViewModel: DeviceEntryForegroundViewModel,
+) :
+    UdfpsAccessibilityOverlayViewModel(
+        udfpsOverlayInteractor,
+        accessibilityInteractor,
+    ) {
+    /** Overlay is only visible if the UDFPS icon is visible on the keyguard. */
+    override fun isVisibleWhenTouchExplorationEnabled(): Flow<Boolean> =
+        combine(
+            deviceEntryFgIconViewModel.viewModel,
+            deviceEntryIconViewModel.deviceEntryViewAlpha,
+        ) { iconViewModel, alpha ->
+            iconViewModel.type == DeviceEntryIconView.IconType.FINGERPRINT &&
+                !iconViewModel.useAodVariant &&
+                alpha == 1f
+        }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/ui/viewmodel/UdfpsAccessibilityOverlayViewModel.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/ui/viewmodel/UdfpsAccessibilityOverlayViewModel.kt
index 80684b4..f5a8870 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/ui/viewmodel/UdfpsAccessibilityOverlayViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/ui/viewmodel/UdfpsAccessibilityOverlayViewModel.kt
@@ -23,48 +23,33 @@
 import com.android.systemui.biometrics.UdfpsUtils
 import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
 import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams
-import com.android.systemui.keyguard.ui.view.DeviceEntryIconView
-import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryForegroundViewModel
-import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryIconViewModel
-import javax.inject.Inject
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.flowOf
 
 /** Models the UI state for the UDFPS accessibility overlay */
 @ExperimentalCoroutinesApi
-class UdfpsAccessibilityOverlayViewModel
-@Inject
-constructor(
+abstract class UdfpsAccessibilityOverlayViewModel(
     udfpsOverlayInteractor: UdfpsOverlayInteractor,
     accessibilityInteractor: AccessibilityInteractor,
-    deviceEntryIconViewModel: DeviceEntryIconViewModel,
-    deviceEntryFgIconViewModel: DeviceEntryForegroundViewModel,
 ) {
     private val udfpsUtils = UdfpsUtils()
     private val udfpsOverlayParams: StateFlow<UdfpsOverlayParams> =
         udfpsOverlayInteractor.udfpsOverlayParams
 
-    /** Overlay is only visible if touch exploration is enabled and UDFPS can be used. */
     val visible: Flow<Boolean> =
         accessibilityInteractor.isTouchExplorationEnabled.flatMapLatest { touchExplorationEnabled ->
             if (touchExplorationEnabled) {
-                combine(
-                    deviceEntryFgIconViewModel.viewModel,
-                    deviceEntryIconViewModel.deviceEntryViewAlpha,
-                ) { iconViewModel, alpha ->
-                    iconViewModel.type == DeviceEntryIconView.IconType.FINGERPRINT &&
-                        !iconViewModel.useAodVariant &&
-                        alpha == 1f
-                }
+                isVisibleWhenTouchExplorationEnabled()
             } else {
                 flowOf(false)
             }
         }
 
+    abstract fun isVisibleWhenTouchExplorationEnabled(): Flow<Boolean>
+
     /** Give directional feedback to help the user authenticate with UDFPS. */
     fun onHoverEvent(v: View, event: MotionEvent): Boolean {
         val overlayParams = udfpsOverlayParams.value
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
index 4c4aa5c..8776ec5 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
@@ -118,6 +118,11 @@
          * Called when the dozing state may have been updated.
          */
         default void onDozingChanged(boolean isDozing) {}
+
+        /**
+         * Called when fingerprint acquisition has started and screen state might need updating.
+         */
+        default void onSideFingerprintAcquisitionStarted() {}
     }
 
     interface PulseCallback {
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
index 5b90ef2..424bd0a 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
@@ -514,6 +514,7 @@
             case REASON_SENSOR_TAP: return "tap";
             case REASON_SENSOR_UDFPS_LONG_PRESS: return "udfps";
             case REASON_SENSOR_QUICK_PICKUP: return "quickPickup";
+            case PULSE_REASON_FINGERPRINT_ACTIVATED: return "fingerprint-triggered";
             default: throw new IllegalArgumentException("invalid reason: " + pulseReason);
         }
     }
@@ -542,7 +543,9 @@
             PULSE_REASON_SENSOR_SIGMOTION, REASON_SENSOR_PICKUP, REASON_SENSOR_DOUBLE_TAP,
             PULSE_REASON_SENSOR_LONG_PRESS, PULSE_REASON_DOCKING, REASON_SENSOR_WAKE_UP_PRESENCE,
             PULSE_REASON_SENSOR_WAKE_REACH, REASON_SENSOR_TAP,
-            REASON_SENSOR_UDFPS_LONG_PRESS, REASON_SENSOR_QUICK_PICKUP})
+            REASON_SENSOR_UDFPS_LONG_PRESS, REASON_SENSOR_QUICK_PICKUP,
+            PULSE_REASON_FINGERPRINT_ACTIVATED
+    })
     public @interface Reason {}
     public static final int PULSE_REASON_NONE = -1;
     public static final int PULSE_REASON_INTENT = 0;
@@ -557,6 +560,7 @@
     public static final int REASON_SENSOR_TAP = 9;
     public static final int REASON_SENSOR_UDFPS_LONG_PRESS = 10;
     public static final int REASON_SENSOR_QUICK_PICKUP = 11;
+    public static final int PULSE_REASON_FINGERPRINT_ACTIVATED = 12;
 
-    public static final int TOTAL_REASONS = 12;
+    public static final int TOTAL_REASONS = 13;
 }
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index 795c3d4..9311187 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -265,6 +265,10 @@
         mDozeLog.traceNotificationPulse();
     }
 
+    private void onSideFingerprintAcquisitionStarted() {
+        requestPulse(DozeLog.PULSE_REASON_FINGERPRINT_ACTIVATED, false, null);
+    }
+
     private static void runIfNotNull(Runnable runnable) {
         if (runnable != null) {
             runnable.run();
@@ -690,5 +694,10 @@
         public void onNotificationAlerted(Runnable onPulseSuppressedListener) {
             onNotification(onPulseSuppressedListener);
         }
+
+        @Override
+        public void onSideFingerprintAcquisitionStarted() {
+            DozeTriggers.this.onSideFingerprintAcquisitionStarted();
+        }
     };
 }
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java
index 4e4b79c..7f3b5eb 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java
@@ -155,12 +155,12 @@
                         return true;
                     }
 
-                    // Don't set expansion if the user doesn't have a pin/password set so that no
-                    // animations are played we're not transitioning to the bouncer.
-                    if (!mLockPatternUtils.isSecure(mUserTracker.getUserId())) {
-                        // Return false so the gesture is not consumed, allowing the dream to wake
-                        // if it wants instead of doing nothing.
-                        return false;
+                    // If scrolling up and keyguard is not locked, dismiss the dream since there's
+                    // no bouncer to show.
+                    if (e1.getY() > e2.getY()
+                            && !mLockPatternUtils.isSecure(mUserTracker.getUserId())) {
+                        mCentralSurfaces.get().awakenDreams();
+                        return true;
                     }
 
                     // For consistency, we adopt the expansion definition found in the
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index f6db978..89a983b 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -122,11 +122,6 @@
     val NEW_UNLOCK_SWIPE_ANIMATION = releasedFlag("new_unlock_swipe_animation")
     val CHARGING_RIPPLE = resourceBooleanFlag(R.bool.flag_charging_ripple, "charging_ripple")
 
-    // TODO(b/254512281): Tracking Bug
-    @JvmField
-    val BOUNCER_USER_SWITCHER =
-        resourceBooleanFlag(R.bool.config_enableBouncerUserSwitcher, "bouncer_user_switcher")
-
     // TODO(b/254512676): Tracking Bug
     @JvmField
     val LOCKSCREEN_CUSTOM_CLOCKS =
@@ -208,10 +203,6 @@
     val WALLPAPER_PICKER_GRID_APPLY_BUTTON =
             unreleasedFlag("wallpaper_picker_grid_apply_button")
 
-    /** Provide new auth messages on the bouncer. */
-    // TODO(b/277961132): Tracking bug.
-    @JvmField val REVAMPED_BOUNCER_MESSAGES = unreleasedFlag("revamped_bouncer_messages")
-
     /** Keyguard Migration */
 
     // TODO(b/297037052): Tracking bug.
@@ -381,9 +372,6 @@
     // 1000 - dock
     val SIMULATE_DOCK_THROUGH_CHARGING = releasedFlag("simulate_dock_through_charging")
 
-    // TODO(b/254512758): Tracking Bug
-    @JvmField val ROUNDED_BOX_RIPPLE = releasedFlag("rounded_box_ripple")
-
     // TODO(b/273509374): Tracking Bug
     @JvmField
     val ALWAYS_SHOW_HOME_CONTROLS_ON_DREAMS =
@@ -448,34 +436,10 @@
     // TODO(b/254512728): Tracking Bug
     @JvmField val NEW_BACK_AFFORDANCE = releasedFlag("new_back_affordance")
 
-    // TODO(b/255854141): Tracking Bug
-    @JvmField
-    val WM_ENABLE_PREDICTIVE_BACK_SYSUI =
-        unreleasedFlag("persist.wm.debug.predictive_back_sysui_enable", teamfood = true)
 
     // TODO(b/270987164): Tracking Bug
     @JvmField val TRACKPAD_GESTURE_FEATURES = releasedFlag("trackpad_gesture_features")
 
-    // TODO(b/263826204): Tracking Bug
-    @JvmField
-    val WM_ENABLE_PREDICTIVE_BACK_BOUNCER_ANIM =
-        unreleasedFlag("persist.wm.debug.predictive_back_bouncer_anim", teamfood = true)
-
-    // TODO(b/238475428): Tracking Bug
-    @JvmField
-    val WM_SHADE_ALLOW_BACK_GESTURE =
-        sysPropBooleanFlag("persist.wm.debug.shade_allow_back_gesture", default = false)
-
-    // TODO(b/238475428): Tracking Bug
-    @JvmField
-    val WM_SHADE_ANIMATE_BACK_GESTURE =
-        unreleasedFlag("persist.wm.debug.shade_animate_back_gesture", teamfood = false)
-
-    // TODO(b/265639042): Tracking Bug
-    @JvmField
-    val WM_ENABLE_PREDICTIVE_BACK_QS_DIALOG_ANIM =
-        unreleasedFlag("persist.wm.debug.predictive_back_qs_dialog_anim", teamfood = true)
-
     // TODO(b/273800936): Tracking Bug
     @JvmField val TRACKPAD_GESTURE_COMMON = releasedFlag("trackpad_gesture_common")
 
@@ -585,10 +549,6 @@
     @JvmField
     val SPLIT_SHADE_SUBPIXEL_OPTIMIZATION = unreleasedFlag("split_shade_subpixel_optimization")
 
-    // TODO(b/288868056): Tracking Bug
-    @JvmField
-    val PARTIAL_SCREEN_SHARING_TASK_SWITCHER = unreleasedFlag("pss_task_switcher")
-
     // TODO(b/278761837): Tracking Bug
     @JvmField val USE_NEW_ACTIVITY_STARTER = releasedFlag(name = "use_new_activity_starter")
 
@@ -609,11 +569,6 @@
     val BIGPICTURE_NOTIFICATION_LAZY_LOADING =
         unreleasedFlag("bigpicture_notification_lazy_loading")
 
-    // TODO(b/292062937): Tracking bug
-    @JvmField
-    val NOTIFICATION_CLEARABLE_REFACTOR =
-            unreleasedFlag("notification_clearable_refactor")
-
     // TODO(b/283740863): Tracking Bug
     @JvmField
     val ENABLE_NEW_PRIVACY_DIALOG = releasedFlag("enable_new_privacy_dialog")
diff --git a/packages/SystemUI/src/com/android/systemui/flags/SystemPropertiesHelper.kt b/packages/SystemUI/src/com/android/systemui/flags/SystemPropertiesHelper.kt
index 6c16097..6fa20de 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/SystemPropertiesHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/SystemPropertiesHelper.kt
@@ -17,19 +17,22 @@
 package com.android.systemui.flags
 
 import android.os.SystemProperties
-import com.android.systemui.dagger.SysUISingleton
-
 import javax.inject.Inject
+import javax.inject.Singleton
 
 /**
  * Proxy to make {@link SystemProperties} easily testable.
  */
-@SysUISingleton
+@Singleton
 open class SystemPropertiesHelper @Inject constructor() {
     fun get(name: String): String {
         return SystemProperties.get(name)
     }
 
+    fun get(name: String, def: String?): String {
+        return SystemProperties.get(name, def)
+    }
+
     fun getBoolean(name: String, default: Boolean): Boolean {
         return SystemProperties.getBoolean(name, default)
     }
diff --git a/packages/SystemUI/src/com/android/systemui/haptics/slider/SeekableSliderEventProducer.kt b/packages/SystemUI/src/com/android/systemui/haptics/slider/SeekableSliderEventProducer.kt
index 629b361..cfa5294 100644
--- a/packages/SystemUI/src/com/android/systemui/haptics/slider/SeekableSliderEventProducer.kt
+++ b/packages/SystemUI/src/com/android/systemui/haptics/slider/SeekableSliderEventProducer.kt
@@ -65,4 +65,11 @@
             SliderEvent(SliderEventType.STOPPED_TRACKING_TOUCH, previousEvent.currentProgress)
         }
     }
+
+    /** The arrow navigation that was operating the slider has stopped. */
+    fun onArrowUp() {
+        _currentEvent.update { previousEvent ->
+            SliderEvent(SliderEventType.ARROW_UP, previousEvent.currentProgress)
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/haptics/slider/SeekableSliderTracker.kt b/packages/SystemUI/src/com/android/systemui/haptics/slider/SeekableSliderTracker.kt
index d89cf63..10098fa 100644
--- a/packages/SystemUI/src/com/android/systemui/haptics/slider/SeekableSliderTracker.kt
+++ b/packages/SystemUI/src/com/android/systemui/haptics/slider/SeekableSliderTracker.kt
@@ -58,7 +58,7 @@
 
     override suspend fun iterateState(event: SliderEvent) {
         when (currentState) {
-            SliderState.IDLE -> handleIdle(event.type)
+            SliderState.IDLE -> handleIdle(event.type, event.currentProgress)
             SliderState.WAIT -> handleWait(event.type, event.currentProgress)
             SliderState.DRAG_HANDLE_ACQUIRED_BY_TOUCH -> handleAcquired(event.type)
             SliderState.DRAG_HANDLE_DRAGGING -> handleDragging(event.type, event.currentProgress)
@@ -67,17 +67,26 @@
             SliderState.DRAG_HANDLE_RELEASED_FROM_TOUCH -> setState(SliderState.IDLE)
             SliderState.JUMP_TRACK_LOCATION_SELECTED -> handleJumpToTrack(event.type)
             SliderState.JUMP_BOOKEND_SELECTED -> handleJumpToBookend(event.type)
+            SliderState.ARROW_HANDLE_MOVED_ONCE -> handleArrowOnce(event.type)
+            SliderState.ARROW_HANDLE_MOVES_CONTINUOUSLY ->
+                handleArrowContinuous(event.type, event.currentProgress)
+            SliderState.ARROW_HANDLE_REACHED_BOOKEND -> handleArrowBookend()
         }
         latestProgress = event.currentProgress
     }
 
-    private fun handleIdle(newEventType: SliderEventType) {
+    private fun handleIdle(newEventType: SliderEventType, currentProgress: Float) {
         if (newEventType == SliderEventType.STARTED_TRACKING_TOUCH) {
             timerJob = launchTimer()
             // The WAIT state will wait for the timer to complete or a slider progress to occur.
             // This will disambiguate between an imprecise touch that acquires the slider handle,
             // and a select and jump operation in the slider track.
             setState(SliderState.WAIT)
+        } else if (newEventType == SliderEventType.PROGRESS_CHANGE_BY_PROGRAM) {
+            val state =
+                if (bookendReached(currentProgress)) SliderState.ARROW_HANDLE_REACHED_BOOKEND
+                else SliderState.ARROW_HANDLE_MOVED_ONCE
+            setState(state)
         }
     }
 
@@ -176,6 +185,13 @@
             SliderState.DRAG_HANDLE_REACHED_BOOKEND -> executeOnBookend()
             SliderState.JUMP_TRACK_LOCATION_SELECTED ->
                 sliderListener.onProgressJump(latestProgress)
+            SliderState.ARROW_HANDLE_MOVED_ONCE -> sliderListener.onSelectAndArrow(latestProgress)
+            SliderState.ARROW_HANDLE_MOVES_CONTINUOUSLY -> sliderListener.onProgress(latestProgress)
+            SliderState.ARROW_HANDLE_REACHED_BOOKEND -> {
+                executeOnBookend()
+                // This transitory execution must also reset the state
+                resetState()
+            }
             else -> {}
         }
     }
@@ -204,6 +220,43 @@
             currentProgress <= config.lowerBookendThreshold
     }
 
+    private fun handleArrowOnce(newEventType: SliderEventType) {
+        val nextState =
+            when (newEventType) {
+                SliderEventType.STARTED_TRACKING_TOUCH -> {
+                    // Launching the timer and going to WAIT
+                    timerJob = launchTimer()
+                    SliderState.WAIT
+                }
+                SliderEventType.PROGRESS_CHANGE_BY_PROGRAM ->
+                    SliderState.ARROW_HANDLE_MOVES_CONTINUOUSLY
+                SliderEventType.ARROW_UP -> SliderState.IDLE
+                else -> SliderState.ARROW_HANDLE_MOVED_ONCE
+            }
+        setState(nextState)
+    }
+
+    private fun handleArrowContinuous(newEventType: SliderEventType, currentProgress: Float) {
+        val reachedBookend = bookendReached(currentProgress)
+        val nextState =
+            when (newEventType) {
+                SliderEventType.ARROW_UP -> SliderState.IDLE
+                SliderEventType.STARTED_TRACKING_TOUCH -> {
+                    // Launching the timer and going to WAIT
+                    timerJob = launchTimer()
+                    SliderState.WAIT
+                }
+                SliderEventType.PROGRESS_CHANGE_BY_PROGRAM -> {
+                    if (reachedBookend) SliderState.ARROW_HANDLE_REACHED_BOOKEND
+                    else SliderState.ARROW_HANDLE_MOVES_CONTINUOUSLY
+                }
+                else -> SliderState.ARROW_HANDLE_MOVES_CONTINUOUSLY
+            }
+        setState(nextState)
+    }
+
+    private fun handleArrowBookend() = setState(SliderState.IDLE)
+
     @VisibleForTesting
     fun setState(state: SliderState) {
         currentState = state
diff --git a/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderEventType.kt b/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderEventType.kt
index 413e277..4a63941 100644
--- a/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderEventType.kt
+++ b/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderEventType.kt
@@ -29,5 +29,5 @@
     /* The slider has stopped tracking touch events. */
     STOPPED_TRACKING_TOUCH,
     /* The external (not touch) stimulus that was modifying the slider progress has stopped. */
-    EXTERNAL_STIMULUS_RELEASE,
+    ARROW_UP,
 }
diff --git a/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderHapticFeedbackConfig.kt b/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderHapticFeedbackConfig.kt
index 6cb68ba..89bfd96 100644
--- a/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderHapticFeedbackConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderHapticFeedbackConfig.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.haptics.slider
 
+import android.view.MotionEvent
 import androidx.annotation.FloatRange
 
 /** Configuration parameters of a [SliderHapticFeedbackProvider] */
@@ -38,6 +39,8 @@
     val numberOfLowTicks: Int = 5,
     /** Maximum velocity allowed for vibration scaling. This is not expected to change. */
     val maxVelocityToScale: Float = 2000f, /* In pixels/sec */
+    /** Axis to use when computing velocity. Must be the same as the slider's axis of movement */
+    val velocityAxis: Int = MotionEvent.AXIS_X,
     /** Vibration scale at the upper bookend of the slider */
     @FloatRange(from = 0.0, to = 1.0) val upperBookendScale: Float = 1f,
     /** Vibration scale at the lower bookend of the slider */
diff --git a/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProvider.kt b/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProvider.kt
index 9e6245a..6f28ab7 100644
--- a/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProvider.kt
@@ -162,27 +162,33 @@
 
     override fun onLowerBookend() {
         if (!hasVibratedAtLowerBookend) {
-            velocityTracker.computeCurrentVelocity(UNITS_SECOND, config.maxVelocityToScale)
-            vibrateOnEdgeCollision(abs(velocityTracker.xVelocity))
+            vibrateOnEdgeCollision(abs(getTrackedVelocity()))
             hasVibratedAtLowerBookend = true
         }
     }
 
     override fun onUpperBookend() {
         if (!hasVibratedAtUpperBookend) {
-            velocityTracker.computeCurrentVelocity(UNITS_SECOND, config.maxVelocityToScale)
-            vibrateOnEdgeCollision(abs(velocityTracker.xVelocity))
+            vibrateOnEdgeCollision(abs(getTrackedVelocity()))
             hasVibratedAtUpperBookend = true
         }
     }
 
     override fun onProgress(@FloatRange(from = 0.0, to = 1.0) progress: Float) {
-        velocityTracker.computeCurrentVelocity(UNITS_SECOND, config.maxVelocityToScale)
-        vibrateDragTexture(abs(velocityTracker.xVelocity), progress)
+        vibrateDragTexture(abs(getTrackedVelocity()), progress)
         hasVibratedAtUpperBookend = false
         hasVibratedAtLowerBookend = false
     }
 
+    private fun getTrackedVelocity(): Float {
+        velocityTracker.computeCurrentVelocity(UNITS_SECOND, config.maxVelocityToScale)
+        return if (velocityTracker.isAxisSupported(config.velocityAxis)) {
+            velocityTracker.getAxisVelocity(config.velocityAxis)
+        } else {
+            0f
+        }
+    }
+
     override fun onProgressJump(@FloatRange(from = 0.0, to = 1.0) progress: Float) {}
 
     override fun onSelectAndArrow(@FloatRange(from = 0.0, to = 1.0) progress: Float) {}
diff --git a/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderState.kt b/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderState.kt
index fe092e6..de6ddd7 100644
--- a/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderState.kt
+++ b/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderState.kt
@@ -32,6 +32,12 @@
     DRAG_HANDLE_REACHED_BOOKEND,
     /* A location in the slider track has been selected. */
     JUMP_TRACK_LOCATION_SELECTED,
-    /* The slider handled moved to a bookend after it was selected. */
+    /* The slider handle moved to a bookend after it was selected. */
     JUMP_BOOKEND_SELECTED,
+    /** The slider handle moved due to single select-and-arrow operation */
+    ARROW_HANDLE_MOVED_ONCE,
+    /** The slider handle moves continuously due to constant select-and-arrow operations */
+    ARROW_HANDLE_MOVES_CONTINUOUSLY,
+    /** The slider handle reached a bookend due to a select-and-arrow operation */
+    ARROW_HANDLE_REACHED_BOOKEND,
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java
index aa4c88a..e23ec89 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java
@@ -403,6 +403,7 @@
     public static final int INDICATION_TYPE_REVERSE_CHARGING = 10;
     public static final int INDICATION_TYPE_BIOMETRIC_MESSAGE = 11;
     public static final int INDICATION_TYPE_BIOMETRIC_MESSAGE_FOLLOW_UP = 12;
+    public static final int INDICATION_IS_DISMISSIBLE = 13;
 
     @IntDef({
             INDICATION_TYPE_NONE,
@@ -417,7 +418,8 @@
             INDICATION_TYPE_USER_LOCKED,
             INDICATION_TYPE_REVERSE_CHARGING,
             INDICATION_TYPE_BIOMETRIC_MESSAGE,
-            INDICATION_TYPE_BIOMETRIC_MESSAGE_FOLLOW_UP
+            INDICATION_TYPE_BIOMETRIC_MESSAGE_FOLLOW_UP,
+            INDICATION_IS_DISMISSIBLE
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface IndicationType{}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index edf9648..e2ab20e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -593,6 +593,13 @@
             mKeyguardViewMediator.doKeyguardTimeout(options);
         }
 
+        // Binder interface
+        public void showDismissibleKeyguard() {
+            trace("showDismissibleKeyguard");
+            checkPermission();
+            mKeyguardViewMediator.showDismissibleKeyguard();
+        }
+
         @Override // Binder interface
         public void setSwitchingUser(boolean switching) {
             trace("setSwitchingUser switching=" + switching);
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
index 1f69cc0..0d40511 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
@@ -397,8 +397,10 @@
             IntentFilter filter = new IntentFilter();
             filter.addAction(Intent.ACTION_DATE_CHANGED);
             filter.addAction(Intent.ACTION_LOCALE_CHANGED);
-            getContext().registerReceiver(mIntentReceiver, filter, null /* permission*/,
-                    null /* scheduler */);
+            mBgHandler.post(() -> {
+                getContext().registerReceiver(mIntentReceiver, filter, null /* permission*/,
+                        null /* scheduler */);
+            });
             mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback);
             mRegistered = true;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
index af5d48d..afef875 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
@@ -39,12 +39,14 @@
 import com.android.systemui.keyguard.ui.view.KeyguardIndicationArea
 import com.android.systemui.keyguard.ui.view.KeyguardRootView
 import com.android.systemui.keyguard.ui.view.layout.KeyguardBlueprintCommandListener
+import com.android.systemui.keyguard.ui.viewmodel.AodAlphaViewModel
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardBlueprintViewModel
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardIndicationAreaViewModel
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
 import com.android.systemui.keyguard.ui.viewmodel.OccludingAppDeviceEntryMessageViewModel
 import com.android.systemui.plugins.FalsingManager
 import com.android.systemui.res.R
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.shade.NotificationShadeWindowView
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
 import com.android.systemui.statusbar.KeyguardIndicationController
@@ -83,6 +85,7 @@
     private val deviceEntryHapticsInteractor: DeviceEntryHapticsInteractor,
     private val vibratorHelper: VibratorHelper,
     private val falsingManager: FalsingManager,
+    private val aodAlphaViewModel: AodAlphaViewModel,
 ) : CoreStartable {
 
     private var rootViewHandle: DisposableHandle? = null
@@ -109,7 +112,9 @@
         bindKeyguardRootView()
         initializeViews()
 
-        KeyguardBlueprintViewBinder.bind(keyguardRootView, keyguardBlueprintViewModel)
+        if (!SceneContainerFlag.isEnabled) {
+            KeyguardBlueprintViewBinder.bind(keyguardRootView, keyguardBlueprintViewModel)
+        }
         keyguardBlueprintCommandListener.start()
     }
 
@@ -126,7 +131,7 @@
             KeyguardIndicationAreaBinder.bind(
                 notificationShadeWindowView.requireViewById(R.id.keyguard_indication_area),
                 keyguardIndicationAreaViewModel,
-                keyguardRootViewModel,
+                aodAlphaViewModel,
                 indicationController,
             )
     }
@@ -142,13 +147,16 @@
     }
 
     private fun bindKeyguardRootView() {
+        if (SceneContainerFlag.isEnabled) {
+            return
+        }
+
         rootViewHandle?.dispose()
         rootViewHandle =
             KeyguardRootViewBinder.bind(
                 keyguardRootView,
                 keyguardRootViewModel,
                 configuration,
-                featureFlags,
                 occludingAppDeviceEntryMessageViewModel,
                 chipbarCoordinator,
                 screenOffAnimationController,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index d7a1906..5cebd96 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -107,6 +107,7 @@
 import androidx.annotation.VisibleForTesting;
 
 import com.android.app.animation.Interpolators;
+import com.android.internal.foldables.FoldGracePeriodProvider;
 import com.android.internal.jank.InteractionJankMonitor;
 import com.android.internal.jank.InteractionJankMonitor.Configuration;
 import com.android.internal.logging.UiEventLogger;
@@ -311,6 +312,11 @@
      */
     public static final String OPTION_FORCE_SHOW = "force_show";
     public static final String SYS_BOOT_REASON_PROP = "sys.boot.reason.last";
+    /**
+     * Boolean option for showKeyguard, when set to true, can show the keyguard without immediately
+     * locking.
+     */
+    public static final String OPTION_SHOW_DISMISSIBLE = "show_dismissible";
     public static final String REBOOT_MAINLINE_UPDATE = "reboot,mainline_update";
     private final DreamOverlayStateController mDreamOverlayStateController;
     private final JavaAdapter mJavaAdapter;
@@ -1321,7 +1327,9 @@
     private DozeParameters mDozeParameters;
     private SelectedUserInteractor mSelectedUserInteractor;
     private KeyguardInteractor mKeyguardInteractor;
-
+    @VisibleForTesting
+    protected FoldGracePeriodProvider mFoldGracePeriodProvider =
+            new FoldGracePeriodProvider();
     private final KeyguardStateController mKeyguardStateController;
     private final KeyguardStateController.Callback mKeyguardStateControllerCallback =
             new KeyguardStateController.Callback() {
@@ -1478,6 +1486,9 @@
 
         mOrderUnlockAndWake = context.getResources().getBoolean(
                 com.android.internal.R.bool.config_orderUnlockAndWake);
+
+        mShowKeyguardWakeLock = mPM.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "show keyguard");
+        mShowKeyguardWakeLock.setReferenceCounted(false);
     }
 
     public void userActivity() {
@@ -1485,9 +1496,6 @@
     }
 
     private void setupLocked() {
-        mShowKeyguardWakeLock = mPM.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "show keyguard");
-        mShowKeyguardWakeLock.setReferenceCounted(false);
-
         IntentFilter filter = new IntentFilter();
         filter.addAction(Intent.ACTION_SHUTDOWN);
         mBroadcastDispatcher.registerReceiver(mBroadcastReceiver, filter);
@@ -1995,7 +2003,7 @@
                 mNeedToReshowWhenReenabled = false;
                 updateInputRestrictedLocked();
 
-                showLocked(null);
+                showKeyguard(null);
 
                 // block until we know the keyguard is done drawing (and post a message
                 // to unblock us after a timeout, so we don't risk blocking too long
@@ -2170,6 +2178,24 @@
     }
 
     /**
+     * Only available if the fold grace period feature is enabled.
+     * Used by PhoneWindowManager to show the keyguard immediately without locking the device.
+     * This method shows the keyguard whether there's a screen lock configured or not (including
+     * screen lock SWIPE or NONE).
+     * This must be safe to call from any thread and with any window manager locks held.
+     */
+    public void showDismissibleKeyguard() {
+        if (mFoldGracePeriodProvider.isEnabled()) {
+            Bundle showKeyguardUnlocked = new Bundle();
+            showKeyguardUnlocked.putBoolean(OPTION_SHOW_DISMISSIBLE, true);
+            showKeyguard(showKeyguardUnlocked);
+        } else {
+            Log.e(TAG, "fold grace period feature isn't enabled, but showKeyguard() method is"
+                    + " being called", new Throwable());
+        }
+    }
+
+    /**
      * Given the state of the keyguard, is the input restricted?
      * Input is restricted when the keyguard is showing, or when the keyguard
      * was suppressed by an app that disabled the keyguard or we haven't been provisioned yet.
@@ -2273,7 +2299,7 @@
         }
 
         if (DEBUG) Log.d(TAG, "doKeyguard: showing the lock screen");
-        showLocked(options);
+        showKeyguard(options);
     }
 
     private void lockProfile(int userId) {
@@ -2335,18 +2361,18 @@
     }
 
     /**
-     * Send message to keyguard telling it to show itself
+     * Send message to keyguard telling it to show itself.
      * @see #handleShow
      */
-    private void showLocked(Bundle options) {
-        Trace.beginSection("KeyguardViewMediator#showLocked acquiring mShowKeyguardWakeLock");
-        if (DEBUG) Log.d(TAG, "showLocked");
+    private void showKeyguard(Bundle options) {
+        Trace.beginSection("KeyguardViewMediator#showKeyguard acquiring mShowKeyguardWakeLock");
+        if (DEBUG) Log.d(TAG, "showKeyguard");
         // ensure we stay awake until we are finished displaying the keyguard
         mShowKeyguardWakeLock.acquire();
         Message msg = mHandler.obtainMessage(SHOW, options);
         // Treat these messages with priority - This call can originate from #doKeyguardTimeout,
-        // meaning the device should lock as soon as possible and not wait for other messages on
-        // the thread to process first.
+        // meaning the device may lock, so it shouldn't wait for other messages on the thread to
+        // process first.
         mHandler.sendMessageAtFrontOfQueue(msg);
         Trace.endSection();
     }
@@ -2724,13 +2750,18 @@
     }
 
     /**
-     * Handle message sent by {@link #showLocked}.
+     * Handle message sent by {@link #showKeyguard}.
      * @see #SHOW
      */
     private void handleShow(Bundle options) {
         Trace.beginSection("KeyguardViewMediator#handleShow");
+        final boolean showUnlocked = options != null
+                && options.getBoolean(OPTION_SHOW_DISMISSIBLE, false);
         final int currentUser = mSelectedUserInteractor.getSelectedUserId();
-        if (mLockPatternUtils.isSecure(currentUser)) {
+        if (showUnlocked) {
+            // tell KeyguardUpdateMonitor to keep the device unlocked until the next lock signal
+            mUpdateMonitor.tryForceIsDismissibleKeyguard();
+        } else if (mLockPatternUtils.isSecure(currentUser)) {
             mLockPatternUtils.getDevicePolicyManager().reportKeyguardSecured(currentUser);
         }
         synchronized (KeyguardViewMediator.this) {
@@ -2755,7 +2786,7 @@
                         + ", which means we're showing in the middle of hiding.");
             }
 
-            // Force if we we're showing in the middle of unlocking, to ensure we end up in the
+            // Force if we're showing in the middle of unlocking, to ensure we end up in the
             // correct state.
             setShowingLocked(true, hidingOrGoingAway /* force */);
             mHiding = false;
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardFaceAuthNotSupportedModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardFaceAuthNotSupportedModule.kt
index d8eb81c..f29b657 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardFaceAuthNotSupportedModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardFaceAuthNotSupportedModule.kt
@@ -16,10 +16,10 @@
 
 package com.android.systemui.keyguard.dagger
 
-import com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepository
-import com.android.systemui.keyguard.data.repository.NoopDeviceEntryFaceAuthRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor
-import com.android.systemui.keyguard.domain.interactor.NoopKeyguardFaceAuthInteractor
+import com.android.systemui.deviceentry.data.repository.DeviceEntryFaceAuthRepository
+import com.android.systemui.deviceentry.data.repository.NoopDeviceEntryFaceAuthRepository
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
+import com.android.systemui.deviceentry.domain.interactor.NoopDeviceEntryFaceAuthInteractor
 import dagger.Binds
 import dagger.Module
 
@@ -33,7 +33,9 @@
 @Module
 interface KeyguardFaceAuthNotSupportedModule {
     @Binds
-    fun keyguardFaceAuthInteractor(impl: NoopKeyguardFaceAuthInteractor): KeyguardFaceAuthInteractor
+    fun keyguardFaceAuthInteractor(
+        impl: NoopDeviceEntryFaceAuthInteractor
+    ): DeviceEntryFaceAuthInteractor
 
     @Binds
     fun deviceEntryFaceAuthRepository(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
index 3925dd1..13e3835 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
@@ -34,6 +34,7 @@
 import com.android.keyguard.dagger.KeyguardStatusViewComponent;
 import com.android.keyguard.dagger.KeyguardUserSwitcherComponent;
 import com.android.keyguard.mediator.ScreenOnCoordinator;
+import com.android.systemui.CoreStartable;
 import com.android.systemui.animation.ActivityLaunchAnimator;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.classifier.FalsingCollector;
@@ -79,9 +80,12 @@
 import com.android.systemui.wallpapers.data.repository.WallpaperRepository;
 import com.android.wm.shell.keyguard.KeyguardTransitions;
 
+import dagger.Binds;
 import dagger.Lazy;
 import dagger.Module;
 import dagger.Provides;
+import dagger.multibindings.ClassKey;
+import dagger.multibindings.IntoMap;
 
 import java.util.concurrent.Executor;
 
@@ -105,7 +109,7 @@
             StartKeyguardTransitionModule.class,
             ResourceTrimmerModule.class,
         })
-public class KeyguardModule {
+public interface KeyguardModule {
     /**
      * Provides our instance of KeyguardViewMediator which is considered optional.
      */
@@ -207,13 +211,19 @@
 
     /** */
     @Provides
-    public ViewMediatorCallback providesViewMediatorCallback(KeyguardViewMediator viewMediator) {
+    static ViewMediatorCallback providesViewMediatorCallback(KeyguardViewMediator viewMediator) {
         return viewMediator.getViewMediatorCallback();
     }
 
     /** */
     @Provides
-    public KeyguardQuickAffordancesMetricsLogger providesKeyguardQuickAffordancesMetricsLogger() {
+    static KeyguardQuickAffordancesMetricsLogger providesKeyguardQuickAffordancesMetricsLogger() {
         return new KeyguardQuickAffordancesMetricsLoggerImpl();
     }
+
+    /** Binds {@link KeyguardUpdateMonitor} as a {@link CoreStartable}. */
+    @Binds
+    @IntoMap
+    @ClassKey(KeyguardUpdateMonitor.class)
+    CoreStartable bindsKeyguardUpdateMonitor(KeyguardUpdateMonitor keyguardUpdateMonitor);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardFaceAuthModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardFaceAuthModule.kt
index ee0eb2d..b373f85 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardFaceAuthModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardFaceAuthModule.kt
@@ -19,8 +19,10 @@
 
 import com.android.systemui.CoreStartable
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor
-import com.android.systemui.keyguard.domain.interactor.SystemUIKeyguardFaceAuthInteractor
+import com.android.systemui.deviceentry.data.repository.DeviceEntryFaceAuthRepository
+import com.android.systemui.deviceentry.data.repository.DeviceEntryFaceAuthRepositoryImpl
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
+import com.android.systemui.deviceentry.domain.interactor.SystemUIDeviceEntryFaceAuthInteractor
 import com.android.systemui.log.table.TableLogBuffer
 import com.android.systemui.log.table.TableLogBufferFactory
 import dagger.Binds
@@ -38,13 +40,13 @@
 
     @Binds
     @IntoMap
-    @ClassKey(SystemUIKeyguardFaceAuthInteractor::class)
-    fun bind(impl: SystemUIKeyguardFaceAuthInteractor): CoreStartable
+    @ClassKey(SystemUIDeviceEntryFaceAuthInteractor::class)
+    fun bind(impl: SystemUIDeviceEntryFaceAuthInteractor): CoreStartable
 
     @Binds
     fun keyguardFaceAuthInteractor(
-        impl: SystemUIKeyguardFaceAuthInteractor
-    ): KeyguardFaceAuthInteractor
+        impl: SystemUIDeviceEntryFaceAuthInteractor
+    ): DeviceEntryFaceAuthInteractor
 
     companion object {
         @Provides
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 31ef100..704ebdd 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,6 +21,7 @@
 import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.keyguard.KeyguardUpdateMonitorCallback
 import com.android.systemui.biometrics.AuthController
+import com.android.systemui.biometrics.data.repository.FacePropertyRepository
 import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
 import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
 import com.android.systemui.common.shared.model.Position
@@ -110,7 +111,7 @@
     val isKeyguardGoingAway: Flow<Boolean>
 
     /** Is the always-on display available to be used? */
-    val isAodAvailable: Flow<Boolean>
+    val isAodAvailable: StateFlow<Boolean>
 
     fun setAodAvailable(value: Boolean)
 
@@ -277,6 +278,7 @@
     @Main private val mainDispatcher: CoroutineDispatcher,
     @Application private val scope: CoroutineScope,
     private val systemClock: SystemClock,
+    facePropertyRepository: FacePropertyRepository,
 ) : KeyguardRepository {
     private val _dismissAction: MutableStateFlow<DismissAction> =
         MutableStateFlow(DismissAction.None)
@@ -338,7 +340,7 @@
             .distinctUntilChanged()
 
     private val _isAodAvailable = MutableStateFlow(false)
-    override val isAodAvailable: Flow<Boolean> = _isAodAvailable.asStateFlow()
+    override val isAodAvailable: StateFlow<Boolean> = _isAodAvailable.asStateFlow()
 
     override fun setAodAvailable(value: Boolean) {
         _isAodAvailable.value = value
@@ -599,27 +601,7 @@
         awaitClose { authController.removeCallback(callback) }
     }
 
-    override val faceSensorLocation: Flow<Point?> = conflatedCallbackFlow {
-        fun sendSensorLocation() {
-            trySendWithFailureLogging(
-                authController.faceSensorLocation,
-                TAG,
-                "AuthController.Callback#onFingerprintLocationChanged"
-            )
-        }
-
-        val callback =
-            object : AuthController.Callback {
-                override fun onFaceSensorLocationChanged() {
-                    sendSensorLocation()
-                }
-            }
-
-        authController.addCallback(callback)
-        sendSensorLocation()
-
-        awaitClose { authController.removeCallback(callback) }
-    }
+    override val faceSensorLocation: Flow<Point?> = facePropertyRepository.sensorLocation
 
     override val biometricUnlockSource: Flow<BiometricUnlockSource?> = conflatedCallbackFlow {
         val callback =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepository.kt
index cb0f186..42f14f1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepository.kt
@@ -140,8 +140,9 @@
     override val revealAmount: Flow<Float> = callbackFlow {
         val updateListener =
             Animator.AnimatorUpdateListener {
-                val value = (it as ValueAnimator).animatedValue
-                trySend(value as Float)
+                val value = (it as ValueAnimator).animatedValue as Float
+                trySend(value)
+
                 if (value <= 0.0f || value >= 1.0f) {
                     scrimLogger.d(TAG, "revealAmount", value)
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractor.kt
index c98f637..ecf78d5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractor.kt
@@ -23,15 +23,18 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
+import com.android.systemui.keyguard.data.repository.BiometricType
 import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository
 import com.android.systemui.res.R
 import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.flow.filterNotNull
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.merge
+import kotlinx.coroutines.launch
 
 /**
  * Encapsulates business logic for device entry events that impact the side fingerprint sensor
@@ -41,6 +44,7 @@
 class DeviceEntrySideFpsOverlayInteractor
 @Inject
 constructor(
+    @Application private val applicationScope: CoroutineScope,
     @Application private val context: Context,
     deviceEntryFingerprintAuthRepository: DeviceEntryFingerprintAuthRepository,
     private val primaryBouncerInteractor: PrimaryBouncerInteractor,
@@ -50,7 +54,13 @@
 
     init {
         if (!DeviceEntryUdfpsRefactor.isEnabled) {
-            alternateBouncerInteractor.setAlternateBouncerUIAvailable(true, TAG)
+            applicationScope.launch {
+                deviceEntryFingerprintAuthRepository.availableFpSensorType.collect { sensorType ->
+                    if (sensorType == BiometricType.SIDE_FINGERPRINT) {
+                        alternateBouncerInteractor.setAlternateBouncerUIAvailable(true, TAG)
+                    }
+                }
+            }
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt
index 52f2759..d7a2aa0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt
@@ -18,7 +18,8 @@
 
 import android.animation.ValueAnimator
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.power.domain.interactor.PowerInteractor
@@ -28,6 +29,7 @@
 import com.android.wm.shell.animation.Interpolators
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
+import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.flow.combine
@@ -39,13 +41,18 @@
 @Inject
 constructor(
     override val transitionRepository: KeyguardTransitionRepository,
-    override val transitionInteractor: KeyguardTransitionInteractor,
-    @Application private val scope: CoroutineScope,
+    transitionInteractor: KeyguardTransitionInteractor,
+    @Background private val scope: CoroutineScope,
+    @Background bgDispatcher: CoroutineDispatcher,
+    @Main mainDispatcher: CoroutineDispatcher,
     private val keyguardInteractor: KeyguardInteractor,
     private val powerInteractor: PowerInteractor,
 ) :
     TransitionInteractor(
         fromState = KeyguardState.ALTERNATE_BOUNCER,
+        transitionInteractor = transitionInteractor,
+        mainDispatcher = mainDispatcher,
+        bgDispatcher = bgDispatcher,
     ) {
 
     override fun start() {
@@ -65,7 +72,7 @@
                 .sample(
                     combine(
                         keyguardInteractor.primaryBouncerShowing,
-                        transitionInteractor.startedKeyguardTransitionStep,
+                        startedKeyguardTransitionStep,
                         powerInteractor.isAwake,
                         keyguardInteractor.isAodAvailable,
                         ::toQuad
@@ -102,20 +109,19 @@
 
     private fun listenForAlternateBouncerToGone() {
         scope.launch {
-            keyguardInteractor.isKeyguardGoingAway
-                .sample(transitionInteractor.finishedKeyguardState, ::Pair)
-                .collect { (isKeyguardGoingAway, keyguardState) ->
-                    if (isKeyguardGoingAway && keyguardState == KeyguardState.ALTERNATE_BOUNCER) {
-                        startTransitionTo(KeyguardState.GONE)
-                    }
+            keyguardInteractor.isKeyguardGoingAway.sample(finishedKeyguardState, ::Pair).collect {
+                (isKeyguardGoingAway, keyguardState) ->
+                if (isKeyguardGoingAway && keyguardState == KeyguardState.ALTERNATE_BOUNCER) {
+                    startTransitionTo(KeyguardState.GONE)
                 }
+            }
         }
     }
 
     private fun listenForAlternateBouncerToPrimaryBouncer() {
         scope.launch {
             keyguardInteractor.primaryBouncerShowing
-                .sample(transitionInteractor.startedKeyguardTransitionStep, ::Pair)
+                .sample(startedKeyguardTransitionStep, ::Pair)
                 .collect { (isPrimaryBouncerShowing, startedKeyguardState) ->
                     if (
                         isPrimaryBouncerShowing &&
@@ -139,6 +145,7 @@
     }
 
     companion object {
+        const val TAG = "FromAlternateBouncerTransitionInteractor"
         val TRANSITION_DURATION_MS = 300.milliseconds
         val TO_GONE_DURATION = 500.milliseconds
         val TO_AOD_DURATION = TRANSITION_DURATION_MS
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt
index 8584401..fedd63b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt
@@ -19,7 +19,8 @@
 import android.animation.ValueAnimator
 import com.android.app.animation.Interpolators
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
 import com.android.systemui.keyguard.shared.model.BiometricUnlockModel.Companion.isWakeAndUnlock
 import com.android.systemui.keyguard.shared.model.DozeStateModel
@@ -29,6 +30,7 @@
 import com.android.systemui.util.kotlin.sample
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
+import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.launch
@@ -38,12 +40,17 @@
 @Inject
 constructor(
     override val transitionRepository: KeyguardTransitionRepository,
-    override val transitionInteractor: KeyguardTransitionInteractor,
-    @Application private val scope: CoroutineScope,
+    transitionInteractor: KeyguardTransitionInteractor,
+    @Background private val scope: CoroutineScope,
+    @Background bgDispatcher: CoroutineDispatcher,
+    @Main mainDispatcher: CoroutineDispatcher,
     private val keyguardInteractor: KeyguardInteractor,
 ) :
     TransitionInteractor(
         fromState = KeyguardState.AOD,
+        transitionInteractor = transitionInteractor,
+        mainDispatcher = mainDispatcher,
+        bgDispatcher = bgDispatcher,
     ) {
 
     override fun start() {
@@ -58,7 +65,7 @@
                 .dozeTransitionTo(DozeStateModel.FINISH)
                 .sample(
                     combine(
-                        transitionInteractor.startedKeyguardTransitionStep,
+                        startedKeyguardTransitionStep,
                         keyguardInteractor.isKeyguardOccluded,
                         ::Pair
                     ),
@@ -77,7 +84,6 @@
                             } else {
                                 TransitionModeOnCanceled.LAST_VALUE
                             }
-
                         startTransitionTo(
                             toState = toState,
                             modeOnCanceled = modeOnCanceled,
@@ -89,16 +95,13 @@
 
     private fun listenForAodToGone() {
         scope.launch {
-            keyguardInteractor.biometricUnlockState
-                .sample(transitionInteractor.finishedKeyguardState, ::Pair)
-                .collect { pair ->
-                    val (biometricUnlockState, keyguardState) = pair
-                    if (
-                        keyguardState == KeyguardState.AOD && isWakeAndUnlock(biometricUnlockState)
-                    ) {
-                        startTransitionTo(KeyguardState.GONE)
-                    }
+            keyguardInteractor.biometricUnlockState.sample(finishedKeyguardState, ::Pair).collect {
+                pair ->
+                val (biometricUnlockState, keyguardState) = pair
+                if (keyguardState == KeyguardState.AOD && isWakeAndUnlock(biometricUnlockState)) {
+                    startTransitionTo(KeyguardState.GONE)
                 }
+            }
         }
     }
     override fun getDefaultAnimatorForTransitionsToState(toState: KeyguardState): ValueAnimator {
@@ -113,6 +116,7 @@
     }
 
     companion object {
+        const val TAG = "FromAodTransitionInteractor"
         private val DEFAULT_DURATION = 500.milliseconds
         val TO_LOCKSCREEN_DURATION = 500.milliseconds
         val TO_GONE_DURATION = DEFAULT_DURATION
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
index eca7088..fcb7698 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
@@ -19,7 +19,8 @@
 import android.animation.ValueAnimator
 import com.android.app.animation.Interpolators
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
 import com.android.systemui.keyguard.shared.model.BiometricUnlockModel.Companion.isWakeAndUnlock
 import com.android.systemui.keyguard.shared.model.KeyguardState
@@ -28,6 +29,7 @@
 import com.android.systemui.util.kotlin.sample
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
+import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.launch
@@ -37,13 +39,18 @@
 @Inject
 constructor(
     override val transitionRepository: KeyguardTransitionRepository,
-    override val transitionInteractor: KeyguardTransitionInteractor,
-    @Application private val scope: CoroutineScope,
+    transitionInteractor: KeyguardTransitionInteractor,
+    @Background private val scope: CoroutineScope,
+    @Background bgDispatcher: CoroutineDispatcher,
+    @Main mainDispatcher: CoroutineDispatcher,
     private val keyguardInteractor: KeyguardInteractor,
     private val powerInteractor: PowerInteractor,
 ) :
     TransitionInteractor(
         fromState = KeyguardState.DOZING,
+        transitionInteractor = transitionInteractor,
+        mainDispatcher = mainDispatcher,
+        bgDispatcher = bgDispatcher,
     ) {
 
     override fun start() {
@@ -57,7 +64,7 @@
             powerInteractor.isAwake
                 .sample(
                     combine(
-                        transitionInteractor.startedKeyguardTransitionStep,
+                        startedKeyguardTransitionStep,
                         keyguardInteractor.isKeyguardOccluded,
                         ::Pair
                     ),
@@ -76,7 +83,7 @@
     private fun listenForDozingToGone() {
         scope.launch {
             keyguardInteractor.biometricUnlockState
-                .sample(transitionInteractor.startedKeyguardTransitionStep, ::Pair)
+                .sample(startedKeyguardTransitionStep, ::Pair)
                 .collect { (biometricUnlockState, lastStartedTransition) ->
                     if (
                         lastStartedTransition.to == KeyguardState.DOZING &&
@@ -96,6 +103,7 @@
     }
 
     companion object {
+        const val TAG = "FromDozingTransitionInteractor"
         private val DEFAULT_DURATION = 500.milliseconds
         val TO_LOCKSCREEN_DURATION = DEFAULT_DURATION
     }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractor.kt
index dac6ef5..a6cdaa8 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractor.kt
@@ -19,7 +19,8 @@
 import android.animation.ValueAnimator
 import com.android.app.animation.Interpolators
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
 import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
 import com.android.systemui.keyguard.shared.model.DozeStateModel
@@ -28,6 +29,7 @@
 import com.android.systemui.util.kotlin.sample
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
+import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.flow.combine
@@ -39,12 +41,17 @@
 @Inject
 constructor(
     override val transitionRepository: KeyguardTransitionRepository,
-    override val transitionInteractor: KeyguardTransitionInteractor,
-    @Application private val scope: CoroutineScope,
+    transitionInteractor: KeyguardTransitionInteractor,
+    @Background private val scope: CoroutineScope,
+    @Background bgDispatcher: CoroutineDispatcher,
+    @Main mainDispatcher: CoroutineDispatcher,
     private val keyguardInteractor: KeyguardInteractor,
 ) :
     TransitionInteractor(
         fromState = KeyguardState.DREAMING_LOCKSCREEN_HOSTED,
+        transitionInteractor = transitionInteractor,
+        mainDispatcher = mainDispatcher,
+        bgDispatcher = bgDispatcher,
     ) {
 
     override fun start() {
@@ -64,7 +71,7 @@
                 .sample(
                     combine(
                         keyguardInteractor.dozeTransitionModel,
-                        transitionInteractor.startedKeyguardTransitionStep,
+                        startedKeyguardTransitionStep,
                         ::Pair
                     ),
                     ::toTriple
@@ -88,7 +95,7 @@
                 .sample(
                     combine(
                         keyguardInteractor.isKeyguardOccluded,
-                        transitionInteractor.startedKeyguardTransitionStep,
+                        startedKeyguardTransitionStep,
                         ::Pair,
                     ),
                     ::toTriple
@@ -108,7 +115,7 @@
     private fun listenForDreamingLockscreenHostedToPrimaryBouncer() {
         scope.launch {
             keyguardInteractor.primaryBouncerShowing
-                .sample(transitionInteractor.startedKeyguardTransitionStep, ::Pair)
+                .sample(startedKeyguardTransitionStep, ::Pair)
                 .collect { (isBouncerShowing, lastStartedTransitionStep) ->
                     if (
                         isBouncerShowing &&
@@ -123,7 +130,7 @@
     private fun listenForDreamingLockscreenHostedToGone() {
         scope.launch {
             keyguardInteractor.biometricUnlockState
-                .sample(transitionInteractor.startedKeyguardTransitionStep, ::Pair)
+                .sample(startedKeyguardTransitionStep, ::Pair)
                 .collect { (biometricUnlockState, lastStartedTransitionStep) ->
                     if (
                         lastStartedTransitionStep.to == KeyguardState.DREAMING_LOCKSCREEN_HOSTED &&
@@ -137,11 +144,7 @@
 
     private fun listenForDreamingLockscreenHostedToDozing() {
         scope.launch {
-            combine(
-                    keyguardInteractor.dozeTransitionModel,
-                    transitionInteractor.startedKeyguardTransitionStep,
-                    ::Pair
-                )
+            combine(keyguardInteractor.dozeTransitionModel, startedKeyguardTransitionStep, ::Pair)
                 .collect { (dozeTransitionModel, lastStartedTransitionStep) ->
                     if (
                         dozeTransitionModel.to == DozeStateModel.DOZE &&
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
index 7fdcf2f..13ffd63 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
@@ -19,7 +19,8 @@
 import android.animation.ValueAnimator
 import com.android.app.animation.Interpolators
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
 import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
 import com.android.systemui.keyguard.shared.model.DozeStateModel
@@ -28,6 +29,7 @@
 import com.android.systemui.util.kotlin.sample
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
+import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.launch
@@ -37,12 +39,17 @@
 @Inject
 constructor(
     override val transitionRepository: KeyguardTransitionRepository,
-    override val transitionInteractor: KeyguardTransitionInteractor,
-    @Application private val scope: CoroutineScope,
+    transitionInteractor: KeyguardTransitionInteractor,
+    @Background private val scope: CoroutineScope,
+    @Background bgDispatcher: CoroutineDispatcher,
+    @Main mainDispatcher: CoroutineDispatcher,
     private val keyguardInteractor: KeyguardInteractor,
 ) :
     TransitionInteractor(
         fromState = KeyguardState.DREAMING,
+        transitionInteractor = transitionInteractor,
+        mainDispatcher = mainDispatcher,
+        bgDispatcher = bgDispatcher,
     ) {
 
     override fun start() {
@@ -66,7 +73,7 @@
     private fun listenForDreamingToOccluded() {
         scope.launch {
             combine(keyguardInteractor.isKeyguardOccluded, keyguardInteractor.isDreaming, ::Pair)
-                .sample(transitionInteractor.startedKeyguardTransitionStep, ::toTriple)
+                .sample(startedKeyguardTransitionStep, ::toTriple)
                 .collect { (isOccluded, isDreaming, lastStartedTransition) ->
                     if (
                         isOccluded &&
@@ -82,7 +89,7 @@
     private fun listenForDreamingToGone() {
         scope.launch {
             keyguardInteractor.biometricUnlockState
-                .sample(transitionInteractor.startedKeyguardTransitionStep, ::Pair)
+                .sample(startedKeyguardTransitionStep, ::Pair)
                 .collect { (biometricUnlockState, lastStartedTransitionStep) ->
                     if (
                         lastStartedTransitionStep.to == KeyguardState.DREAMING &&
@@ -96,11 +103,7 @@
 
     private fun listenForDreamingToAodOrDozing() {
         scope.launch {
-            combine(
-                    keyguardInteractor.dozeTransitionModel,
-                    transitionInteractor.finishedKeyguardState,
-                    ::Pair
-                )
+            combine(keyguardInteractor.dozeTransitionModel, finishedKeyguardState, ::Pair)
                 .collect { (dozeTransitionModel, keyguardState) ->
                     if (keyguardState == KeyguardState.DREAMING) {
                         if (dozeTransitionModel.to == DozeStateModel.DOZE) {
@@ -123,6 +126,7 @@
     }
 
     companion object {
+        const val TAG = "FromDreamingTransitionInteractor"
         private val DEFAULT_DURATION = 500.milliseconds
         val TO_LOCKSCREEN_DURATION = 1167.milliseconds
     }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt
new file mode 100644
index 0000000..19fd7f9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.domain.interactor
+
+import android.animation.ValueAnimator
+import com.android.app.animation.Interpolators
+import com.android.systemui.Flags
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import javax.inject.Inject
+import kotlin.time.Duration.Companion.milliseconds
+import kotlinx.coroutines.CoroutineDispatcher
+
+@SysUISingleton
+class FromGlanceableHubTransitionInteractor
+@Inject
+constructor(
+    override val transitionRepository: KeyguardTransitionRepository,
+    transitionInteractor: KeyguardTransitionInteractor,
+    @Main mainDispatcher: CoroutineDispatcher,
+    @Background bgDispatcher: CoroutineDispatcher,
+) :
+    TransitionInteractor(
+        fromState = KeyguardState.GLANCEABLE_HUB,
+        transitionInteractor = transitionInteractor,
+        mainDispatcher = mainDispatcher,
+        bgDispatcher = bgDispatcher,
+    ) {
+    override fun start() {
+        if (!Flags.communalHub()) {
+            return
+        }
+    }
+
+    override fun getDefaultAnimatorForTransitionsToState(toState: KeyguardState): ValueAnimator {
+        return ValueAnimator().apply {
+            interpolator = Interpolators.LINEAR
+            duration = DEFAULT_DURATION.inWholeMilliseconds
+        }
+    }
+
+    companion object {
+        const val TAG = "FromGlanceableHubTransitionInteractor"
+        val DEFAULT_DURATION = 500.milliseconds
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
index 62a0b0e..742790e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
@@ -19,7 +19,8 @@
 import android.animation.ValueAnimator
 import com.android.app.animation.Interpolators
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.TransitionModeOnCanceled
@@ -28,6 +29,7 @@
 import com.android.systemui.util.kotlin.sample
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
+import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.launch
@@ -37,13 +39,18 @@
 @Inject
 constructor(
     override val transitionRepository: KeyguardTransitionRepository,
-    override val transitionInteractor: KeyguardTransitionInteractor,
-    @Application private val scope: CoroutineScope,
+    transitionInteractor: KeyguardTransitionInteractor,
+    @Background private val scope: CoroutineScope,
+    @Background bgDispatcher: CoroutineDispatcher,
+    @Main mainDispatcher: CoroutineDispatcher,
     private val keyguardInteractor: KeyguardInteractor,
     private val powerInteractor: PowerInteractor,
 ) :
     TransitionInteractor(
         fromState = KeyguardState.GONE,
+        transitionInteractor = transitionInteractor,
+        mainDispatcher = mainDispatcher,
+        bgDispatcher = bgDispatcher,
     ) {
 
     override fun start() {
@@ -57,7 +64,7 @@
     private fun listenForGoneToLockscreen() {
         scope.launch {
             keyguardInteractor.isKeyguardShowing
-                .sample(transitionInteractor.startedKeyguardTransitionStep, ::Pair)
+                .sample(startedKeyguardTransitionStep, ::Pair)
                 .collect { (isKeyguardShowing, lastStartedStep) ->
                     if (isKeyguardShowing && lastStartedStep.to == KeyguardState.GONE) {
                         startTransitionTo(KeyguardState.LOCKSCREEN)
@@ -69,7 +76,7 @@
     private fun listenForGoneToDreamingLockscreenHosted() {
         scope.launch {
             keyguardInteractor.isActiveDreamLockscreenHosted
-                .sample(transitionInteractor.startedKeyguardTransitionStep, ::Pair)
+                .sample(startedKeyguardTransitionStep, ::Pair)
                 .collect { (isActiveDreamLockscreenHosted, lastStartedStep) ->
                     if (isActiveDreamLockscreenHosted && lastStartedStep.to == KeyguardState.GONE) {
                         startTransitionTo(KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
@@ -83,7 +90,7 @@
             keyguardInteractor.isAbleToDream
                 .sample(
                     combine(
-                        transitionInteractor.startedKeyguardTransitionStep,
+                        startedKeyguardTransitionStep,
                         keyguardInteractor.isActiveDreamLockscreenHosted,
                         ::Pair
                     ),
@@ -106,7 +113,7 @@
             powerInteractor.isAsleep
                 .sample(
                     combine(
-                        transitionInteractor.startedKeyguardTransitionStep,
+                        startedKeyguardTransitionStep,
                         keyguardInteractor.isAodAvailable,
                         ::Pair
                     ),
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
index cecc653..2d0baa8 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
@@ -18,9 +18,9 @@
 
 import android.animation.ValueAnimator
 import com.android.app.animation.Interpolators
-import com.android.app.tracing.coroutines.launch
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.flags.Flags
 import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
@@ -39,20 +39,25 @@
 import java.util.UUID
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
+import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.onStart
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
 
 @SysUISingleton
 class FromLockscreenTransitionInteractor
 @Inject
 constructor(
     override val transitionRepository: KeyguardTransitionRepository,
-    override val transitionInteractor: KeyguardTransitionInteractor,
-    @Application private val scope: CoroutineScope,
+    transitionInteractor: KeyguardTransitionInteractor,
+    @Background private val scope: CoroutineScope,
+    @Background bgDispatcher: CoroutineDispatcher,
+    @Main mainDispatcher: CoroutineDispatcher,
     private val keyguardInteractor: KeyguardInteractor,
     private val flags: FeatureFlags,
     private val shadeRepository: ShadeRepository,
@@ -61,6 +66,9 @@
 ) :
     TransitionInteractor(
         fromState = KeyguardState.LOCKSCREEN,
+        transitionInteractor = transitionInteractor,
+        mainDispatcher = mainDispatcher,
+        bgDispatcher = bgDispatcher,
     ) {
 
     override fun start() {
@@ -147,12 +155,12 @@
 
     private fun listenForLockscreenToDreaming() {
         val invalidFromStates = setOf(KeyguardState.AOD, KeyguardState.DOZING)
-        scope.launch("$TAG#listenForLockscreenToDreaming") {
+        scope.launch {
             keyguardInteractor.isAbleToDream
                 .sample(
                     combine(
-                        transitionInteractor.startedKeyguardTransitionStep,
-                        transitionInteractor.finishedKeyguardState,
+                        startedKeyguardTransitionStep,
+                        finishedKeyguardState,
                         keyguardInteractor.isActiveDreamLockscreenHosted,
                         ::Triple
                     ),
@@ -180,9 +188,9 @@
     }
 
     private fun listenForLockscreenToPrimaryBouncer() {
-        scope.launch("$TAG#listenForLockscreenToPrimaryBouncer") {
+        scope.launch {
             keyguardInteractor.primaryBouncerShowing
-                .sample(transitionInteractor.startedKeyguardTransitionStep, ::Pair)
+                .sample(startedKeyguardTransitionStep, ::Pair)
                 .collect { pair ->
                     val (isBouncerShowing, lastStartedTransitionStep) = pair
                     if (
@@ -195,9 +203,9 @@
     }
 
     private fun listenForLockscreenToAlternateBouncer() {
-        scope.launch("$TAG#listenForLockscreenToAlternateBouncer") {
+        scope.launch {
             keyguardInteractor.alternateBouncerShowing
-                .sample(transitionInteractor.startedKeyguardTransitionStep, ::Pair)
+                .sample(startedKeyguardTransitionStep, ::Pair)
                 .collect { pair ->
                     val (isAlternateBouncerShowing, lastStartedTransitionStep) = pair
                     if (
@@ -213,11 +221,11 @@
     /* Starts transitions when manually dragging up the bouncer from the lockscreen. */
     private fun listenForLockscreenToPrimaryBouncerDragging() {
         var transitionId: UUID? = null
-        scope.launch("$TAG#listenForLockscreenToPrimaryBouncerDragging") {
+        scope.launch {
             shadeRepository.legacyShadeExpansion
                 .sample(
                     combine(
-                        transitionInteractor.startedKeyguardTransitionStep,
+                        startedKeyguardTransitionStep,
                         keyguardInteractor.statusBarState,
                         keyguardInteractor.isKeyguardUnlocked,
                         ::Triple
@@ -225,64 +233,66 @@
                     ::toQuad
                 )
                 .collect { (shadeExpansion, keyguardState, statusBarState, isKeyguardUnlocked) ->
-                    val id = transitionId
-                    if (id != null) {
-                        if (keyguardState.to == KeyguardState.PRIMARY_BOUNCER) {
-                            // An existing `id` means a transition is started, and calls to
-                            // `updateTransition` will control it until FINISHED or CANCELED
-                            var nextState =
-                                if (shadeExpansion == 0f) {
-                                    TransitionState.FINISHED
-                                } else if (shadeExpansion == 1f) {
-                                    TransitionState.CANCELED
-                                } else {
-                                    TransitionState.RUNNING
+                    withContext(mainDispatcher) {
+                        val id = transitionId
+                        if (id != null) {
+                            if (keyguardState.to == KeyguardState.PRIMARY_BOUNCER) {
+                                // An existing `id` means a transition is started, and calls to
+                                // `updateTransition` will control it until FINISHED or CANCELED
+                                var nextState =
+                                    if (shadeExpansion == 0f) {
+                                        TransitionState.FINISHED
+                                    } else if (shadeExpansion == 1f) {
+                                        TransitionState.CANCELED
+                                    } else {
+                                        TransitionState.RUNNING
+                                    }
+                                transitionRepository.updateTransition(
+                                    id,
+                                    1f - shadeExpansion,
+                                    nextState,
+                                )
+
+                                if (
+                                    nextState == TransitionState.CANCELED ||
+                                        nextState == TransitionState.FINISHED
+                                ) {
+                                    transitionId = null
                                 }
-                            transitionRepository.updateTransition(
-                                id,
-                                1f - shadeExpansion,
-                                nextState,
-                            )
 
-                            if (
-                                nextState == TransitionState.CANCELED ||
-                                    nextState == TransitionState.FINISHED
-                            ) {
-                                transitionId = null
-                            }
-
-                            // If canceled, just put the state back
-                            // TODO(b/278086361): This logic should happen in
-                            //  FromPrimaryBouncerInteractor.
-                            if (nextState == TransitionState.CANCELED) {
-                                transitionRepository.startTransition(
-                                    TransitionInfo(
-                                        ownerName = name,
-                                        from = KeyguardState.PRIMARY_BOUNCER,
-                                        to = KeyguardState.LOCKSCREEN,
-                                        animator =
-                                            getDefaultAnimatorForTransitionsToState(
-                                                    KeyguardState.LOCKSCREEN
-                                                )
-                                                .apply { duration = 0 }
+                                // If canceled, just put the state back
+                                // TODO(b/278086361): This logic should happen in
+                                //  FromPrimaryBouncerInteractor.
+                                if (nextState == TransitionState.CANCELED) {
+                                    transitionRepository.startTransition(
+                                        TransitionInfo(
+                                            ownerName = name,
+                                            from = KeyguardState.PRIMARY_BOUNCER,
+                                            to = KeyguardState.LOCKSCREEN,
+                                            animator =
+                                                getDefaultAnimatorForTransitionsToState(
+                                                        KeyguardState.LOCKSCREEN
+                                                    )
+                                                    .apply { duration = 0 }
+                                        )
                                     )
-                                )
+                                }
                             }
-                        }
-                    } else {
-                        // TODO (b/251849525): Remove statusbarstate check when that state is
-                        // integrated into KeyguardTransitionRepository
-                        if (
-                            keyguardState.to == KeyguardState.LOCKSCREEN &&
-                                shadeRepository.legacyShadeTracking.value &&
-                                !isKeyguardUnlocked &&
-                                statusBarState == KEYGUARD
-                        ) {
-                            transitionId =
-                                startTransitionTo(
-                                    toState = KeyguardState.PRIMARY_BOUNCER,
-                                    animator = null, // transition will be manually controlled
-                                )
+                        } else {
+                            // TODO (b/251849525): Remove statusbarstate check when that state is
+                            // integrated into KeyguardTransitionRepository
+                            if (
+                                keyguardState.to == KeyguardState.LOCKSCREEN &&
+                                    shadeRepository.legacyShadeTracking.value &&
+                                    !isKeyguardUnlocked &&
+                                    statusBarState == KEYGUARD
+                            ) {
+                                transitionId =
+                                    startTransitionTo(
+                                        toState = KeyguardState.PRIMARY_BOUNCER,
+                                        animator = null, // transition will be manually controlled
+                                    )
+                            }
                         }
                     }
                 }
@@ -290,7 +300,7 @@
     }
 
     fun dismissKeyguard() {
-        startTransitionTo(KeyguardState.GONE)
+        scope.launch { startTransitionTo(KeyguardState.GONE) }
     }
 
     private fun listenForLockscreenToGone() {
@@ -298,9 +308,9 @@
             return
         }
 
-        scope.launch("$TAG#listenForLockscreenToGone") {
+        scope.launch {
             keyguardInteractor.isKeyguardGoingAway
-                .sample(transitionInteractor.startedKeyguardTransitionStep, ::Pair)
+                .sample(startedKeyguardTransitionStep, ::Pair)
                 .collect { pair ->
                     val (isKeyguardGoingAway, lastStartedStep) = pair
                     if (isKeyguardGoingAway && lastStartedStep.to == KeyguardState.LOCKSCREEN) {
@@ -315,9 +325,9 @@
             return
         }
 
-        scope.launch("$TAG#listenForLockscreenToGoneDragging") {
+        scope.launch {
             keyguardInteractor.isKeyguardGoingAway
-                .sample(transitionInteractor.startedKeyguardTransitionStep, ::Pair)
+                .sample(startedKeyguardTransitionStep, ::Pair)
                 .collect { pair ->
                     val (isKeyguardGoingAway, lastStartedStep) = pair
                     if (isKeyguardGoingAway && lastStartedStep.to == KeyguardState.LOCKSCREEN) {
@@ -328,23 +338,22 @@
     }
 
     private fun listenForLockscreenToOccluded() {
-        scope.launch("$TAG#listenForLockscreenToOccluded") {
-            keyguardInteractor.isKeyguardOccluded
-                .sample(transitionInteractor.startedKeyguardState, ::Pair)
-                .collect { (isOccluded, keyguardState) ->
-                    if (isOccluded && keyguardState == KeyguardState.LOCKSCREEN) {
-                        startTransitionTo(KeyguardState.OCCLUDED)
-                    }
+        scope.launch {
+            keyguardInteractor.isKeyguardOccluded.sample(startedKeyguardState, ::Pair).collect {
+                (isOccluded, keyguardState) ->
+                if (isOccluded && keyguardState == KeyguardState.LOCKSCREEN) {
+                    startTransitionTo(KeyguardState.OCCLUDED)
                 }
+            }
         }
     }
 
     private fun listenForLockscreenToAodOrDozing() {
-        scope.launch("$TAG#listenForLockscreenToAodOrDozing") {
+        scope.launch {
             powerInteractor.isAsleep
                 .sample(
                     combine(
-                        transitionInteractor.startedKeyguardTransitionStep,
+                        startedKeyguardTransitionStep,
                         keyguardInteractor.isAodAvailable,
                         ::Pair
                     ),
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt
index 6a8555c..40061f4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt
@@ -19,7 +19,8 @@
 import android.animation.ValueAnimator
 import com.android.app.animation.Interpolators
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.power.domain.interactor.PowerInteractor
@@ -27,6 +28,7 @@
 import com.android.systemui.util.kotlin.sample
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
+import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.launch
@@ -36,13 +38,18 @@
 @Inject
 constructor(
     override val transitionRepository: KeyguardTransitionRepository,
-    override val transitionInteractor: KeyguardTransitionInteractor,
-    @Application private val scope: CoroutineScope,
+    transitionInteractor: KeyguardTransitionInteractor,
+    @Background private val scope: CoroutineScope,
+    @Background bgDispatcher: CoroutineDispatcher,
+    @Main mainDispatcher: CoroutineDispatcher,
     private val keyguardInteractor: KeyguardInteractor,
     private val powerInteractor: PowerInteractor,
 ) :
     TransitionInteractor(
         fromState = KeyguardState.OCCLUDED,
+        transitionInteractor = transitionInteractor,
+        mainDispatcher = mainDispatcher,
+        bgDispatcher = bgDispatcher,
     ) {
 
     override fun start() {
@@ -57,7 +64,7 @@
     private fun listenForOccludedToPrimaryBouncer() {
         scope.launch {
             keyguardInteractor.primaryBouncerShowing
-                .sample(transitionInteractor.startedKeyguardTransitionStep, ::Pair)
+                .sample(startedKeyguardTransitionStep, ::Pair)
                 .collect { (isBouncerShowing, lastStartedTransitionStep) ->
                     if (
                         isBouncerShowing && lastStartedTransitionStep.to == KeyguardState.OCCLUDED
@@ -70,13 +77,12 @@
 
     private fun listenForOccludedToDreaming() {
         scope.launch {
-            keyguardInteractor.isAbleToDream
-                .sample(transitionInteractor.finishedKeyguardState, ::Pair)
-                .collect { (isAbleToDream, keyguardState) ->
-                    if (isAbleToDream && keyguardState == KeyguardState.OCCLUDED) {
-                        startTransitionTo(KeyguardState.DREAMING)
-                    }
+            keyguardInteractor.isAbleToDream.sample(finishedKeyguardState, ::Pair).collect {
+                (isAbleToDream, keyguardState) ->
+                if (isAbleToDream && keyguardState == KeyguardState.OCCLUDED) {
+                    startTransitionTo(KeyguardState.DREAMING)
                 }
+            }
         }
     }
 
@@ -86,7 +92,7 @@
                 .sample(
                     combine(
                         keyguardInteractor.isKeyguardShowing,
-                        transitionInteractor.startedKeyguardTransitionStep,
+                        startedKeyguardTransitionStep,
                         ::Pair
                     ),
                     ::toTriple
@@ -111,7 +117,7 @@
                 .sample(
                     combine(
                         keyguardInteractor.isKeyguardShowing,
-                        transitionInteractor.startedKeyguardTransitionStep,
+                        startedKeyguardTransitionStep,
                         ::Pair
                     ),
                     ::toTriple
@@ -135,7 +141,7 @@
             powerInteractor.isAsleep
                 .sample(
                     combine(
-                        transitionInteractor.startedKeyguardTransitionStep,
+                        startedKeyguardTransitionStep,
                         keyguardInteractor.isAodAvailable,
                         ::Pair
                     ),
@@ -154,7 +160,7 @@
     private fun listenForOccludedToAlternateBouncer() {
         scope.launch {
             keyguardInteractor.alternateBouncerShowing
-                .sample(transitionInteractor.startedKeyguardTransitionStep, ::Pair)
+                .sample(startedKeyguardTransitionStep, ::Pair)
                 .collect { (isAlternateBouncerShowing, lastStartedTransitionStep) ->
                     if (
                         isAlternateBouncerShowing &&
@@ -183,6 +189,7 @@
     }
 
     companion object {
+        const val TAG = "FromOccludedTransitionInteractor"
         private val DEFAULT_DURATION = 500.milliseconds
         val TO_LOCKSCREEN_DURATION = 933.milliseconds
         val TO_AOD_DURATION = DEFAULT_DURATION
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
index 5f246e1..c62055f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
@@ -19,7 +19,8 @@
 import android.animation.ValueAnimator
 import com.android.keyguard.KeyguardSecurityModel
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.flags.Flags
 import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
@@ -35,6 +36,7 @@
 import com.android.wm.shell.animation.Interpolators
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
+import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.combine
@@ -47,8 +49,10 @@
 @Inject
 constructor(
     override val transitionRepository: KeyguardTransitionRepository,
-    override val transitionInteractor: KeyguardTransitionInteractor,
-    @Application private val scope: CoroutineScope,
+    transitionInteractor: KeyguardTransitionInteractor,
+    @Background private val scope: CoroutineScope,
+    @Background bgDispatcher: CoroutineDispatcher,
+    @Main mainDispatcher: CoroutineDispatcher,
     private val keyguardInteractor: KeyguardInteractor,
     private val flags: FeatureFlags,
     private val keyguardSecurityModel: KeyguardSecurityModel,
@@ -57,6 +61,9 @@
 ) :
     TransitionInteractor(
         fromState = KeyguardState.PRIMARY_BOUNCER,
+        transitionInteractor = transitionInteractor,
+        mainDispatcher = mainDispatcher,
+        bgDispatcher = bgDispatcher,
     ) {
 
     override fun start() {
@@ -115,7 +122,7 @@
             .distinctUntilChanged()
 
     fun dismissPrimaryBouncer() {
-        startTransitionTo(KeyguardState.GONE)
+        scope.launch { startTransitionTo(KeyguardState.GONE) }
     }
 
     private fun listenForPrimaryBouncerToLockscreenOrOccluded() {
@@ -124,7 +131,7 @@
                 .sample(
                     combine(
                         powerInteractor.isAwake,
-                        transitionInteractor.startedKeyguardTransitionStep,
+                        startedKeyguardTransitionStep,
                         keyguardInteractor.isKeyguardOccluded,
                         keyguardInteractor.isActiveDreamLockscreenHosted,
                         ::toQuad
@@ -158,7 +165,7 @@
                 .sample(
                     combine(
                         powerInteractor.isAsleep,
-                        transitionInteractor.startedKeyguardTransitionStep,
+                        startedKeyguardTransitionStep,
                         keyguardInteractor.isAodAvailable,
                         ::Triple
                     ),
@@ -185,7 +192,7 @@
                 .sample(
                     combine(
                         keyguardInteractor.isActiveDreamLockscreenHosted,
-                        transitionInteractor.startedKeyguardTransitionStep,
+                        startedKeyguardTransitionStep,
                         ::Pair
                     ),
                     ::toTriple
@@ -213,7 +220,7 @@
 
         scope.launch {
             keyguardInteractor.isKeyguardGoingAway
-                .sample(transitionInteractor.startedKeyguardTransitionStep, ::Pair)
+                .sample(startedKeyguardTransitionStep, ::Pair)
                 .collect { (isKeyguardGoingAway, lastStartedTransitionStep) ->
                     if (
                         isKeyguardGoingAway &&
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractor.kt
deleted file mode 100644
index 046916a..0000000
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractor.kt
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- *   Copyright (C) 2023 The Android Open Source Project
- *
- *   Licensed under the Apache License, Version 2.0 (the "License");
- *   you may not use this file except in compliance with the License.
- *   You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *   Unless required by applicable law or agreed to in writing, software
- *   distributed under the License is distributed on an "AS IS" BASIS,
- *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *   See the License for the specific language governing permissions and
- *   limitations under the License.
- */
-
-package com.android.systemui.keyguard.domain.interactor
-
-import com.android.systemui.keyguard.shared.model.FaceAuthenticationStatus
-import com.android.systemui.keyguard.shared.model.FaceDetectionStatus
-import kotlinx.coroutines.flow.Flow
-
-/**
- * Interactor that exposes API to get the face authentication status and handle any events that can
- * cause face authentication to run.
- */
-interface KeyguardFaceAuthInteractor {
-
-    /** Current authentication status */
-    val authenticationStatus: Flow<FaceAuthenticationStatus>
-
-    /** Current detection status */
-    val detectionStatus: Flow<FaceDetectionStatus>
-
-    /** Can face auth be run right now */
-    fun canFaceAuthRun(): Boolean
-
-    /** Whether face auth is currently running or not. */
-    fun isRunning(): Boolean
-
-    /** Whether face auth is in lock out state. */
-    fun isLockedOut(): Boolean
-
-    /** Whether face auth is enrolled and enabled for the current user */
-    fun isFaceAuthEnabledAndEnrolled(): Boolean
-
-    /** Whether the current user is authenticated successfully with face auth */
-    fun isAuthenticated(): Boolean
-    /**
-     * Register listener for use from code that cannot use [authenticationStatus] or
-     * [detectionStatus]
-     */
-    fun registerListener(listener: FaceAuthenticationListener)
-
-    /** Unregister previously registered listener */
-    fun unregisterListener(listener: FaceAuthenticationListener)
-
-    fun onUdfpsSensorTouched()
-    fun onAssistantTriggeredOnLockScreen()
-    fun onDeviceLifted()
-    fun onQsExpansionStared()
-    fun onNotificationPanelClicked()
-    fun onSwipeUpOnBouncer()
-    fun onPrimaryBouncerUserInput()
-    fun onAccessibilityAction()
-    fun onWalletLaunched()
-
-    /** Whether face auth is considered class 3 */
-    fun isFaceAuthStrong(): Boolean
-}
-
-/**
- * Listener that can be registered with the [KeyguardFaceAuthInteractor] to receive updates about
- * face authentication & detection updates.
- *
- * This is present to make it easier for use the new face auth API for code that cannot use
- * [KeyguardFaceAuthInteractor.authenticationStatus] or [KeyguardFaceAuthInteractor.detectionStatus]
- * flows.
- */
-interface FaceAuthenticationListener {
-    /** Receive face isAuthenticated updates */
-    fun onAuthenticatedChanged(isAuthenticated: Boolean)
-
-    /** Receive face authentication status updates */
-    fun onAuthenticationStatusChanged(status: FaceAuthenticationStatus)
-
-    /** Receive status updates whenever face detection runs */
-    fun onDetectionStatusChanged(status: FaceDetectionStatus)
-
-    fun onLockoutStateChanged(isLockedOut: Boolean)
-
-    fun onRunningStateChanged(isRunning: Boolean)
-
-    fun onAuthEnrollmentStateChanged(enrolled: Boolean)
-}
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 21651ba2..6eb3b64 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
@@ -104,7 +104,7 @@
     val dozeTimeTick: Flow<Long> = repository.dozeTimeTick
 
     /** Whether Always-on Display mode is available. */
-    val isAodAvailable: Flow<Boolean> = repository.isAodAvailable
+    val isAodAvailable: StateFlow<Boolean> = repository.isAodAvailable
 
     /** Doze transition information. */
     val dozeTransitionModel: Flow<DozeTransitionModel> = repository.dozeTransitionModel
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 ba7b987..91f8420 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
@@ -42,6 +42,7 @@
                     is FromGoneTransitionInteractor -> Log.d(TAG, "Started $it")
                     is FromLockscreenTransitionInteractor -> Log.d(TAG, "Started $it")
                     is FromDreamingTransitionInteractor -> Log.d(TAG, "Started $it")
+                    is FromGlanceableHubTransitionInteractor -> Log.d(TAG, "Started $it")
                     is FromOccludedTransitionInteractor -> Log.d(TAG, "Started $it")
                     is FromDozingTransitionInteractor -> Log.d(TAG, "Started $it")
                     is FromAlternateBouncerTransitionInteractor -> Log.d(TAG, "Started $it")
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 f7d1543..5b0c562 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
@@ -154,6 +154,8 @@
     val dozingToLockscreenTransition: Flow<TransitionStep> =
         repository.transition(DOZING, LOCKSCREEN)
 
+    val transitions = repository.transitions
+
     /** Receive all [TransitionStep] matching a filter of [from]->[to] */
     fun transition(from: KeyguardState, to: KeyguardState): Flow<TransitionStep> {
         return repository.transition(from, to)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt
index 2d43897..19d00cf 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt
@@ -22,12 +22,15 @@
 import com.android.systemui.keyguard.data.repository.LightRevealScrimRepository
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.power.shared.model.ScreenPowerState
 import com.android.systemui.statusbar.LightRevealEffect
 import com.android.systemui.util.kotlin.sample
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.launch
 
 @ExperimentalCoroutinesApi
@@ -39,6 +42,7 @@
     private val lightRevealScrimRepository: LightRevealScrimRepository,
     @Application private val scope: CoroutineScope,
     private val scrimLogger: ScrimLogger,
+    powerInteractor: PowerInteractor,
 ) {
 
     init {
@@ -73,7 +77,16 @@
             lightRevealScrimRepository.revealEffect
         )
 
-    val revealAmount = lightRevealScrimRepository.revealAmount
+    val revealAmount =
+        lightRevealScrimRepository.revealAmount.filter {
+            // When the screen is off we do not want to keep producing frames as this is causing
+            // (invisible) jank. However, we need to still pass through 1f and 0f to ensure that the
+            // correct end states are respected even if the screen turned off (or was still off)
+            // when the animation finished
+            powerInteractor.screenPowerState.value != ScreenPowerState.SCREEN_OFF ||
+                it == 1f ||
+                it == 0f
+        }
 
     companion object {
 
@@ -96,6 +109,7 @@
                 KeyguardState.AOD -> false
                 KeyguardState.DREAMING -> true
                 KeyguardState.DREAMING_LOCKSCREEN_HOSTED -> true
+                KeyguardState.GLANCEABLE_HUB -> true
                 KeyguardState.ALTERNATE_BOUNCER -> true
                 KeyguardState.PRIMARY_BOUNCER -> true
                 KeyguardState.LOCKSCREEN -> true
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/NoopKeyguardFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/NoopKeyguardFaceAuthInteractor.kt
deleted file mode 100644
index cd6ab31..0000000
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/NoopKeyguardFaceAuthInteractor.kt
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- *   Copyright (C) 2023 The Android Open Source Project
- *
- *   Licensed under the Apache License, Version 2.0 (the "License");
- *   you may not use this file except in compliance with the License.
- *   You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *   Unless required by applicable law or agreed to in writing, software
- *   distributed under the License is distributed on an "AS IS" BASIS,
- *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *   See the License for the specific language governing permissions and
- *   limitations under the License.
- */
-
-package com.android.systemui.keyguard.domain.interactor
-
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.shared.model.FaceAuthenticationStatus
-import com.android.systemui.keyguard.shared.model.FaceDetectionStatus
-import javax.inject.Inject
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.emptyFlow
-
-/**
- * Implementation of the interactor that noops all face auth operations.
- *
- * This is required for SystemUI variants that do not support face authentication but still inject
- * other SysUI components that depend on [KeyguardFaceAuthInteractor]
- */
-@SysUISingleton
-class NoopKeyguardFaceAuthInteractor @Inject constructor() : KeyguardFaceAuthInteractor {
-    override val authenticationStatus: Flow<FaceAuthenticationStatus>
-        get() = emptyFlow()
-    override val detectionStatus: Flow<FaceDetectionStatus>
-        get() = emptyFlow()
-
-    override fun canFaceAuthRun(): Boolean = false
-
-    override fun isRunning(): Boolean = false
-
-    override fun isLockedOut(): Boolean = false
-
-    override fun isFaceAuthEnabledAndEnrolled(): Boolean = false
-
-    override fun isFaceAuthStrong(): Boolean = false
-
-    override fun isAuthenticated(): Boolean = false
-
-    override fun registerListener(listener: FaceAuthenticationListener) {}
-
-    override fun unregisterListener(listener: FaceAuthenticationListener) {}
-
-    override fun onUdfpsSensorTouched() {}
-
-    override fun onAssistantTriggeredOnLockScreen() {}
-
-    override fun onDeviceLifted() {}
-
-    override fun onQsExpansionStared() {}
-
-    override fun onNotificationPanelClicked() {}
-
-    override fun onSwipeUpOnBouncer() {}
-    override fun onPrimaryBouncerUserInput() {}
-    override fun onAccessibilityAction() {}
-    override fun onWalletLaunched() = Unit
-}
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 56f5529..d95c38e 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
@@ -67,4 +67,10 @@
     abstract fun fromAlternateBouncer(
         impl: FromAlternateBouncerTransitionInteractor
     ): TransitionInteractor
+
+    @Binds
+    @IntoSet
+    abstract fun fromGlanceableHub(
+        impl: FromGlanceableHubTransitionInteractor
+    ): TransitionInteractor
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt
index d5ac283..5c2df45 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt
@@ -24,8 +24,11 @@
 import com.android.systemui.keyguard.shared.model.TransitionModeOnCanceled
 import com.android.systemui.util.kotlin.sample
 import java.util.UUID
+import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.flowOn
 import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
 
 /**
  * Each TransitionInteractor is responsible for determining under which conditions to notify
@@ -40,14 +43,25 @@
  */
 sealed class TransitionInteractor(
     val fromState: KeyguardState,
+    val transitionInteractor: KeyguardTransitionInteractor,
+    val mainDispatcher: CoroutineDispatcher,
+    val bgDispatcher: CoroutineDispatcher,
 ) {
     val name = this::class.simpleName ?: "UnknownTransitionInteractor"
-
     abstract val transitionRepository: KeyguardTransitionRepository
-    abstract val transitionInteractor: KeyguardTransitionInteractor
     abstract fun start()
 
-    fun startTransitionTo(
+    /* Use background dispatcher for all [KeyguardTransitionInteractor] flows. Necessary because
+     * the [sample] utility internally runs a collect on the Unconfined dispatcher, resulting
+     * in continuations on the main thread. We don't want that for classes that inherit from this.
+     */
+    val startedKeyguardTransitionStep =
+        transitionInteractor.startedKeyguardTransitionStep.flowOn(bgDispatcher)
+    // The following are MutableSharedFlows, and do not require flowOn
+    val startedKeyguardState = transitionInteractor.startedKeyguardState
+    val finishedKeyguardState = transitionInteractor.finishedKeyguardState
+
+    suspend fun startTransitionTo(
         toState: KeyguardState,
         animator: ValueAnimator? = getDefaultAnimatorForTransitionsToState(toState),
         modeOnCanceled: TransitionModeOnCanceled = TransitionModeOnCanceled.LAST_VALUE
@@ -67,16 +81,17 @@
             )
             return null
         }
-
-        return transitionRepository.startTransition(
-            TransitionInfo(
-                name,
-                fromState,
-                toState,
-                animator,
-                modeOnCanceled,
+        return withContext(mainDispatcher) {
+            transitionRepository.startTransition(
+                TransitionInfo(
+                    name,
+                    fromState,
+                    toState,
+                    animator,
+                    modeOnCanceled,
+                )
             )
-        )
+        }
     }
 
     /** This signal may come in before the occlusion signal, and can provide a custom transition */
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 f5bcab9..92612b8 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
@@ -62,6 +62,12 @@
      * unlocked if SWIPE security method is used, or if face lockscreen bypass is false.
      */
     LOCKSCREEN,
+    /**
+     * Device is locked or on dream and user has swiped from the right edge to enter the glanceable
+     * hub UI. From this state, the user can swipe from the left edge to go back to the lock screen
+     * or dream, as well as swipe down for the notifications and up for the bouncer.
+     */
+    GLANCEABLE_HUB,
     /*
      * 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
@@ -95,6 +101,7 @@
                 DOZING -> false
                 DREAMING -> false
                 DREAMING_LOCKSCREEN_HOSTED -> false
+                GLANCEABLE_HUB -> true
                 AOD -> false
                 ALTERNATE_BOUNCER -> true
                 PRIMARY_BOUNCER -> true
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/SysUiFaceAuthenticateOptions.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/SysUiFaceAuthenticateOptions.kt
index 942cd60..b3d0f918 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/SysUiFaceAuthenticateOptions.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/SysUiFaceAuthenticateOptions.kt
@@ -32,7 +32,7 @@
 import android.os.PowerManager.WAKE_REASON_UNKNOWN
 import android.util.Log
 import com.android.internal.logging.UiEventLogger
-import com.android.keyguard.FaceAuthUiEvent
+import com.android.systemui.deviceentry.shared.FaceAuthUiEvent
 
 /**
  * Wrapper for [FaceAuthenticateOptions] to convert SystemUI values to their corresponding value in
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.kt
index 1277585..4abda74 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.kt
@@ -17,9 +17,13 @@
 
 import android.view.animation.Interpolator
 import com.android.app.animation.Interpolators.LINEAR
+import com.android.app.tracing.coroutines.launch
 import com.android.keyguard.logging.KeyguardTransitionAnimationLogger
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.keyguard.shared.model.TransitionState.CANCELED
 import com.android.systemui.keyguard.shared.model.TransitionState.FINISHED
 import com.android.systemui.keyguard.shared.model.TransitionState.RUNNING
@@ -31,9 +35,12 @@
 import kotlin.time.Duration
 import kotlin.time.Duration.Companion.milliseconds
 import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.channels.BufferOverflow
 import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.mapNotNull
 
 /**
  * Assists in creating sub-flows for a KeyguardTransition. Call [setup] once for a transition, and
@@ -44,21 +51,49 @@
 @Inject
 constructor(
     @Application private val scope: CoroutineScope,
+    private val transitionInteractor: KeyguardTransitionInteractor,
     private val logger: KeyguardTransitionAnimationLogger,
 ) {
+    private val transitionMap = mutableMapOf<Edge, MutableSharedFlow<TransitionStep>>()
 
-    /**
-     * Invoke once per transition between FROM->TO states to get access to
-     * [SharedFlowBuilder#sharedFlow].
-     */
+    init {
+        scope.launch("KeyguardTransitionAnimationFlow") {
+            transitionInteractor.transitions.collect {
+                // FROM->TO
+                transitionMap[Edge(it.from, it.to)]?.emit(it)
+                // FROM->(ANY)
+                transitionMap[Edge(it.from, null)]?.emit(it)
+                // (ANY)->TO
+                transitionMap[Edge(null, it.to)]?.emit(it)
+            }
+        }
+    }
+
+    private fun getOrCreateFlow(edge: Edge): MutableSharedFlow<TransitionStep> {
+        return transitionMap.getOrPut(edge) {
+            MutableSharedFlow<TransitionStep>(
+                extraBufferCapacity = 10,
+                onBufferOverflow = BufferOverflow.DROP_OLDEST
+            )
+        }
+    }
+
+    /** Invoke once per transition between FROM->TO states to get access to a shared flow. */
     fun setup(
         duration: Duration,
-        stepFlow: Flow<TransitionStep>,
-    ) = SharedFlowBuilder(duration, stepFlow)
+        from: KeyguardState?,
+        to: KeyguardState?,
+    ): FlowBuilder {
+        if (from == null && to == null) {
+            throw IllegalArgumentException("from and to are both null")
+        }
 
-    inner class SharedFlowBuilder(
+        return FlowBuilder(duration, Edge(from, to))
+    }
+
+    inner class FlowBuilder(
         private val transitionDuration: Duration,
-        private val stepFlow: Flow<TransitionStep>,
+        private val edge: Edge,
     ) {
         /**
          * Transitions will occur over a [transitionDuration] with [TransitionStep]s being emitted
@@ -89,7 +124,6 @@
             val start = (startTime / transitionDuration).toFloat()
             val chunks = (transitionDuration / duration).toFloat()
             logger.logCreate(name, start)
-            var isComplete = true
 
             fun stepToValue(step: TransitionStep): Float? {
                 val value = (step.value - start) * chunks
@@ -98,17 +132,13 @@
                     // middle, it is possible this animation is being skipped but we need to inform
                     // the ViewModels of the last update
                     STARTED -> {
-                        isComplete = false
                         onStart?.invoke()
                         max(0f, min(1f, value))
                     }
                     // Always send a final value of 1. Because of rounding, [value] may never be
                     // exactly 1.
                     RUNNING ->
-                        if (isComplete) {
-                            null
-                        } else if (value >= 1f) {
-                            isComplete = true
+                        if (value >= 1f) {
                             1f
                         } else if (value >= 0f) {
                             value
@@ -119,19 +149,21 @@
                 }?.let { onStep(interpolator.getInterpolation(it)) }
             }
 
-            return stepFlow
+            return getOrCreateFlow(edge)
                 .map { step ->
-                    val value =
-                        when (step.transitionState) {
-                            STARTED -> stepToValue(step)
-                            RUNNING -> stepToValue(step)
-                            CANCELED -> onCancel?.invoke()
-                            FINISHED -> onFinish?.invoke()
-                        }
-                    logger.logTransitionStep(name, step, value)
-                    value
+                    StateToValue(
+                            step.transitionState,
+                            when (step.transitionState) {
+                                STARTED -> stepToValue(step)
+                                RUNNING -> stepToValue(step)
+                                CANCELED -> onCancel?.invoke()
+                                FINISHED -> onFinish?.invoke()
+                            }
+                        )
+                        .also { logger.logTransitionStep(name, step, it.value) }
                 }
-                .filterNotNull()
+                .distinctUntilChanged()
+                .mapNotNull { stateToValue -> stateToValue.value }
         }
 
         /**
@@ -141,4 +173,14 @@
             return sharedFlow(duration = 1.milliseconds, onStep = { value }, onFinish = { value })
         }
     }
+
+    data class Edge(
+        val from: KeyguardState?,
+        val to: KeyguardState?,
+    )
+
+    data class StateToValue(
+        val transitionState: TransitionState,
+        val value: Float?,
+    )
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt
index 78906ac..b2a3549 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt
@@ -17,21 +17,24 @@
 package com.android.systemui.keyguard.ui.binder
 
 import android.view.View
+import android.view.ViewGroup
 import androidx.constraintlayout.widget.ConstraintLayout
 import androidx.constraintlayout.widget.ConstraintSet
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.repeatOnLifecycle
 import com.android.systemui.classifier.Classifier
 import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
-import com.android.systemui.keyguard.ui.SwipeUpAnywhereGestureHandler
+import com.android.systemui.deviceentry.ui.binder.UdfpsAccessibilityOverlayBinder
+import com.android.systemui.deviceentry.ui.view.UdfpsAccessibilityOverlay
+import com.android.systemui.deviceentry.ui.viewmodel.AlternateBouncerUdfpsAccessibilityOverlayViewModel
 import com.android.systemui.keyguard.ui.view.DeviceEntryIconView
+import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerDependencies
 import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerUdfpsIconViewModel
-import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerViewModel
 import com.android.systemui.lifecycle.repeatWhenAttached
 import com.android.systemui.plugins.FalsingManager
 import com.android.systemui.res.R
 import com.android.systemui.scrim.ScrimView
-import com.android.systemui.statusbar.gesture.TapGestureDetector
+import dagger.Lazy
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.launch
 
@@ -47,21 +50,24 @@
     @JvmStatic
     fun bind(
         view: ConstraintLayout,
-        viewModel: AlternateBouncerViewModel,
-        falsingManager: FalsingManager,
-        swipeUpAnywhereGestureHandler: SwipeUpAnywhereGestureHandler,
-        tapGestureDetector: TapGestureDetector,
-        alternateBouncerUdfpsIconViewModel: AlternateBouncerUdfpsIconViewModel,
+        alternateBouncerDependencies: AlternateBouncerDependencies,
     ) {
         if (DeviceEntryUdfpsRefactor.isUnexpectedlyInLegacyMode()) {
             return
         }
-        optionallyAddUdfpsView(
+        optionallyAddUdfpsViews(
             view = view,
-            alternateBouncerUdfpsIconViewModel = alternateBouncerUdfpsIconViewModel,
+            udfpsIconViewModel = alternateBouncerDependencies.udfpsIconViewModel,
+            udfpsA11yOverlayViewModel =
+                alternateBouncerDependencies.udfpsAccessibilityOverlayViewModel,
         )
 
         val scrim = view.requireViewById(R.id.alternate_bouncer_scrim) as ScrimView
+        val viewModel = alternateBouncerDependencies.viewModel
+        val swipeUpAnywhereGestureHandler =
+            alternateBouncerDependencies.swipeUpAnywhereGestureHandler
+        val falsingManager = alternateBouncerDependencies.falsingManager
+        val tapGestureDetector = alternateBouncerDependencies.tapGestureDetector
         view.repeatWhenAttached { alternateBouncerViewContainer ->
             repeatOnLifecycle(Lifecycle.State.STARTED) {
                 scrim.viewAlpha = 0f
@@ -102,44 +108,79 @@
         }
     }
 
-    private fun optionallyAddUdfpsView(
+    private fun optionallyAddUdfpsViews(
         view: ConstraintLayout,
-        alternateBouncerUdfpsIconViewModel: AlternateBouncerUdfpsIconViewModel,
+        udfpsIconViewModel: AlternateBouncerUdfpsIconViewModel,
+        udfpsA11yOverlayViewModel: Lazy<AlternateBouncerUdfpsAccessibilityOverlayViewModel>,
     ) {
         view.repeatWhenAttached {
             repeatOnLifecycle(Lifecycle.State.CREATED) {
                 launch {
-                    alternateBouncerUdfpsIconViewModel.iconLocation.collect { iconLocation ->
-                        val viewId = R.id.alternate_bouncer_udfps_icon_view
-                        var udfpsView = view.getViewById(viewId)
+                    udfpsIconViewModel.iconLocation.collect { iconLocation ->
+                        // add UDFPS a11y overlay
+                        val udfpsA11yOverlayViewId =
+                            R.id.alternate_bouncer_udfps_accessibility_overlay
+                        var udfpsA11yOverlay = view.getViewById(udfpsA11yOverlayViewId)
+                        if (udfpsA11yOverlay == null) {
+                            udfpsA11yOverlay =
+                                UdfpsAccessibilityOverlay(view.context).apply {
+                                    id = udfpsA11yOverlayViewId
+                                }
+                            view.addView(udfpsA11yOverlay)
+                            UdfpsAccessibilityOverlayBinder.bind(
+                                udfpsA11yOverlay,
+                                udfpsA11yOverlayViewModel.get(),
+                            )
+                        }
+
+                        // add UDFPS icon view
+                        val udfpsViewId = R.id.alternate_bouncer_udfps_icon_view
+                        var udfpsView = view.getViewById(udfpsViewId)
                         if (udfpsView == null) {
                             udfpsView =
-                                DeviceEntryIconView(view.context, null).apply { id = viewId }
+                                DeviceEntryIconView(view.context, null).apply {
+                                    id = udfpsViewId
+                                    contentDescription =
+                                        context.resources.getString(
+                                            R.string.accessibility_fingerprint_label
+                                        )
+                                }
                             view.addView(udfpsView)
                             AlternateBouncerUdfpsViewBinder.bind(
                                 udfpsView,
-                                alternateBouncerUdfpsIconViewModel,
+                                udfpsIconViewModel,
                             )
                         }
 
                         val constraintSet = ConstraintSet().apply { clone(view) }
                         constraintSet.apply {
-                            constrainWidth(viewId, iconLocation.width)
-                            constrainHeight(viewId, iconLocation.height)
+                            // udfpsView:
+                            constrainWidth(udfpsViewId, iconLocation.width)
+                            constrainHeight(udfpsViewId, iconLocation.height)
                             connect(
-                                viewId,
+                                udfpsViewId,
                                 ConstraintSet.TOP,
                                 ConstraintSet.PARENT_ID,
                                 ConstraintSet.TOP,
                                 iconLocation.top,
                             )
                             connect(
-                                viewId,
+                                udfpsViewId,
                                 ConstraintSet.START,
                                 ConstraintSet.PARENT_ID,
                                 ConstraintSet.START,
                                 iconLocation.left
                             )
+
+                            // udfpsA11yOverlayView:
+                            constrainWidth(
+                                udfpsA11yOverlayViewId,
+                                ViewGroup.LayoutParams.MATCH_PARENT
+                            )
+                            constrainHeight(
+                                udfpsA11yOverlayViewId,
+                                ViewGroup.LayoutParams.MATCH_PARENT
+                            )
                         }
                         constraintSet.applyTo(view)
                     }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
index eee5206..96e83b0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
@@ -241,7 +241,6 @@
                                 vibratorHelper?.vibrate(KeyguardBottomAreaVibrations.Activated)
                                 settingsMenu.setOnTouchListener(
                                     KeyguardSettingsButtonOnTouchListener(
-                                        view = settingsMenu,
                                         viewModel = viewModel.settingsMenuViewModel,
                                     )
                                 )
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt
index 05fe0b2..bf763b4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt
@@ -16,13 +16,20 @@
 
 package com.android.systemui.keyguard.ui.binder
 
+import android.animation.Animator
+import android.animation.ValueAnimator
+import android.transition.Transition
 import android.transition.TransitionManager
+import android.transition.TransitionSet
+import android.transition.TransitionValues
+import android.view.ViewGroup
 import androidx.annotation.VisibleForTesting
 import androidx.constraintlayout.helper.widget.Layer
 import androidx.constraintlayout.widget.ConstraintLayout
 import androidx.constraintlayout.widget.ConstraintSet
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.repeatOnLifecycle
+import com.android.app.animation.Interpolators
 import com.android.systemui.Flags.migrateClocksToBlueprint
 import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
 import com.android.systemui.keyguard.ui.view.layout.sections.ClockSection
@@ -32,6 +39,8 @@
 import com.android.systemui.res.R
 import kotlinx.coroutines.launch
 
+private const val KEYGUARD_STATUS_VIEW_CUSTOM_CLOCK_MOVE_DURATION_MS = 1000L
+
 object KeyguardClockViewBinder {
     @JvmStatic
     fun bind(
@@ -69,7 +78,13 @@
                 launch {
                     if (!migrateClocksToBlueprint()) return@launch
                     viewModel.clockShouldBeCentered.collect {
-                        applyConstraints(clockSection, keyguardRootView, true)
+                        viewModel.clock?.let {
+                            if (it.largeClock.config.hasCustomPositionUpdatedAnimation) {
+                                playClockCenteringAnimation(clockSection, keyguardRootView, it)
+                            } else {
+                                applyConstraints(clockSection, keyguardRootView, true)
+                            }
+                        }
                     }
                 }
             }
@@ -125,12 +140,84 @@
         clockSection: ClockSection,
         rootView: ConstraintLayout,
         animated: Boolean,
+        set: TransitionSet? = null,
     ) {
         val constraintSet = ConstraintSet().apply { clone(rootView) }
         clockSection.applyConstraints(constraintSet)
         if (animated) {
-            TransitionManager.beginDelayedTransition(rootView)
+            set?.let { TransitionManager.beginDelayedTransition(rootView, it) }
+                ?: run { TransitionManager.beginDelayedTransition(rootView) }
         }
         constraintSet.applyTo(rootView)
     }
+
+    private fun playClockCenteringAnimation(
+        clockSection: ClockSection,
+        keyguardRootView: ConstraintLayout,
+        clock: ClockController,
+    ) {
+        // Find the clock, so we can exclude it from this transition.
+        val clockView = clock.largeClock.view
+        val set = TransitionSet()
+        val adapter = SplitShadeTransitionAdapter(clock)
+        adapter.setInterpolator(Interpolators.LINEAR)
+        adapter.setDuration(KEYGUARD_STATUS_VIEW_CUSTOM_CLOCK_MOVE_DURATION_MS)
+        adapter.addTarget(clockView)
+        set.addTransition(adapter)
+        applyConstraints(clockSection, keyguardRootView, true, set)
+    }
+
+    internal class SplitShadeTransitionAdapter
+    @VisibleForTesting
+    constructor(private val clock: ClockController) : Transition() {
+        private fun captureValues(transitionValues: TransitionValues) {
+            transitionValues.values[PROP_BOUNDS_LEFT] = transitionValues.view.left
+            val locationInWindowTmp = IntArray(2)
+            transitionValues.view.getLocationInWindow(locationInWindowTmp)
+            transitionValues.values[PROP_X_IN_WINDOW] = locationInWindowTmp[0]
+        }
+
+        override fun captureEndValues(transitionValues: TransitionValues) {
+            captureValues(transitionValues)
+        }
+
+        override fun captureStartValues(transitionValues: TransitionValues) {
+            captureValues(transitionValues)
+        }
+
+        override fun createAnimator(
+            sceneRoot: ViewGroup,
+            startValues: TransitionValues?,
+            endValues: TransitionValues?
+        ): Animator? {
+            if (startValues == null || endValues == null) {
+                return null
+            }
+            val anim = ValueAnimator.ofFloat(0f, 1f)
+            val fromLeft = startValues.values[PROP_BOUNDS_LEFT] as Int
+            val fromWindowX = startValues.values[PROP_X_IN_WINDOW] as Int
+            val toWindowX = endValues.values[PROP_X_IN_WINDOW] as Int
+            // Using windowX, to determine direction, instead of left, as in RTL the difference of
+            // toLeft - fromLeft is always positive, even when moving left.
+            val direction = if (toWindowX - fromWindowX > 0) 1 else -1
+            anim.addUpdateListener { animation: ValueAnimator ->
+                clock.largeClock.animations.onPositionUpdated(
+                    fromLeft,
+                    direction,
+                    animation.animatedFraction
+                )
+            }
+            return anim
+        }
+
+        override fun getTransitionProperties(): Array<String> {
+            return TRANSITION_PROPERTIES
+        }
+
+        companion object {
+            private const val PROP_BOUNDS_LEFT = "splitShadeTransitionAdapter:boundsLeft"
+            private const val PROP_X_IN_WINDOW = "splitShadeTransitionAdapter:xInWindow"
+            private val TRANSITION_PROPERTIES = arrayOf(PROP_BOUNDS_LEFT, PROP_X_IN_WINDOW)
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt
index 4c33d90..7c1368a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt
@@ -23,8 +23,8 @@
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.repeatOnLifecycle
 import com.android.systemui.Flags.keyguardBottomAreaRefactor
+import com.android.systemui.keyguard.ui.viewmodel.AodAlphaViewModel
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardIndicationAreaViewModel
-import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
 import com.android.systemui.lifecycle.repeatWhenAttached
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.KeyguardIndicationController
@@ -51,7 +51,7 @@
     fun bind(
         view: ViewGroup,
         viewModel: KeyguardIndicationAreaViewModel,
-        keyguardRootViewModel: KeyguardRootViewModel,
+        aodAlphaViewModel: AodAlphaViewModel,
         indicationController: KeyguardIndicationController,
     ): DisposableHandle {
         indicationController.setIndicationArea(view)
@@ -69,7 +69,7 @@
                 repeatOnLifecycle(Lifecycle.State.STARTED) {
                     launch {
                         if (keyguardBottomAreaRefactor()) {
-                            keyguardRootViewModel.alpha.collect { alpha ->
+                            aodAlphaViewModel.alpha.collect { alpha ->
                                 view.apply {
                                     this.importantForAccessibility =
                                         if (alpha == 0f) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
index 362e7e6..2aebd99 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
@@ -42,9 +42,9 @@
 import com.android.systemui.common.shared.model.TintedIcon
 import com.android.systemui.common.ui.ConfigurationState
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryHapticsInteractor
-import com.android.systemui.flags.FeatureFlagsClassic
 import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl
 import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.ui.viewmodel.BurnInParameters
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
 import com.android.systemui.keyguard.ui.viewmodel.OccludingAppDeviceEntryMessageViewModel
 import com.android.systemui.lifecycle.repeatWhenAttached
@@ -68,7 +68,10 @@
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.coroutineScope
 import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.flow.update
 import kotlinx.coroutines.launch
 
 /** Bind occludingAppDeviceEntryMessageViewModel to run whenever the keyguard view is attached. */
@@ -81,7 +84,6 @@
         view: ViewGroup,
         viewModel: KeyguardRootViewModel,
         configuration: ConfigurationState,
-        featureFlags: FeatureFlagsClassic,
         occludingAppDeviceEntryMessageViewModel: OccludingAppDeviceEntryMessageViewModel,
         chipbarCoordinator: ChipbarCoordinator,
         screenOffAnimationController: ScreenOffAnimationController,
@@ -108,6 +110,8 @@
             }
         }
 
+        val burnInParams = MutableStateFlow(BurnInParameters())
+
         val disposableHandle =
             view.repeatWhenAttached {
                 repeatOnLifecycle(Lifecycle.State.CREATED) {
@@ -164,35 +168,41 @@
                             // large clock isn't added to burnInLayer due to its scale transition
                             // so we also need to add translation to it here
                             // same as translationX
-                            viewModel.translationY.collect { y ->
-                                childViews[burnInLayerId]?.translationY = y
-                                childViews[largeClockId]?.translationY = y
-                            }
-                        }
-
-                        launch {
-                            viewModel.translationX.collect { x ->
-                                childViews[burnInLayerId]?.translationX = x
-                                childViews[largeClockId]?.translationX = x
-                            }
-                        }
-
-                        launch {
-                            viewModel.scale.collect { (scale, scaleClockOnly) ->
-                                if (scaleClockOnly) {
-                                    // For clocks except weather clock, we have scale transition
-                                    // besides translate
-                                    childViews[largeClockId]?.let {
-                                        it.scaleX = scale
-                                        it.scaleY = scale
-                                    }
-                                } else {
-                                    // For weather clock, large clock should have only scale
-                                    // transition with other parts in burnInLayer
-                                    childViews[burnInLayerId]?.scaleX = scale
-                                    childViews[burnInLayerId]?.scaleY = scale
+                            burnInParams
+                                .flatMapLatest { params -> viewModel.translationY(params) }
+                                .collect { y ->
+                                    childViews[burnInLayerId]?.translationY = y
+                                    childViews[largeClockId]?.translationY = y
                                 }
-                            }
+                        }
+
+                        launch {
+                            burnInParams
+                                .flatMapLatest { params -> viewModel.translationX(params) }
+                                .collect { x ->
+                                    childViews[burnInLayerId]?.translationX = x
+                                    childViews[largeClockId]?.translationX = x
+                                }
+                        }
+
+                        launch {
+                            burnInParams
+                                .flatMapLatest { params -> viewModel.scale(params) }
+                                .collect { scaleViewModel ->
+                                    if (scaleViewModel.scaleClockOnly) {
+                                        // For clocks except weather clock, we have scale transition
+                                        // besides translate
+                                        childViews[largeClockId]?.let {
+                                            it.scaleX = scaleViewModel.scale
+                                            it.scaleY = scaleViewModel.scale
+                                        }
+                                    } else {
+                                        // For weather clock, large clock should have only scale
+                                        // transition with other parts in burnInLayer
+                                        childViews[burnInLayerId]?.scaleX = scaleViewModel.scale
+                                        childViews[burnInLayerId]?.scaleY = scaleViewModel.scale
+                                    }
+                                }
                         }
 
                         if (NotificationIconContainerRefactor.isEnabled) {
@@ -255,6 +265,7 @@
                                 vibratorHelper.performHapticFeedback(
                                     view,
                                     HapticFeedbackConstants.CONFIRM,
+                                    HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING,
                                 )
                             }
                         }
@@ -264,6 +275,7 @@
                                 vibratorHelper.performHapticFeedback(
                                     view,
                                     HapticFeedbackConstants.REJECT,
+                                    HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING,
                                 )
                             }
                         }
@@ -272,10 +284,12 @@
             }
 
         if (!migrateClocksToBlueprint()) {
-            viewModel.clockControllerProvider = clockControllerProvider
+            burnInParams.update { current ->
+                current.copy(clockControllerProvider = clockControllerProvider)
+            }
         }
 
-        onLayoutChangeListener = OnLayoutChange(viewModel)
+        onLayoutChangeListener = OnLayoutChange(viewModel, burnInParams)
         view.addOnLayoutChangeListener(onLayoutChangeListener)
 
         // Views will be added or removed after the call to bind(). This is needed to avoid many
@@ -294,7 +308,9 @@
 
         view.setOnApplyWindowInsetsListener { v: View, insets: WindowInsets ->
             val insetTypes = WindowInsets.Type.systemBars() or WindowInsets.Type.displayCutout()
-            viewModel.topInset = insets.getInsetsIgnoringVisibility(insetTypes).top
+            burnInParams.update { current ->
+                current.copy(topInset = insets.getInsetsIgnoringVisibility(insetTypes).top)
+            }
             insets
         }
 
@@ -331,8 +347,10 @@
         )
     }
 
-    private class OnLayoutChange(private val viewModel: KeyguardRootViewModel) :
-        OnLayoutChangeListener {
+    private class OnLayoutChange(
+        private val viewModel: KeyguardRootViewModel,
+        private val burnInParams: MutableStateFlow<BurnInParameters>,
+    ) : OnLayoutChangeListener {
         override fun onLayoutChange(
             view: View,
             left: Int,
@@ -353,7 +371,7 @@
             }
 
             view.findViewById<View>(R.id.keyguard_status_view)?.let { statusView ->
-                viewModel.statusViewTop = statusView.top
+                burnInParams.update { current -> current.copy(statusViewTop = statusView.top) }
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSettingsButtonOnTouchListener.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSettingsButtonOnTouchListener.kt
index c54203c..c6dfcb0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSettingsButtonOnTouchListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSettingsButtonOnTouchListener.kt
@@ -20,12 +20,10 @@
 import android.view.MotionEvent
 import android.view.View
 import android.view.ViewConfiguration
-import com.android.systemui.animation.view.LaunchableLinearLayout
 import com.android.systemui.common.ui.view.rawDistanceFrom
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardSettingsMenuViewModel
 
 class KeyguardSettingsButtonOnTouchListener(
-    private val view: LaunchableLinearLayout,
     private val viewModel: KeyguardSettingsMenuViewModel,
 ) : View.OnTouchListener {
 
@@ -41,8 +39,10 @@
             MotionEvent.ACTION_UP -> {
                 view.isPressed = false
                 val distanceMoved =
-                    motionEvent
-                        .rawDistanceFrom(downPositionDisplayCoords.x, downPositionDisplayCoords.y)
+                    motionEvent.rawDistanceFrom(
+                        downPositionDisplayCoords.x,
+                        downPositionDisplayCoords.y
+                    )
                 val isClick = distanceMoved < ViewConfiguration.getTouchSlop()
                 viewModel.onTouchGestureEnded(isClick)
                 if (isClick) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSettingsViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSettingsViewBinder.kt
index 11e63e7..f67cb68 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSettingsViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSettingsViewBinder.kt
@@ -23,7 +23,6 @@
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.repeatOnLifecycle
 import com.android.systemui.animation.ActivityLaunchAnimator
-import com.android.systemui.animation.view.LaunchableLinearLayout
 import com.android.systemui.common.ui.binder.IconViewBinder
 import com.android.systemui.common.ui.binder.TextViewBinder
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardLongPressViewModel
@@ -43,15 +42,13 @@
 
 object KeyguardSettingsViewBinder {
     fun bind(
-        parentView: View,
+        view: View,
         viewModel: KeyguardSettingsMenuViewModel,
         longPressViewModel: KeyguardLongPressViewModel,
-        rootViewModel: KeyguardRootViewModel,
+        rootViewModel: KeyguardRootViewModel?,
         vibratorHelper: VibratorHelper,
         activityStarter: ActivityStarter
     ): DisposableHandle {
-        val view = parentView.requireViewById<LaunchableLinearLayout>(R.id.keyguard_settings_button)
-
         val disposableHandle =
             view.repeatWhenAttached {
                 repeatOnLifecycle(Lifecycle.State.STARTED) {
@@ -62,7 +59,6 @@
                                 vibratorHelper.vibrate(KeyguardBottomAreaVibrations.Activated)
                                 view.setOnTouchListener(
                                     KeyguardSettingsButtonOnTouchListener(
-                                        view = view,
                                         viewModel = viewModel,
                                     )
                                 )
@@ -96,7 +92,7 @@
                     }
 
                     launch {
-                        rootViewModel.lastRootViewTapPosition.filterNotNull().collect { point ->
+                        rootViewModel?.lastRootViewTapPosition?.filterNotNull()?.collect { point ->
                             if (view.isVisible) {
                                 val hitRect = Rect()
                                 view.getHitRect(hitRect)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSmartspaceViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSmartspaceViewBinder.kt
index e36819b..81ce8f0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSmartspaceViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSmartspaceViewBinder.kt
@@ -23,11 +23,13 @@
 import androidx.constraintlayout.widget.ConstraintSet
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.repeatOnLifecycle
+import com.android.systemui.Flags.migrateClocksToBlueprint
 import com.android.systemui.keyguard.ui.view.layout.sections.SmartspaceSection
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel
 import com.android.systemui.lifecycle.repeatWhenAttached
 import com.android.systemui.res.R
+import com.android.systemui.shared.R as sharedR
 import kotlinx.coroutines.launch
 
 object KeyguardSmartspaceViewBinder {
@@ -41,6 +43,7 @@
         keyguardRootView.repeatWhenAttached {
             repeatOnLifecycle(Lifecycle.State.STARTED) {
                 launch {
+                    if (!migrateClocksToBlueprint()) return@launch
                     clockViewModel.hasCustomWeatherDataDisplay.collect { hasCustomWeatherDataDisplay
                         ->
                         if (hasCustomWeatherDataDisplay) {
@@ -69,9 +72,10 @@
                 smartspaceViewModel.isSmartspaceEnabled &&
                     smartspaceViewModel.isDateWeatherDecoupled
             ) {
-                val dateView = constraintLayout.requireViewById<View>(smartspaceViewModel.dateId)
+                val dateView =
+                    constraintLayout.requireViewById<View>(sharedR.id.date_smartspace_view)
                 val weatherView =
-                    constraintLayout.requireViewById<View>(smartspaceViewModel.weatherId)
+                    constraintLayout.requireViewById<View>(sharedR.id.weather_smartspace_view)
                 addView(weatherView)
                 addView(dateView)
             }
@@ -88,8 +92,10 @@
                 smartspaceViewModel.isSmartspaceEnabled &&
                     smartspaceViewModel.isDateWeatherDecoupled
             ) {
-                val dateView = smartspaceViewModel.dateView
-                val weatherView = smartspaceViewModel.weatherView
+                val dateView =
+                    constraintLayout.requireViewById<View>(sharedR.id.date_smartspace_view)
+                val weatherView =
+                    constraintLayout.requireViewById<View>(sharedR.id.weather_smartspace_view)
                 removeView(weatherView)
                 removeView(dateView)
             }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
index 03e45fd..eb3afb7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
@@ -367,7 +367,6 @@
                     keyguardRootView,
                     keyguardRootViewModel,
                     configuration,
-                    featureFlags,
                     occludingAppDeviceEntryMessageViewModel,
                     chipbarCoordinator,
                     screenOffAnimationController,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardRemotePreviewManager.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardRemotePreviewManager.kt
index 24240df..940d1e1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardRemotePreviewManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardRemotePreviewManager.kt
@@ -142,6 +142,11 @@
             return true
         }
 
+        if (renderer == null || onDestroy == null) {
+            Log.wtf(TAG, "Renderer/onDestroy should not be null.")
+            return true
+        }
+
         when (message.what) {
             KeyguardPreviewConstants.MESSAGE_ID_SLOT_SELECTED -> {
                 message.data.getString(KeyguardPreviewConstants.KEY_SLOT_ID)?.let { slotId ->
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardRootView.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardRootView.kt
index f2b28d9..f33aed0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardRootView.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardRootView.kt
@@ -29,4 +29,8 @@
     ConstraintLayout(
         context,
         attrs,
-    )
+    ) {
+    init {
+        clipChildren = false
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/SplitShadeKeyguardBlueprint.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/SplitShadeKeyguardBlueprint.kt
index 16539db..24d0602 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/SplitShadeKeyguardBlueprint.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/SplitShadeKeyguardBlueprint.kt
@@ -30,10 +30,11 @@
 import com.android.systemui.keyguard.ui.view.layout.sections.DefaultStatusBarSection
 import com.android.systemui.keyguard.ui.view.layout.sections.DefaultStatusViewSection
 import com.android.systemui.keyguard.ui.view.layout.sections.KeyguardSectionsModule
-import com.android.systemui.keyguard.ui.view.layout.sections.SmartspaceSection
 import com.android.systemui.keyguard.ui.view.layout.sections.SplitShadeClockSection
 import com.android.systemui.keyguard.ui.view.layout.sections.SplitShadeGuidelines
+import com.android.systemui.keyguard.ui.view.layout.sections.SplitShadeMediaSection
 import com.android.systemui.keyguard.ui.view.layout.sections.SplitShadeNotificationStackScrollLayoutSection
+import com.android.systemui.keyguard.ui.view.layout.sections.SplitShadeSmartspaceSection
 import com.android.systemui.util.kotlin.getOrNull
 import java.util.Optional
 import javax.inject.Inject
@@ -61,8 +62,9 @@
     aodNotificationIconsSection: AodNotificationIconsSection,
     aodBurnInSection: AodBurnInSection,
     communalTutorialIndicatorSection: CommunalTutorialIndicatorSection,
-    smartspaceSection: SmartspaceSection,
+    smartspaceSection: SplitShadeSmartspaceSection,
     clockSection: SplitShadeClockSection,
+    mediaSection: SplitShadeMediaSection,
 ) : KeyguardBlueprint {
     override val id: String = ID
 
@@ -81,6 +83,7 @@
             aodBurnInSection,
             communalTutorialIndicatorSection,
             clockSection,
+            mediaSection,
             defaultDeviceEntrySection, // Add LAST: Intentionally has z-order above other views.
         )
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodBurnInSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodBurnInSection.kt
index d89e1e4..1ccc6cc 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodBurnInSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodBurnInSection.kt
@@ -28,6 +28,7 @@
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel
 import com.android.systemui.res.R
+import com.android.systemui.shared.R as sharedR
 import javax.inject.Inject
 
 /** Adds a layer to group elements for translation for burn-in preventation */
@@ -87,7 +88,7 @@
         burnInLayer.apply {
             if (smartspaceViewModel.isSmartspaceEnabled) {
                 val smartspaceView =
-                    constraintLayout.requireViewById<View>(smartspaceViewModel.smartspaceViewId)
+                    constraintLayout.requireViewById<View>(sharedR.id.bc_smartspace_view)
                 addView(smartspaceView)
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt
index b429ab4..f560b5f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt
@@ -32,6 +32,7 @@
 import com.android.systemui.keyguard.shared.model.KeyguardSection
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel
 import com.android.systemui.res.R
+import com.android.systemui.shared.R as sharedR
 import com.android.systemui.statusbar.notification.icon.ui.viewbinder.AlwaysOnDisplayNotificationIconViewStore
 import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerViewBinder
 import com.android.systemui.statusbar.notification.icon.ui.viewbinder.StatusBarIconViewBindingFailureTracker
@@ -117,7 +118,7 @@
             }
         constraintSet.apply {
             if (migrateClocksToBlueprint()) {
-                connect(nicId, TOP, smartspaceViewModel.smartspaceViewId, BOTTOM, bottomMargin)
+                connect(nicId, TOP, sharedR.id.bc_smartspace_view, BOTTOM, bottomMargin)
                 setGoneMargin(nicId, BOTTOM, bottomMargin)
             } else {
                 connect(nicId, TOP, R.id.keyguard_status_view, topAlignment, bottomMargin)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt
index 656c75c..b5f32c8 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt
@@ -32,7 +32,6 @@
 import com.android.systemui.keyguard.shared.model.KeyguardSection
 import com.android.systemui.keyguard.ui.binder.KeyguardClockViewBinder
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
-import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel
 import com.android.systemui.plugins.clocks.ClockController
 import com.android.systemui.plugins.clocks.ClockFaceLayout
 import com.android.systemui.res.R
@@ -55,7 +54,6 @@
 constructor(
     private val clockInteractor: KeyguardClockInteractor,
     protected val keyguardClockViewModel: KeyguardClockViewModel,
-    private val smartspaceViewModel: KeyguardSmartspaceViewModel,
     private val context: Context,
     private val splitShadeStateController: SplitShadeStateController,
 ) : KeyguardSection() {
@@ -119,8 +117,8 @@
                         com.android.systemui.customization.R.dimen.small_clock_padding_top
                     ) +
                     context.resources.getDimensionPixelSize(R.dimen.keyguard_smartspace_top_offset)
-            largeClockTopMargin += smartspaceViewModel.getDimen(DATE_WEATHER_VIEW_HEIGHT)
-            largeClockTopMargin += smartspaceViewModel.getDimen(ENHANCED_SMARTSPACE_HEIGHT)
+            largeClockTopMargin += getDimen(DATE_WEATHER_VIEW_HEIGHT)
+            largeClockTopMargin += getDimen(ENHANCED_SMARTSPACE_HEIGHT)
             if (!keyguardClockViewModel.useLargeClock) {
                 largeClockTopMargin -=
                     context.resources.getDimensionPixelSize(
@@ -163,6 +161,12 @@
         }
     }
 
+    private fun getDimen(name: String): Int {
+        val res = context.packageManager.getResourcesForApplication(context.packageName)
+        val id = res.getIdentifier(name, "dimen", context.packageName)
+        return res.getDimensionPixelSize(id)
+    }
+
     companion object {
         private const val DATE_WEATHER_VIEW_HEIGHT = "date_weather_view_height"
         private const val ENHANCED_SMARTSPACE_HEIGHT = "enhanced_smartspace_height"
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSection.kt
index 66c137f..ea05c1d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSection.kt
@@ -25,8 +25,8 @@
 import com.android.systemui.keyguard.shared.model.KeyguardSection
 import com.android.systemui.keyguard.ui.binder.KeyguardIndicationAreaBinder
 import com.android.systemui.keyguard.ui.view.KeyguardIndicationArea
+import com.android.systemui.keyguard.ui.viewmodel.AodAlphaViewModel
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardIndicationAreaViewModel
-import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.KeyguardIndicationController
 import javax.inject.Inject
@@ -37,7 +37,7 @@
 constructor(
     private val context: Context,
     private val keyguardIndicationAreaViewModel: KeyguardIndicationAreaViewModel,
-    private val keyguardRootViewModel: KeyguardRootViewModel,
+    private val aodAlphaViewModel: AodAlphaViewModel,
     private val indicationController: KeyguardIndicationController,
 ) : KeyguardSection() {
     private val indicationAreaViewId = R.id.keyguard_indication_area
@@ -56,7 +56,7 @@
                 KeyguardIndicationAreaBinder.bind(
                     constraintLayout.requireViewById(R.id.keyguard_indication_area),
                     keyguardIndicationAreaViewModel,
-                    keyguardRootViewModel,
+                    aodAlphaViewModel,
                     indicationController,
                 )
         }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt
index e7b6e44..b0eee0a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt
@@ -31,6 +31,7 @@
 import com.android.systemui.res.R
 import com.android.systemui.scene.shared.flag.SceneContainerFlags
 import com.android.systemui.shade.NotificationPanelView
+import com.android.systemui.shared.R as sharedR
 import com.android.systemui.statusbar.notification.stack.AmbientState
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
 import com.android.systemui.statusbar.notification.stack.NotificationStackSizeCalculator
@@ -80,7 +81,7 @@
                 connect(
                     R.id.nssl_placeholder,
                     TOP,
-                    smartspaceViewModel.smartspaceViewId,
+                    sharedR.id.bc_smartspace_view,
                     BOTTOM,
                     bottomMargin
                 )
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultUdfpsAccessibilityOverlaySection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultUdfpsAccessibilityOverlaySection.kt
index e1a33de..3265d79 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultUdfpsAccessibilityOverlaySection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultUdfpsAccessibilityOverlaySection.kt
@@ -23,7 +23,7 @@
 import com.android.systemui.Flags
 import com.android.systemui.deviceentry.ui.binder.UdfpsAccessibilityOverlayBinder
 import com.android.systemui.deviceentry.ui.view.UdfpsAccessibilityOverlay
-import com.android.systemui.deviceentry.ui.viewmodel.UdfpsAccessibilityOverlayViewModel
+import com.android.systemui.deviceentry.ui.viewmodel.DeviceEntryUdfpsAccessibilityOverlayViewModel
 import com.android.systemui.keyguard.shared.model.KeyguardSection
 import com.android.systemui.res.R
 import javax.inject.Inject
@@ -35,7 +35,7 @@
 @Inject
 constructor(
     private val context: Context,
-    private val viewModel: UdfpsAccessibilityOverlayViewModel,
+    private val viewModel: DeviceEntryUdfpsAccessibilityOverlayViewModel,
 ) : KeyguardSection() {
     private val viewId = R.id.udfps_accessibility_overlay
     private var cachedConstraintLayout: ConstraintLayout? = null
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt
index 8cd51cd..eacd466 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.keyguard.ui.view.layout.sections
 
 import android.content.Context
+import android.view.View
 import androidx.constraintlayout.widget.ConstraintLayout
 import androidx.constraintlayout.widget.ConstraintSet
 import androidx.constraintlayout.widget.ConstraintSet.BOTTOM
@@ -44,13 +45,9 @@
     val smartspaceController: LockscreenSmartspaceController,
     val keyguardUnlockAnimationController: KeyguardUnlockAnimationController,
 ) : KeyguardSection() {
-    var smartspaceView by keyguardSmartspaceViewModel::smartspaceView
-    var weatherView by keyguardSmartspaceViewModel::weatherView
-    var dateView by keyguardSmartspaceViewModel::dateView
-
-    val smartspaceViewId = keyguardSmartspaceViewModel.smartspaceViewId
-    val weatherViewId = keyguardSmartspaceViewModel.weatherId
-    val dateViewId = keyguardSmartspaceViewModel.dateId
+    private var smartspaceView: View? = null
+    private var weatherView: View? = null
+    private var dateView: View? = null
 
     override fun addViews(constraintLayout: ConstraintLayout) {
         if (!migrateClocksToBlueprint()) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeClockSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeClockSection.kt
index 1302bfa..19ba1aa 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeClockSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeClockSection.kt
@@ -20,7 +20,6 @@
 import androidx.constraintlayout.widget.ConstraintSet
 import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
-import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.policy.SplitShadeStateController
 import javax.inject.Inject
@@ -30,17 +29,9 @@
 constructor(
     clockInteractor: KeyguardClockInteractor,
     keyguardClockViewModel: KeyguardClockViewModel,
-    smartspaceViewModel: KeyguardSmartspaceViewModel,
     context: Context,
     splitShadeStateController: SplitShadeStateController,
-) :
-    ClockSection(
-        clockInteractor,
-        keyguardClockViewModel,
-        smartspaceViewModel,
-        context,
-        splitShadeStateController
-    ) {
+) : ClockSection(clockInteractor, keyguardClockViewModel, context, splitShadeStateController) {
     override fun applyDefaultConstraints(constraints: ConstraintSet) {
         super.applyDefaultConstraints(constraints)
         val largeClockEndGuideline =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeMediaSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeMediaSection.kt
new file mode 100644
index 0000000..f20ab06
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeMediaSection.kt
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.view.layout.sections
+
+import android.content.Context
+import android.view.View
+import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
+import android.widget.FrameLayout
+import androidx.constraintlayout.widget.Barrier
+import androidx.constraintlayout.widget.ConstraintLayout
+import androidx.constraintlayout.widget.ConstraintSet
+import androidx.constraintlayout.widget.ConstraintSet.BOTTOM
+import androidx.constraintlayout.widget.ConstraintSet.END
+import androidx.constraintlayout.widget.ConstraintSet.MATCH_CONSTRAINT
+import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
+import androidx.constraintlayout.widget.ConstraintSet.START
+import androidx.constraintlayout.widget.ConstraintSet.TOP
+import com.android.systemui.Flags.migrateClocksToBlueprint
+import com.android.systemui.keyguard.shared.model.KeyguardSection
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel
+import com.android.systemui.media.controls.ui.KeyguardMediaController
+import com.android.systemui.res.R
+import com.android.systemui.shade.NotificationPanelView
+import com.android.systemui.shared.R as sharedR
+import javax.inject.Inject
+
+/** Aligns media on left side for split shade, below smartspace, date, and weather. */
+class SplitShadeMediaSection
+@Inject
+constructor(
+    private val context: Context,
+    private val notificationPanelView: NotificationPanelView,
+    private val keyguardSmartspaceViewModel: KeyguardSmartspaceViewModel,
+    private val keyguardMediaController: KeyguardMediaController
+) : KeyguardSection() {
+    private val mediaContainerId = R.id.status_view_media_container
+    private val smartSpaceBarrier = R.id.smart_space_barrier_bottom
+
+    override fun addViews(constraintLayout: ConstraintLayout) {
+        if (!migrateClocksToBlueprint()) {
+            return
+        }
+
+        notificationPanelView.findViewById<View>(mediaContainerId)?.let {
+            notificationPanelView.removeView(it)
+        }
+
+        val mediaFrame =
+            FrameLayout(context, null).apply {
+                id = mediaContainerId
+                val padding = context.resources.getDimensionPixelSize(R.dimen.qs_media_padding)
+                val horizontalPadding =
+                    padding +
+                        context.resources.getDimensionPixelSize(
+                            R.dimen.status_view_margin_horizontal
+                        )
+
+                setPaddingRelative(horizontalPadding, padding, horizontalPadding, padding)
+            }
+        constraintLayout.addView(mediaFrame)
+        keyguardMediaController.attachSplitShadeContainer(mediaFrame)
+    }
+
+    override fun bindData(constraintLayout: ConstraintLayout) {}
+
+    override fun applyConstraints(constraintSet: ConstraintSet) {
+        if (!migrateClocksToBlueprint()) {
+            return
+        }
+
+        constraintSet.apply {
+            constrainWidth(mediaContainerId, MATCH_CONSTRAINT)
+            constrainHeight(mediaContainerId, WRAP_CONTENT)
+
+            createBarrier(
+                smartSpaceBarrier,
+                Barrier.BOTTOM,
+                0,
+                *intArrayOf(
+                    sharedR.id.bc_smartspace_view,
+                    sharedR.id.date_smartspace_view,
+                    sharedR.id.weather_smartspace_view,
+                )
+            )
+            connect(mediaContainerId, TOP, smartSpaceBarrier, BOTTOM)
+            connect(mediaContainerId, START, PARENT_ID, START)
+            connect(mediaContainerId, END, R.id.split_shade_guideline, END)
+        }
+    }
+
+    override fun removeViews(constraintLayout: ConstraintLayout) {
+        if (!migrateClocksToBlueprint()) {
+            return
+        }
+
+        constraintLayout.removeView(mediaContainerId)
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeNotificationStackScrollLayoutSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeNotificationStackScrollLayoutSection.kt
index 0f8e673..756a4cc 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeNotificationStackScrollLayoutSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeNotificationStackScrollLayoutSection.kt
@@ -23,11 +23,13 @@
 import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
 import androidx.constraintlayout.widget.ConstraintSet.START
 import androidx.constraintlayout.widget.ConstraintSet.TOP
+import com.android.systemui.Flags.centralizedStatusBarDimensRefactor
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel
 import com.android.systemui.res.R
 import com.android.systemui.scene.shared.flag.SceneContainerFlags
+import com.android.systemui.shade.LargeScreenHeaderHelper
 import com.android.systemui.shade.NotificationPanelView
 import com.android.systemui.statusbar.notification.stack.AmbientState
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
@@ -35,6 +37,7 @@
 import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer
 import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationStackAppearanceViewModel
 import com.android.systemui.statusbar.notification.stack.ui.viewmodel.SharedNotificationContainerViewModel
+import dagger.Lazy
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
 
@@ -53,6 +56,7 @@
     notificationStackSizeCalculator: NotificationStackSizeCalculator,
     private val smartspaceViewModel: KeyguardSmartspaceViewModel,
     @Main mainDispatcher: CoroutineDispatcher,
+    private val largeScreenHeaderHelperLazy: Lazy<LargeScreenHeaderHelper>,
 ) :
     NotificationStackScrollLayoutSection(
         context,
@@ -72,7 +76,13 @@
         }
         constraintSet.apply {
             val splitShadeTopMargin =
-                context.resources.getDimensionPixelSize(R.dimen.large_screen_shade_header_height)
+                if (centralizedStatusBarDimensRefactor()) {
+                    largeScreenHeaderHelperLazy.get().getLargeScreenHeaderHeight()
+                } else {
+                    context.resources.getDimensionPixelSize(
+                        R.dimen.large_screen_shade_header_height
+                    )
+                }
             connect(R.id.nssl_placeholder, TOP, PARENT_ID, TOP, splitShadeTopMargin)
 
             connect(R.id.nssl_placeholder, START, PARENT_ID, START)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeSmartspaceSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeSmartspaceSection.kt
new file mode 100644
index 0000000..8728ada
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeSmartspaceSection.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.view.layout.sections
+
+import android.content.Context
+import com.android.systemui.keyguard.KeyguardUnlockAnimationController
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel
+import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController
+import javax.inject.Inject
+
+/*
+ * We need this class for the splitShadeBlueprint so `addViews` and `removeViews` will be called
+ * when switching to and from splitShade.
+ */
+class SplitShadeSmartspaceSection
+@Inject
+constructor(
+    keyguardClockViewModel: KeyguardClockViewModel,
+    keyguardSmartspaceViewModel: KeyguardSmartspaceViewModel,
+    context: Context,
+    smartspaceController: LockscreenSmartspaceController,
+    keyguardUnlockAnimationController: KeyguardUnlockAnimationController,
+) :
+    SmartspaceSection(
+        keyguardClockViewModel,
+        keyguardSmartspaceViewModel,
+        context,
+        smartspaceController,
+        keyguardUnlockAnimationController,
+    )
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerDependencies.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerDependencies.kt
new file mode 100644
index 0000000..6846886
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerDependencies.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import com.android.systemui.deviceentry.ui.viewmodel.AlternateBouncerUdfpsAccessibilityOverlayViewModel
+import com.android.systemui.keyguard.ui.SwipeUpAnywhereGestureHandler
+import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.statusbar.gesture.TapGestureDetector
+import dagger.Lazy
+import javax.inject.Inject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+
+/** Provides dependencies for the AlternateBouncerViewBinder. */
+@ExperimentalCoroutinesApi
+class AlternateBouncerDependencies
+@Inject
+constructor(
+    val viewModel: AlternateBouncerViewModel,
+    val falsingManager: FalsingManager,
+    val swipeUpAnywhereGestureHandler: SwipeUpAnywhereGestureHandler,
+    val tapGestureDetector: TapGestureDetector,
+    val udfpsIconViewModel: AlternateBouncerUdfpsIconViewModel,
+    val udfpsAccessibilityOverlayViewModel:
+        Lazy<AlternateBouncerUdfpsAccessibilityOverlayViewModel>,
+)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToAodTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToAodTransitionViewModel.kt
index a8e3be7..b4b48a8 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToAodTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToAodTransitionViewModel.kt
@@ -19,7 +19,6 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
 import com.android.systemui.keyguard.domain.interactor.FromAlternateBouncerTransitionInteractor
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
@@ -38,14 +37,14 @@
 class AlternateBouncerToAodTransitionViewModel
 @Inject
 constructor(
-    interactor: KeyguardTransitionInteractor,
     deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor,
     animationFlow: KeyguardTransitionAnimationFlow,
 ) : DeviceEntryIconTransition {
     private val transitionAnimation =
         animationFlow.setup(
             duration = FromAlternateBouncerTransitionInteractor.TO_AOD_DURATION,
-            stepFlow = interactor.transition(KeyguardState.ALTERNATE_BOUNCER, KeyguardState.AOD),
+            from = KeyguardState.ALTERNATE_BOUNCER,
+            to = KeyguardState.AOD,
         )
 
     val deviceEntryBackgroundViewAlpha: Flow<Float> =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToGoneTransitionViewModel.kt
index 5d6b0ce..3737e6f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToGoneTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToGoneTransitionViewModel.kt
@@ -18,7 +18,6 @@
 
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.domain.interactor.FromAlternateBouncerTransitionInteractor.Companion.TO_GONE_DURATION
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER
 import com.android.systemui.keyguard.shared.model.ScrimAlpha
@@ -37,14 +36,14 @@
 class AlternateBouncerToGoneTransitionViewModel
 @Inject
 constructor(
-    interactor: KeyguardTransitionInteractor,
     bouncerToGoneFlows: BouncerToGoneFlows,
     animationFlow: KeyguardTransitionAnimationFlow,
 ) : DeviceEntryIconTransition {
     private val transitionAnimation =
         animationFlow.setup(
             duration = TO_GONE_DURATION,
-            stepFlow = interactor.transition(ALTERNATE_BOUNCER, KeyguardState.GONE),
+            from = ALTERNATE_BOUNCER,
+            to = KeyguardState.GONE,
         )
 
     /** Scrim alpha values */
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerUdfpsIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerUdfpsIconViewModel.kt
index f4ae365..fa4de04 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerUdfpsIconViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerUdfpsIconViewModel.kt
@@ -20,12 +20,12 @@
 import android.hardware.biometrics.SensorLocationInternal
 import com.android.settingslib.Utils
 import com.android.systemui.biometrics.data.repository.FingerprintPropertyRepository
+import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
 import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
 import com.android.systemui.keyguard.ui.view.DeviceEntryIconView
 import com.android.systemui.res.R
 import javax.inject.Inject
-import kotlin.math.roundToInt
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.combine
@@ -45,6 +45,7 @@
     deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor,
     deviceEntryBackgroundViewModel: DeviceEntryBackgroundViewModel,
     fingerprintPropertyRepository: FingerprintPropertyRepository,
+    udfpsOverlayInteractor: UdfpsOverlayInteractor,
 ) {
     private val isSupported: Flow<Boolean> = deviceEntryUdfpsInteractor.isUdfpsSupported
     val iconLocation: Flow<IconLocation> =
@@ -73,11 +74,7 @@
             .onStart {
                 emit(Utils.getColorAttrDefaultColor(context, android.R.attr.textColorPrimary))
             }
-    private val fgIconPadding: Flow<Int> =
-        configurationInteractor.scaleForResolution.map { scale ->
-            (context.resources.getDimensionPixelSize(R.dimen.lock_icon_padding) * scale)
-                .roundToInt()
-        }
+    private val fgIconPadding: Flow<Int> = udfpsOverlayInteractor.iconPadding
     val fgViewModel: Flow<DeviceEntryForegroundViewModel.ForegroundIconViewModel> =
         combine(
             fgIconColor,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModel.kt
index 8e729f7..2526f0a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModel.kt
@@ -19,7 +19,6 @@
 
 import android.graphics.Color
 import com.android.systemui.keyguard.domain.interactor.FromAlternateBouncerTransitionInteractor.Companion.TRANSITION_DURATION_MS
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
 import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
@@ -37,7 +36,6 @@
 @Inject
 constructor(
     private val statusBarKeyguardViewManager: StatusBarKeyguardViewManager,
-    transitionInteractor: KeyguardTransitionInteractor,
     animationFlow: KeyguardTransitionAnimationFlow,
 ) {
     // When we're fully transitioned to the AlternateBouncer, the alpha of the scrim should be:
@@ -46,7 +44,8 @@
         animationFlow
             .setup(
                 duration = TRANSITION_DURATION_MS,
-                stepFlow = transitionInteractor.anyStateToAlternateBouncerTransition,
+                from = null,
+                to = ALTERNATE_BOUNCER,
             )
             .sharedFlow(
                 duration = TRANSITION_DURATION_MS,
@@ -60,7 +59,8 @@
         animationFlow
             .setup(
                 TRANSITION_DURATION_MS,
-                transitionInteractor.transitionStepsFromState(ALTERNATE_BOUNCER),
+                from = ALTERNATE_BOUNCER,
+                to = null,
             )
             .sharedFlow(
                 duration = TRANSITION_DURATION_MS,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodAlphaViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodAlphaViewModel.kt
new file mode 100644
index 0000000..d4ea728
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodAlphaViewModel.kt
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import javax.inject.Inject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.merge
+import kotlinx.coroutines.flow.onStart
+
+/** Models UI state for the alpha of the AOD (always-on display). */
+@SysUISingleton
+class AodAlphaViewModel
+@Inject
+constructor(
+    keyguardInteractor: KeyguardInteractor,
+    keyguardTransitionInteractor: KeyguardTransitionInteractor,
+    occludedToLockscreenTransitionViewModel: OccludedToLockscreenTransitionViewModel,
+) {
+
+    /** The alpha level for the entire lockscreen while in AOD. */
+    val alpha: Flow<Float> =
+        combine(
+                keyguardTransitionInteractor.transitionValue(KeyguardState.GONE).onStart {
+                    emit(0f)
+                },
+                merge(
+                    keyguardInteractor.keyguardAlpha,
+                    occludedToLockscreenTransitionViewModel.lockscreenAlpha,
+                )
+            ) { transitionToGone, alpha ->
+                if (transitionToGone == 1f) {
+                    // Ensures content is not visible when in GONE state
+                    0f
+                } else {
+                    alpha
+                }
+            }
+            .distinctUntilChanged()
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt
new file mode 100644
index 0000000..780e323
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import android.util.MathUtils
+import com.android.app.animation.Interpolators
+import com.android.keyguard.KeyguardClockSwitch
+import com.android.systemui.Flags
+import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.domain.interactor.BurnInInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.BurnInModel
+import com.android.systemui.plugins.clocks.ClockController
+import com.android.systemui.res.R
+import javax.inject.Inject
+import javax.inject.Provider
+import kotlin.math.max
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.merge
+import kotlinx.coroutines.flow.onStart
+
+/**
+ * Models UI state for elements that need to apply anti-burn-in tactics when showing in AOD
+ * (always-on display).
+ */
+@SysUISingleton
+class AodBurnInViewModel
+@Inject
+constructor(
+    private val burnInInteractor: BurnInInteractor,
+    private val configurationInteractor: ConfigurationInteractor,
+    private val keyguardInteractor: KeyguardInteractor,
+    private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
+    private val goneToAodTransitionViewModel: GoneToAodTransitionViewModel,
+    private val occludedToLockscreenTransitionViewModel: OccludedToLockscreenTransitionViewModel,
+    private val keyguardClockViewModel: KeyguardClockViewModel,
+) {
+    /** Alpha for elements that appear and move during the animation -> AOD */
+    val alpha: Flow<Float> = goneToAodTransitionViewModel.enterFromTopAnimationAlpha
+
+    /** Horizontal translation for elements that need to apply anti-burn-in tactics. */
+    fun translationX(
+        params: BurnInParameters,
+    ): Flow<Float> {
+        return burnIn(params).map { it.translationX.toFloat() }
+    }
+
+    /** Vertical translation for elements that need to apply anti-burn-in tactics. */
+    fun translationY(
+        params: BurnInParameters,
+    ): Flow<Float> {
+        return configurationInteractor
+            .dimensionPixelSize(R.dimen.keyguard_enter_from_top_translation_y)
+            .flatMapLatest { enterFromTopAmount ->
+                combine(
+                    keyguardInteractor.keyguardTranslationY.onStart { emit(0f) },
+                    burnIn(params).map { it.translationY.toFloat() }.onStart { emit(0f) },
+                    goneToAodTransitionViewModel
+                        .enterFromTopTranslationY(enterFromTopAmount)
+                        .onStart { emit(0f) },
+                    occludedToLockscreenTransitionViewModel.lockscreenTranslationY.onStart {
+                        emit(0f)
+                    },
+                ) {
+                    keyguardTransitionY,
+                    burnInTranslationY,
+                    goneToAodTransitionTranslationY,
+                    occludedToLockscreenTransitionTranslationY ->
+
+                    // All values need to be combined for a smooth translation
+                    keyguardTransitionY +
+                        burnInTranslationY +
+                        goneToAodTransitionTranslationY +
+                        occludedToLockscreenTransitionTranslationY
+                }
+            }
+            .distinctUntilChanged()
+    }
+
+    /** Scale for elements that need to apply anti-burn-in tactics. */
+    fun scale(
+        params: BurnInParameters,
+    ): Flow<BurnInScaleViewModel> {
+        return burnIn(params).map {
+            BurnInScaleViewModel(
+                scale = it.scale,
+                scaleClockOnly = it.scaleClockOnly,
+            )
+        }
+    }
+
+    private fun burnIn(
+        params: BurnInParameters,
+    ): Flow<BurnInModel> {
+        return combine(
+            merge(
+                    keyguardTransitionInteractor.goneToAodTransition.map { it.value },
+                    keyguardTransitionInteractor.dozeAmountTransition.map { it.value },
+                )
+                .map { dozeAmount -> Interpolators.FAST_OUT_SLOW_IN.getInterpolation(dozeAmount) },
+            burnInInteractor.keyguardBurnIn,
+        ) { interpolated, burnIn ->
+            val useScaleOnly =
+                (clockController(params.clockControllerProvider)
+                    ?.get()
+                    ?.config
+                    ?.useAlternateSmartspaceAODTransition
+                    ?: false) && keyguardClockViewModel.clockSize.value == KeyguardClockSwitch.LARGE
+
+            if (useScaleOnly) {
+                BurnInModel(
+                    translationX = 0,
+                    translationY = 0,
+                    scale = MathUtils.lerp(burnIn.scale, 1f, 1f - interpolated),
+                )
+            } else {
+                // Ensure the desired translation doesn't encroach on the top inset
+                val burnInY = MathUtils.lerp(0, burnIn.translationY, interpolated).toInt()
+                val translationY =
+                    if (Flags.migrateClocksToBlueprint()) {
+                        burnInY
+                    } else {
+                        max(params.topInset, params.statusViewTop + burnInY) - params.statusViewTop
+                    }
+
+                BurnInModel(
+                    translationX = MathUtils.lerp(0, burnIn.translationX, interpolated).toInt(),
+                    translationY = translationY,
+                    scale =
+                        MathUtils.lerp(
+                            /* start= */ burnIn.scale,
+                            /* stop= */ 1f,
+                            /* amount= */ 1f - interpolated,
+                        ),
+                    scaleClockOnly = true,
+                )
+            }
+        }
+    }
+
+    private fun clockController(
+        provider: Provider<ClockController>?,
+    ): Provider<ClockController>? {
+        return if (Flags.migrateClocksToBlueprint()) {
+            Provider { keyguardClockViewModel.clock }
+        } else {
+            provider
+        }
+    }
+}
+
+/** UI-sourced parameters to pass into the various methods of [AodBurnInViewModel]. */
+data class BurnInParameters(
+    val clockControllerProvider: Provider<ClockController>? = null,
+    /** System insets that keyguard needs to stay out of */
+    val topInset: Int = 0,
+    /** Status view top, without translation added in */
+    val statusViewTop: Int = 0,
+)
+
+/**
+ * Models UI state of the scaling to apply to elements that need to be scaled for anti-burn-in
+ * purposes.
+ */
+data class BurnInScaleViewModel(
+    val scale: Float = 1f,
+    /** Whether the scale only applies to clock UI elements. */
+    val scaleClockOnly: Boolean = false,
+)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModel.kt
index 2b14521..b92a9a0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModel.kt
@@ -18,7 +18,6 @@
 
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.domain.interactor.FromAodTransitionInteractor
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
@@ -31,14 +30,14 @@
 class AodToGoneTransitionViewModel
 @Inject
 constructor(
-    interactor: KeyguardTransitionInteractor,
     animationFlow: KeyguardTransitionAnimationFlow,
 ) : DeviceEntryIconTransition {
 
     private val transitionAnimation =
         animationFlow.setup(
             duration = FromAodTransitionInteractor.TO_GONE_DURATION,
-            stepFlow = interactor.transition(KeyguardState.AOD, KeyguardState.GONE),
+            from = KeyguardState.AOD,
+            to = KeyguardState.GONE,
         )
 
     override val deviceEntryParentViewAlpha = transitionAnimation.immediatelyTransitionTo(0f)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModel.kt
index 5e552e1..266fd02 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModel.kt
@@ -19,7 +19,7 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
 import com.android.systemui.keyguard.domain.interactor.FromAodTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
 import javax.inject.Inject
@@ -37,7 +37,6 @@
 class AodToLockscreenTransitionViewModel
 @Inject
 constructor(
-    interactor: KeyguardTransitionInteractor,
     deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor,
     animationFlow: KeyguardTransitionAnimationFlow,
 ) : DeviceEntryIconTransition {
@@ -45,7 +44,8 @@
     private val transitionAnimation =
         animationFlow.setup(
             duration = TO_LOCKSCREEN_DURATION,
-            stepFlow = interactor.aodToLockscreenTransition,
+            from = KeyguardState.AOD,
+            to = KeyguardState.LOCKSCREEN,
         )
 
     /** Ensure alpha is set to be visible */
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModel.kt
index d283af3..105a7ed 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModel.kt
@@ -18,7 +18,6 @@
 
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.domain.interactor.FromAodTransitionInteractor
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
@@ -29,13 +28,13 @@
 class AodToOccludedTransitionViewModel
 @Inject
 constructor(
-    interactor: KeyguardTransitionInteractor,
     animationFlow: KeyguardTransitionAnimationFlow,
 ) : DeviceEntryIconTransition {
     private val transitionAnimation =
         animationFlow.setup(
             duration = FromAodTransitionInteractor.TO_OCCLUDED_DURATION,
-            stepFlow = interactor.transition(KeyguardState.AOD, KeyguardState.OCCLUDED),
+            from = KeyguardState.AOD,
+            to = KeyguardState.OCCLUDED,
         )
 
     override val deviceEntryParentViewAlpha = transitionAnimation.immediatelyTransitionTo(0f)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlows.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlows.kt
index 41dc157..924fc5d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlows.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlows.kt
@@ -21,7 +21,6 @@
 import com.android.systemui.flags.FeatureFlagsClassic
 import com.android.systemui.flags.Flags
 import com.android.systemui.keyguard.domain.interactor.KeyguardDismissActionInteractor
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
 import com.android.systemui.keyguard.shared.model.ScrimAlpha
@@ -41,7 +40,6 @@
 class BouncerToGoneFlows
 @Inject
 constructor(
-    private val interactor: KeyguardTransitionInteractor,
     private val statusBarStateController: SysuiStatusBarStateController,
     private val primaryBouncerInteractor: PrimaryBouncerInteractor,
     private val keyguardDismissActionInteractor: Lazy<KeyguardDismissActionInteractor>,
@@ -76,7 +74,8 @@
         val transitionAnimation =
             animationFlow.setup(
                 duration = duration,
-                stepFlow = interactor.transition(fromState, GONE)
+                from = fromState,
+                to = GONE,
             )
 
         return shadeInteractor.shadeExpansion.flatMapLatest { shadeExpansion ->
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModel.kt
index 0b34326..e4610c1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModel.kt
@@ -18,7 +18,7 @@
 
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.domain.interactor.FromDozingTransitionInteractor
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
 import javax.inject.Inject
@@ -34,13 +34,13 @@
 class DozingToLockscreenTransitionViewModel
 @Inject
 constructor(
-    interactor: KeyguardTransitionInteractor,
     animationFlow: KeyguardTransitionAnimationFlow,
 ) : DeviceEntryIconTransition {
     private val transitionAnimation =
         animationFlow.setup(
             duration = FromDozingTransitionInteractor.TO_LOCKSCREEN_DURATION,
-            stepFlow = interactor.dozingToLockscreenTransition,
+            from = KeyguardState.DOZING,
+            to = KeyguardState.LOCKSCREEN,
         )
 
     val shortcutsAlpha: Flow<Float> =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingHostedToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingHostedToLockscreenTransitionViewModel.kt
index 8bcf3f8..67568e1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingHostedToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingHostedToLockscreenTransitionViewModel.kt
@@ -18,7 +18,7 @@
 
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.domain.interactor.FromDreamingLockscreenHostedTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
@@ -28,14 +28,14 @@
 class DreamingHostedToLockscreenTransitionViewModel
 @Inject
 constructor(
-    interactor: KeyguardTransitionInteractor,
     animationFlow: KeyguardTransitionAnimationFlow,
 ) {
 
     private val transitionAnimation =
         animationFlow.setup(
             duration = TO_LOCKSCREEN_DURATION,
-            stepFlow = interactor.dreamingLockscreenHostedToLockscreenTransition,
+            from = KeyguardState.DREAMING_LOCKSCREEN_HOSTED,
+            to = KeyguardState.LOCKSCREEN,
         )
 
     val shortcutsAlpha: Flow<Float> =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt
index 5f620af..ead2d48 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt
@@ -22,6 +22,7 @@
 import com.android.systemui.keyguard.domain.interactor.FromDreamingTransitionInteractor
 import com.android.systemui.keyguard.domain.interactor.FromDreamingTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
@@ -52,7 +53,8 @@
     private val transitionAnimation =
         animationFlow.setup(
             duration = TO_LOCKSCREEN_DURATION,
-            stepFlow = keyguardTransitionInteractor.dreamingToLockscreenTransition,
+            from = KeyguardState.DREAMING,
+            to = KeyguardState.LOCKSCREEN,
         )
 
     val transitionEnded =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModel.kt
index 3f27eb0..ba04fd3 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModel.kt
@@ -20,7 +20,7 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
 import com.android.systemui.keyguard.domain.interactor.FromGoneTransitionInteractor.Companion.TO_AOD_DURATION
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
 import javax.inject.Inject
@@ -36,7 +36,6 @@
 class GoneToAodTransitionViewModel
 @Inject
 constructor(
-    interactor: KeyguardTransitionInteractor,
     deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor,
     animationFlow: KeyguardTransitionAnimationFlow,
 ) : DeviceEntryIconTransition {
@@ -44,7 +43,8 @@
     private val transitionAnimation =
         animationFlow.setup(
             duration = TO_AOD_DURATION,
-            stepFlow = interactor.goneToAodTransition,
+            from = KeyguardState.GONE,
+            to = KeyguardState.AOD,
         )
 
     /** y-translation from the top of the screen for AOD */
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingLockscreenHostedTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingLockscreenHostedTransitionViewModel.kt
index bba790a..b527463 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingLockscreenHostedTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingLockscreenHostedTransitionViewModel.kt
@@ -18,7 +18,7 @@
 
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.domain.interactor.FromGoneTransitionInteractor.Companion.TO_DREAMING_DURATION
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
@@ -32,14 +32,14 @@
 class GoneToDreamingLockscreenHostedTransitionViewModel
 @Inject
 constructor(
-    interactor: KeyguardTransitionInteractor,
     animationFlow: KeyguardTransitionAnimationFlow,
 ) {
 
     private val transitionAnimation =
         animationFlow.setup(
             duration = TO_DREAMING_DURATION,
-            stepFlow = interactor.goneToDreamingLockscreenHostedTransition,
+            from = KeyguardState.GONE,
+            to = KeyguardState.DREAMING_LOCKSCREEN_HOSTED,
         )
 
     /** Lockscreen views alpha - hide immediately */
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModel.kt
index 6762ba6..102242a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModel.kt
@@ -19,7 +19,7 @@
 import com.android.app.animation.Interpolators.EMPHASIZED_ACCELERATE
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.domain.interactor.FromGoneTransitionInteractor.Companion.TO_DREAMING_DURATION
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
@@ -30,14 +30,14 @@
 class GoneToDreamingTransitionViewModel
 @Inject
 constructor(
-    private val interactor: KeyguardTransitionInteractor,
     animationFlow: KeyguardTransitionAnimationFlow,
 ) {
 
     private val transitionAnimation =
         animationFlow.setup(
             duration = TO_DREAMING_DURATION,
-            stepFlow = interactor.goneToDreamingTransition,
+            from = KeyguardState.GONE,
+            to = KeyguardState.DREAMING,
         )
 
     /** Lockscreen views y-translation */
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToLockscreenTransitionViewModel.kt
index adae8ab..793abb4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToLockscreenTransitionViewModel.kt
@@ -18,7 +18,7 @@
 
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.domain.interactor.FromGoneTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
@@ -28,14 +28,14 @@
 class GoneToLockscreenTransitionViewModel
 @Inject
 constructor(
-    interactor: KeyguardTransitionInteractor,
     animationFlow: KeyguardTransitionAnimationFlow,
 ) {
 
     private val transitionAnimation =
         animationFlow.setup(
             duration = TO_LOCKSCREEN_DURATION,
-            stepFlow = interactor.goneToLockscreenTransition
+            from = KeyguardState.GONE,
+            to = KeyguardState.LOCKSCREEN
         )
 
     val shortcutsAlpha: Flow<Float> =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt
index 528a2ee..5bb2782 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.keyguard.ui.viewmodel
 
+import android.content.Context
 import androidx.constraintlayout.helper.widget.Layer
 import com.android.keyguard.KeyguardClockSwitch.LARGE
 import com.android.keyguard.KeyguardClockSwitch.SMALL
@@ -25,6 +26,9 @@
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.keyguard.shared.model.SettingsClockSize
 import com.android.systemui.plugins.clocks.ClockController
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.policy.SplitShadeStateController
+import com.android.systemui.util.Utils
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.SharingStarted
@@ -36,9 +40,10 @@
 class KeyguardClockViewModel
 @Inject
 constructor(
-    val keyguardInteractor: KeyguardInteractor,
-    val keyguardClockInteractor: KeyguardClockInteractor,
+    keyguardInteractor: KeyguardInteractor,
+    private val keyguardClockInteractor: KeyguardClockInteractor,
     @Application private val applicationScope: CoroutineScope,
+    private val splitShadeStateController: SplitShadeStateController,
 ) {
     var burnInLayer: Layer? = null
     val useLargeClock: Boolean
@@ -85,4 +90,43 @@
             started = SharingStarted.WhileSubscribed(),
             initialValue = false
         )
+
+    // Needs to use a non application context to get display cutout.
+    fun getSmallClockTopMargin(context: Context) =
+        if (splitShadeStateController.shouldUseSplitNotificationShade(context.resources)) {
+            context.resources.getDimensionPixelSize(R.dimen.keyguard_split_shade_top_margin)
+        } else {
+            context.resources.getDimensionPixelSize(R.dimen.keyguard_clock_top_margin) +
+                Utils.getStatusBarHeaderHeightKeyguard(context)
+        }
+
+    fun getLargeClockTopMargin(context: Context): Int {
+        var largeClockTopMargin =
+            context.resources.getDimensionPixelSize(R.dimen.status_bar_height) +
+                context.resources.getDimensionPixelSize(
+                    com.android.systemui.customization.R.dimen.small_clock_padding_top
+                ) +
+                context.resources.getDimensionPixelSize(R.dimen.keyguard_smartspace_top_offset)
+        largeClockTopMargin += getDimen(context, DATE_WEATHER_VIEW_HEIGHT)
+        largeClockTopMargin += getDimen(context, ENHANCED_SMARTSPACE_HEIGHT)
+        if (!useLargeClock) {
+            largeClockTopMargin -=
+                context.resources.getDimensionPixelSize(
+                    com.android.systemui.customization.R.dimen.small_clock_height
+                )
+        }
+
+        return largeClockTopMargin
+    }
+
+    private fun getDimen(context: Context, name: String): Int {
+        val res = context.packageManager.getResourcesForApplication(context.packageName)
+        val id = res.getIdentifier(name, "dimen", context.packageName)
+        return res.getDimensionPixelSize(id)
+    }
+
+    companion object {
+        private const val DATE_WEATHER_VIEW_HEIGHT = "date_weather_view_height"
+        private const val ENHANCED_SMARTSPACE_HEIGHT = "enhanced_smartspace_height"
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
index 26dace0..5059e6b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
@@ -18,27 +18,17 @@
 package com.android.systemui.keyguard.ui.viewmodel
 
 import android.graphics.Point
-import android.util.MathUtils
 import android.view.View.VISIBLE
-import com.android.app.animation.Interpolators
-import com.android.keyguard.KeyguardClockSwitch.LARGE
-import com.android.systemui.Flags.migrateClocksToBlueprint
 import com.android.systemui.Flags.newAodTransition
 import com.android.systemui.common.shared.model.NotificationContainerBounds
-import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
-import com.android.systemui.flags.FeatureFlagsClassic
-import com.android.systemui.keyguard.domain.interactor.BurnInInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
-import com.android.systemui.keyguard.shared.model.BurnInModel
 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.plugins.clocks.ClockController
-import com.android.systemui.res.R
 import com.android.systemui.statusbar.notification.domain.interactor.NotificationsKeyguardInteractor
 import com.android.systemui.statusbar.phone.DozeParameters
 import com.android.systemui.statusbar.phone.ScreenOffAnimationController
@@ -49,51 +39,29 @@
 import com.android.systemui.util.ui.toAnimatedValueFlow
 import com.android.systemui.util.ui.zip
 import javax.inject.Inject
-import javax.inject.Provider
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.filter
-import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.merge
-import kotlinx.coroutines.flow.onStart
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class KeyguardRootViewModel
 @Inject
 constructor(
-    configurationInteractor: ConfigurationInteractor,
     private val deviceEntryInteractor: DeviceEntryInteractor,
     private val dozeParameters: DozeParameters,
     private val keyguardInteractor: KeyguardInteractor,
-    private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
+    keyguardTransitionInteractor: KeyguardTransitionInteractor,
     private val notificationsKeyguardInteractor: NotificationsKeyguardInteractor,
-    private val burnInInteractor: BurnInInteractor,
-    private val keyguardClockViewModel: KeyguardClockViewModel,
-    private val goneToAodTransitionViewModel: GoneToAodTransitionViewModel,
-    private val aodToLockscreenTransitionViewModel: AodToLockscreenTransitionViewModel,
-    private val occludedToLockscreenTransitionViewModel: OccludedToLockscreenTransitionViewModel,
+    aodToLockscreenTransitionViewModel: AodToLockscreenTransitionViewModel,
     screenOffAnimationController: ScreenOffAnimationController,
-    // TODO(b/310989341): remove after changing migrate_clocks_to_blueprint to aconfig
-    private val featureFlags: FeatureFlagsClassic,
+    private val aodBurnInViewModel: AodBurnInViewModel,
+    aodAlphaViewModel: AodAlphaViewModel,
 ) {
-    var clockControllerProvider: Provider<ClockController>? = null
-        get() {
-            if (migrateClocksToBlueprint()) {
-                return Provider { keyguardClockViewModel.clock }
-            } else {
-                return field
-            }
-        }
-
-    /** System insets that keyguard needs to stay out of */
-    var topInset: Int = 0
-    /** Status view top, without translation added in */
-    var statusViewTop: Int = 0
 
     val burnInLayerVisibility: Flow<Int> =
         keyguardTransitionInteractor.startedKeyguardState
@@ -110,96 +78,25 @@
         keyguardInteractor.notificationContainerBounds
 
     /** An observable for the alpha level for the entire keyguard root view. */
-    val alpha: Flow<Float> =
-        combine(
-                keyguardTransitionInteractor.transitionValue(GONE).onStart { emit(0f) },
-                merge(
-                    keyguardInteractor.keyguardAlpha,
-                    occludedToLockscreenTransitionViewModel.lockscreenAlpha,
-                )
-            ) { transitionToGone, alpha ->
-                if (transitionToGone == 1f) {
-                    // Ensures content is not visible when in GONE state
-                    0f
-                } else {
-                    alpha
-                }
-            }
-            .distinctUntilChanged()
-
-    private fun burnIn(): Flow<BurnInModel> {
-        val dozingAmount: Flow<Float> =
-            merge(
-                keyguardTransitionInteractor.goneToAodTransition.map { it.value },
-                keyguardTransitionInteractor.dozeAmountTransition.map { it.value },
-            )
-
-        return combine(dozingAmount, burnInInteractor.keyguardBurnIn) { dozeAmount, burnIn ->
-            val interpolation = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(dozeAmount)
-            val useScaleOnly =
-                (clockControllerProvider?.get()?.config?.useAlternateSmartspaceAODTransition
-                    ?: false) && keyguardClockViewModel.clockSize.value == LARGE
-            if (useScaleOnly) {
-                BurnInModel(
-                    translationX = 0,
-                    translationY = 0,
-                    scale = MathUtils.lerp(burnIn.scale, 1f, 1f - interpolation),
-                )
-            } else {
-                // Ensure the desired translation doesn't encroach on the top inset
-                val burnInY = MathUtils.lerp(0, burnIn.translationY, interpolation).toInt()
-                val translationY =
-                    if (migrateClocksToBlueprint()) {
-                        burnInY
-                    } else {
-                        -(statusViewTop - Math.max(topInset, statusViewTop + burnInY))
-                    }
-                BurnInModel(
-                    translationX = MathUtils.lerp(0, burnIn.translationX, interpolation).toInt(),
-                    translationY = translationY,
-                    scale = MathUtils.lerp(burnIn.scale, 1f, 1f - interpolation),
-                    scaleClockOnly = true,
-                )
-            }
-        }
-    }
+    val alpha: Flow<Float> = aodAlphaViewModel.alpha
 
     /** Specific alpha value for elements visible during [KeyguardState.LOCKSCREEN] */
     val lockscreenStateAlpha: Flow<Float> = aodToLockscreenTransitionViewModel.lockscreenAlpha
 
     /** For elements that appear and move during the animation -> AOD */
-    val burnInLayerAlpha: Flow<Float> = goneToAodTransitionViewModel.enterFromTopAnimationAlpha
+    val burnInLayerAlpha: Flow<Float> = aodBurnInViewModel.alpha
 
-    val translationY: Flow<Float> =
-        configurationInteractor
-            .dimensionPixelSize(R.dimen.keyguard_enter_from_top_translation_y)
-            .flatMapLatest { enterFromTopAmount ->
-                combine(
-                    keyguardInteractor.keyguardTranslationY.onStart { emit(0f) },
-                    burnIn().map { it.translationY.toFloat() }.onStart { emit(0f) },
-                    goneToAodTransitionViewModel
-                        .enterFromTopTranslationY(enterFromTopAmount)
-                        .onStart { emit(0f) },
-                    occludedToLockscreenTransitionViewModel.lockscreenTranslationY.onStart {
-                        emit(0f)
-                    },
-                ) {
-                    keyguardTransitionY,
-                    burnInTranslationY,
-                    goneToAodTransitionTranslationY,
-                    occludedToLockscreenTransitionTranslationY ->
-                    // All values need to be combined for a smooth translation
-                    keyguardTransitionY +
-                        burnInTranslationY +
-                        goneToAodTransitionTranslationY +
-                        occludedToLockscreenTransitionTranslationY
-                }
-            }
-            .distinctUntilChanged()
+    fun translationY(params: BurnInParameters): Flow<Float> {
+        return aodBurnInViewModel.translationY(params)
+    }
 
-    val translationX: Flow<Float> = burnIn().map { it.translationX.toFloat() }
+    fun translationX(params: BurnInParameters): Flow<Float> {
+        return aodBurnInViewModel.translationX(params)
+    }
 
-    val scale: Flow<Pair<Float, Boolean>> = burnIn().map { Pair(it.scale, it.scaleClockOnly) }
+    fun scale(params: BurnInParameters): Flow<BurnInScaleViewModel> {
+        return aodBurnInViewModel.scale(params)
+    }
 
     /** Is the notification icon container visible? */
     val isNotifIconContainerVisible: Flow<AnimatedValue<Boolean>> =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSmartspaceViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSmartspaceViewModel.kt
index 26e7ee8..a1dd720 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSmartspaceViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSmartspaceViewModel.kt
@@ -16,47 +16,66 @@
 
 package com.android.systemui.keyguard.ui.viewmodel
 
-import android.content.Context
-import android.util.Log
-import android.view.View
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController
 import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
 
 @SysUISingleton
 class KeyguardSmartspaceViewModel
 @Inject
-constructor(val context: Context, val smartspaceController: LockscreenSmartspaceController) {
+constructor(
+    @Application applicationScope: CoroutineScope,
+    smartspaceController: LockscreenSmartspaceController,
+    keyguardClockViewModel: KeyguardClockViewModel,
+) {
+    /** Whether the smartspace section is available in the build. */
     val isSmartspaceEnabled: Boolean = smartspaceController.isEnabled()
+    /** Whether the weather area is available in the build. */
+    // TODO(b/317891876): this should be a Flow as the value can change over time.
     val isWeatherEnabled: Boolean = smartspaceController.isWeatherEnabled()
+    /** Whether the data and weather areas are decoupled in the build. */
     val isDateWeatherDecoupled: Boolean = smartspaceController.isDateWeatherDecoupled()
-    val smartspaceViewId: Int
-        get() = getId("bc_smartspace_view")
 
-    val dateId: Int
-        get() = getId("date_smartspace_view")
+    /** Whether the date area should be visible. */
+    val isDateVisible: StateFlow<Boolean> =
+        keyguardClockViewModel.hasCustomWeatherDataDisplay
+            .map { !it }
+            .stateIn(
+                scope = applicationScope,
+                started = SharingStarted.WhileSubscribed(),
+                initialValue = !keyguardClockViewModel.hasCustomWeatherDataDisplay.value,
+            )
 
-    val weatherId: Int
-        get() = getId("weather_smartspace_view")
-
-    var smartspaceView: View? = null
-    var weatherView: View? = null
-    var dateView: View? = null
-
-    private fun getId(name: String): Int {
-        return context.resources.getIdentifier(name, "id", context.packageName).also {
-            if (it == 0) {
-                Log.d(TAG, "Cannot resolve id $name")
+    /** Whether the weather area should be visible. */
+    val isWeatherVisible: StateFlow<Boolean> =
+        keyguardClockViewModel.hasCustomWeatherDataDisplay
+            .map { clockIncludesCustomWeatherDisplay ->
+                isWeatherVisible(
+                    clockIncludesCustomWeatherDisplay = clockIncludesCustomWeatherDisplay,
+                    isWeatherEnabled = isWeatherEnabled,
+                )
             }
-        }
-    }
-    fun getDimen(name: String): Int {
-        val res = context.packageManager.getResourcesForApplication(context.packageName)
-        val id = res.getIdentifier(name, "dimen", context.packageName)
-        return res.getDimensionPixelSize(id)
-    }
+            .stateIn(
+                scope = applicationScope,
+                started = SharingStarted.WhileSubscribed(),
+                initialValue =
+                    isWeatherVisible(
+                        clockIncludesCustomWeatherDisplay =
+                            keyguardClockViewModel.hasCustomWeatherDataDisplay.value,
+                        isWeatherEnabled = isWeatherEnabled,
+                    )
+            )
 
-    companion object {
-        private val TAG = KeyguardSmartspaceViewModel::class.java.simpleName
+    private fun isWeatherVisible(
+        clockIncludesCustomWeatherDisplay: Boolean,
+        isWeatherEnabled: Boolean,
+    ): Boolean {
+        return !clockIncludesCustomWeatherDisplay && isWeatherEnabled
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt
index d57e569..36bbe4e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt
@@ -33,6 +33,7 @@
 constructor(
     private val interactor: KeyguardBlueprintInteractor,
     private val authController: AuthController,
+    val longPress: KeyguardLongPressViewModel,
 ) {
     val isUdfpsVisible: Boolean
         get() = authController.isUdfpsSupported
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt
index 539db7f..2b28a71 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt
@@ -38,7 +38,6 @@
     deviceEntryInteractor: DeviceEntryInteractor,
     communalInteractor: CommunalInteractor,
     val longPress: KeyguardLongPressViewModel,
-    val keyguardRoot: KeyguardRootViewModel,
     val notifications: NotificationsPlaceholderViewModel,
 ) {
     /** The key of the scene we should switch to when swiping up. */
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModel.kt
index 65614f4..7bf51a7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModel.kt
@@ -19,7 +19,7 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
 import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
 import javax.inject.Inject
@@ -36,7 +36,6 @@
 class LockscreenToAodTransitionViewModel
 @Inject
 constructor(
-    interactor: KeyguardTransitionInteractor,
     deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor,
     shadeDependentFlows: ShadeDependentFlows,
     animationFlow: KeyguardTransitionAnimationFlow,
@@ -45,7 +44,8 @@
     private val transitionAnimation =
         animationFlow.setup(
             duration = FromLockscreenTransitionInteractor.TO_AOD_DURATION,
-            stepFlow = interactor.lockscreenToAodTransition,
+            from = KeyguardState.LOCKSCREEN,
+            to = KeyguardState.AOD,
         )
 
     val deviceEntryBackgroundViewAlpha: Flow<Float> =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDozingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDozingTransitionViewModel.kt
index accb20c..4c0cd2f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDozingTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDozingTransitionViewModel.kt
@@ -18,7 +18,7 @@
 
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor.Companion.TO_DOZING_DURATION
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
@@ -28,14 +28,14 @@
 class LockscreenToDozingTransitionViewModel
 @Inject
 constructor(
-    interactor: KeyguardTransitionInteractor,
     animationFlow: KeyguardTransitionAnimationFlow,
 ) {
 
     private val transitionAnimation =
         animationFlow.setup(
             duration = TO_DOZING_DURATION,
-            stepFlow = interactor.lockscreenToDozingTransition
+            from = KeyguardState.LOCKSCREEN,
+            to = KeyguardState.DOZING,
         )
 
     val shortcutsAlpha: Flow<Float> =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingHostedTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingHostedTransitionViewModel.kt
index c649b12..19b9cf47 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingHostedTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingHostedTransitionViewModel.kt
@@ -18,7 +18,7 @@
 
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor.Companion.TO_DREAMING_HOSTED_DURATION
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
@@ -28,14 +28,14 @@
 class LockscreenToDreamingHostedTransitionViewModel
 @Inject
 constructor(
-    interactor: KeyguardTransitionInteractor,
     animationFlow: KeyguardTransitionAnimationFlow,
 ) {
 
     private val transitionAnimation =
         animationFlow.setup(
             duration = TO_DREAMING_HOSTED_DURATION,
-            stepFlow = interactor.lockscreenToDreamingLockscreenHostedTransition
+            from = KeyguardState.LOCKSCREEN,
+            to = KeyguardState.DREAMING_LOCKSCREEN_HOSTED,
         )
 
     val shortcutsAlpha: Flow<Float> =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModel.kt
index 7f75b54..13522a6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModel.kt
@@ -19,7 +19,7 @@
 import com.android.app.animation.Interpolators.EMPHASIZED_ACCELERATE
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor.Companion.TO_DREAMING_DURATION
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
 import javax.inject.Inject
@@ -34,14 +34,14 @@
 class LockscreenToDreamingTransitionViewModel
 @Inject
 constructor(
-    interactor: KeyguardTransitionInteractor,
     shadeDependentFlows: ShadeDependentFlows,
     animationFlow: KeyguardTransitionAnimationFlow,
 ) : DeviceEntryIconTransition {
     private val transitionAnimation =
         animationFlow.setup(
             duration = TO_DREAMING_DURATION,
-            stepFlow = interactor.lockscreenToDreamingTransition,
+            from = KeyguardState.LOCKSCREEN,
+            to = KeyguardState.DREAMING,
         )
 
     /** Lockscreen views y-translation */
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModel.kt
index 9e19713..a26ef07 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModel.kt
@@ -18,7 +18,6 @@
 
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
@@ -35,14 +34,14 @@
 class LockscreenToGoneTransitionViewModel
 @Inject
 constructor(
-    interactor: KeyguardTransitionInteractor,
     animationFlow: KeyguardTransitionAnimationFlow,
 ) : DeviceEntryIconTransition {
 
     private val transitionAnimation =
         animationFlow.setup(
             duration = FromLockscreenTransitionInteractor.TO_GONE_DURATION,
-            stepFlow = interactor.transition(KeyguardState.LOCKSCREEN, KeyguardState.GONE),
+            from = KeyguardState.LOCKSCREEN,
+            to = KeyguardState.GONE,
         )
 
     val shortcutsAlpha: Flow<Float> =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModel.kt
index 9db0b77..dd6652e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModel.kt
@@ -20,7 +20,7 @@
 import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor.Companion.TO_OCCLUDED_DURATION
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
 import com.android.systemui.res.R
@@ -37,7 +37,6 @@
 class LockscreenToOccludedTransitionViewModel
 @Inject
 constructor(
-    interactor: KeyguardTransitionInteractor,
     shadeDependentFlows: ShadeDependentFlows,
     configurationInteractor: ConfigurationInteractor,
     animationFlow: KeyguardTransitionAnimationFlow,
@@ -46,7 +45,8 @@
     private val transitionAnimation =
         animationFlow.setup(
             duration = TO_OCCLUDED_DURATION,
-            stepFlow = interactor.lockscreenToOccludedTransition,
+            from = KeyguardState.LOCKSCREEN,
+            to = KeyguardState.OCCLUDED,
         )
 
     /** Lockscreen views alpha */
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt
index 52e3257..ce47f3c6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt
@@ -18,7 +18,6 @@
 
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
@@ -26,7 +25,6 @@
 import kotlin.time.Duration.Companion.milliseconds
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.map
 
 /**
  * Breaks down LOCKSCREEN->PRIMARY BOUNCER transition into discrete steps for corresponding views to
@@ -37,21 +35,21 @@
 class LockscreenToPrimaryBouncerTransitionViewModel
 @Inject
 constructor(
-    interactor: KeyguardTransitionInteractor,
     shadeDependentFlows: ShadeDependentFlows,
     animationFlow: KeyguardTransitionAnimationFlow,
 ) : DeviceEntryIconTransition {
     private val transitionAnimation =
         animationFlow.setup(
             duration = FromLockscreenTransitionInteractor.TO_PRIMARY_BOUNCER_DURATION,
-            stepFlow =
-                interactor.transition(KeyguardState.LOCKSCREEN, KeyguardState.PRIMARY_BOUNCER),
+            from = KeyguardState.LOCKSCREEN,
+            to = KeyguardState.PRIMARY_BOUNCER,
         )
 
     val shortcutsAlpha: Flow<Float> =
-        interactor.transition(KeyguardState.LOCKSCREEN, KeyguardState.PRIMARY_BOUNCER).map {
-            1 - it.value
-        }
+        transitionAnimation.sharedFlow(
+            duration = FromLockscreenTransitionInteractor.TO_PRIMARY_BOUNCER_DURATION,
+            onStep = { 1f - it }
+        )
 
     override val deviceEntryParentViewAlpha: Flow<Float> =
         shadeDependentFlows.transitionFlow(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModel.kt
index ed5e83c..07c1141 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModel.kt
@@ -19,7 +19,6 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
 import com.android.systemui.keyguard.domain.interactor.FromOccludedTransitionInteractor
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
@@ -35,14 +34,14 @@
 class OccludedToAodTransitionViewModel
 @Inject
 constructor(
-    interactor: KeyguardTransitionInteractor,
     deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor,
     animationFlow: KeyguardTransitionAnimationFlow,
 ) : DeviceEntryIconTransition {
     private val transitionAnimation =
         animationFlow.setup(
             duration = FromOccludedTransitionInteractor.TO_AOD_DURATION,
-            stepFlow = interactor.transition(KeyguardState.OCCLUDED, KeyguardState.AOD),
+            from = KeyguardState.OCCLUDED,
+            to = KeyguardState.AOD,
         )
 
     val deviceEntryBackgroundViewAlpha: Flow<Float> =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt
index 4c24f83..90195bd 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt
@@ -21,7 +21,7 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
 import com.android.systemui.keyguard.domain.interactor.FromOccludedTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
 import com.android.systemui.res.R
@@ -41,7 +41,6 @@
 class OccludedToLockscreenTransitionViewModel
 @Inject
 constructor(
-    interactor: KeyguardTransitionInteractor,
     deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor,
     configurationInteractor: ConfigurationInteractor,
     animationFlow: KeyguardTransitionAnimationFlow,
@@ -50,7 +49,8 @@
     private val transitionAnimation =
         animationFlow.setup(
             duration = TO_LOCKSCREEN_DURATION,
-            stepFlow = interactor.occludedToLockscreenTransition,
+            from = KeyguardState.OCCLUDED,
+            to = KeyguardState.LOCKSCREEN,
         )
 
     /** Lockscreen views y-translation */
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OffToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OffToLockscreenTransitionViewModel.kt
index 93482ea..74094be 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OffToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OffToLockscreenTransitionViewModel.kt
@@ -17,7 +17,7 @@
 package com.android.systemui.keyguard.ui.viewmodel
 
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
@@ -27,14 +27,14 @@
 class OffToLockscreenTransitionViewModel
 @Inject
 constructor(
-    interactor: KeyguardTransitionInteractor,
     animationFlow: KeyguardTransitionAnimationFlow,
 ) {
 
     private val transitionAnimation =
         animationFlow.setup(
             duration = 250.milliseconds,
-            stepFlow = interactor.offToLockscreenTransition
+            from = KeyguardState.OFF,
+            to = KeyguardState.LOCKSCREEN,
         )
 
     val shortcutsAlpha: Flow<Float> =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModel.kt
index b0e2aa2..cd8e2f1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModel.kt
@@ -19,7 +19,6 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
 import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
@@ -39,14 +38,14 @@
 class PrimaryBouncerToAodTransitionViewModel
 @Inject
 constructor(
-    interactor: KeyguardTransitionInteractor,
     deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor,
     animationFlow: KeyguardTransitionAnimationFlow,
 ) : DeviceEntryIconTransition {
     private val transitionAnimation =
         animationFlow.setup(
             duration = FromPrimaryBouncerTransitionInteractor.TO_AOD_DURATION,
-            stepFlow = interactor.transition(KeyguardState.PRIMARY_BOUNCER, KeyguardState.AOD),
+            from = KeyguardState.PRIMARY_BOUNCER,
+            to = KeyguardState.AOD,
         )
 
     val deviceEntryBackgroundViewAlpha: Flow<Float> =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt
index 9dbe97f..4f28b46 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt
@@ -22,7 +22,6 @@
 import com.android.systemui.flags.Flags
 import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor.Companion.TO_GONE_DURATION
 import com.android.systemui.keyguard.domain.interactor.KeyguardDismissActionInteractor
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
 import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
 import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
 import com.android.systemui.keyguard.shared.model.ScrimAlpha
@@ -44,7 +43,6 @@
 class PrimaryBouncerToGoneTransitionViewModel
 @Inject
 constructor(
-    interactor: KeyguardTransitionInteractor,
     private val statusBarStateController: SysuiStatusBarStateController,
     private val primaryBouncerInteractor: PrimaryBouncerInteractor,
     keyguardDismissActionInteractor: Lazy<KeyguardDismissActionInteractor>,
@@ -55,7 +53,8 @@
     private val transitionAnimation =
         animationFlow.setup(
             duration = TO_GONE_DURATION,
-            stepFlow = interactor.transition(PRIMARY_BOUNCER, GONE)
+            from = PRIMARY_BOUNCER,
+            to = GONE,
         )
 
     private var leaveShadeOpen: Boolean = false
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModel.kt
index b2eed60..284a134 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModel.kt
@@ -19,7 +19,6 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
 import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
@@ -28,7 +27,6 @@
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.emptyFlow
 import kotlinx.coroutines.flow.flatMapLatest
-import kotlinx.coroutines.flow.map
 
 /**
  * Breaks down PRIMARY BOUNCER->LOCKSCREEN transition into discrete steps for corresponding views to
@@ -39,15 +37,14 @@
 class PrimaryBouncerToLockscreenTransitionViewModel
 @Inject
 constructor(
-    interactor: KeyguardTransitionInteractor,
     deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor,
     animationFlow: KeyguardTransitionAnimationFlow,
 ) : DeviceEntryIconTransition {
     private val transitionAnimation =
         animationFlow.setup(
             duration = FromPrimaryBouncerTransitionInteractor.TO_LOCKSCREEN_DURATION,
-            stepFlow =
-                interactor.transition(KeyguardState.PRIMARY_BOUNCER, KeyguardState.LOCKSCREEN),
+            from = KeyguardState.PRIMARY_BOUNCER,
+            to = KeyguardState.LOCKSCREEN,
         )
 
     val deviceEntryBackgroundViewAlpha: Flow<Float> =
@@ -60,9 +57,10 @@
         }
 
     val shortcutsAlpha: Flow<Float> =
-        interactor.transition(KeyguardState.PRIMARY_BOUNCER, KeyguardState.LOCKSCREEN).map {
-            it.value
-        }
+        transitionAnimation.sharedFlow(
+            duration = FromPrimaryBouncerTransitionInteractor.TO_LOCKSCREEN_DURATION,
+            onStep = { it }
+        )
 
     override val deviceEntryParentViewAlpha: Flow<Float> =
         transitionAnimation.immediatelyTransitionTo(1f)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/SideFpsProgressBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/SideFpsProgressBarViewModel.kt
index 1dbf1f1..693e3b7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/SideFpsProgressBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/SideFpsProgressBarViewModel.kt
@@ -28,13 +28,16 @@
 import com.android.systemui.biometrics.shared.model.isDefaultOrientation
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFingerprintAuthInteractor
 import com.android.systemui.keyguard.shared.model.AcquiredFingerprintAuthenticationStatus
 import com.android.systemui.keyguard.shared.model.ErrorFingerprintAuthenticationStatus
 import com.android.systemui.keyguard.shared.model.FailFingerprintAuthenticationStatus
 import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
 import com.android.systemui.res.R
+import com.android.systemui.statusbar.phone.DozeServiceHost
 import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Job
 import kotlinx.coroutines.flow.Flow
@@ -43,6 +46,7 @@
 import kotlinx.coroutines.flow.collectLatest
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flowOn
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.onCompletion
@@ -54,9 +58,13 @@
 @Inject
 constructor(
     private val context: Context,
-    private val fpAuthRepository: DeviceEntryFingerprintAuthRepository,
+    private val fpAuthRepository: DeviceEntryFingerprintAuthInteractor,
     private val sfpsSensorInteractor: SideFpsSensorInteractor,
+    // todo (b/317432075) Injecting DozeServiceHost directly instead of using it through
+    //  DozeInteractor as DozeServiceHost already depends on DozeInteractor.
+    private val dozeServiceHost: DozeServiceHost,
     displayStateInteractor: DisplayStateInteractor,
+    @Main private val mainDispatcher: CoroutineDispatcher,
     @Application private val applicationScope: CoroutineScope,
 ) {
     private val _progress = MutableStateFlow(0.0f)
@@ -168,18 +176,21 @@
                     return@collectLatest
                 }
                 animatorJob =
-                    fpAuthRepository.authenticationStatus
-                        .onEach { authStatus ->
+                    combine(
+                            sfpsSensorInteractor.authenticationDuration,
+                            fpAuthRepository.authenticationStatus,
+                            ::Pair
+                        )
+                        .onEach { (authDuration, authStatus) ->
                             when (authStatus) {
                                 is AcquiredFingerprintAuthenticationStatus -> {
                                     if (authStatus.fingerprintCaptureStarted) {
                                         _visible.value = true
+                                        dozeServiceHost.fireSideFpsAcquisitionStarted()
                                         _animator?.cancel()
                                         _animator =
                                             ValueAnimator.ofFloat(0.0f, 1.0f)
-                                                .setDuration(
-                                                    sfpsSensorInteractor.authenticationDuration
-                                                )
+                                                .setDuration(authDuration)
                                                 .apply {
                                                     addUpdateListener {
                                                         _progress.value = it.animatedValue as Float
@@ -209,6 +220,7 @@
                                 else -> Unit
                             }
                         }
+                        .flowOn(mainDispatcher)
                         .onCompletion { _animator?.cancel() }
                         .launchIn(applicationScope)
             }
diff --git a/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt b/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt
index 3c2facb..9e6c552 100644
--- a/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt
@@ -2,9 +2,9 @@
 
 import android.hardware.face.FaceManager
 import android.hardware.face.FaceSensorPropertiesInternal
-import com.android.keyguard.FaceAuthUiEvent
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.shared.model.ErrorFaceAuthenticationStatus
+import com.android.systemui.deviceentry.shared.FaceAuthUiEvent
+import com.android.systemui.deviceentry.shared.model.ErrorFaceAuthenticationStatus
 import com.android.systemui.log.core.LogLevel.DEBUG
 import com.android.systemui.log.dagger.FaceAuthLog
 import com.android.systemui.power.shared.model.WakeSleepReason
diff --git a/packages/SystemUI/src/com/android/systemui/log/SideFpsLogger.kt b/packages/SystemUI/src/com/android/systemui/log/SideFpsLogger.kt
index 919072a..171656a 100644
--- a/packages/SystemUI/src/com/android/systemui/log/SideFpsLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/SideFpsLogger.kt
@@ -108,4 +108,13 @@
             }
         )
     }
+
+    fun authDurationChanged(duration: Long) {
+        buffer.log(
+            TAG,
+            LogLevel.DEBUG,
+            { long1 = duration },
+            { "SideFpsSensor auth duration changed: $long1" }
+        )
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
index 0d5ba64..24cb8ff 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
@@ -18,7 +18,9 @@
 
 import android.os.Build;
 
+import com.android.systemui.common.data.repository.PackageChangeRepository;
 import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.deviceentry.data.repository.DeviceEntryFaceAuthRepositoryImpl;
 import com.android.systemui.log.LogBuffer;
 import com.android.systemui.log.LogBufferFactory;
 import com.android.systemui.log.LogcatEchoTracker;
@@ -461,7 +463,7 @@
 
     /**
      * Provides a {@link LogBuffer} for use by
-     * {@link com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepositoryImpl}.
+     * {@link DeviceEntryFaceAuthRepositoryImpl}.
      */
     @Provides
     @SysUISingleton
@@ -600,4 +602,12 @@
     public static LogBuffer provideQBluetoothTileDialogLogBuffer(LogBufferFactory factory) {
         return factory.create("BluetoothTileDialogLog", 50);
     }
+
+    /** Provides a {@link LogBuffer} for {@link PackageChangeRepository} */
+    @Provides
+    @SysUISingleton
+    @PackageChangeRepoLog
+    public static LogBuffer providePackageChangeRepoLogBuffer(LogBufferFactory factory) {
+        return factory.create("PackageChangeRepo", 50);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileUser.kt b/packages/SystemUI/src/com/android/systemui/log/dagger/PackageChangeRepoLog.kt
similarity index 73%
rename from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileUser.kt
rename to packages/SystemUI/src/com/android/systemui/log/dagger/PackageChangeRepoLog.kt
index efc7431..93b776c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileUser.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/PackageChangeRepoLog.kt
@@ -14,12 +14,14 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.custom.di.bound
+package com.android.systemui.log.dagger
 
+import com.android.systemui.common.data.repository.PackageChangeRepository
+import com.android.systemui.log.LogBuffer
 import javax.inject.Qualifier
 
-/** User associated with current custom tile binding. */
+/** A [LogBuffer] for [PackageChangeRepository]. */
 @Qualifier
 @MustBeDocumented
 @Retention(AnnotationRetention.RUNTIME)
-annotation class CustomTileUser
+annotation class PackageChangeRepoLog
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 b98e9c2..5caa27f 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
@@ -81,9 +81,12 @@
     /** Set from the notification and used as fallback when PlaybackState cannot be determined */
     val isClearable: Boolean = true,
 
-    /** Timestamp when this player was last active. */
+    /** Milliseconds since boot when this player was last active. */
     var lastActive: Long = 0L,
 
+    /** Timestamp in milliseconds when this player was created. */
+    var createdTimestampMillis: Long = 0L,
+
     /** Instance ID for logging purposes */
     val instanceId: InstanceId,
 
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataFilter.kt b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataFilter.kt
index 724241d..185a783 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataFilter.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataFilter.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.media.controls.pipeline
 
 import android.content.Context
+import android.content.pm.UserInfo
 import android.os.SystemProperties
 import android.util.Log
 import com.android.internal.annotations.KeepForWeakReference
@@ -88,7 +89,11 @@
     private val userTrackerCallback =
         object : UserTracker.Callback {
             override fun onUserChanged(newUser: Int, userContext: Context) {
-                handleUserSwitched(newUser)
+                handleUserSwitched()
+            }
+
+            override fun onProfilesChanged(profiles: List<UserInfo>) {
+                handleProfileChanged()
             }
         }
 
@@ -109,7 +114,10 @@
         }
         allEntries.put(key, data)
 
-        if (!lockscreenUserManager.isCurrentProfile(data.userId)) {
+        if (
+            !lockscreenUserManager.isCurrentProfile(data.userId) ||
+                !lockscreenUserManager.isProfileAvailable(data.userId)
+        ) {
             return
         }
 
@@ -231,7 +239,20 @@
     }
 
     @VisibleForTesting
-    internal fun handleUserSwitched(id: Int) {
+    internal fun handleProfileChanged() {
+        // TODO(b/317221348) re-add media removed when profile is available.
+        allEntries.forEach { (key, data) ->
+            if (!lockscreenUserManager.isProfileAvailable(data.userId)) {
+                // Only remove media when the profile is unavailable.
+                if (DEBUG) Log.d(TAG, "Removing $key after profile change")
+                userEntries.remove(key, data)
+                listeners.forEach { listener -> listener.onMediaDataRemoved(key) }
+            }
+        }
+    }
+
+    @VisibleForTesting
+    internal fun handleUserSwitched() {
         // If the user changes, remove all current MediaData objects and inform listeners
         val listenersCopy = listeners
         val keyCopy = userEntries.keys.toMutableList()
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt
index 3e8b49d..47df3b7 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt
@@ -400,7 +400,12 @@
             val oldKey = findExistingEntry(key, sbn.packageName)
             if (oldKey == null) {
                 val instanceId = logger.getNewInstanceId()
-                val temp = LOADING.copy(packageName = sbn.packageName, instanceId = instanceId)
+                val temp =
+                    LOADING.copy(
+                        packageName = sbn.packageName,
+                        instanceId = instanceId,
+                        createdTimestampMillis = systemClock.currentTimeMillis(),
+                    )
                 mediaEntries.put(key, temp)
                 isNewlyActiveEntry = true
             } else if (oldKey != key) {
@@ -454,7 +459,8 @@
                     resumeAction = action,
                     hasCheckedForResume = true,
                     instanceId = instanceId,
-                    appUid = appUid
+                    appUid = appUid,
+                    createdTimestampMillis = systemClock.currentTimeMillis(),
                 )
             mediaEntries.put(packageName, resumeData)
             logSingleVsMultipleMediaAdded(appUid, packageName, instanceId)
@@ -732,6 +738,7 @@
 
         val mediaAction = getResumeMediaAction(resumeAction)
         val lastActive = systemClock.elapsedRealtime()
+        val createdTimestampMillis = currentEntry?.createdTimestampMillis ?: 0L
         foregroundExecutor.execute {
             onMediaDataLoaded(
                 packageName,
@@ -757,6 +764,7 @@
                     notificationKey = packageName,
                     hasCheckedForResume = true,
                     lastActive = lastActive,
+                    createdTimestampMillis = createdTimestampMillis,
                     instanceId = instanceId,
                     appUid = appUid,
                     isExplicit = isExplicit,
@@ -907,6 +915,7 @@
         }
 
         val lastActive = systemClock.elapsedRealtime()
+        val createdTimestampMillis = currentEntry?.createdTimestampMillis ?: 0L
         foregroundExecutor.execute {
             val resumeAction: Runnable? = mediaEntries[key]?.resumeAction
             val hasCheckedForResume = mediaEntries[key]?.hasCheckedForResume == true
@@ -937,6 +946,7 @@
                     isPlaying = isPlaying,
                     isClearable = !sbn.isOngoing,
                     lastActive = lastActive,
+                    createdTimestampMillis = createdTimestampMillis,
                     instanceId = instanceId,
                     appUid = appUid,
                     isExplicit = isExplicit,
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/KeyguardMediaController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/KeyguardMediaController.kt
index 945bf9a..e15e038 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/KeyguardMediaController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/KeyguardMediaController.kt
@@ -27,6 +27,7 @@
 import android.view.ViewGroup
 import androidx.annotation.VisibleForTesting
 import com.android.systemui.Dumpable
+import com.android.systemui.Flags.migrateClocksToBlueprint
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.dump.DumpManager
@@ -180,7 +181,11 @@
     /** Called whenever the media hosts visibility changes */
     private fun onMediaHostVisibilityChanged(visible: Boolean) {
         refreshMediaPosition(reason = "onMediaHostVisibilityChanged")
+
         if (visible) {
+            if (migrateClocksToBlueprint() && useSplitShade) {
+                return
+            }
             mediaHost.hostView.layoutParams.apply {
                 height = ViewGroup.LayoutParams.WRAP_CONTENT
                 width = ViewGroup.LayoutParams.MATCH_PARENT
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 0385aeb..523414c 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
@@ -1153,7 +1153,7 @@
                 qsExpansion > 0.4f && onLockscreen -> LOCATION_QS
                 onLockscreen && isSplitShadeExpanding() -> LOCATION_QS
                 onLockscreen && isTransformingToFullShadeAndInQQS() -> LOCATION_QQS
-                // TODO(b/308813166): revisit logic once interactions between the hub and
+                // TODO(b/311234666): revisit logic once interactions between the hub and
                 //  shade/keyguard state are finalized
                 isCommunalShowing && communalInteractor.isCommunalEnabled -> LOCATION_COMMUNAL_HUB
                 onLockscreen && allowMediaPlayerOnLockScreen -> LOCATION_LOCKSCREEN
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 e827a1e..3e6d46c 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
@@ -25,12 +25,12 @@
 import com.android.internal.statusbar.IUndoMediaTransferCallback
 import com.android.systemui.CoreStartable
 import com.android.systemui.Dumpable
-import com.android.systemui.res.R
 import com.android.systemui.common.shared.model.Text
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.media.taptotransfer.MediaTttFlags
 import com.android.systemui.media.taptotransfer.common.MediaTttUtils
+import com.android.systemui.res.R
 import com.android.systemui.statusbar.CommandQueue
 import com.android.systemui.temporarydisplay.TemporaryViewDisplayController
 import com.android.systemui.temporarydisplay.ViewPriority
@@ -162,7 +162,7 @@
         logger: MediaTttSenderLogger,
         instanceId: InstanceId,
     ): ChipbarInfo {
-        val packageName = checkNotNull(routeInfo.clientPackageName)
+        val packageName = routeInfo.clientPackageName
         val otherDeviceName =
             if (routeInfo.name.isBlank()) {
                 context.getString(R.string.media_ttt_default_device_type)
@@ -171,7 +171,7 @@
             }
         val icon =
             MediaTttUtils.getIconInfoFromPackageName(context, packageName, isReceiver = false) {
-                logger.logPackageNotFound(packageName)
+                packageName?.let { logger.logPackageNotFound(it) }
             }
 
         val timeout =
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/MediaProjectionTaskSwitcherCoreStartable.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/MediaProjectionTaskSwitcherCoreStartable.kt
index 3c50127..2408af1 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/MediaProjectionTaskSwitcherCoreStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/MediaProjectionTaskSwitcherCoreStartable.kt
@@ -17,23 +17,22 @@
 package com.android.systemui.mediaprojection.taskswitcher
 
 import com.android.systemui.CoreStartable
+import com.android.systemui.Flags.pssTaskSwitcher
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
 import com.android.systemui.mediaprojection.taskswitcher.ui.TaskSwitcherNotificationCoordinator
+import dagger.Lazy
 import javax.inject.Inject
 
 @SysUISingleton
 class MediaProjectionTaskSwitcherCoreStartable
 @Inject
 constructor(
-    private val notificationCoordinator: TaskSwitcherNotificationCoordinator,
-    private val featureFlags: FeatureFlags,
+    private val notificationCoordinatorLazy: Lazy<TaskSwitcherNotificationCoordinator>,
 ) : CoreStartable {
 
     override fun start() {
-        if (featureFlags.isEnabled(Flags.PARTIAL_SCREEN_SHARING_TASK_SWITCHER)) {
-            notificationCoordinator.start()
+        if (pssTaskSwitcher()) {
+            notificationCoordinatorLazy.get().start()
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java
index 0320dec..092f1ed 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java
@@ -302,9 +302,6 @@
             final NavigationBarView navBarView = getNavigationBarView(displayId);
             if (navBarView != null) {
                 navBarView.showPinningEnterExitToast(entering);
-            } else if (displayId == mDisplayTracker.getDefaultDisplayId()
-                    && mTaskbarDelegate.isInitialized()) {
-                mTaskbarDelegate.showPinningEnterExitToast(entering);
             }
         }
 
@@ -314,9 +311,6 @@
             final NavigationBarView navBarView = getNavigationBarView(displayId);
             if (navBarView != null) {
                 navBarView.showPinningEscapeToast();
-            } else if (displayId == mDisplayTracker.getDefaultDisplayId()
-                    && mTaskbarDelegate.isInitialized()) {
-                mTaskbarDelegate.showPinningEscapeToast();
             }
         }
     };
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
index 62c7343..0167287 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
@@ -504,6 +504,11 @@
     }
 
     @Override
+    public void setNavigationBarLumaSamplingEnabled(int displayId, boolean enable) {
+        mOverviewProxyService.onNavigationBarLumaSamplingEnabled(displayId, enable);
+    }
+
+    @Override
     public void showPinningEnterExitToast(boolean entering) {
         updateSysuiFlags();
         if (mScreenPinningNotify == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonView.java
index 6ec46f6..df6843d 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonView.java
@@ -61,6 +61,7 @@
 import com.android.systemui.assist.AssistManager;
 import com.android.systemui.recents.OverviewProxyService;
 import com.android.systemui.res.R;
+import com.android.systemui.shared.navigationbar.KeyButtonRipple;
 import com.android.systemui.shared.system.QuickStepContract;
 
 public class KeyButtonView extends ImageView implements ButtonInterface {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
index e660b97..0d641ac 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -758,7 +758,8 @@
     }
 
     private void updateMLModelState() {
-        boolean newState = mIsGestureHandlingEnabled && DeviceConfig.getBoolean(
+        boolean newState = mIsGestureHandlingEnabled && mContext.getResources().getBoolean(
+                R.bool.config_useBackGestureML) && DeviceConfig.getBoolean(
                 DeviceConfig.NAMESPACE_SYSTEMUI,
                 SystemUiDeviceConfigFlags.USE_BACK_GESTURE_ML_MODEL, false);
 
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
index a6c6233..7e06f5a 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
@@ -87,6 +87,7 @@
 import java.util.Objects;
 
 import javax.inject.Inject;
+import javax.inject.Provider;
 
 /**
  */
@@ -149,6 +150,7 @@
     public static final String EXTRA_CONFIRM_ONLY = "extra_confirm_only";
 
     private final Context mContext;
+    private final SystemUIDialog.Factory mSystemUIDialogFactory;
     private final NotificationManager mNoMan;
     private final PowerManager mPowerMan;
     private final KeyguardManager mKeyguard;
@@ -186,11 +188,17 @@
     /**
      */
     @Inject
-    public PowerNotificationWarnings(Context context, ActivityStarter activityStarter,
-            BroadcastSender broadcastSender, Lazy<BatteryController> batteryControllerLazy,
-            DialogLaunchAnimator dialogLaunchAnimator, UiEventLogger uiEventLogger,
-            GlobalSettings globalSettings, UserTracker userTracker) {
+    public PowerNotificationWarnings(
+            Context context,
+            ActivityStarter activityStarter,
+            BroadcastSender broadcastSender,
+            Lazy<BatteryController> batteryControllerLazy,
+            DialogLaunchAnimator dialogLaunchAnimator,
+            UiEventLogger uiEventLogger,
+            UserTracker userTracker,
+            SystemUIDialog.Factory systemUIDialogFactory) {
         mContext = context;
+        mSystemUIDialogFactory = systemUIDialogFactory;
         mNoMan = mContext.getSystemService(NotificationManager.class);
         mPowerMan = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
         mKeyguard = mContext.getSystemService(KeyguardManager.class);
@@ -444,7 +452,7 @@
 
     private void showHighTemperatureDialog() {
         if (mHighTempDialog != null) return;
-        final SystemUIDialog d = new SystemUIDialog(mContext);
+        final SystemUIDialog d = mSystemUIDialogFactory.create();
         d.setIconAttribute(android.R.attr.alertDialogIcon);
         d.setTitle(R.string.high_temp_title);
         d.setMessage(R.string.high_temp_dialog_message);
@@ -479,7 +487,7 @@
 
     private void showThermalShutdownDialog() {
         if (mThermalShutdownDialog != null) return;
-        final SystemUIDialog d = new SystemUIDialog(mContext);
+        final SystemUIDialog d = mSystemUIDialogFactory.create();
         d.setIconAttribute(android.R.attr.alertDialogIcon);
         d.setTitle(R.string.thermal_shutdown_title);
         d.setMessage(R.string.thermal_shutdown_dialog_message);
@@ -643,7 +651,7 @@
 
     private void showStartSaverConfirmation(Bundle extras) {
         if (mSaverConfirmation != null || mUseExtraSaverConfirmation) return;
-        final SystemUIDialog d = new SystemUIDialog(mContext);
+        final SystemUIDialog d = mSystemUIDialogFactory.create();
         final boolean confirmOnly = extras.getBoolean(BatterySaverUtils.EXTRA_CONFIRM_TEXT_ONLY);
         final int batterySaverTriggerMode =
                 extras.getInt(BatterySaverUtils.EXTRA_POWER_SAVE_MODE_TRIGGER,
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
index 1534653..958ace35 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
@@ -48,12 +48,13 @@
 import com.android.settingslib.fuelgauge.Estimate;
 import com.android.settingslib.utils.ThreadUtils;
 import com.android.systemui.CoreStartable;
-import com.android.systemui.res.R;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.res.R;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.policy.ConfigurationController;
 
 import java.io.PrintWriter;
 import java.util.Arrays;
@@ -62,7 +63,10 @@
 import javax.inject.Inject;
 
 @SysUISingleton
-public class PowerUI implements CoreStartable, CommandQueue.Callbacks {
+public class PowerUI implements
+        CoreStartable,
+        ConfigurationController.ConfigurationListener,
+        CommandQueue.Callbacks {
 
     static final String TAG = "PowerUI";
     static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
@@ -223,7 +227,7 @@
     }
 
     @Override
-    public void onConfigurationChanged(Configuration newConfig) {
+    public void onConfigChanged(Configuration newConfig) {
         final int mask = ActivityInfo.CONFIG_MCC | ActivityInfo.CONFIG_MNC;
 
         // Safe to modify mLastConfiguration here as it's only updated by the main thread (here).
diff --git a/packages/SystemUI/src/com/android/systemui/power/dagger/PowerModule.java b/packages/SystemUI/src/com/android/systemui/power/dagger/PowerModule.java
index 7184fa0..8dd0ea0 100644
--- a/packages/SystemUI/src/com/android/systemui/power/dagger/PowerModule.java
+++ b/packages/SystemUI/src/com/android/systemui/power/dagger/PowerModule.java
@@ -16,14 +16,19 @@
 
 package com.android.systemui.power.dagger;
 
+import com.android.systemui.CoreStartable;
 import com.android.systemui.power.EnhancedEstimates;
 import com.android.systemui.power.EnhancedEstimatesImpl;
 import com.android.systemui.power.PowerNotificationWarnings;
 import com.android.systemui.power.PowerUI;
 import com.android.systemui.power.data.repository.PowerRepositoryModule;
+import com.android.systemui.statusbar.policy.ConfigurationController;
 
 import dagger.Binds;
 import dagger.Module;
+import dagger.multibindings.ClassKey;
+import dagger.multibindings.IntoMap;
+import dagger.multibindings.IntoSet;
 
 
 /** Dagger Module for code in the power package. */
@@ -33,6 +38,17 @@
         }
 )
 public interface PowerModule {
+    /** Starts PowerUI.  */
+    @Binds
+    @IntoMap
+    @ClassKey(PowerUI.class)
+    CoreStartable bindPowerUIStartable(PowerUI impl);
+
+    /** Listen to config changes for PowerUI.  */
+    @Binds
+    @IntoSet
+    ConfigurationController.ConfigurationListener bindPowerUIConfigChanges(PowerUI impl);
+
     /** */
     @Binds
     EnhancedEstimates bindEnhancedEstimates(EnhancedEstimatesImpl enhancedEstimates);
diff --git a/packages/SystemUI/src/com/android/systemui/power/shared/model/WakeSleepReason.kt b/packages/SystemUI/src/com/android/systemui/power/shared/model/WakeSleepReason.kt
index faf9fbe..75055668 100644
--- a/packages/SystemUI/src/com/android/systemui/power/shared/model/WakeSleepReason.kt
+++ b/packages/SystemUI/src/com/android/systemui/power/shared/model/WakeSleepReason.kt
@@ -51,7 +51,10 @@
     BIOMETRIC(isTouch = false, PowerManager.WAKE_REASON_BIOMETRIC),
 
     /** Something else happened to wake up or sleep the device. */
-    OTHER(isTouch = false, PowerManager.WAKE_REASON_UNKNOWN);
+    OTHER(isTouch = false, PowerManager.WAKE_REASON_UNKNOWN),
+
+    /** Device goes to sleep due to folding of a foldable device. */
+    FOLD(isTouch = false, PowerManager.GO_TO_SLEEP_REASON_DEVICE_FOLD);
 
     companion object {
         fun fromPowerManagerWakeReason(reason: Int): WakeSleepReason {
@@ -72,6 +75,7 @@
         fun fromPowerManagerSleepReason(reason: Int): WakeSleepReason {
             return when (reason) {
                 PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON -> POWER_BUTTON
+                PowerManager.GO_TO_SLEEP_REASON_DEVICE_FOLD -> FOLD
                 else -> OTHER
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt b/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt
index 6f35cfb..b5def41 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt
@@ -148,7 +148,8 @@
     private val deviceConfigProxy: DeviceConfigProxy,
     private val dialogLaunchAnimator: DialogLaunchAnimator,
     private val broadcastDispatcher: BroadcastDispatcher,
-    private val dumpManager: DumpManager
+    private val dumpManager: DumpManager,
+    private val systemUIDialogFactory: SystemUIDialog.Factory,
 ) : Dumpable, FgsManagerController {
 
     companion object {
@@ -375,7 +376,7 @@
     override fun showDialog(expandable: Expandable?) {
         synchronized(lock) {
             if (dialog == null) {
-                val dialog = SystemUIDialog(context)
+                val dialog = systemUIDialogFactory.create()
                 dialog.setTitle(R.string.fgs_manager_dialog_title)
                 dialog.setMessage(R.string.fgs_manager_dialog_message)
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index 0644237..a3b9254 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -18,6 +18,8 @@
 
 import static android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS;
 
+import static com.android.systemui.Flags.centralizedStatusBarDimensRefactor;
+
 import android.content.Context;
 import android.graphics.Canvas;
 import android.graphics.Path;
@@ -30,6 +32,7 @@
 import com.android.systemui.Dumpable;
 import com.android.systemui.qs.customize.QSCustomizer;
 import com.android.systemui.res.R;
+import com.android.systemui.shade.LargeScreenHeaderHelper;
 import com.android.systemui.shade.TouchLogger;
 import com.android.systemui.util.LargeScreenUtils;
 
@@ -162,8 +165,12 @@
             QuickStatusBarHeaderController quickStatusBarHeaderController) {
         int topPadding = QSUtils.getQsHeaderSystemIconsAreaHeight(mContext);
         if (!LargeScreenUtils.shouldUseLargeScreenShadeHeader(mContext.getResources())) {
-            topPadding = mContext.getResources()
-                    .getDimensionPixelSize(R.dimen.large_screen_shade_header_height);
+            topPadding =
+                    centralizedStatusBarDimensRefactor()
+                            ? LargeScreenHeaderHelper.getLargeScreenHeaderHeight(mContext)
+                            : mContext.getResources()
+                                    .getDimensionPixelSize(
+                                            R.dimen.large_screen_shade_header_height);
         }
         mQSPanelContainer.setPaddingRelative(
                 mQSPanelContainer.getPaddingStart(),
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index ddd7d67..51b94dd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -189,6 +189,7 @@
     public void setBrightnessView(@NonNull View view) {
         if (mBrightnessView != null) {
             removeView(mBrightnessView);
+            mChildrenLayoutTop.remove(mBrightnessView);
             mMovableContentStartIndex--;
         }
         addView(view, 0);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
index 5eb9620..ef58a60 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
@@ -56,14 +56,18 @@
     private final QSCustomizerController mQsCustomizerController;
     private final QSTileRevealController.Factory mQsTileRevealControllerFactory;
     private final FalsingManager mFalsingManager;
-    private final BrightnessController mBrightnessController;
-    private final BrightnessSliderController mBrightnessSliderController;
-    private final BrightnessMirrorHandler mBrightnessMirrorHandler;
+    private BrightnessController mBrightnessController;
+    private BrightnessSliderController mBrightnessSliderController;
+    private BrightnessMirrorHandler mBrightnessMirrorHandler;
     private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
     private boolean mListening;
 
     private final boolean mSceneContainerEnabled;
 
+    private int mLastDensity;
+    private final BrightnessSliderController.Factory mBrightnessSliderControllerFactory;
+    private final BrightnessController.Factory mBrightnessControllerFactory;
+
     private View.OnTouchListener mTileLayoutTouchListener = new View.OnTouchListener() {
         @Override
         public boolean onTouch(View v, MotionEvent event) {
@@ -93,6 +97,8 @@
         mQsCustomizerController = qsCustomizerController;
         mQsTileRevealControllerFactory = qsTileRevealControllerFactory;
         mFalsingManager = falsingManager;
+        mBrightnessSliderControllerFactory = brightnessSliderFactory;
+        mBrightnessControllerFactory = brightnessControllerFactory;
 
         mBrightnessSliderController = brightnessSliderFactory.create(getContext(), mView);
         mView.setBrightnessView(mBrightnessSliderController.getRootView());
@@ -100,6 +106,7 @@
         mBrightnessController = brightnessControllerFactory.create(mBrightnessSliderController);
         mBrightnessMirrorHandler = new BrightnessMirrorHandler(mBrightnessController);
         mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
+        mLastDensity = view.getResources().getConfiguration().densityDpi;
         mSceneContainerEnabled = sceneContainerFlags.isEnabled();
     }
 
@@ -147,11 +154,31 @@
     @Override
     protected void onConfigurationChanged() {
         mView.updateResources();
+        int newDensity = mView.getResources().getConfiguration().densityDpi;
+        if (newDensity != mLastDensity) {
+            mLastDensity = newDensity;
+            reinflateBrightnessSlider();
+        }
+
         if (mView.isListening()) {
             refreshAllTiles();
         }
     }
 
+    private void reinflateBrightnessSlider() {
+        mBrightnessController.unregisterCallbacks();
+        mBrightnessSliderController =
+                mBrightnessSliderControllerFactory.create(getContext(), mView);
+        mView.setBrightnessView(mBrightnessSliderController.getRootView());
+        mBrightnessController = mBrightnessControllerFactory.create(mBrightnessSliderController);
+        mBrightnessMirrorHandler.setBrightnessController(mBrightnessController);
+        mBrightnessSliderController.init();
+        if (mListening) {
+            mBrightnessController.registerCallbacks();
+        }
+    }
+
+
     @Override
     protected void onSplitShadeChanged(boolean shouldUseSplitNotificationShade) {
         ((PagedTileLayout) mView.getOrCreateTileLayout())
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
index 3e50dd3..ac0bd29 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
@@ -49,6 +49,7 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.List;
 import java.util.Objects;
 import java.util.function.Consumer;
 import java.util.stream.Collectors;
@@ -231,32 +232,39 @@
         if (!collapsedView && mQsTileRevealController != null) {
             mQsTileRevealController.updateRevealedTiles(tiles);
         }
-        boolean shouldChange = false;
+        boolean shouldChangeAll = false;
+        // If the new tiles are a prefix of the old tiles, we delete the extra tiles (from the old).
+        // If not (even if they share a prefix) we remove all and add all the new ones.
         if (tiles.size() <= mRecords.size()) {
             int i = 0;
+            // Iterate through the requested tiles and check if they are the same as the existing
+            // tiles.
             for (QSTile tile : tiles) {
                 if (tile != mRecords.get(i).tile) {
-                    shouldChange = true;
+                    shouldChangeAll = true;
                     break;
                 }
                 i++;
             }
 
-            // If the first tiles are the same as the new ones, remove any extras.
-            if (!shouldChange) {
-                while (i < mRecords.size()) {
-                    QSPanelControllerBase.TileRecord record = mRecords.get(i);
+            // If the first tiles are the same as the new ones, we reuse them and remove any extra
+            // tiles.
+            if (!shouldChangeAll && i < mRecords.size()) {
+                List<TileRecord> extraRecords = mRecords.subList(i, mRecords.size());
+                for (QSPanelControllerBase.TileRecord record : extraRecords) {
                     mView.removeTile(record);
                     record.tile.removeCallback(record.callback);
-                    i++;
                 }
+                extraRecords.clear();
                 mCachedSpecs = getTilesSpecs();
             }
         } else {
-            shouldChange = true;
+            shouldChangeAll = true;
         }
 
-        if (shouldChange) {
+        // If we detected that the existing tiles are different than the requested tiles, clear them
+        // and add the new tiles.
+        if (shouldChangeAll) {
             for (QSPanelControllerBase.TileRecord record : mRecords) {
                 mView.removeTile(record);
                 record.tile.removeCallback(record.callback);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/ReduceBrightColorsController.java b/packages/SystemUI/src/com/android/systemui/qs/ReduceBrightColorsController.java
index 36dc743..a01d658 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/ReduceBrightColorsController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/ReduceBrightColorsController.java
@@ -67,9 +67,7 @@
                 synchronized (mListeners) {
                     if (setting != null && mListeners.size() != 0) {
                         if (setting.equals(Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED)) {
-                            for (Listener listener : mListeners) {
-                                listener.onActivated(mManager.isReduceBrightColorsActivated());
-                            }
+                            dispatchOnActivated(mManager.isReduceBrightColorsActivated());
                         }
                     }
                 }
@@ -125,6 +123,13 @@
         mManager.setReduceBrightColorsActivated(activated);
     }
 
+    private void dispatchOnActivated(boolean activated) {
+        ArrayList<Listener> copy = new ArrayList<>(mListeners);
+        for (Listener l : copy) {
+            l.onActivated(activated);
+        }
+    }
+
     /**
      * Listener invoked whenever the Reduce Bright Colors settings are changed.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
index 47b0624..a45d6f6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
@@ -259,7 +259,11 @@
     private State getState(Collection<QSTile> tiles, String spec) {
         for (QSTile tile : tiles) {
             if (spec.equals(tile.getTileSpec())) {
-                return tile.getState().copy();
+                if (tile.isTileReady()) {
+                    return tile.getState().copy();
+                } else {
+                    return null;
+                }
             }
         }
         return null;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt
index 5d28c8c..957cb1e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt
@@ -319,7 +319,7 @@
 
     override fun dumpProto(systemUIProtoDump: SystemUIProtoDump, args: Array<String>) {
         val data =
-            currentTiles.value.map { it.tile.state }.mapNotNull { it.toProto() }.toTypedArray()
+            currentTiles.value.map { it.tile.state }.mapNotNull { it?.toProto() }.toTypedArray()
         systemUIProtoDump.tiles = data
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
index ccf7afb..c9b0022 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
@@ -55,6 +55,7 @@
 
     private final DataSaverController mDataSaverController;
     private final DialogLaunchAnimator mDialogLaunchAnimator;
+    private final SystemUIDialog.Factory mSystemUIDialogFactory;
 
     @Inject
     public DataSaverTile(
@@ -68,12 +69,14 @@
             ActivityStarter activityStarter,
             QSLogger qsLogger,
             DataSaverController dataSaverController,
-            DialogLaunchAnimator dialogLaunchAnimator
+            DialogLaunchAnimator dialogLaunchAnimator,
+            SystemUIDialog.Factory systemUIDialogFactory
     ) {
         super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger,
                 statusBarStateController, activityStarter, qsLogger);
         mDataSaverController = dataSaverController;
         mDialogLaunchAnimator = dialogLaunchAnimator;
+        mSystemUIDialogFactory = systemUIDialogFactory;
         mDataSaverController.observe(getLifecycle(), this);
     }
 
@@ -98,7 +101,7 @@
         // Show a dialog to confirm first. Dialogs shown by the DialogLaunchAnimator must be created
         // and shown on the main thread, so we post it to the UI handler.
         mUiHandler.post(() -> {
-            SystemUIDialog dialog = new SystemUIDialog(mContext);
+            SystemUIDialog dialog = mSystemUIDialogFactory.create();
             dialog.setTitle(com.android.internal.R.string.data_saver_enable_title);
             dialog.setMessage(com.android.internal.R.string.data_saver_description);
             dialog.setPositiveButton(com.android.internal.R.string.data_saver_enable_button,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
index f37f58d..e89cc5a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
@@ -194,7 +194,8 @@
         ActivityStarter.OnDismissAction dismissAction = () -> {
             if (shouldAnimateFromView) {
                 mDialogLaunchAnimator.showFromView(dialog, view, new DialogCuj(
-                        InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN, INTERACTION_JANK_TAG));
+                        InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN, INTERACTION_JANK_TAG),
+                        /* animateBackgroundBoundsChange= */ true);
             } else {
                 dialog.show();
             }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandler.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandler.kt
index 840db26..fe10eaa 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandler.kt
@@ -18,6 +18,8 @@
 
 import android.app.PendingIntent
 import android.content.Intent
+import android.content.pm.PackageManager
+import android.os.UserHandle
 import android.view.View
 import com.android.internal.jank.InteractionJankMonitor
 import com.android.systemui.animation.ActivityLaunchAnimator
@@ -32,13 +34,23 @@
 interface QSTileIntentUserInputHandler {
 
     fun handle(view: View?, intent: Intent)
-    fun handle(view: View?, pendingIntent: PendingIntent)
+
+    /** @param requestLaunchingDefaultActivity used in case !pendingIndent.isActivity */
+    fun handle(
+        view: View?,
+        pendingIntent: PendingIntent,
+        requestLaunchingDefaultActivity: Boolean = false
+    )
 }
 
 @SysUISingleton
 class QSTileIntentUserInputHandlerImpl
 @Inject
-constructor(private val activityStarter: ActivityStarter) : QSTileIntentUserInputHandler {
+constructor(
+    private val activityStarter: ActivityStarter,
+    private val packageManager: PackageManager,
+    private val userHandle: UserHandle,
+) : QSTileIntentUserInputHandler {
 
     override fun handle(view: View?, intent: Intent) {
         val animationController: ActivityLaunchAnimator.Controller? =
@@ -52,17 +64,41 @@
     }
 
     // TODO(b/249804373): make sure to allow showing activities over the lockscreen. See b/292112939
-    override fun handle(view: View?, pendingIntent: PendingIntent) {
-        if (!pendingIntent.isActivity) {
-            return
-        }
-        val animationController: ActivityLaunchAnimator.Controller? =
-            view?.let {
-                ActivityLaunchAnimator.Controller.fromView(
-                    it,
-                    InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE,
+    override fun handle(
+        view: View?,
+        pendingIntent: PendingIntent,
+        requestLaunchingDefaultActivity: Boolean
+    ) {
+        if (pendingIntent.isActivity) {
+            val animationController: ActivityLaunchAnimator.Controller? =
+                view?.let {
+                    ActivityLaunchAnimator.Controller.fromView(
+                        it,
+                        InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE,
+                    )
+                }
+            activityStarter.postStartActivityDismissingKeyguard(pendingIntent, animationController)
+        } else if (requestLaunchingDefaultActivity) {
+            val intent =
+                Intent(Intent.ACTION_MAIN)
+                    .addCategory(Intent.CATEGORY_LAUNCHER)
+                    .setPackage(pendingIntent.creatorPackage)
+                    .addFlags(
+                        Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
+                    )
+            val intents =
+                packageManager.queryIntentActivitiesAsUser(
+                    intent,
+                    PackageManager.ResolveInfoFlags.of(0L),
+                    userHandle.identifier
                 )
-            }
-        activityStarter.postStartActivityDismissingKeyguard(pendingIntent, animationController)
+            intents
+                .firstOrNull { it.activityInfo.exported }
+                ?.let { resolved ->
+                    intent.setPackage(null)
+                    intent.setComponent(resolved.activityInfo.componentName)
+                    handle(view, intent)
+                }
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/logging/QSTileLogger.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/logging/QSTileLogger.kt
index 0a9a6d3..bc016bd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/logging/QSTileLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/logging/QSTileLogger.kt
@@ -158,6 +158,33 @@
             )
     }
 
+    fun logError(
+        tileSpec: TileSpec,
+        message: String,
+        error: Throwable,
+    ) {
+        tileSpec
+            .getLogBuffer()
+            .log(
+                tileSpec.getLogTag(),
+                LogLevel.ERROR,
+                {},
+                { message },
+                error,
+            )
+    }
+
+    fun logCustomTileUserActionDelivered(tileSpec: TileSpec) {
+        tileSpec
+            .getLogBuffer()
+            .log(
+                tileSpec.getLogTag(),
+                LogLevel.DEBUG,
+                {},
+                { "user action delivered to the service" },
+            )
+    }
+
     private fun TileSpec.getLogTag(): String = "${TAG_FORMAT_PREFIX}_${this.spec}"
 
     private fun TileSpec.getLogBuffer(): LogBuffer =
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/di/NewQSTileFactory.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/di/NewQSTileFactory.kt
index 382cfe2..6c9a8a4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/di/NewQSTileFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/di/NewQSTileFactory.kt
@@ -56,7 +56,7 @@
     override fun createTile(tileSpec: String): QSTile? {
         val viewModel: QSTileViewModel =
             when (val spec = TileSpec.create(tileSpec)) {
-                is TileSpec.CustomTileSpec -> null
+                is TileSpec.CustomTileSpec -> createCustomTileViewModel(spec)
                 is TileSpec.PlatformTileSpec -> tileMap[tileSpec]?.get()
                 is TileSpec.Invalid -> null
             }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapper.kt
index e075e76..2b8c335 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapper.kt
@@ -24,6 +24,7 @@
 import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
 import com.android.systemui.qs.tiles.viewmodel.QSTileState
 import com.android.systemui.res.R
+import com.android.systemui.util.time.SystemClock
 import java.time.Instant
 import java.time.LocalDateTime
 import java.time.format.DateTimeFormatter
@@ -36,10 +37,12 @@
 constructor(
     @Main private val resources: Resources,
     private val theme: Theme,
+    private val clock: SystemClock,
 ) : QSTileDataToStateMapper<AlarmTileModel> {
     companion object {
         val formatter12Hour: DateTimeFormatter = DateTimeFormatter.ofPattern("E hh:mm a")
         val formatter24Hour: DateTimeFormatter = DateTimeFormatter.ofPattern("E HH:mm")
+        val formatterDateOnly: DateTimeFormatter = DateTimeFormatter.ofPattern("E MMM d")
     }
     override fun map(config: QSTileConfig, data: AlarmTileModel): QSTileState =
         QSTileState.build(resources, theme, config.uiConfig) {
@@ -47,14 +50,32 @@
                 is AlarmTileModel.NextAlarmSet -> {
                     activationState = QSTileState.ActivationState.ACTIVE
 
-                    val localDateTime =
+                    val alarmDateTime =
                         LocalDateTime.ofInstant(
                             Instant.ofEpochMilli(data.alarmClockInfo.triggerTime),
                             TimeZone.getDefault().toZoneId()
                         )
-                    secondaryLabel =
-                        if (data.is24HourFormat) formatter24Hour.format(localDateTime)
-                        else formatter12Hour.format(localDateTime)
+
+                    val nowDateTime =
+                        LocalDateTime.ofInstant(
+                            Instant.ofEpochMilli(clock.currentTimeMillis()),
+                            TimeZone.getDefault().toZoneId()
+                        )
+
+                    // Edge case: If it's 8:00:30 right now and alarm is requested for next week at
+                    // 8:00:29, we still want to show the date. Same at nanosecond level.
+                    val nextWeekThisTime = nowDateTime.plusWeeks(1).withSecond(0).withNano(0)
+
+                    // is the alarm over a week away?
+                    val shouldShowDateAndHideTime = alarmDateTime >= nextWeekThisTime
+
+                    if (shouldShowDateAndHideTime) {
+                        secondaryLabel = formatterDateOnly.format(alarmDateTime)
+                    } else {
+                        secondaryLabel =
+                            if (data.is24HourFormat) formatter24Hour.format(alarmDateTime)
+                            else formatter12Hour.format(alarmDateTime)
+                    }
                 }
                 is AlarmTileModel.NoAlarmSet -> {
                     activationState = QSTileState.ActivationState.INACTIVE
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/alarm/domain/interactor/AlarmTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/alarm/domain/interactor/AlarmTileUserActionInteractor.kt
index afca57c..0ad520b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/alarm/domain/interactor/AlarmTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/alarm/domain/interactor/AlarmTileUserActionInteractor.kt
@@ -18,9 +18,7 @@
 
 import android.content.Intent
 import android.provider.AlarmClock
-import com.android.internal.jank.InteractionJankMonitor
-import com.android.systemui.animation.ActivityLaunchAnimator
-import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler
 import com.android.systemui.qs.tiles.base.interactor.QSTileInput
 import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
 import com.android.systemui.qs.tiles.impl.alarm.domain.model.AlarmTileModel
@@ -31,34 +29,20 @@
 class AlarmTileUserActionInteractor
 @Inject
 constructor(
-    private val activityStarter: ActivityStarter,
+    private val inputHandler: QSTileIntentUserInputHandler,
 ) : QSTileUserActionInteractor<AlarmTileModel> {
     override suspend fun handleInput(input: QSTileInput<AlarmTileModel>): Unit =
         with(input) {
             when (action) {
                 is QSTileUserAction.Click -> {
-                    val animationController =
-                        action.view?.let {
-                            ActivityLaunchAnimator.Controller.fromView(
-                                it,
-                                InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE
-                            )
-                        }
                     if (
                         data is AlarmTileModel.NextAlarmSet &&
                             data.alarmClockInfo.showIntent != null
                     ) {
                         val pendingIndent = data.alarmClockInfo.showIntent
-                        activityStarter.postStartActivityDismissingKeyguard(
-                            pendingIndent,
-                            animationController
-                        )
+                        inputHandler.handle(action.view, pendingIndent, true)
                     } else {
-                        activityStarter.postStartActivityDismissingKeyguard(
-                            Intent(AlarmClock.ACTION_SHOW_ALARMS),
-                            0,
-                            animationController
-                        )
+                        inputHandler.handle(action.view, Intent(AlarmClock.ACTION_SHOW_ALARMS))
                     }
                 }
                 is QSTileUserAction.LongClick -> {}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/CustomTileInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/CustomTileInteractor.kt
deleted file mode 100644
index 14bf25d..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/CustomTileInteractor.kt
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.qs.tiles.impl.custom
-
-import android.os.UserHandle
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
-import com.android.systemui.qs.tiles.impl.custom.domain.entity.CustomTileDataModel
-import com.android.systemui.qs.tiles.impl.di.QSTileScope
-import javax.inject.Inject
-import kotlinx.coroutines.flow.Flow
-
-@QSTileScope
-class CustomTileInteractor @Inject constructor() : QSTileDataInteractor<CustomTileDataModel> {
-
-    override fun tileData(
-        user: UserHandle,
-        triggers: Flow<DataUpdateTrigger>
-    ): Flow<CustomTileDataModel> {
-        TODO("Not yet implemented")
-    }
-
-    override fun availability(user: UserHandle): Flow<Boolean> {
-        TODO("Not yet implemented")
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/CustomTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/CustomTileMapper.kt
deleted file mode 100644
index e23a5c2..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/CustomTileMapper.kt
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.qs.tiles.impl.custom
-
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
-import com.android.systemui.qs.tiles.impl.custom.domain.entity.CustomTileDataModel
-import com.android.systemui.qs.tiles.impl.di.QSTileScope
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
-import javax.inject.Inject
-
-@QSTileScope
-class CustomTileMapper @Inject constructor() : QSTileDataToStateMapper<CustomTileDataModel> {
-
-    override fun map(config: QSTileConfig, data: CustomTileDataModel): QSTileState {
-        TODO("Not yet implemented")
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/CustomTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/CustomTileUserActionInteractor.kt
deleted file mode 100644
index f34704b..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/CustomTileUserActionInteractor.kt
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.qs.tiles.impl.custom
-
-import com.android.systemui.qs.tiles.base.interactor.QSTileInput
-import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
-import com.android.systemui.qs.tiles.impl.custom.domain.entity.CustomTileDataModel
-import com.android.systemui.qs.tiles.impl.di.QSTileScope
-import javax.inject.Inject
-
-@QSTileScope
-class CustomTileUserActionInteractor @Inject constructor() :
-    QSTileUserActionInteractor<CustomTileDataModel> {
-
-    override suspend fun handleInput(input: QSTileInput<CustomTileDataModel>) {
-        TODO("Not yet implemented")
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/CustomTileComponent.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/CustomTileComponent.kt
index 88bc8fa..7b099c2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/CustomTileComponent.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/CustomTileComponent.kt
@@ -16,7 +16,10 @@
 
 package com.android.systemui.qs.tiles.impl.custom.di
 
+import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTilePackageUpdatesRepository
 import com.android.systemui.qs.tiles.impl.custom.domain.entity.CustomTileDataModel
+import com.android.systemui.qs.tiles.impl.custom.domain.interactor.CustomTileInteractor
+import com.android.systemui.qs.tiles.impl.custom.domain.interactor.CustomTileServiceInteractor
 import com.android.systemui.qs.tiles.impl.di.QSTileComponent
 import com.android.systemui.qs.tiles.impl.di.QSTileScope
 import dagger.Subcomponent
@@ -25,6 +28,12 @@
 @Subcomponent(modules = [QSTileConfigModule::class, CustomTileModule::class])
 interface CustomTileComponent : QSTileComponent<CustomTileDataModel> {
 
+    fun customTileInterfaceInteractor(): CustomTileServiceInteractor
+
+    fun customTileInteractor(): CustomTileInteractor
+
+    fun customTilePackageUpdatesRepository(): CustomTilePackageUpdatesRepository
+
     @Subcomponent.Builder
     interface Builder {
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/CustomTileModule.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/CustomTileModule.kt
index ba8b23a..196fa12 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/CustomTileModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/CustomTileModule.kt
@@ -20,14 +20,16 @@
 import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
 import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
 import com.android.systemui.qs.tiles.base.viewmodel.QSTileCoroutineScopeFactory
-import com.android.systemui.qs.tiles.impl.custom.CustomTileInteractor
-import com.android.systemui.qs.tiles.impl.custom.CustomTileMapper
-import com.android.systemui.qs.tiles.impl.custom.CustomTileUserActionInteractor
 import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTileDefaultsRepository
 import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTileDefaultsRepositoryImpl
+import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTilePackageUpdatesRepository
+import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTilePackageUpdatesRepositoryImpl
 import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTileRepository
 import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTileRepositoryImpl
+import com.android.systemui.qs.tiles.impl.custom.domain.CustomTileMapper
 import com.android.systemui.qs.tiles.impl.custom.domain.entity.CustomTileDataModel
+import com.android.systemui.qs.tiles.impl.custom.domain.interactor.CustomTileDataInteractor
+import com.android.systemui.qs.tiles.impl.custom.domain.interactor.CustomTileUserActionInteractor
 import com.android.systemui.qs.tiles.impl.di.QSTileScope
 import dagger.Binds
 import dagger.Module
@@ -40,7 +42,7 @@
 
     @Binds
     fun bindDataInteractor(
-        dataInteractor: CustomTileInteractor
+        dataInteractor: CustomTileDataInteractor
     ): QSTileDataInteractor<CustomTileDataModel>
 
     @Binds
@@ -58,6 +60,11 @@
 
     @Binds fun bindCustomTileRepository(impl: CustomTileRepositoryImpl): CustomTileRepository
 
+    @Binds
+    abstract fun bindCustomTilePackageUpdatesRepository(
+        impl: CustomTilePackageUpdatesRepositoryImpl
+    ): CustomTilePackageUpdatesRepository
+
     companion object {
 
         @Provides
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileBoundComponent.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileBoundComponent.kt
deleted file mode 100644
index d382d20..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileBoundComponent.kt
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.qs.tiles.impl.custom.di.bound
-
-import android.os.UserHandle
-import dagger.BindsInstance
-import dagger.Subcomponent
-import kotlinx.coroutines.CoroutineScope
-
-/** @see CustomTileBoundScope */
-@CustomTileBoundScope
-@Subcomponent(modules = [CustomTileBoundModule::class])
-interface CustomTileBoundComponent {
-
-    @Subcomponent.Builder
-    interface Builder {
-        @BindsInstance fun user(@CustomTileUser user: UserHandle): Builder
-        @BindsInstance fun coroutineScope(@CustomTileBoundScope scope: CoroutineScope): Builder
-
-        fun build(): CustomTileBoundComponent
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileBoundModule.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileBoundModule.kt
deleted file mode 100644
index 889424a..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileBoundModule.kt
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.qs.tiles.impl.custom.di.bound
-
-import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTilePackageUpdatesRepository
-import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTilePackageUpdatesRepositoryImpl
-import dagger.Binds
-import dagger.Module
-
-@Module
-interface CustomTileBoundModule {
-
-    @Binds
-    fun bindCustomTilePackageUpdatesRepository(
-        impl: CustomTilePackageUpdatesRepositoryImpl
-    ): CustomTilePackageUpdatesRepository
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileBoundScope.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileBoundScope.kt
deleted file mode 100644
index 4a4ba2b..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileBoundScope.kt
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.qs.tiles.impl.custom.di.bound
-
-import javax.inject.Scope
-
-/**
- * Scope annotation for bound custom tile scope. This scope lives when a particular
- * [com.android.systemui.qs.external.CustomTile] is listening and bound to the
- * [android.service.quicksettings.TileService].
- */
-@MustBeDocumented
-@Retention(AnnotationRetention.RUNTIME)
-@Scope
-annotation class CustomTileBoundScope
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/CustomTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/CustomTileMapper.kt
new file mode 100644
index 0000000..875079c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/CustomTileMapper.kt
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.custom.domain
+
+import android.annotation.SuppressLint
+import android.app.IUriGrantsManager
+import android.content.Context
+import android.graphics.drawable.Drawable
+import android.os.UserHandle
+import android.service.quicksettings.Tile
+import android.widget.Button
+import android.widget.Switch
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
+import com.android.systemui.qs.tiles.impl.custom.domain.entity.CustomTileDataModel
+import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
+import com.android.systemui.qs.tiles.viewmodel.QSTileState
+import javax.inject.Inject
+
+@SysUISingleton
+class CustomTileMapper
+@Inject
+constructor(
+    private val context: Context,
+    private val uriGrantsManager: IUriGrantsManager,
+) : QSTileDataToStateMapper<CustomTileDataModel> {
+
+    override fun map(config: QSTileConfig, data: CustomTileDataModel): QSTileState {
+        val userContext = context.createContextAsUser(UserHandle(data.user.identifier), 0)
+
+        val iconResult =
+            getIconProvider(
+                userContext = userContext,
+                icon = data.tile.icon,
+                callingAppUid = data.callingAppUid,
+                packageName = data.componentName.packageName,
+                defaultIcon = data.defaultTileIcon,
+            )
+
+        return QSTileState.build(iconResult.iconProvider, data.tile.label) {
+            var tileState: Int = data.tile.state
+            if (data.hasPendingBind) {
+                tileState = Tile.STATE_UNAVAILABLE
+            }
+
+            icon = iconResult.iconProvider
+            activationState =
+                if (iconResult.failedToLoad) {
+                    QSTileState.ActivationState.INACTIVE
+                } else {
+                    QSTileState.ActivationState.valueOf(tileState)
+                }
+
+            if (!data.tile.subtitle.isNullOrEmpty()) {
+                secondaryLabel = data.tile.subtitle
+            }
+
+            contentDescription = data.tile.contentDescription
+            stateDescription = data.tile.stateDescription
+
+            if (!data.isToggleable) {
+                sideViewIcon = QSTileState.SideViewIcon.Chevron
+            }
+
+            supportedActions =
+                if (tileState == Tile.STATE_UNAVAILABLE) {
+                    setOf(QSTileState.UserAction.LONG_CLICK)
+                } else {
+                    setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK)
+                }
+            expandedAccessibilityClass =
+                if (data.isToggleable) {
+                    Switch::class
+                } else {
+                    Button::class
+                }
+        }
+    }
+
+    @SuppressLint("MissingPermission") // android.permission.INTERACT_ACROSS_USERS_FULL
+    private fun getIconProvider(
+        userContext: Context,
+        icon: android.graphics.drawable.Icon?,
+        callingAppUid: Int,
+        packageName: String,
+        defaultIcon: android.graphics.drawable.Icon?,
+    ): IconResult {
+        var failedToLoad = false
+        val drawable: Drawable? =
+            try {
+                icon?.loadDrawableCheckingUriGrant(
+                    userContext,
+                    uriGrantsManager,
+                    callingAppUid,
+                    packageName,
+                )
+            } catch (e: Exception) {
+                failedToLoad = true
+                null
+            } ?: defaultIcon?.loadDrawable(userContext)
+        return IconResult(
+            {
+                drawable?.constantState?.newDrawable()?.let {
+                    Icon.Loaded(it, contentDescription = null)
+                }
+            },
+            failedToLoad,
+        )
+    }
+
+    class IconResult(
+        val iconProvider: () -> Icon?,
+        val failedToLoad: Boolean,
+    )
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/entity/CustomTileDataModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/entity/CustomTileDataModel.kt
index f095c01..5b6ff1e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/entity/CustomTileDataModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/entity/CustomTileDataModel.kt
@@ -20,16 +20,14 @@
 import android.graphics.drawable.Icon
 import android.os.UserHandle
 import android.service.quicksettings.Tile
-import com.android.systemui.qs.tiles.impl.custom.di.bound.CustomTileBoundComponent
 
 data class CustomTileDataModel(
     val user: UserHandle,
     val componentName: ComponentName,
     val tile: Tile,
+    val isToggleable: Boolean,
     val callingAppUid: Int,
     val hasPendingBind: Boolean,
-    val shouldShowChevron: Boolean,
-    val defaultTileLabel: CharSequence?,
-    val defaultTileIcon: Icon?,
-    val component: CustomTileBoundComponent,
+    val defaultTileLabel: CharSequence,
+    val defaultTileIcon: Icon,
 )
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileDataInteractor.kt
new file mode 100644
index 0000000..cff95d8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileDataInteractor.kt
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.custom.domain.interactor
+
+import android.os.UserHandle
+import android.service.quicksettings.Tile
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
+import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.impl.custom.data.entity.CustomTileDefaults
+import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTileDefaultsRepository
+import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTilePackageUpdatesRepository
+import com.android.systemui.qs.tiles.impl.custom.domain.entity.CustomTileDataModel
+import com.android.systemui.qs.tiles.impl.di.QSTileScope
+import com.android.systemui.user.data.repository.UserRepository
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.mapNotNull
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.flow.onStart
+import kotlinx.coroutines.flow.shareIn
+import kotlinx.coroutines.launch
+
+@QSTileScope
+@OptIn(ExperimentalCoroutinesApi::class)
+class CustomTileDataInteractor
+@Inject
+constructor(
+    private val tileSpec: TileSpec.CustomTileSpec,
+    private val defaultsRepository: CustomTileDefaultsRepository,
+    private val serviceInteractor: CustomTileServiceInteractor,
+    private val customTileInteractor: CustomTileInteractor,
+    private val packageUpdatesRepository: CustomTilePackageUpdatesRepository,
+    userRepository: UserRepository,
+    @QSTileScope private val tileScope: CoroutineScope,
+) : QSTileDataInteractor<CustomTileDataModel> {
+
+    private val mutableUserFlow = MutableStateFlow(userRepository.getSelectedUserInfo().userHandle)
+    private val bindingFlow =
+        mutableUserFlow
+            .flatMapLatest { user ->
+                ConflatedCallbackFlow.conflatedCallbackFlow {
+                    serviceInteractor.setUser(user)
+
+                    // Wait for the CustomTileInteractor to become initialized first, because
+                    // binding
+                    // the service might access it
+                    customTileInteractor.initForUser(user)
+                    // Bind the TileService for not active tile
+                    serviceInteractor.bindOnStart()
+
+                    packageUpdatesRepository
+                        .getPackageChangesForUser(user)
+                        .onEach {
+                            defaultsRepository.requestNewDefaults(
+                                user,
+                                tileSpec.componentName,
+                                true
+                            )
+                        }
+                        .launchIn(this)
+
+                    send(Unit)
+                    awaitClose { serviceInteractor.unbind() }
+                }
+            }
+            .shareIn(tileScope, SharingStarted.WhileSubscribed())
+
+    init {
+        // Initialize binding once to flush all the pending messages inside
+        // CustomTileServiceInteractor and then unbind if the tile data isn't observed. This ensures
+        // that all the interactors are loaded and warmed up before binding.
+        tileScope.launch { bindingFlow.first() }
+    }
+
+    override fun tileData(
+        user: UserHandle,
+        triggers: Flow<DataUpdateTrigger>
+    ): Flow<CustomTileDataModel> {
+        tileScope.launch { mutableUserFlow.emit(user) }
+        return bindingFlow.combine(triggers) { _, _ -> }.flatMapLatest { dataFlow(user) }
+    }
+
+    private fun dataFlow(user: UserHandle): Flow<CustomTileDataModel> =
+        combine(
+            serviceInteractor.refreshEvents.onStart { emit(Unit) },
+            serviceInteractor.callingAppIds,
+            customTileInteractor.getTiles(user),
+            defaultsRepository.defaults(user).mapNotNull { it as? CustomTileDefaults.Result },
+        ) { _: Unit, callingAppId: Int, tile: Tile, defaults: CustomTileDefaults.Result ->
+            CustomTileDataModel(
+                user = user,
+                componentName = tileSpec.componentName,
+                tile = tile,
+                callingAppUid = callingAppId,
+                hasPendingBind = serviceInteractor.hasPendingBind(),
+                defaultTileLabel = defaults.label,
+                defaultTileIcon = defaults.icon,
+                isToggleable = customTileInteractor.isTileToggleable(),
+            )
+        }
+
+    override fun availability(user: UserHandle): Flow<Boolean> =
+        with(defaultsRepository) {
+            requestNewDefaults(user, tileSpec.componentName)
+            return defaults(user).map { it is CustomTileDefaults.Result }
+        }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileInteractor.kt
index 10b012d..fd96fc5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileInteractor.kt
@@ -19,12 +19,14 @@
 import android.os.UserHandle
 import android.service.quicksettings.Tile
 import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.qs.pipeline.shared.TileSpec
 import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTileDefaultsRepository
 import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTileRepository
 import com.android.systemui.qs.tiles.impl.di.QSTileScope
 import javax.inject.Inject
 import kotlin.coroutines.CoroutineContext
 import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
 import kotlinx.coroutines.channels.BufferOverflow
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableSharedFlow
@@ -32,21 +34,29 @@
 import kotlinx.coroutines.flow.flowOn
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.sync.Mutex
+import kotlinx.coroutines.sync.withLock
 
 /** Manages updates of the [Tile] assigned for the current custom tile. */
 @QSTileScope
 class CustomTileInteractor
 @Inject
 constructor(
+    private val tileSpec: TileSpec.CustomTileSpec,
     private val defaultsRepository: CustomTileDefaultsRepository,
     private val customTileRepository: CustomTileRepository,
     @QSTileScope private val tileScope: CoroutineScope,
     @Background private val backgroundContext: CoroutineContext,
 ) {
 
+    private val userMutex = Mutex()
     private val tileUpdates =
         MutableSharedFlow<Tile>(replay = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
 
+    private var currentUser: UserHandle? = null
+    private var updatesJob: Job? = null
+
     /** [Tile] updates. [updateTile] to emit a new one. */
     fun getTiles(user: UserHandle): Flow<Tile> = customTileRepository.getTiles(user)
 
@@ -55,7 +65,7 @@
      *
      * @throws IllegalStateException when the repository stores a tile for another user. This means
      *   the tile hasn't been updated for the current user. Can happen when this is accessed before
-     *   [init] returns.
+     *   [initForUser] returns.
      */
     fun getTile(user: UserHandle): Tile =
         customTileRepository.getTile(user)
@@ -67,45 +77,60 @@
     suspend fun isTileToggleable(): Boolean = customTileRepository.isTileToggleable()
 
     /**
-     * Initializes the repository for the current user. Suspends until it's safe to call [tile]
+     * Initializes the repository for the current user. Suspends until it's safe to call [getTile]
      * which needs at least one of the following:
      * - defaults are loaded;
      * - receive tile update in [updateTile];
      * - restoration happened for a persisted tile.
      */
     suspend fun initForUser(user: UserHandle) {
-        launchUpdates(user)
-        customTileRepository.restoreForTheUserIfNeeded(user, customTileRepository.isTileActive())
-        // Suspend to make sure it gets the tile from one of the sources: restoration, defaults, or
-        // tile update.
-        customTileRepository.getTiles(user).firstOrNull()
+        userMutex.withLock {
+            if (currentUser == user) {
+                return
+            }
+            updatesJob?.cancel()
+            defaultsRepository.requestNewDefaults(user, tileSpec.componentName)
+            launchUpdates(user)
+            customTileRepository.restoreForTheUserIfNeeded(
+                user,
+                customTileRepository.isTileActive()
+            )
+            // Suspend to make sure it gets the tile from one of the sources: restoration, defaults,
+            // or
+            // tile update.
+            customTileRepository.getTiles(user).firstOrNull()
+            currentUser = user
+        }
     }
 
     private fun launchUpdates(user: UserHandle) {
-        tileUpdates
-            .onEach {
-                customTileRepository.updateWithTile(
-                    user,
-                    it,
-                    customTileRepository.isTileActive(),
-                )
+        updatesJob =
+            tileScope.launch {
+                tileUpdates
+                    .onEach {
+                        customTileRepository.updateWithTile(
+                            user,
+                            it,
+                            customTileRepository.isTileActive(),
+                        )
+                    }
+                    .flowOn(backgroundContext)
+                    .launchIn(this)
+                defaultsRepository
+                    .defaults(user)
+                    .onEach {
+                        customTileRepository.updateWithDefaults(
+                            user,
+                            it,
+                            customTileRepository.isTileActive(),
+                        )
+                    }
+                    .flowOn(backgroundContext)
+                    .launchIn(this)
             }
-            .flowOn(backgroundContext)
-            .launchIn(tileScope)
-        defaultsRepository
-            .defaults(user)
-            .onEach {
-                customTileRepository.updateWithDefaults(
-                    user,
-                    it,
-                    customTileRepository.isTileActive(),
-                )
-            }
-            .flowOn(backgroundContext)
-            .launchIn(tileScope)
     }
 
-    /** Updates current [Tile]. Emits a new event in [tiles]. */
+    /** Updates current [Tile]. Emits a new event in [getTiles]. */
     fun updateTile(newTile: Tile) {
         tileUpdates.tryEmit(newTile)
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileServiceInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileServiceInteractor.kt
new file mode 100644
index 0000000..acff40f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileServiceInteractor.kt
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.custom.domain.interactor
+
+import android.app.PendingIntent
+import android.content.ComponentName
+import android.os.IBinder
+import android.os.Process
+import android.os.RemoteException
+import android.os.UserHandle
+import android.service.quicksettings.IQSTileService
+import android.service.quicksettings.Tile
+import android.service.quicksettings.TileService
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.qs.external.CustomTileInterface
+import com.android.systemui.qs.external.TileServiceManager
+import com.android.systemui.qs.external.TileServices
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.tiles.base.logging.QSTileLogger
+import com.android.systemui.qs.tiles.impl.di.QSTileScope
+import com.android.systemui.user.data.repository.UserRepository
+import dagger.Lazy
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.channels.produce
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.launch
+
+/**
+ * Communicates with [TileService] via [TileServiceManager] and [IQSTileService]. This interactor is
+ * also responsible for the binding to the [TileService].
+ */
+@OptIn(ExperimentalCoroutinesApi::class)
+@QSTileScope
+class CustomTileServiceInteractor
+@Inject
+constructor(
+    private val tileSpec: TileSpec.CustomTileSpec,
+    private val activityStarter: ActivityStarter,
+    private val userActionInteractor: Lazy<CustomTileUserActionInteractor>,
+    private val customTileInteractor: CustomTileInteractor,
+    private val userRepository: UserRepository,
+    private val qsTileLogger: QSTileLogger,
+    private val tileServices: TileServices,
+    @QSTileScope private val tileScope: CoroutineScope,
+) {
+
+    private val tileReceivingInterface = ReceivingInterface()
+    private var tileServiceManager: TileServiceManager? = null
+    private val tileServiceInterface: IQSTileService
+        get() = getTileServiceManager().tileService
+
+    private var currentUser: UserHandle = userRepository.getSelectedUserInfo().userHandle
+    private var destructionJob: Job? = null
+
+    val callingAppIds: Flow<Int>
+        get() = tileReceivingInterface.mutableCallingAppIds
+    val refreshEvents: Flow<Unit>
+        get() = tileReceivingInterface.mutableRefreshEvents
+
+    /** Clears all pending binding for an active tile and binds not active one. */
+    fun bindOnStart() {
+        try {
+            with(getTileServiceManager()) {
+                if (isActiveTile) {
+                    clearPendingBind()
+                } else {
+                    setBindRequested(true)
+                    tileServiceInterface.onStartListening()
+                }
+            }
+        } catch (e: RemoteException) {
+            qsTileLogger.logError(tileSpec, "Binding to the service failed", e)
+        }
+    }
+
+    /** Binds active tile WITHOUT CLEARING pending binds. */
+    fun bindOnClick() {
+        try {
+            with(getTileServiceManager()) {
+                if (isActiveTile) {
+                    setBindRequested(true)
+                    tileServiceInterface.onStartListening()
+                }
+            }
+        } catch (e: RemoteException) {
+            qsTileLogger.logError(tileSpec, "Binding to the service on click failed", e)
+        }
+    }
+
+    /** Releases resources held by the binding and prepares the interactor to be collected */
+    fun unbind() {
+        try {
+            with(userActionInteractor.get()) {
+                clearLastClickedView()
+                tileServiceInterface.onStopListening()
+                revokeToken(false)
+                setShowingDialog(false)
+            }
+            getTileServiceManager().setBindRequested(false)
+        } catch (e: RemoteException) {
+            qsTileLogger.logError(tileSpec, "Unbinding failed", e)
+        }
+    }
+
+    /**
+     * Checks if [TileServiceManager] has a pending [android.service.quicksettings.TileService]
+     * bind.
+     */
+    fun hasPendingBind(): Boolean = getTileServiceManager().hasPendingBind()
+
+    /** Sets a [user] for the custom tile to use. User change triggers service rebinding. */
+    fun setUser(user: UserHandle) {
+        if (user == currentUser) {
+            return
+        }
+        currentUser = user
+        destructionJob?.cancel()
+
+        tileServiceManager = null
+    }
+
+    /** Sends click event to [TileService] using [IQSTileService.onClick]. */
+    fun onClick(token: IBinder) {
+        tileServiceInterface.onClick(token)
+    }
+
+    private fun getTileServiceManager(): TileServiceManager =
+        synchronized(tileServices) {
+            if (tileServiceManager == null) {
+                tileServices
+                    .getTileWrapper(tileReceivingInterface)
+                    .also { destructionJob = createDestructionJob() }
+                    .also { tileServiceManager = it }
+            } else {
+                tileServiceManager!!
+            }
+        }
+
+    /**
+     * This job used to free the resources when the [QSTileScope] coroutine scope gets cancelled by
+     * the View Model.
+     */
+    private fun createDestructionJob(): Job =
+        tileScope.launch {
+            produce<Unit> {
+                awaitClose {
+                    userActionInteractor.get().revokeToken(true)
+                    tileServices.freeService(tileReceivingInterface, getTileServiceManager())
+                    destructionJob = null
+                }
+            }
+        }
+
+    private inner class ReceivingInterface : CustomTileInterface {
+
+        override val user: Int
+            get() = currentUser.identifier
+        override val qsTile: Tile
+            get() = customTileInteractor.getTile(currentUser)
+        override val component: ComponentName = tileSpec.componentName
+
+        val mutableCallingAppIds = MutableStateFlow(Process.INVALID_UID)
+        val mutableRefreshEvents = MutableSharedFlow<Unit>()
+
+        override fun getTileSpec(): String = tileSpec.spec
+
+        override fun refreshState() {
+            tileScope.launch { mutableRefreshEvents.emit(Unit) }
+        }
+
+        override fun updateTileState(tile: Tile, uid: Int) {
+            customTileInteractor.updateTile(tile)
+            mutableCallingAppIds.tryEmit(uid)
+        }
+
+        override fun onDialogShown() {
+            userActionInteractor.get().setShowingDialog(true)
+        }
+
+        override fun onDialogHidden() =
+            with(userActionInteractor.get()) {
+                setShowingDialog(false)
+                revokeToken(true)
+            }
+
+        override fun startActivityAndCollapse(pendingIntent: PendingIntent) {
+            userActionInteractor.get().startActivityAndCollapse(pendingIntent)
+        }
+
+        override fun startUnlockAndRun() {
+            activityStarter.postQSRunnableDismissingKeyguard {
+                tileServiceInterface.onUnlockComplete()
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileUserActionInteractor.kt
new file mode 100644
index 0000000..c3e1fea
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileUserActionInteractor.kt
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.custom.domain.interactor
+
+import android.app.PendingIntent
+import android.content.ComponentName
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.net.Uri
+import android.os.Binder
+import android.os.IBinder
+import android.os.RemoteException
+import android.os.UserHandle
+import android.provider.Settings
+import android.service.quicksettings.TileService
+import android.view.IWindowManager
+import android.view.View
+import android.view.WindowManager
+import androidx.annotation.GuardedBy
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.interactor.QSTileInput
+import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.logging.QSTileLogger
+import com.android.systemui.qs.tiles.impl.custom.domain.entity.CustomTileDataModel
+import com.android.systemui.qs.tiles.impl.di.QSTileScope
+import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
+import com.android.systemui.settings.DisplayTracker
+import java.util.concurrent.atomic.AtomicReference
+import javax.inject.Inject
+import kotlin.coroutines.CoroutineContext
+import kotlinx.coroutines.withContext
+
+@QSTileScope
+class CustomTileUserActionInteractor
+@Inject
+constructor(
+    private val context: Context,
+    private val tileSpec: TileSpec,
+    private val qsTileLogger: QSTileLogger,
+    private val windowManager: IWindowManager,
+    private val displayTracker: DisplayTracker,
+    private val qsTileIntentUserInputHandler: QSTileIntentUserInputHandler,
+    @Background private val backgroundContext: CoroutineContext,
+    private val serviceInteractor: CustomTileServiceInteractor,
+) : QSTileUserActionInteractor<CustomTileDataModel> {
+
+    private val token: IBinder = Binder()
+
+    @GuardedBy("token") private var isTokenGranted: Boolean = false
+    @GuardedBy("token") private var isShowingDialog: Boolean = false
+    private val lastClickedView: AtomicReference<View> = AtomicReference<View>()
+
+    override suspend fun handleInput(input: QSTileInput<CustomTileDataModel>) =
+        with(input) {
+            when (action) {
+                is QSTileUserAction.Click -> click(action.view, data.tile.activityLaunchForClick)
+                is QSTileUserAction.LongClick ->
+                    longClick(user, action.view, data.componentName, data.tile.state)
+            }
+            qsTileLogger.logCustomTileUserActionDelivered(tileSpec)
+        }
+
+    private fun click(
+        view: View?,
+        activityLaunchForClick: PendingIntent?,
+    ) {
+        grantToken()
+        try {
+            // Bind active tile to deliver user action
+            serviceInteractor.bindOnClick()
+            if (activityLaunchForClick == null) {
+                lastClickedView.set(view)
+                serviceInteractor.onClick(token)
+            } else {
+                qsTileIntentUserInputHandler.handle(view, activityLaunchForClick)
+            }
+        } catch (e: RemoteException) {
+            qsTileLogger.logError(tileSpec, "Failed to deliver click", e)
+        }
+    }
+
+    fun revokeToken(ignoreShownDialog: Boolean) {
+        synchronized(token) {
+            if (isTokenGranted && (ignoreShownDialog || !isShowingDialog)) {
+                try {
+                    windowManager.removeWindowToken(token, displayTracker.defaultDisplayId)
+                } catch (e: RemoteException) {
+                    qsTileLogger.logError(tileSpec, "Failed to remove a window token", e)
+                }
+                isTokenGranted = false
+            }
+        }
+    }
+
+    fun setShowingDialog(isShowingDialog: Boolean) {
+        synchronized(token) { this.isShowingDialog = isShowingDialog }
+    }
+
+    fun startActivityAndCollapse(pendingIntent: PendingIntent) {
+        if (!pendingIntent.isActivity) {
+            return
+        }
+        if (!isTokenGranted) {
+            return
+        }
+        qsTileIntentUserInputHandler.handle(lastClickedView.getAndSet(null), pendingIntent)
+    }
+
+    fun clearLastClickedView() = lastClickedView.set(null)
+
+    private fun grantToken() {
+        synchronized(token) {
+            if (!isTokenGranted) {
+                try {
+                    windowManager.addWindowToken(
+                        token,
+                        WindowManager.LayoutParams.TYPE_QS_DIALOG,
+                        displayTracker.defaultDisplayId,
+                        null /* options */
+                    )
+                } catch (e: RemoteException) {
+                    qsTileLogger.logError(tileSpec, "Failed to grant a window token", e)
+                }
+                isTokenGranted = true
+            }
+        }
+    }
+
+    private suspend fun longClick(
+        user: UserHandle,
+        view: View?,
+        componentName: ComponentName,
+        state: Int
+    ) {
+        val resolvedIntent: Intent? =
+            resolveIntent(
+                    Intent(TileService.ACTION_QS_TILE_PREFERENCES).apply {
+                        setPackage(componentName.packageName)
+                    },
+                    user,
+                )
+                ?.apply {
+                    putExtra(Intent.EXTRA_COMPONENT_NAME, componentName)
+                    putExtra(TileService.EXTRA_STATE, state)
+                }
+        if (resolvedIntent == null) {
+            qsTileIntentUserInputHandler.handle(
+                view,
+                Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
+                    .setData(
+                        Uri.fromParts(IntentFilter.SCHEME_PACKAGE, componentName.packageName, null)
+                    )
+            )
+        } else {
+            qsTileIntentUserInputHandler.handle(view, resolvedIntent)
+        }
+    }
+
+    /**
+     * Returns an intent resolved by [android.content.pm.PackageManager.resolveActivityAsUser] or
+     * null.
+     */
+    private suspend fun resolveIntent(intent: Intent, user: UserHandle): Intent? =
+        withContext(backgroundContext) {
+            val activityInfo =
+                context.packageManager
+                    .resolveActivityAsUser(intent, 0, user.identifier)
+                    ?.activityInfo
+            activityInfo ?: return@withContext null
+            with(activityInfo) {
+                Intent(TileService.ACTION_QS_TILE_PREFERENCES).apply {
+                    setClassName(packageName, name)
+                }
+            }
+        }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt
index be1b740..b927e41 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt
@@ -32,7 +32,7 @@
  * // TODO(b/http://b/299909989): Clean up legacy mappings after the transition
  */
 data class QSTileState(
-    val icon: () -> Icon,
+    val icon: () -> Icon?,
     val label: CharSequence,
     val activationState: ActivationState,
     val secondaryLabel: CharSequence?,
@@ -60,7 +60,7 @@
             )
         }
 
-        fun build(icon: () -> Icon, label: CharSequence, build: Builder.() -> Unit): QSTileState =
+        fun build(icon: () -> Icon?, label: CharSequence, build: Builder.() -> Unit): QSTileState =
             Builder(icon, label).apply(build).build()
     }
 
@@ -108,7 +108,7 @@
     }
 
     class Builder(
-        var icon: () -> Icon,
+        var icon: () -> Icon?,
         var label: CharSequence,
     ) {
         var activationState: ActivationState = ActivationState.INACTIVE
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModel.kt
index ef3df48..226e2fa0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModel.kt
@@ -45,7 +45,10 @@
      */
     fun onUserChanged(user: UserHandle)
 
-    /** Triggers the emission of the new [QSTileState] in a [state]. */
+    /**
+     * Triggers the emission of the new [QSTileState] in a [state]. The new value can still be
+     * skipped if there is no change.
+     */
     fun forceUpdate()
 
     /** Notifies underlying logic about user input. */
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt
index 977df81..4780a2e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt
@@ -37,6 +37,7 @@
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Job
 import kotlinx.coroutines.flow.collectIndexed
+import kotlinx.coroutines.flow.first
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.onEach
@@ -60,27 +61,34 @@
     private val listeningClients: MutableCollection<Any> = mutableSetOf()
 
     // Cancels the jobs when the adapter is no longer alive
-    private var availabilityJob: Job? = null
+    private var tileAdapterJob: Job? = null
     // Cancels the jobs when clients stop listening
     private var stateJob: Job? = null
 
     init {
-        availabilityJob =
+        tileAdapterJob =
             applicationScope.launch {
-                qsTileViewModel.isAvailable.collectIndexed { index, isAvailable ->
-                    if (!isAvailable) {
-                        qsHost.removeTile(tileSpec)
-                    }
-                    // qsTileViewModel.isAvailable flow often starts with isAvailable == true.
-                    // That's
-                    // why we only allow isAvailable == true once and throw an exception afterwards.
-                    if (index > 0 && isAvailable) {
-                        // See com.android.systemui.qs.pipeline.domain.model.AutoAddable for
-                        // additional
-                        // guidance on how to auto add your tile
-                        throw UnsupportedOperationException("Turning on tile is not supported now")
+                launch {
+                    qsTileViewModel.isAvailable.collectIndexed { index, isAvailable ->
+                        if (!isAvailable) {
+                            qsHost.removeTile(tileSpec)
+                        }
+                        // qsTileViewModel.isAvailable flow often starts with isAvailable == true.
+                        // That's
+                        // why we only allow isAvailable == true once and throw an exception
+                        // afterwards.
+                        if (index > 0 && isAvailable) {
+                            // See com.android.systemui.qs.pipeline.domain.model.AutoAddable for
+                            // additional
+                            // guidance on how to auto add your tile
+                            throw UnsupportedOperationException(
+                                "Turning on tile is not supported now"
+                            )
+                        }
                     }
                 }
+                // Warm up tile with some initial state
+                launch { qsTileViewModel.state.first() }
             }
 
         // QSTileHost doesn't call this when userId is initialized
@@ -185,7 +193,7 @@
 
     override fun destroy() {
         stateJob?.cancel()
-        availabilityJob?.cancel()
+        tileAdapterJob?.cancel()
         qsTileViewModel.destroy()
     }
 
@@ -222,8 +230,9 @@
             QSTile.BooleanState().apply {
                 spec = config.tileSpec.spec
                 label = viewModelState.label
-                // This value is synthetic and doesn't have any meaning
-                value = false
+                // This value is synthetic and doesn't have any meaning. It's only needed to satisfy
+                // CTS tests.
+                value = viewModelState.activationState == QSTileState.ActivationState.ACTIVE
 
                 secondaryLabel = viewModelState.secondaryLabel
                 handlesLongClick =
@@ -233,6 +242,7 @@
                     when (val stateIcon = viewModelState.icon()) {
                         is Icon.Loaded -> DrawableIcon(stateIcon.drawable)
                         is Icon.Resource -> ResourceIcon.get(stateIcon.res)
+                        null -> null
                     }
                 }
                 state = viewModelState.activationState.legacyState
diff --git a/packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt b/packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt
index acd7510..41cd221 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt
@@ -23,7 +23,6 @@
 import android.content.Intent
 import android.provider.Settings
 import android.view.LayoutInflater
-import androidx.annotation.VisibleForTesting
 import com.android.internal.jank.InteractionJankMonitor
 import com.android.internal.logging.UiEventLogger
 import com.android.systemui.res.R
@@ -44,31 +43,15 @@
  * Controller for [UserDialog].
  */
 @SysUISingleton
-class UserSwitchDialogController @VisibleForTesting constructor(
-    private val userDetailViewAdapterProvider: Provider<UserDetailView.Adapter>,
-    private val activityStarter: ActivityStarter,
-    private val falsingManager: FalsingManager,
-    private val dialogLaunchAnimator: DialogLaunchAnimator,
-    private val uiEventLogger: UiEventLogger,
-    private val dialogFactory: (Context) -> SystemUIDialog
+class UserSwitchDialogController @Inject constructor(
+        private val userDetailViewAdapterProvider: Provider<UserDetailView.Adapter>,
+        private val activityStarter: ActivityStarter,
+        private val falsingManager: FalsingManager,
+        private val dialogLaunchAnimator: DialogLaunchAnimator,
+        private val uiEventLogger: UiEventLogger,
+        private val dialogFactory: SystemUIDialog.Factory
 ) {
 
-    @Inject
-    constructor(
-        userDetailViewAdapterProvider: Provider<UserDetailView.Adapter>,
-        activityStarter: ActivityStarter,
-        falsingManager: FalsingManager,
-        dialogLaunchAnimator: DialogLaunchAnimator,
-        uiEventLogger: UiEventLogger
-    ) : this(
-        userDetailViewAdapterProvider,
-        activityStarter,
-        falsingManager,
-        dialogLaunchAnimator,
-        uiEventLogger,
-        { SystemUIDialog(it) }
-    )
-
     companion object {
         private const val INTERACTION_JANK_TAG = "switch_user"
         private val USER_SETTINGS_INTENT = Intent(Settings.ACTION_USER_SETTINGS)
@@ -81,7 +64,7 @@
      * [userDetailViewAdapterProvider] and show it as launched from [expandable].
      */
     fun showDialog(context: Context, expandable: Expandable) {
-        with(dialogFactory(context)) {
+        with(dialogFactory.create()) {
             setShowForAllUsers(true)
             setCanceledOnTouchOutside(true)
 
diff --git a/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayDialogController.java b/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayDialogController.java
index 4b21e44..9076182 100644
--- a/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayDialogController.java
+++ b/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayDialogController.java
@@ -21,19 +21,22 @@
 import android.annotation.TestApi;
 import android.content.Context;
 import android.content.res.Configuration;
+import android.content.res.Resources;
 import android.hardware.devicestate.DeviceStateManager;
 import android.hardware.devicestate.DeviceStateManagerGlobal;
+import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup.LayoutParams;
 import android.widget.LinearLayout;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.systemui.CoreStartable;
-import com.android.systemui.res.R;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.res.R;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.phone.SystemUIDialog;
+import com.android.systemui.statusbar.policy.ConfigurationController;
 
 import com.airbnb.lottie.LottieAnimationView;
 import com.airbnb.lottie.LottieDrawable;
@@ -57,7 +60,10 @@
  */
 @SuppressLint("VisibleForTests") // TODO(b/260264542) Migrate away from DeviceStateManagerGlobal
 @SysUISingleton
-public class RearDisplayDialogController implements CoreStartable, CommandQueue.Callbacks {
+public class RearDisplayDialogController implements
+        CoreStartable,
+        ConfigurationController.ConfigurationListener,
+        CommandQueue.Callbacks {
 
     private int[] mFoldedStates;
     private boolean mStartedFolded;
@@ -68,20 +74,27 @@
     private DeviceStateManager.DeviceStateCallback mDeviceStateManagerCallback =
             new DeviceStateManagerCallback();
 
-    private final Context mContext;
     private final CommandQueue mCommandQueue;
     private final Executor mExecutor;
+    private final Resources mResources;
+    private final LayoutInflater mLayoutInflater;
+    private final SystemUIDialog.Factory mSystemUIDialogFactory;
 
-    @VisibleForTesting
-    SystemUIDialog mRearDisplayEducationDialog;
+    private SystemUIDialog mRearDisplayEducationDialog;
     @Nullable LinearLayout mDialogViewContainer;
 
     @Inject
-    public RearDisplayDialogController(Context context, CommandQueue commandQueue,
-            @Main Executor executor) {
-        mContext = context;
+    public RearDisplayDialogController(
+            CommandQueue commandQueue,
+            @Main Executor executor,
+            @Main Resources resources,
+            LayoutInflater layoutInflater,
+            SystemUIDialog.Factory systemUIDialogFactory) {
         mCommandQueue = commandQueue;
         mExecutor = executor;
+        mResources = resources;
+        mLayoutInflater = layoutInflater;
+        mSystemUIDialogFactory = systemUIDialogFactory;
     }
 
     @Override
@@ -96,12 +109,11 @@
     }
 
     @Override
-    public void onConfigurationChanged(Configuration newConfig) {
+    public void onConfigChanged(Configuration newConfig) {
         if (mRearDisplayEducationDialog != null && mRearDisplayEducationDialog.isShowing()
                 && mDialogViewContainer != null) {
             // Refresh the dialog view when configuration is changed.
-            Context dialogContext = mRearDisplayEducationDialog.getContext();
-            View dialogView = createDialogView(dialogContext);
+            View dialogView = createDialogView(mRearDisplayEducationDialog.getContext());
             mDialogViewContainer.removeAllViews();
             mDialogViewContainer.addView(dialogView);
         }
@@ -110,9 +122,7 @@
     private void createAndShowDialog() {
         mServiceNotified = false;
         Context dialogContext = mRearDisplayEducationDialog.getContext();
-
         View dialogView = createDialogView(dialogContext);
-
         mDialogViewContainer = new LinearLayout(dialogContext);
         mDialogViewContainer.setLayoutParams(
                 new LinearLayout.LayoutParams(
@@ -129,11 +139,11 @@
 
     private View createDialogView(Context context) {
         View dialogView;
+        LayoutInflater inflater = mLayoutInflater.cloneInContext(context);
         if (mStartedFolded) {
-            dialogView = View.inflate(context,
-                    R.layout.activity_rear_display_education, null);
+            dialogView = inflater.inflate(R.layout.activity_rear_display_education, null);
         } else {
-            dialogView = View.inflate(context,
+            dialogView = inflater.inflate(
                     R.layout.activity_rear_display_education_opened, null);
         }
         LottieAnimationView animationView = dialogView.findViewById(
@@ -168,9 +178,9 @@
      * Ensures we're not using old values from when the dialog may have been shown previously.
      */
     private void initializeValues(int startingBaseState) {
-        mRearDisplayEducationDialog = new SystemUIDialog(mContext);
+        mRearDisplayEducationDialog = mSystemUIDialogFactory.create();
         if (mFoldedStates == null) {
-            mFoldedStates = mContext.getResources().getIntArray(
+            mFoldedStates = mResources.getIntArray(
                     com.android.internal.R.array.config_foldedDeviceStates);
         }
         mStartedFolded = isFoldedState(startingBaseState);
diff --git a/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayModule.kt b/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayModule.kt
new file mode 100644
index 0000000..6ab294d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayModule.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.reardisplay
+
+import com.android.systemui.CoreStartable
+import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.ClassKey
+import dagger.multibindings.IntoMap
+import dagger.multibindings.IntoSet
+
+@Module
+interface RearDisplayModule {
+
+    /** Start RearDisplayDialogController. */
+    @Binds
+    @IntoMap
+    @ClassKey(RearDisplayDialogController::class)
+    abstract fun bindRearDisplayDialogControllerStartable(
+        impl: RearDisplayDialogController
+    ): CoreStartable
+
+    /** Listen to config changes for RearDisplayDialogController. */
+    @Binds
+    @IntoSet
+    fun bindRearDisplayDialogControllerConfigChanges(
+        impl: RearDisplayDialogController
+    ): ConfigurationListener
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 45917e8..fd53423 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -1037,6 +1037,19 @@
         }
     }
 
+    public void onNavigationBarLumaSamplingEnabled(int displayId, boolean enable) {
+        try {
+            if (mOverviewProxy != null) {
+                mOverviewProxy.onNavigationBarLumaSamplingEnabled(displayId, enable);
+            } else {
+                Log.e(TAG_OPS, "Failed to get overview proxy to enable/disable nav bar luma"
+                        + "sampling");
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG_OPS, "Failed to call onNavigationBarLumaSamplingEnabled()", e);
+        }
+    }
+
     private void updateEnabledState() {
         final int currentUser = mUserTracker.getUserId();
         mIsEnabled = mContext.getPackageManager().resolveServiceAsUser(mQuickStepIntent,
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index b041f95..4ee65b9 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -23,13 +23,17 @@
 
 import com.android.systemui.CoreStartable;
 import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.policy.ConfigurationController;
 
 import java.io.PrintWriter;
 
 /**
  * A proxy to a Recents implementation.
  */
-public class Recents implements CoreStartable, CommandQueue.Callbacks {
+public class Recents implements
+        CoreStartable,
+        ConfigurationController.ConfigurationListener,
+        CommandQueue.Callbacks {
 
     private final Context mContext;
     private final RecentsImplementation mImpl;
@@ -53,7 +57,7 @@
     }
 
     @Override
-    public void onConfigurationChanged(Configuration newConfig) {
+    public void onConfigChanged(Configuration newConfig) {
         mImpl.onConfigurationChanged(newConfig);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsModule.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsModule.java
index 77a4b9f7..1108917 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsModule.java
@@ -18,14 +18,17 @@
 
 import android.content.Context;
 
-import com.android.systemui.res.R;
+import com.android.systemui.CoreStartable;
 import com.android.systemui.dagger.ContextComponentHelper;
+import com.android.systemui.res.R;
+import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
 
 import dagger.Binds;
 import dagger.Module;
 import dagger.Provides;
 import dagger.multibindings.ClassKey;
 import dagger.multibindings.IntoMap;
+import dagger.multibindings.IntoSet;
 
 /**
  * Dagger injection module for {@link RecentsImplementation}
@@ -33,6 +36,28 @@
 @Module
 public abstract class RecentsModule {
 
+    /** Start Recents.  */
+    @Binds
+    @IntoMap
+    @ClassKey(Recents.class)
+    abstract CoreStartable bindRecentsStartable(Recents impl);
+
+    /** Listen to config changes for Recents.  */
+    @Binds
+    @IntoSet
+    abstract ConfigurationListener bindRecentsConfigChanges(Recents impl);
+
+    /** Start ScreenPinningRequest.  */
+    @Binds
+    @IntoMap
+    @ClassKey(ScreenPinningRequest.class)
+    abstract CoreStartable bindScreenPinningRequestStartable(ScreenPinningRequest impl);
+
+    /** Listen to config changes for ScreenPinningRequest.  */
+    @Binds
+    @IntoSet
+    abstract ConfigurationListener bindScreenPinningRequestConfigChanges(ScreenPinningRequest impl);
+
     /**
      * @return The {@link RecentsImplementation} from the config.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
index 3e574e7..2b717cb 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
@@ -54,25 +54,29 @@
 import androidx.annotation.NonNull;
 
 import com.android.systemui.CoreStartable;
-import com.android.systemui.res.R;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.navigationbar.NavigationBarController;
 import com.android.systemui.navigationbar.NavigationBarView;
 import com.android.systemui.navigationbar.NavigationModeController;
+import com.android.systemui.res.R;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.shared.system.QuickStepContract;
+import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.util.leak.RotationUtils;
 
+import dagger.Lazy;
+
 import java.util.ArrayList;
 
 import javax.inject.Inject;
 
-import dagger.Lazy;
-
 @SysUISingleton
-public class ScreenPinningRequest implements View.OnClickListener,
-        NavigationModeController.ModeChangedListener, CoreStartable {
+public class ScreenPinningRequest implements
+        View.OnClickListener,
+        NavigationModeController.ModeChangedListener,
+        CoreStartable,
+        ConfigurationController.ConfigurationListener {
     private static final String TAG = "ScreenPinningRequest";
 
     private final Context mContext;
@@ -149,7 +153,7 @@
     }
 
     @Override
-    public void onConfigurationChanged(Configuration newConfig) {
+    public void onConfigChanged(Configuration newConfig) {
         if (mRequestWindow != null) {
             mRequestWindow.onConfigurationChanged();
         }
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
index 5abb4dd..c96651c 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
@@ -44,6 +44,7 @@
 import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE
 import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED
 import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING
+import com.android.systemui.statusbar.NotificationShadeWindowController
 import com.android.systemui.statusbar.notification.stack.shared.flexiNotifsEnabled
 import com.android.systemui.util.asIndenting
 import com.android.systemui.util.printSection
@@ -83,6 +84,7 @@
     private val powerInteractor: PowerInteractor,
     private val simBouncerInteractor: Lazy<SimBouncerInteractor>,
     private val authenticationInteractor: Lazy<AuthenticationInteractor>,
+    private val windowController: NotificationShadeWindowController,
 ) : CoreStartable {
 
     override fun start() {
@@ -92,6 +94,7 @@
             automaticallySwitchScenes()
             hydrateSystemUiState()
             collectFalsingSignals()
+            hydrateWindowFocus()
         } else {
             sceneLogger.logFrameworkEnabled(
                 isEnabled = false,
@@ -348,6 +351,20 @@
         }
     }
 
+    /** Keeps the focus state of the window view up-to-date. */
+    private fun hydrateWindowFocus() {
+        applicationScope.launch {
+            sceneInteractor.transitionState
+                .mapNotNull { transitionState ->
+                    (transitionState as? ObservableTransitionState.Idle)?.scene
+                }
+                .distinctUntilChanged()
+                .collect { sceneKey ->
+                    windowController.setNotificationShadeFocusable(sceneKey != SceneKey.Gone)
+                }
+        }
+    }
+
     private fun switchToScene(targetSceneKey: SceneKey, loggingReason: String) {
         sceneInteractor.changeScene(
             scene = SceneModel(targetSceneKey),
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
index bd43307..7aa0dad 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
@@ -32,6 +32,7 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.Process;
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.UserHandle;
@@ -143,6 +144,7 @@
         channel.enableVibration(true);
         mNotificationManager.createNotificationChannel(channel);
 
+        int currentUid = Process.myUid();
         int currentUserId = mUserContextTracker.getUserContext().getUserId();
         UserHandle currentUser = new UserHandle(currentUserId);
         switch (action) {
@@ -166,7 +168,7 @@
                 mRecorder = new ScreenMediaRecorder(
                         mUserContextTracker.getUserContext(),
                         mMainHandler,
-                        currentUserId,
+                        currentUid,
                         mAudioSource,
                         captureTarget,
                         this
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java
index 3aab3bf..a170d0da 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java
@@ -83,7 +83,7 @@
     private Surface mInputSurface;
     private VirtualDisplay mVirtualDisplay;
     private MediaRecorder mMediaRecorder;
-    private int mUser;
+    private int mUid;
     private ScreenRecordingMuxer mMuxer;
     private ScreenInternalAudioRecorder mAudio;
     private ScreenRecordingAudioSource mAudioSource;
@@ -94,12 +94,12 @@
     ScreenMediaRecorderListener mListener;
 
     public ScreenMediaRecorder(Context context, Handler handler,
-            int user, ScreenRecordingAudioSource audioSource,
+            int uid, ScreenRecordingAudioSource audioSource,
             MediaProjectionCaptureTarget captureRegion,
             ScreenMediaRecorderListener listener) {
         mContext = context;
         mHandler = handler;
-        mUser = user;
+        mUid = uid;
         mCaptureRegion = captureRegion;
         mListener = listener;
         mAudioSource = audioSource;
@@ -111,7 +111,7 @@
         IMediaProjectionManager mediaService =
                 IMediaProjectionManager.Stub.asInterface(b);
         IMediaProjection proj = null;
-        proj = mediaService.createProjection(mUser, mContext.getPackageName(),
+        proj = mediaService.createProjection(mUid, mContext.getPackageName(),
                     MediaProjectionManager.TYPE_SCREEN_CAPTURE, false);
         IMediaProjection projection = IMediaProjection.Stub.asInterface(proj.asBinder());
         if (mCaptureRegion != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
index 9f416bb..f2fa0ef 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
@@ -135,6 +135,10 @@
         val filter = IntentFilter().apply {
             addAction(Intent.ACTION_LOCALE_CHANGED)
             addAction(Intent.ACTION_USER_INFO_CHANGED)
+            addAction(Intent.ACTION_PROFILE_ADDED)
+            addAction(Intent.ACTION_PROFILE_REMOVED)
+            addAction(Intent.ACTION_PROFILE_AVAILABLE)
+            addAction(Intent.ACTION_PROFILE_UNAVAILABLE)
             // These get called when a managed profile goes in or out of quiet mode.
             addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE)
             addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE)
@@ -157,7 +161,11 @@
             Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE,
             Intent.ACTION_MANAGED_PROFILE_ADDED,
             Intent.ACTION_MANAGED_PROFILE_REMOVED,
-            Intent.ACTION_MANAGED_PROFILE_UNLOCKED -> {
+            Intent.ACTION_MANAGED_PROFILE_UNLOCKED,
+            Intent.ACTION_PROFILE_ADDED,
+            Intent.ACTION_PROFILE_REMOVED,
+            Intent.ACTION_PROFILE_AVAILABLE,
+            Intent.ACTION_PROFILE_UNAVAILABLE -> {
                 handleProfilesChanged()
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java
index d13edf0..d382b7a 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java
@@ -21,6 +21,8 @@
 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
 import static android.view.WindowManagerPolicyConstants.EXTRA_FROM_BRIGHTNESS_KEY;
 
+import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
+
 import android.app.Activity;
 import android.content.res.Configuration;
 import android.graphics.Rect;
@@ -87,6 +89,17 @@
         if (mShadeInteractor.isQsExpanded().getValue()) {
             finish();
         }
+
+        View view = findViewById(R.id.brightness_mirror_container);
+        if (view != null) {
+            collectFlow(view, mShadeInteractor.isQsExpanded(), this::onShadeStateChange);
+        }
+    }
+
+    private void onShadeStateChange(boolean isQsExpanded) {
+        if (isQsExpanded) {
+            requestFinish();
+        }
     }
 
     private void setWindowAttributes() {
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessMirrorHandler.kt b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessMirrorHandler.kt
index 51aa339..701d814 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessMirrorHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessMirrorHandler.kt
@@ -19,9 +19,16 @@
 import com.android.systemui.statusbar.policy.BrightnessMirrorController
 import com.android.systemui.statusbar.policy.BrightnessMirrorController.BrightnessMirrorListener
 
-class BrightnessMirrorHandler(private val brightnessController: MirroredBrightnessController) {
+class BrightnessMirrorHandler(brightnessController: MirroredBrightnessController) {
 
-    private var mirrorController: BrightnessMirrorController? = null
+    var mirrorController: BrightnessMirrorController? = null
+        private set
+
+    var brightnessController: MirroredBrightnessController = brightnessController
+        set(value) {
+            field = value
+            updateBrightnessMirror()
+        }
 
     private val brightnessMirrorListener = BrightnessMirrorListener { updateBrightnessMirror() }
 
@@ -33,7 +40,7 @@
         mirrorController?.removeCallback(brightnessMirrorListener)
     }
 
-    fun setController(controller: BrightnessMirrorController) {
+    fun setController(controller: BrightnessMirrorController?) {
         mirrorController?.removeCallback(brightnessMirrorListener)
         mirrorController = controller
         mirrorController?.addCallback(brightnessMirrorListener)
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java
index bc5090f..be1fa2b 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java
@@ -227,7 +227,7 @@
                 mListener.onChanged(mTracking, progress, false);
                 SeekableSliderEventProducer eventProducer =
                         mBrightnessSliderHapticPlugin.getSeekableSliderEventProducer();
-                if (eventProducer != null) {
+                if (eventProducer != null && fromUser) {
                     eventProducer.onProgressChanged(seekBar, progress, fromUser);
                 }
             }
diff --git a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
index ab69acb..782d651 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
@@ -17,6 +17,8 @@
 package com.android.systemui.shade
 
 import android.content.Context
+import android.os.PowerManager
+import android.os.SystemClock
 import android.view.GestureDetector
 import android.view.MotionEvent
 import android.view.View
@@ -44,6 +46,7 @@
     private val communalViewModel: CommunalViewModel,
     private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
     private val shadeInteractor: ShadeInteractor,
+    private val powerManager: PowerManager,
 ) {
     /** The container view for the hub. This will not be initialized until [initView] is called. */
     private lateinit var communalContainerView: View
@@ -157,7 +160,10 @@
         // If the hub is fully visible, send all touch events to it.
         val communalVisible = hubShowing && !hubOccluded
         if (communalVisible) {
-            return communalContainerView.dispatchTouchEvent(ev)
+            dispatchTouchEvent(ev)
+            // Return true regardless of dispatch result as some touches at the start of a gesture
+            // may return false from dispatchTouchEvent.
+            return true
         }
 
         if (edgeSwipeRegionWidth == 0) {
@@ -172,15 +178,34 @@
                 x >= communalContainerView.width - edgeSwipeRegionWidth
             if (inOpeningSwipeRegion && !hubOccluded) {
                 isTrackingOpenGesture = true
-                return communalContainerView.dispatchTouchEvent(ev)
+                dispatchTouchEvent(ev)
+                // Return true regardless of dispatch result as some touches at the start of a
+                // gesture may return false from dispatchTouchEvent.
+                return true
             }
         } else if (isTrackingOpenGesture) {
             if (isUp || isCancel) {
                 isTrackingOpenGesture = false
             }
-            return communalContainerView.dispatchTouchEvent(ev)
+            dispatchTouchEvent(ev)
+            // Return true regardless of dispatch result as some touches at the start of a gesture
+            // may return false from dispatchTouchEvent.
+            return true
         }
 
         return false
     }
+
+    /**
+     * Dispatches the touch event to the communal container and sends a user activity event to reset
+     * the screen timeout.
+     */
+    private fun dispatchTouchEvent(ev: MotionEvent) {
+        communalContainerView.dispatchTouchEvent(ev)
+        powerManager.userActivity(
+            SystemClock.uptimeMillis(),
+            PowerManager.USER_ACTIVITY_EVENT_TOUCH,
+            0
+        )
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/shade/LargeScreenHeaderHelper.kt b/packages/SystemUI/src/com/android/systemui/shade/LargeScreenHeaderHelper.kt
new file mode 100644
index 0000000..c74f038
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/shade/LargeScreenHeaderHelper.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shade
+
+import android.content.Context
+import com.android.internal.policy.SystemBarUtils
+import com.android.systemui.res.R
+import javax.inject.Inject
+import kotlin.math.max
+
+class LargeScreenHeaderHelper @Inject constructor(private val context: Context) {
+
+    fun getLargeScreenHeaderHeight(): Int = getLargeScreenHeaderHeight(context)
+
+    companion object {
+        @JvmStatic
+        fun getLargeScreenHeaderHeight(context: Context): Int {
+            val defaultHeight =
+                context.resources.getDimensionPixelSize(R.dimen.large_screen_shade_header_height)
+            val statusBarHeight = SystemBarUtils.getStatusBarHeight(context)
+            // Height has to be at least as tall as the status bar, as the status bar height takes
+            // into account display cutouts.
+            return max(defaultHeight, statusBarHeight)
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 17eb3c8..aeccf00 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -26,6 +26,8 @@
 import static com.android.keyguard.KeyguardClockSwitch.SMALL;
 import static com.android.systemui.Flags.keyguardBottomAreaRefactor;
 import static com.android.systemui.Flags.migrateClocksToBlueprint;
+import static com.android.systemui.Flags.predictiveBackAnimateShade;
+import static com.android.systemui.Flags.smartspaceRelocateToBottom;
 import static com.android.systemui.classifier.Classifier.BOUNCER_UNLOCK;
 import static com.android.systemui.classifier.Classifier.GENERIC;
 import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
@@ -121,6 +123,7 @@
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.DisplayId;
 import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor;
 import com.android.systemui.doze.DozeLog;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.dump.DumpsysTableLogger;
@@ -131,7 +134,6 @@
 import com.android.systemui.keyguard.KeyguardViewConfigurator;
 import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor;
 import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor;
-import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor;
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
 import com.android.systemui.keyguard.domain.interactor.NaturalScrollingSettingObserver;
@@ -327,7 +329,7 @@
     private final PulseExpansionHandler mPulseExpansionHandler;
     private final KeyguardBypassController mKeyguardBypassController;
     private final KeyguardUpdateMonitor mUpdateMonitor;
-    private final KeyguardFaceAuthInteractor mKeyguardFaceAuthInteractor;
+    private final DeviceEntryFaceAuthInteractor mDeviceEntryFaceAuthInteractor;
     private final ConversationNotificationManager mConversationNotificationManager;
     private final AuthController mAuthController;
     private final MediaHierarchyManager mMediaHierarchyManager;
@@ -779,7 +781,7 @@
             ActiveNotificationsInteractor activeNotificationsInteractor,
             ShadeAnimationInteractor shadeAnimationInteractor,
             KeyguardViewConfigurator keyguardViewConfigurator,
-            KeyguardFaceAuthInteractor keyguardFaceAuthInteractor,
+            DeviceEntryFaceAuthInteractor deviceEntryFaceAuthInteractor,
             SplitShadeStateController splitShadeStateController,
             PowerInteractor powerInteractor,
             KeyguardClockPositionAlgorithm keyguardClockPositionAlgorithm,
@@ -891,7 +893,7 @@
         mShadeHeaderController = shadeHeaderController;
         mLayoutInflater = layoutInflater;
         mFeatureFlags = featureFlags;
-        mAnimateBack = mFeatureFlags.isEnabled(Flags.WM_SHADE_ANIMATE_BACK_GESTURE);
+        mAnimateBack = predictiveBackAnimateShade();
         mTrackpadGestureFeaturesEnabled = mFeatureFlags.isEnabled(Flags.TRACKPAD_GESTURE_FEATURES);
         mFalsingCollector = falsingCollector;
         mWakeUpCoordinator = coordinator;
@@ -936,7 +938,7 @@
         mScreenOffAnimationController = screenOffAnimationController;
         mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController;
         mLastDownEvents = new NPVCDownEventState.Buffer(MAX_DOWN_EVENT_BUFFER_SIZE);
-        mKeyguardFaceAuthInteractor = keyguardFaceAuthInteractor;
+        mDeviceEntryFaceAuthInteractor = deviceEntryFaceAuthInteractor;
 
         int currentMode = navigationModeController.addListener(
                 mode -> mIsGestureNavigation = QuickStepContract.isGesturalMode(mode));
@@ -1160,9 +1162,9 @@
         // Occluded->Lockscreen
         collectFlow(mView, mKeyguardTransitionInteractor.getOccludedToLockscreenTransition(),
                 mOccludedToLockscreenTransition, mMainDispatcher);
-        if (!KeyguardShadeMigrationNssl.isEnabled()) {
-            collectFlow(mView, mOccludedToLockscreenTransitionViewModel.getLockscreenAlpha(),
+        collectFlow(mView, mOccludedToLockscreenTransitionViewModel.getLockscreenAlpha(),
                 setTransitionAlpha(mNotificationStackScrollLayoutController), mMainDispatcher);
+        if (!KeyguardShadeMigrationNssl.isEnabled()) {
             collectFlow(mView,
                     mOccludedToLockscreenTransitionViewModel.getLockscreenTranslationY(),
                     setTransitionY(mNotificationStackScrollLayoutController), mMainDispatcher);
@@ -1192,8 +1194,10 @@
                 mLockscreenToOccludedTransition, mMainDispatcher);
         collectFlow(mView, mLockscreenToOccludedTransitionViewModel.getLockscreenAlpha(),
                 setTransitionAlpha(mNotificationStackScrollLayoutController), mMainDispatcher);
-        collectFlow(mView, mLockscreenToOccludedTransitionViewModel.getLockscreenTranslationY(),
-                setTransitionY(mNotificationStackScrollLayoutController), mMainDispatcher);
+        if (!KeyguardShadeMigrationNssl.isEnabled()) {
+            collectFlow(mView, mLockscreenToOccludedTransitionViewModel.getLockscreenTranslationY(),
+                    setTransitionY(mNotificationStackScrollLayoutController), mMainDispatcher);
+        }
 
         // Primary bouncer->Gone (ensures lockscreen content is not visible on successful auth)
         collectFlow(mView, mPrimaryBouncerToGoneTransitionViewModel.getLockscreenAlpha(),
@@ -1211,7 +1215,7 @@
                 .setMaxLengthSeconds(0.4f).build();
         mStatusBarMinHeight = SystemBarUtils.getStatusBarHeight(mView.getContext());
         mStatusBarHeaderHeightKeyguard = Utils.getStatusBarHeaderHeightKeyguard(mView.getContext());
-        mClockPositionAlgorithm.loadDimens(mResources);
+        mClockPositionAlgorithm.loadDimens(mView.getContext(), mResources);
         mIndicationBottomPadding = mResources.getDimensionPixelSize(
                 R.dimen.keyguard_indication_bottom_padding);
         int statusbarHeight = SystemBarUtils.getStatusBarHeight(mView.getContext());
@@ -1425,7 +1429,12 @@
             int index = mView.indexOfChild(mKeyguardBottomArea);
             mView.removeView(mKeyguardBottomArea);
             KeyguardBottomAreaView oldBottomArea = mKeyguardBottomArea;
-            setKeyguardBottomArea(mKeyguardBottomAreaViewControllerProvider.get().getView());
+            KeyguardBottomAreaViewController keyguardBottomAreaViewController =
+                    mKeyguardBottomAreaViewControllerProvider.get();
+            if (smartspaceRelocateToBottom()) {
+                keyguardBottomAreaViewController.init();
+            }
+            setKeyguardBottomArea(keyguardBottomAreaViewController.getView());
             mKeyguardBottomArea.initFrom(oldBottomArea);
             mView.addView(mKeyguardBottomArea, index);
 
@@ -1463,6 +1472,9 @@
     }
 
     private void attachSplitShadeMediaPlayerContainer(FrameLayout container) {
+        if (migrateClocksToBlueprint()) {
+            return;
+        }
         mKeyguardMediaController.attachSplitShadeContainer(container);
     }
 
@@ -1745,14 +1757,9 @@
         } else {
             layout = mNotificationContainerParent;
         }
-
-        if (migrateClocksToBlueprint()) {
-            mKeyguardInteractor.setClockShouldBeCentered(mSplitShadeEnabled && shouldBeCentered);
-        } else {
-            mKeyguardStatusViewController.updateAlignment(
-                    layout, mSplitShadeEnabled, shouldBeCentered, animate);
-            mKeyguardUnfoldTransition.ifPresent(t -> t.setStatusViewCentered(shouldBeCentered));
-        }
+        mKeyguardStatusViewController.updateAlignment(
+                layout, mSplitShadeEnabled, shouldBeCentered, animate);
+        mKeyguardUnfoldTransition.ifPresent(t -> t.setStatusViewCentered(shouldBeCentered));
     }
 
     private boolean shouldKeyguardStatusViewBeCentered() {
@@ -2473,6 +2480,13 @@
             return 0;
         }
         if (!mKeyguardBypassController.getBypassEnabled()) {
+            if (migrateClocksToBlueprint()) {
+                View nsslPlaceholder = mView.getRootView().findViewById(R.id.nssl_placeholder);
+                if (!mSplitShadeEnabled && nsslPlaceholder != null) {
+                    return nsslPlaceholder.getTop();
+                }
+            }
+
             return mClockPositionResult.stackScrollerPadding;
         }
         int collapsedPosition = mHeadsUpInset;
@@ -2966,9 +2980,9 @@
                     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.
-                    mKeyguardFaceAuthInteractor.onNotificationPanelClicked();
+                    mDeviceEntryFaceAuthInteractor.onNotificationPanelClicked();
 
-                    if (mKeyguardFaceAuthInteractor.canFaceAuthRun()) {
+                    if (mDeviceEntryFaceAuthInteractor.canFaceAuthRun()) {
                         mUpdateMonitor.requestActiveUnlock(
                                 ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT,
                                 "lockScreenEmptySpaceTap");
@@ -4349,8 +4363,7 @@
         @Override
         public void onHeadsUpPinned(NotificationEntry entry) {
             if (!isKeyguardShowing()) {
-                mNotificationStackScrollLayoutController.generateHeadsUpAnimation(
-                        entry.getHeadsUpAnimationView(), true);
+                mNotificationStackScrollLayoutController.generateHeadsUpAnimation(entry, true);
             }
         }
 
@@ -4362,8 +4375,7 @@
             // notification
             // will stick to the top without any interaction.
             if (isFullyCollapsed() && entry.isRowHeadsUp() && !isKeyguardShowing()) {
-                mNotificationStackScrollLayoutController.generateHeadsUpAnimation(
-                        entry.getHeadsUpAnimationView(), false);
+                mNotificationStackScrollLayoutController.generateHeadsUpAnimation(entry, false);
                 entry.setHeadsUpIsVisible();
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
index 3cf468f..cde2a62 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
@@ -54,13 +54,10 @@
 import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl;
 import com.android.systemui.keyguard.shared.model.TransitionState;
 import com.android.systemui.keyguard.shared.model.TransitionStep;
-import com.android.systemui.keyguard.ui.SwipeUpAnywhereGestureHandler;
 import com.android.systemui.keyguard.ui.binder.AlternateBouncerViewBinder;
-import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerUdfpsIconViewModel;
-import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerViewModel;
+import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerDependencies;
 import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel;
 import com.android.systemui.log.BouncerLogger;
-import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.res.R;
 import com.android.systemui.shared.animation.DisableSubpixelTextTransitionListener;
 import com.android.systemui.statusbar.DragDownHelper;
@@ -69,7 +66,6 @@
 import com.android.systemui.statusbar.NotificationShadeDepthController;
 import com.android.systemui.statusbar.NotificationShadeWindowController;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
-import com.android.systemui.statusbar.gesture.TapGestureDetector;
 import com.android.systemui.statusbar.notification.domain.interactor.NotificationLaunchAnimationInteractor;
 import com.android.systemui.statusbar.notification.stack.AmbientState;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
@@ -204,11 +200,7 @@
             AlternateBouncerInteractor alternateBouncerInteractor,
             SelectedUserInteractor selectedUserInteractor,
             Lazy<JavaAdapter> javaAdapter,
-            Lazy<AlternateBouncerViewModel> alternateBouncerViewModel,
-            Lazy<FalsingManager> falsingManager,
-            Lazy<SwipeUpAnywhereGestureHandler> swipeUpAnywhereGestureHandler,
-            Lazy<TapGestureDetector> tapGestureDetector,
-            Lazy<AlternateBouncerUdfpsIconViewModel> alternateBouncerUdfpsIconViewModel) {
+            Lazy<AlternateBouncerDependencies> alternateBouncerDependencies) {
         mLockscreenShadeTransitionController = transitionController;
         mFalsingCollector = falsingCollector;
         mStatusBarStateController = statusBarStateController;
@@ -250,24 +242,21 @@
                 messageAreaControllerFactory,
                 bouncerMessageInteractor,
                 bouncerLogger,
-                featureFlagsClassic,
                 selectedUserInteractor);
 
         if (DeviceEntryUdfpsRefactor.isEnabled()) {
             AlternateBouncerViewBinder.bind(
                     mView.findViewById(R.id.alternate_bouncer),
-                    alternateBouncerViewModel.get(),
-                    falsingManager.get(),
-                    swipeUpAnywhereGestureHandler.get(),
-                    tapGestureDetector.get(),
-                    alternateBouncerUdfpsIconViewModel.get()
+                    alternateBouncerDependencies.get()
             );
             javaAdapter.get().alwaysCollectFlow(
-                    alternateBouncerViewModel.get().getForcePluginOpen(),
-                    forcePluginOpen ->
+                    alternateBouncerDependencies.get().getViewModel()
+                            .getForcePluginOpen(),
+                        forcePluginOpen ->
                             mNotificationShadeWindowController.setForcePluginOpen(
                                     forcePluginOpen,
-                                    alternateBouncerViewModel.get()
+                                    alternateBouncerDependencies.get()
+                                            .getViewModel()
                             )
             );
         }
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt
index 9c8a286..84cad1d 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt
@@ -27,6 +27,7 @@
 import androidx.constraintlayout.widget.ConstraintSet.START
 import androidx.constraintlayout.widget.ConstraintSet.TOP
 import androidx.lifecycle.lifecycleScope
+import com.android.systemui.Flags.centralizedStatusBarDimensRefactor
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.flags.FeatureFlags
@@ -47,10 +48,11 @@
 import com.android.systemui.util.LargeScreenUtils
 import com.android.systemui.util.ViewController
 import com.android.systemui.util.concurrency.DelayableExecutor
-import kotlinx.coroutines.launch
+import dagger.Lazy
 import java.util.function.Consumer
 import javax.inject.Inject
 import kotlin.reflect.KMutableProperty0
+import kotlinx.coroutines.launch
 
 @VisibleForTesting
 internal const val INSET_DEBOUNCE_MILLIS = 500L
@@ -67,7 +69,8 @@
         private val featureFlags: FeatureFlags,
         private val
             notificationStackScrollLayoutController: NotificationStackScrollLayoutController,
-        private val splitShadeStateController: SplitShadeStateController
+        private val splitShadeStateController: SplitShadeStateController,
+        private val largeScreenHeaderHelperLazy: Lazy<LargeScreenHeaderHelper>,
 ) : ViewController<NotificationsQuickSettingsContainer>(view), QSContainerController {
 
     private var splitShadeEnabled = false
@@ -186,7 +189,11 @@
     }
 
     private fun calculateLargeShadeHeaderHeight(): Int {
-        return resources.getDimensionPixelSize(R.dimen.large_screen_shade_header_height)
+        return if (centralizedStatusBarDimensRefactor()) {
+            largeScreenHeaderHelperLazy.get().getLargeScreenHeaderHeight()
+        } else {
+            resources.getDimensionPixelSize(R.dimen.large_screen_shade_header_height)
+        }
     }
 
     private fun calculateShadeHeaderHeight(): Int {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
index 8397caa..f3e9c75 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
@@ -20,6 +20,7 @@
 import static android.view.WindowInsets.Type.ime;
 
 import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE;
+import static com.android.systemui.Flags.centralizedStatusBarDimensRefactor;
 import static com.android.systemui.classifier.Classifier.QS_COLLAPSE;
 import static com.android.systemui.shade.NotificationPanelViewController.COUNTER_PANEL_OPEN_QS;
 import static com.android.systemui.shade.NotificationPanelViewController.FLING_COLLAPSE;
@@ -66,9 +67,9 @@
 import com.android.systemui.Dumpable;
 import com.android.systemui.classifier.Classifier;
 import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.fragments.FragmentHostManager;
-import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor;
 import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl;
 import com.android.systemui.media.controls.pipeline.MediaDataManager;
 import com.android.systemui.media.controls.ui.MediaHierarchyManager;
@@ -126,6 +127,7 @@
     private final Lazy<NotificationPanelViewController> mPanelViewControllerLazy;
 
     private final NotificationPanelView mPanelView;
+    private final Lazy<LargeScreenHeaderHelper> mLargeScreenHeaderHelperLazy;
     private final KeyguardStatusBarView mKeyguardStatusBar;
     private final FrameLayout mQsFrame;
 
@@ -152,7 +154,7 @@
     private final RecordingController mRecordingController;
     private final LockscreenGestureLogger mLockscreenGestureLogger;
     private final ShadeLogger mShadeLog;
-    private final KeyguardFaceAuthInteractor mKeyguardFaceAuthInteractor;
+    private final DeviceEntryFaceAuthInteractor mDeviceEntryFaceAuthInteractor;
     private final CastController mCastController;
     private final SplitShadeStateController mSplitShadeStateController;
     private final InteractionJankMonitor mInteractionJankMonitor;
@@ -338,16 +340,18 @@
             InteractionJankMonitor interactionJankMonitor,
             ShadeLogger shadeLog,
             DumpManager dumpManager,
-            KeyguardFaceAuthInteractor keyguardFaceAuthInteractor,
+            DeviceEntryFaceAuthInteractor deviceEntryFaceAuthInteractor,
             ShadeRepository shadeRepository,
             ShadeInteractor shadeInteractor,
             ActiveNotificationsInteractor activeNotificationsInteractor,
             JavaAdapter javaAdapter,
             CastController castController,
-            SplitShadeStateController splitShadeStateController
+            SplitShadeStateController splitShadeStateController,
+            Lazy<LargeScreenHeaderHelper> largeScreenHeaderHelperLazy
     ) {
         mPanelViewControllerLazy = panelViewControllerLazy;
         mPanelView = panelView;
+        mLargeScreenHeaderHelperLazy = largeScreenHeaderHelperLazy;
         mQsFrame = mPanelView.findViewById(R.id.qs_frame);
         mKeyguardStatusBar = mPanelView.findViewById(R.id.keyguard_header);
         mResources = mPanelView.getResources();
@@ -384,7 +388,7 @@
         mLockscreenGestureLogger = lockscreenGestureLogger;
         mMetricsLogger = metricsLogger;
         mShadeLog = shadeLog;
-        mKeyguardFaceAuthInteractor = keyguardFaceAuthInteractor;
+        mDeviceEntryFaceAuthInteractor = deviceEntryFaceAuthInteractor;
         mCastController = castController;
         mInteractionJankMonitor = interactionJankMonitor;
         mShadeRepository = shadeRepository;
@@ -449,7 +453,10 @@
         mUseLargeScreenShadeHeader =
                 LargeScreenUtils.shouldUseLargeScreenShadeHeader(mPanelView.getResources());
         mLargeScreenShadeHeaderHeight =
-                mResources.getDimensionPixelSize(R.dimen.large_screen_shade_header_height);
+                centralizedStatusBarDimensRefactor()
+                        ? mLargeScreenHeaderHelperLazy.get().getLargeScreenHeaderHeight()
+                        : mResources.getDimensionPixelSize(
+                                R.dimen.large_screen_shade_header_height);
         int topMargin = mUseLargeScreenShadeHeader ? mLargeScreenShadeHeaderHeight :
                 mResources.getDimensionPixelSize(R.dimen.notification_panel_margin_top);
         mShadeHeaderController.setLargeScreenActive(mUseLargeScreenShadeHeader);
@@ -972,7 +979,7 @@
         // When expanding QS, let's authenticate the user if possible,
         // this will speed up notification actions.
         if (height == 0 && !mKeyguardStateController.canDismissLockScreen()) {
-            mKeyguardFaceAuthInteractor.onQsExpansionStared();
+            mDeviceEntryFaceAuthInteractor.onQsExpansionStared();
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt
index 2460a33..a66bacd 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt
@@ -38,6 +38,7 @@
 import com.android.app.animation.Interpolators
 import com.android.settingslib.Utils
 import com.android.systemui.Dumpable
+import com.android.systemui.Flags.centralizedStatusBarDimensRefactor
 import com.android.systemui.animation.ShadeInterpolation
 import com.android.systemui.battery.BatteryMeterView
 import com.android.systemui.battery.BatteryMeterViewController
@@ -432,6 +433,9 @@
             changes += combinedShadeHeadersConstraintManager.emptyCutoutConstraints()
         }
 
+        if (centralizedStatusBarDimensRefactor()) {
+            view.setPadding(view.paddingLeft, sbInsets.top, view.paddingRight, view.paddingBottom)
+        }
         view.updateAllConstraints(changes)
         updateBatteryMode()
     }
diff --git a/packages/SystemUI/src/com/android/systemui/startable/Dependencies.kt b/packages/SystemUI/src/com/android/systemui/startable/Dependencies.kt
new file mode 100644
index 0000000..5e57f1d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/startable/Dependencies.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.startable
+
+import com.android.systemui.CoreStartable
+import kotlin.reflect.KClass
+
+/**
+ * Allows a [CoreStartable] to declare that it must be started after its dependencies.
+ *
+ * This creates a partial, topological ordering. See [com.android.systemui.SystemUIApplication] for
+ * how this ordering is enforced at runtime.
+ */
+@MustBeDocumented
+@Target(AnnotationTarget.CLASS)
+@Retention(AnnotationRetention.RUNTIME)
+annotation class Dependencies(vararg val value: KClass<out CoreStartable> = [])
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 08415cb..d6d3e67 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -31,6 +31,7 @@
 import static com.android.systemui.DejankUtils.whitelistIpcs;
 import static com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED;
 import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.IMPORTANT_MSG_MIN_DURATION;
+import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_IS_DISMISSIBLE;
 import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_ALIGNMENT;
 import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_BATTERY;
 import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_BIOMETRIC_MESSAGE;
@@ -178,6 +179,7 @@
     private KeyguardInteractor mKeyguardInteractor;
     private String mPersistentUnlockMessage;
     private String mAlignmentIndication;
+    private boolean mForceIsDismissible;
     private CharSequence mTrustGrantedIndication;
     private CharSequence mTransientIndication;
     private CharSequence mBiometricMessage;
@@ -442,6 +444,7 @@
 
         // Update persistent messages. The following methods should only be called if we're on the
         // lock screen:
+        updateForceIsDimissibileChanged();
         updateLockScreenDisclosureMsg();
         updateLockScreenOwnerInfo();
         updateLockScreenBatteryMsg(animate);
@@ -458,6 +461,22 @@
         updateDeviceEntryIndication(false);
     }
 
+    private void updateForceIsDimissibileChanged() {
+        if (mForceIsDismissible) {
+            mRotateTextViewController.updateIndication(
+                    INDICATION_IS_DISMISSIBLE,
+                    new KeyguardIndication.Builder()
+                            .setMessage(mContext.getResources().getString(
+                                    com.android.systemui.res.R.string.dismissible_keyguard_swipe)
+                            )
+                            .setTextColor(mInitialTextColorState)
+                            .build(),
+                    /* updateImmediately */ true);
+        } else {
+            mRotateTextViewController.hideIndication(INDICATION_IS_DISMISSIBLE);
+        }
+    }
+
     private void updateLockScreenDisclosureMsg() {
         if (mOrganizationOwnedDevice) {
             mBackgroundExecutor.execute(() -> {
@@ -1311,6 +1330,12 @@
         }
 
         @Override
+        public void onForceIsDismissibleChanged(boolean forceIsDismissible) {
+            mForceIsDismissible = forceIsDismissible;
+            updateDeviceEntryIndication(false);
+        }
+
+        @Override
         public void onTrustGrantedForCurrentUser(
                 boolean dismissKeyguard,
                 boolean newlyUnlocked,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt
index 39b7930..6e85074 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt
@@ -253,6 +253,8 @@
     initialHeight: Int? = null
 ) : View(context, attrs) {
 
+    private val logString = this::class.simpleName!! + "@" + hashCode()
+
     /** Listener that is called if the scrim's opaqueness changes */
     var isScrimOpaqueChangedListener: Consumer<Boolean>? = null
 
@@ -267,13 +269,13 @@
             if (field != value) {
                 field = value
                 if (value <= 0.0f || value >= 1.0f) {
-                    scrimLogger?.d(TAG, "revealAmount", "$value on ${logString()}")
+                    scrimLogger?.d(TAG, "revealAmount", "$value on $logString")
                 }
                 revealEffect.setRevealAmountOnScrim(value, this)
                 updateScrimOpaque()
                 Trace.traceCounter(
                     Trace.TRACE_TAG_APP,
-                    "light_reveal_amount",
+                    "light_reveal_amount $logString",
                     (field * 100).toInt()
                 )
                 invalidate()
@@ -290,7 +292,7 @@
                 field = value
 
                 revealEffect.setRevealAmountOnScrim(revealAmount, this)
-                scrimLogger?.d(TAG, "revealEffect", "$value on ${logString()}")
+                scrimLogger?.d(TAG, "revealEffect", "$value on $logString")
                 invalidate()
             }
         }
@@ -350,7 +352,7 @@
             if (field != value) {
                 field = value
                 isScrimOpaqueChangedListener?.accept(field)
-                scrimLogger?.d(TAG, "isScrimOpaque", "$value on ${logString()}")
+                scrimLogger?.d(TAG, "isScrimOpaque", "$value on $logString")
             }
         }
 
@@ -368,13 +370,13 @@
 
     override fun setAlpha(alpha: Float) {
         super.setAlpha(alpha)
-        scrimLogger?.d(TAG, "alpha", "$alpha on ${logString()}")
+        scrimLogger?.d(TAG, "alpha", "$alpha on $logString")
         updateScrimOpaque()
     }
 
     override fun setVisibility(visibility: Int) {
         super.setVisibility(visibility)
-        scrimLogger?.d(TAG, "visibility", "$visibility on ${logString()}")
+        scrimLogger?.d(TAG, "visibility", "$visibility on $logString")
         updateScrimOpaque()
     }
 
@@ -467,8 +469,4 @@
                 PorterDuff.Mode.MULTIPLY
             )
     }
-
-    private fun logString(): String {
-        return this::class.simpleName!! + "@" + hashCode()
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
index 2438298..7f8be1c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
@@ -22,6 +22,7 @@
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.keyguard.WakefulnessLifecycle
 import com.android.systemui.keyguard.domain.interactor.NaturalScrollingSettingObserver
+import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl
 import com.android.systemui.media.controls.ui.MediaHierarchyManager
 import com.android.systemui.navigationbar.gestural.Utilities.isTrackpadScroll
 import com.android.systemui.plugins.ActivityStarter
@@ -888,6 +889,9 @@
                     isDraggingDown = false
                     isTrackpadReverseScroll = false
                     shadeRepository.setLegacyLockscreenShadeTracking(false)
+                    if (KeyguardShadeMigrationNssl.isEnabled) {
+                        return true
+                    }
                 } else {
                     stopDragging()
                     return false
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
index 93c55de..71efbab 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
@@ -44,6 +44,13 @@
 
     boolean isCurrentProfile(int userId);
 
+    /**
+     *
+     * @param userId user Id
+     * @return true if user profile is running.
+     */
+    boolean isProfileAvailable(int userId);
+
     /** Adds a listener to be notified when the current user changes. */
     void addUserChangedListener(UserChangedListener listener);
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
index 05c3839..24ac70e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
@@ -15,14 +15,17 @@
  */
 package com.android.systemui.statusbar;
 
+import static android.app.Flags.keyguardPrivateNotifications;
+import static android.app.StatusBarManager.ACTION_KEYGUARD_PRIVATE_NOTIFICATIONS_CHANGED;
+import static android.app.StatusBarManager.EXTRA_KM_PRIVATE_NOTIFS_ALLOWED;
 import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED;
 import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS;
 import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS;
+import static android.os.Flags.allowPrivateProfile;
 import static android.os.UserHandle.USER_ALL;
 import static android.os.UserHandle.USER_NULL;
 import static android.provider.Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS;
 import static android.provider.Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS;
-import static android.os.Flags.allowPrivateProfile;
 
 import static com.android.systemui.DejankUtils.whitelistIpcs;
 
@@ -39,15 +42,13 @@
 import android.content.IntentSender;
 import android.content.pm.UserInfo;
 import android.database.ContentObserver;
+import android.database.ExecutorContentObserver;
 import android.net.Uri;
-import android.os.Handler;
-import android.os.HandlerExecutor;
 import android.os.Looper;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.Settings;
 import android.util.Log;
-import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
 
@@ -77,17 +78,17 @@
 import com.android.systemui.util.ListenerSet;
 import com.android.systemui.util.settings.SecureSettings;
 
+import dagger.Lazy;
+
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
-import java.util.concurrent.Executor;
 import java.util.Objects;
+import java.util.concurrent.Executor;
 
 import javax.inject.Inject;
 
-import dagger.Lazy;
-
 /**
  * Handles keeping track of the current user, profiles, and various things related to hiding
  * contents, redacting notifications, and the lockscreen.
@@ -149,6 +150,25 @@
             new ListenerSet<>();
     private final Collection<Uri> mLockScreenUris = new ArrayList<>();
 
+    protected final BroadcastReceiver mKeyguardReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            final String action = intent.getAction();
+
+            if (ACTION_KEYGUARD_PRIVATE_NOTIFICATIONS_CHANGED.equals(action)) {
+                if (mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) {
+                    mKeyguardAllowingNotifications =
+                            intent.getBooleanExtra(EXTRA_KM_PRIVATE_NOTIFS_ALLOWED, false);
+                    if (mCurrentUserId == getSendingUserId()) {
+                        boolean changed = updateLockscreenNotificationSetting();
+                        if (changed) {
+                            notifyNotificationStateChanged();
+                        }
+                    }
+                }
+            }
+        }
+    };
 
     protected final BroadcastReceiver mAllUsersReceiver = new BroadcastReceiver() {
         @Override
@@ -207,7 +227,7 @@
                 updateCurrentProfilesCache();
                 if (mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) {
                     final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL);
-                    mBackgroundHandler.post(() -> {
+                    mBackgroundExecutor.execute(() -> {
                         initValuesForUser(userId);
                     });
                 }
@@ -254,7 +274,12 @@
 
                     updateLockscreenNotificationSetting();
                     updatePublicMode();
-                    mPresenter.onUserSwitched(mCurrentUserId);
+                    if (mPresenter != null) {
+                        mPresenter.onUserSwitched(mCurrentUserId);
+                    } else {
+                        Log.w(TAG, "user switch before setup with presenter",
+                                new Exception());
+                    }
 
                     for (UserChangedListener listener : mListeners) {
                         listener.onUserChanged(mCurrentUserId);
@@ -263,8 +288,7 @@
             };
 
     protected final Context mContext;
-    private final Handler mMainHandler;
-    private final Handler mBackgroundHandler;
+    private final Executor mMainExecutor;
     private final Executor mBackgroundExecutor;
     /** The current user and its profiles (possibly including a communal profile). */
     protected final SparseArray<UserInfo> mCurrentProfiles = new SparseArray<>();
@@ -287,8 +311,7 @@
             Lazy<OverviewProxyService> overviewProxyServiceLazy,
             KeyguardManager keyguardManager,
             StatusBarStateController statusBarStateController,
-            @Main Handler mainHandler,
-            @Background Handler backgroundHandler,
+            @Main Executor mainExecutor,
             @Background Executor backgroundExecutor,
             DeviceProvisionedController deviceProvisionedController,
             KeyguardStateController keyguardStateController,
@@ -297,8 +320,7 @@
             LockPatternUtils lockPatternUtils,
             FeatureFlagsClassic featureFlags) {
         mContext = context;
-        mMainHandler = mainHandler;
-        mBackgroundHandler = backgroundHandler;
+        mMainExecutor = mainExecutor;
         mBackgroundExecutor = backgroundExecutor;
         mDevicePolicyManager = devicePolicyManager;
         mUserManager = userManager;
@@ -321,15 +343,25 @@
         mLockScreenUris.add(SHOW_PRIVATE_LOCKSCREEN);
 
         dumpManager.registerDumpable(this);
+
+        if (keyguardPrivateNotifications()) {
+            init();
+        }
     }
 
     public void setUpWithPresenter(NotificationPresenter presenter) {
         mPresenter = presenter;
 
-        mLockscreenSettingsObserver = new ContentObserver(
+        if (!keyguardPrivateNotifications()) {
+            init();
+        }
+    }
+
+    private void init() {
+        mLockscreenSettingsObserver = new ExecutorContentObserver(
                 mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)
-                        ? mBackgroundHandler
-                        : mMainHandler) {
+                        ? mBackgroundExecutor
+                        : mMainExecutor) {
 
             @Override
             public void onChange(boolean selfChange, Collection<Uri> uris, int flags) {
@@ -376,7 +408,7 @@
             }
         };
 
-        mSettingsObserver = new ContentObserver(mMainHandler) {
+        mSettingsObserver = new ExecutorContentObserver(mMainExecutor) {
             @Override
             public void onChange(boolean selfChange) {
                 updateLockscreenNotificationSetting();
@@ -408,6 +440,11 @@
                 new IntentFilter(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED),
                 mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)
                         ? mBackgroundExecutor : null, UserHandle.ALL);
+        if (keyguardPrivateNotifications()) {
+            mBroadcastDispatcher.registerReceiver(mKeyguardReceiver,
+                    new IntentFilter(ACTION_KEYGUARD_PRIVATE_NOTIFICATIONS_CHANGED),
+                    mBackgroundExecutor, UserHandle.ALL);
+        }
 
         IntentFilter filter = new IntentFilter();
         filter.addAction(Intent.ACTION_USER_ADDED);
@@ -427,14 +464,14 @@
         mContext.registerReceiver(mBaseBroadcastReceiver, internalFilter, PERMISSION_SELF, null,
                 Context.RECEIVER_EXPORTED_UNAUDITED);
 
-        mUserTracker.addCallback(mUserChangedCallback, new HandlerExecutor(mMainHandler));
+        mUserTracker.addCallback(mUserChangedCallback, mMainExecutor);
 
         mCurrentUserId = mUserTracker.getUserId(); // in case we reg'd receiver too late
         updateCurrentProfilesCache();
 
         if (mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) {
             // Set  up
-            mBackgroundHandler.post(() -> {
+            mBackgroundExecutor.execute(() -> {
                 @SuppressLint("MissingPermission") List<UserInfo> users = mUserManager.getUsers();
                 for (int i = users.size() - 1; i >= 0; i--) {
                     initValuesForUser(users.get(i).id);
@@ -449,6 +486,10 @@
         mLockscreenSettingsObserver.onChange(
                 false, mLockScreenUris, 0, UserHandle.of(userId));
         updateDpcSettings(userId);
+
+        if (keyguardPrivateNotifications()) {
+            updateGlobalKeyguardSettings();
+        }
     }
 
     public boolean shouldShowLockscreenNotifications() {
@@ -461,6 +502,13 @@
         }
     }
 
+    @SuppressLint("AndroidFrameworkRequiresPermission")
+    public boolean isProfileAvailable(int userId) {
+        synchronized (mLock) {
+            return mUserManager.isUserRunning(userId);
+        }
+    }
+
     private void setShowLockscreenNotifications(boolean show) {
         mShowLockscreenNotifications = show;
     }
@@ -470,8 +518,12 @@
         boolean allowedByDpm;
 
         if (mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) {
-            show = mUsersUsersAllowingNotifications.get(mCurrentUserId)
-                    && mKeyguardAllowingNotifications;
+            if (keyguardPrivateNotifications()) {
+                show = mUsersUsersAllowingNotifications.get(mCurrentUserId);
+            } else {
+                show = mUsersUsersAllowingNotifications.get(mCurrentUserId)
+                        && mKeyguardAllowingNotifications;
+            }
             // If DPC never notified us about a user, that means they have no policy for the user,
             // and they allow the behavior
             allowedByDpm = mUsersDpcAllowingNotifications.get(mCurrentUserId, true);
@@ -514,8 +566,13 @@
                 1,
                 userId) != 0;
         mUsersUsersAllowingNotifications.put(userId, newAllowLockscreen);
-        boolean keyguardChanged = updateGlobalKeyguardSettings();
-        return (newAllowLockscreen != originalAllowLockscreen) || keyguardChanged;
+
+        if (keyguardPrivateNotifications()) {
+            return (newAllowLockscreen != originalAllowLockscreen);
+        } else {
+            boolean keyguardChanged = updateGlobalKeyguardSettings();
+            return (newAllowLockscreen != originalAllowLockscreen) || keyguardChanged;
+        }
     }
 
     @WorkerThread
@@ -553,8 +610,14 @@
                 Log.i(TAG, "Asking for redact notifs dpm override too early", new Throwable());
                 return false;
             }
-            return mUsersUsersAllowingPrivateNotifications.get(userHandle)
-                    && mUsersDpcAllowingPrivateNotifications.get(userHandle);
+            if (keyguardPrivateNotifications()) {
+                return mUsersUsersAllowingPrivateNotifications.get(userHandle)
+                        && mUsersDpcAllowingPrivateNotifications.get(userHandle)
+                        && mKeyguardAllowingNotifications;
+            } else {
+                return mUsersUsersAllowingPrivateNotifications.get(userHandle)
+                        && mUsersDpcAllowingPrivateNotifications.get(userHandle);
+            }
         } else {
             if (userHandle == USER_ALL) {
                 return true;
@@ -641,9 +704,14 @@
                 Log.wtf(TAG, "Asking for show notifs dpm override too early", new Throwable());
                 updateDpcSettings(userHandle);
             }
-            return mUsersUsersAllowingNotifications.get(userHandle)
-                    && mUsersDpcAllowingNotifications.get(userHandle)
-                    && mKeyguardAllowingNotifications;
+            if (keyguardPrivateNotifications()) {
+                return mUsersUsersAllowingNotifications.get(userHandle)
+                        && mUsersDpcAllowingNotifications.get(userHandle);
+            } else {
+                return mUsersUsersAllowingNotifications.get(userHandle)
+                        && mUsersDpcAllowingNotifications.get(userHandle)
+                        && mKeyguardAllowingNotifications;
+            }
         } else {
             if (isCurrentProfile(userHandle) && userHandle != mCurrentUserId) {
                 return true;
@@ -682,7 +750,12 @@
                 ent.getSbn().getNotification().visibility == Notification.VISIBILITY_PRIVATE;
         boolean userForcesRedaction = packageHasVisibilityOverride(ent.getSbn().getKey());
 
-        return userForcesRedaction || notificationRequestsRedaction && isNotifRedacted;
+        if (keyguardPrivateNotifications()) {
+            return !mKeyguardAllowingNotifications
+                    || userForcesRedaction || notificationRequestsRedaction && isNotifRedacted;
+        } else {
+            return userForcesRedaction || notificationRequestsRedaction && isNotifRedacted;
+        }
     }
 
     private boolean packageHasVisibilityOverride(String key) {
@@ -719,7 +792,7 @@
                 }
             }
         }
-        mMainHandler.post(() -> {
+        mMainExecutor.execute(() -> {
             for (UserChangedListener listener : mListeners) {
                 listener.onCurrentProfilesChanged(mCurrentProfiles);
             }
@@ -818,7 +891,7 @@
 
     private void notifyNotificationStateChanged() {
         if (!Looper.getMainLooper().isCurrentThread()) {
-            mMainHandler.post(() -> {
+            mMainExecutor.execute(() -> {
                 for (NotificationStateChangedListener listener : mNotifStateChangedListeners) {
                     listener.onNotificationStateChanged();
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
index 984fcad..7a2e82f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
@@ -323,9 +323,14 @@
      * Update the icon dimens and drawable with current resources
      */
     public void updateIconDimens() {
-        reloadDimens();
-        updateDrawable();
-        maybeUpdateIconScaleDimens();
+        Trace.beginSection("StatusBarIconView#updateIconDimens");
+        try {
+            reloadDimens();
+            updateDrawable();
+            maybeUpdateIconScaleDimens();
+        } finally {
+            Trace.endSection();
+        }
     }
 
     private void reloadDimens() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
index a957095..32cd56c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.statusbar.dagger;
 
+import static com.android.systemui.Flags.predictiveBackAnimateDialogs;
+
 import android.content.Context;
 import android.os.RemoteException;
 import android.service.dreams.IDreamManager;
@@ -31,8 +33,6 @@
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dump.DumpHandler;
 import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
 import com.android.systemui.media.controls.pipeline.MediaDataManager;
 import com.android.systemui.power.domain.interactor.PowerInteractor;
 import com.android.systemui.settings.DisplayTracker;
@@ -230,11 +230,11 @@
     /** */
     @Provides
     @SysUISingleton
-    static AnimationFeatureFlags provideAnimationFeatureFlags(FeatureFlags featureFlags) {
+    static AnimationFeatureFlags provideAnimationFeatureFlags() {
         return new AnimationFeatureFlags() {
             @Override
             public boolean isPredictiveBackQsDialogAnim() {
-                return featureFlags.isEnabled(Flags.WM_ENABLE_PREDICTIVE_BACK_QS_DIALOG_ANIM);
+                return predictiveBackAnimateDialogs();
             }
         };
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/StatusBarDataLayerModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/StatusBarDataLayerModule.kt
index 29d53fc..9f878b2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/data/StatusBarDataLayerModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/StatusBarDataLayerModule.kt
@@ -16,6 +16,7 @@
 package com.android.systemui.statusbar.data
 
 import com.android.systemui.statusbar.data.repository.KeyguardStatusBarRepositoryModule
+import com.android.systemui.statusbar.data.repository.RemoteInputRepositoryModule
 import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryModule
 import com.android.systemui.statusbar.phone.data.StatusBarPhoneDataLayerModule
 import dagger.Module
@@ -24,6 +25,7 @@
     includes =
         [
             KeyguardStatusBarRepositoryModule::class,
+            RemoteInputRepositoryModule::class,
             StatusBarModeRepositoryModule::class,
             StatusBarPhoneDataLayerModule::class
         ]
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/RemoteInputRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/RemoteInputRepository.kt
new file mode 100644
index 0000000..c0302bc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/RemoteInputRepository.kt
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.data.repository
+
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.NotificationRemoteInputManager
+import com.android.systemui.statusbar.RemoteInputController
+import dagger.Binds
+import dagger.Module
+import javax.inject.Inject
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+
+/**
+ * Repository used for tracking the state of notification remote input (e.g. when the user presses
+ * "reply" on a notification and the keyboard opens).
+ */
+interface RemoteInputRepository {
+    /** Whether remote input is currently active for any notification. */
+    val isRemoteInputActive: Flow<Boolean>
+}
+
+@SysUISingleton
+class RemoteInputRepositoryImpl
+@Inject
+constructor(
+    private val notificationRemoteInputManager: NotificationRemoteInputManager,
+) : RemoteInputRepository {
+    override val isRemoteInputActive: Flow<Boolean> = conflatedCallbackFlow {
+        trySend(false) // initial value is false
+        val callback =
+            object : RemoteInputController.Callback {
+                override fun onRemoteInputActive(active: Boolean) {
+                    trySend(active)
+                }
+            }
+        notificationRemoteInputManager.addControllerCallback(callback)
+        awaitClose { notificationRemoteInputManager.removeControllerCallback(callback) }
+    }
+}
+
+@Module
+interface RemoteInputRepositoryModule {
+    @Binds fun bindImpl(impl: RemoteInputRepositoryImpl): RemoteInputRepository
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/domain/interactor/RemoteInputInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/domain/interactor/RemoteInputInteractor.kt
new file mode 100644
index 0000000..68f727b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/domain/interactor/RemoteInputInteractor.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.domain.interactor
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.data.repository.RemoteInputRepository
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+
+/**
+ * Interactor used for business logic pertaining to the notification remote input (e.g. when the
+ * user presses "reply" on a notification and the keyboard opens).
+ */
+@SysUISingleton
+class RemoteInputInteractor @Inject constructor(remoteInputRepository: RemoteInputRepository) {
+    /** Is remote input currently active for a notification? */
+    val isRemoteInputActive: Flow<Boolean> = remoteInputRepository.isRemoteInputActive
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
index 0c67279..3f2c818 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
@@ -31,6 +31,7 @@
 import com.android.systemui.shade.ShadeViewController
 import com.android.systemui.statusbar.StatusBarState
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.domain.interactor.NotificationsKeyguardInteractor
 import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
 import com.android.systemui.statusbar.notification.stack.StackStateAnimator
@@ -58,6 +59,7 @@
     private val dozeParameters: DozeParameters,
     private val screenOffAnimationController: ScreenOffAnimationController,
     private val logger: NotificationWakeUpCoordinatorLogger,
+    private val notifsKeyguardInteractor: NotificationsKeyguardInteractor,
 ) :
     OnHeadsUpChangedListener,
     StatusBarStateController.StateListener,
@@ -144,6 +146,7 @@
                 for (listener in wakeUpListeners) {
                     listener.onFullyHiddenChanged(value)
                 }
+                notifsKeyguardInteractor.setNotificationsFullyHidden(value)
             }
         }
 
@@ -216,6 +219,7 @@
                 for (listener in wakeUpListeners) {
                     listener.onPulseExpandingChanged(pulseExpanding)
                 }
+                notifsKeyguardInteractor.setPulseExpanding(pulseExpanding)
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
index 70ccc4f..80ef14b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar.notification.collection.inflation;
 
+import static com.android.systemui.Flags.screenshareNotificationHiding;
 import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED;
 import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_EXPANDED;
 import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_PUBLIC;
@@ -243,7 +244,11 @@
         params.setUseIncreasedCollapsedHeight(useIncreasedCollapsedHeight);
         params.setUseLowPriority(isLowPriority);
 
-        if (mNotificationLockscreenUserManager.needsRedaction(entry)) {
+        // If screenshareNotificationHiding is enabled, both public and private views should be
+        // inflated to avoid any latency associated with reinflating all notification views when
+        // screen share starts and stops
+        if (screenshareNotificationHiding()
+                || mNotificationLockscreenUserManager.needsRedaction(entry)) {
             params.requireContentViews(FLAG_CONTENT_VIEW_PUBLIC);
         } else {
             params.markContentViewsFreeable(FLAG_CONTENT_VIEW_PUBLIC);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/NotificationDataLayerModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/NotificationDataLayerModule.kt
index 5435fb5..2cac000 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/NotificationDataLayerModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/NotificationDataLayerModule.kt
@@ -15,8 +15,6 @@
  */
 package com.android.systemui.statusbar.notification.data
 
-import com.android.systemui.statusbar.notification.data.repository.NotificationsKeyguardStateRepositoryModule
 import dagger.Module
 
-@Module(includes = [NotificationsKeyguardStateRepositoryModule::class])
-interface NotificationDataLayerModule
+@Module(includes = []) interface NotificationDataLayerModule
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/NotificationsKeyguardViewStateRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/NotificationsKeyguardViewStateRepository.kt
index 2cc1403..bd6ea30 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/NotificationsKeyguardViewStateRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/NotificationsKeyguardViewStateRepository.kt
@@ -15,59 +15,16 @@
  */
 package com.android.systemui.statusbar.notification.data.repository
 
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator
-import dagger.Binds
-import dagger.Module
 import javax.inject.Inject
-import kotlinx.coroutines.channels.awaitClose
-import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
 
 /** View-states pertaining to notifications on the keyguard. */
-interface NotificationsKeyguardViewStateRepository {
+@SysUISingleton
+class NotificationsKeyguardViewStateRepository @Inject constructor() {
     /** Are notifications fully hidden from view? */
-    val areNotificationsFullyHidden: Flow<Boolean>
+    val areNotificationsFullyHidden = MutableStateFlow(false)
 
     /** Is a pulse expansion occurring? */
-    val isPulseExpanding: Flow<Boolean>
-}
-
-@Module
-interface NotificationsKeyguardStateRepositoryModule {
-    @Binds
-    fun bindImpl(
-        impl: NotificationsKeyguardViewStateRepositoryImpl
-    ): NotificationsKeyguardViewStateRepository
-}
-
-@SysUISingleton
-class NotificationsKeyguardViewStateRepositoryImpl
-@Inject
-constructor(
-    wakeUpCoordinator: NotificationWakeUpCoordinator,
-) : NotificationsKeyguardViewStateRepository {
-    override val areNotificationsFullyHidden: Flow<Boolean> = conflatedCallbackFlow {
-        val listener =
-            object : NotificationWakeUpCoordinator.WakeUpListener {
-                override fun onFullyHiddenChanged(isFullyHidden: Boolean) {
-                    trySend(isFullyHidden)
-                }
-            }
-        trySend(wakeUpCoordinator.notificationsFullyHidden)
-        wakeUpCoordinator.addListener(listener)
-        awaitClose { wakeUpCoordinator.removeListener(listener) }
-    }
-
-    override val isPulseExpanding: Flow<Boolean> = conflatedCallbackFlow {
-        val listener =
-            object : NotificationWakeUpCoordinator.WakeUpListener {
-                override fun onPulseExpandingChanged(isPulseExpanding: Boolean) {
-                    trySend(isPulseExpanding)
-                }
-            }
-        trySend(wakeUpCoordinator.isPulseExpanding())
-        wakeUpCoordinator.addListener(listener)
-        awaitClose { wakeUpCoordinator.removeListener(listener) }
-    }
+    val isPulseExpanding = MutableStateFlow(false)
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsKeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsKeyguardInteractor.kt
index 73341db..a6361cb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsKeyguardInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsKeyguardInteractor.kt
@@ -15,24 +15,29 @@
  */
 package com.android.systemui.statusbar.notification.domain.interactor
 
-import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.statusbar.notification.data.repository.NotificationsKeyguardViewStateRepository
 import javax.inject.Inject
-import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.flowOn
 
 /** Domain logic pertaining to notifications on the keyguard. */
 class NotificationsKeyguardInteractor
 @Inject
 constructor(
-    repository: NotificationsKeyguardViewStateRepository,
-    @Background backgroundDispatcher: CoroutineDispatcher,
+    private val repository: NotificationsKeyguardViewStateRepository,
 ) {
     /** Is a pulse expansion occurring? */
-    val isPulseExpanding: Flow<Boolean> = repository.isPulseExpanding.flowOn(backgroundDispatcher)
+    val isPulseExpanding: Flow<Boolean> = repository.isPulseExpanding
 
     /** Are notifications fully hidden from view? */
-    val areNotificationsFullyHidden: Flow<Boolean> =
-        repository.areNotificationsFullyHidden.flowOn(backgroundDispatcher)
+    val areNotificationsFullyHidden: Flow<Boolean> = repository.areNotificationsFullyHidden
+
+    /** Updates whether notifications are fully hidden from view. */
+    fun setNotificationsFullyHidden(fullyHidden: Boolean) {
+        repository.areNotificationsFullyHidden.value = fullyHidden
+    }
+
+    /** Updates whether a pulse expansion is occurring. */
+    fun setPulseExpanding(pulseExpanding: Boolean) {
+        repository.isPulseExpanding.value = pulseExpanding
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractor.kt
index 9215568..b913355 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractor.kt
@@ -17,6 +17,7 @@
 
 package com.android.systemui.statusbar.notification.icon.domain.interactor
 
+import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
 import com.android.systemui.statusbar.data.repository.NotificationListenerSettingsRepository
 import com.android.systemui.statusbar.notification.data.repository.NotificationsKeyguardViewStateRepository
@@ -26,11 +27,13 @@
 import com.android.wm.shell.bubbles.Bubbles
 import java.util.Optional
 import javax.inject.Inject
+import kotlin.coroutines.CoroutineContext
 import kotlin.jvm.optionals.getOrNull
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOn
 
 /** Domain logic related to notification icons. */
 class NotificationIconsInteractor
@@ -103,35 +106,41 @@
 class AlwaysOnDisplayNotificationIconsInteractor
 @Inject
 constructor(
+    @Background bgContext: CoroutineContext,
     deviceEntryInteractor: DeviceEntryInteractor,
     iconsInteractor: NotificationIconsInteractor,
 ) {
     val aodNotifs: Flow<Set<ActiveNotificationModel>> =
-        deviceEntryInteractor.isBypassEnabled.flatMapLatest { isBypassEnabled ->
-            iconsInteractor.filteredNotifSet(
-                showAmbient = false,
-                showDismissed = false,
-                showRepliedMessages = false,
-                showPulsing = !isBypassEnabled,
-            )
-        }
+        deviceEntryInteractor.isBypassEnabled
+            .flatMapLatest { isBypassEnabled ->
+                iconsInteractor.filteredNotifSet(
+                    showAmbient = false,
+                    showDismissed = false,
+                    showRepliedMessages = false,
+                    showPulsing = !isBypassEnabled,
+                )
+            }
+            .flowOn(bgContext)
 }
 
 /** Domain logic related to notification icons shown in the status bar. */
 class StatusBarNotificationIconsInteractor
 @Inject
 constructor(
+    @Background bgContext: CoroutineContext,
     iconsInteractor: NotificationIconsInteractor,
     settingsRepository: NotificationListenerSettingsRepository,
 ) {
     val statusBarNotifs: Flow<Set<ActiveNotificationModel>> =
-        settingsRepository.showSilentStatusIcons.flatMapLatest { showSilentIcons ->
-            iconsInteractor.filteredNotifSet(
-                forceShowHeadsUp = true,
-                showAmbient = false,
-                showLowPriority = showSilentIcons,
-                showDismissed = false,
-                showRepliedMessages = false,
-            )
-        }
+        settingsRepository.showSilentStatusIcons
+            .flatMapLatest { showSilentIcons ->
+                iconsInteractor.filteredNotifSet(
+                    forceShowHeadsUp = true,
+                    showAmbient = false,
+                    showLowPriority = showSilentIcons,
+                    showDismissed = false,
+                    showRepliedMessages = false,
+                )
+            }
+            .flowOn(bgContext)
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerAlwaysOnDisplayViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerAlwaysOnDisplayViewBinder.kt
index d7c29f1..38489f9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerAlwaysOnDisplayViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerAlwaysOnDisplayViewBinder.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.statusbar.notification.icon.ui.viewbinder
 
 import androidx.lifecycle.lifecycleScope
+import com.android.app.tracing.traceSection
 import com.android.systemui.common.ui.ConfigurationState
 import com.android.systemui.keyguard.ui.binder.KeyguardRootViewBinder
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
@@ -44,25 +45,27 @@
     private val viewStore: AlwaysOnDisplayNotificationIconViewStore,
 ) {
     fun bindWhileAttached(view: NotificationIconContainer): DisposableHandle {
-        return view.repeatWhenAttached {
-            lifecycleScope.launch {
-                launch {
-                    NotificationIconContainerViewBinder.bind(
-                        view = view,
-                        viewModel = viewModel,
-                        configuration = configuration,
-                        systemBarUtilsState = systemBarUtilsState,
-                        failureTracker = failureTracker,
-                        viewStore = viewStore,
-                    )
-                }
-                launch {
-                    KeyguardRootViewBinder.bindAodNotifIconVisibility(
-                        view = view,
-                        isVisible = keyguardRootViewModel.isNotifIconContainerVisible,
-                        configuration = configuration,
-                        screenOffAnimationController = screenOffAnimationController,
-                    )
+        return traceSection("NICAlwaysOnDisplay#bindWhileAttached") {
+            view.repeatWhenAttached {
+                lifecycleScope.launch {
+                    launch {
+                        NotificationIconContainerViewBinder.bind(
+                            view = view,
+                            viewModel = viewModel,
+                            configuration = configuration,
+                            systemBarUtilsState = systemBarUtilsState,
+                            failureTracker = failureTracker,
+                            viewStore = viewStore,
+                        )
+                    }
+                    launch {
+                        KeyguardRootViewBinder.bindAodNotifIconVisibility(
+                            view = view,
+                            isVisible = keyguardRootViewModel.isNotifIconContainerVisible,
+                            configuration = configuration,
+                            screenOffAnimationController = screenOffAnimationController,
+                        )
+                    }
                 }
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerStatusBarViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerStatusBarViewBinder.kt
index 8e089b1..3599f1f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerStatusBarViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerStatusBarViewBinder.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.statusbar.notification.icon.ui.viewbinder
 
 import androidx.lifecycle.lifecycleScope
+import com.android.app.tracing.traceSection
 import com.android.systemui.common.ui.ConfigurationState
 import com.android.systemui.lifecycle.repeatWhenAttached
 import com.android.systemui.statusbar.notification.collection.NotifCollection
@@ -39,16 +40,18 @@
     private val viewStore: StatusBarNotificationIconViewStore,
 ) {
     fun bindWhileAttached(view: NotificationIconContainer): DisposableHandle {
-        return view.repeatWhenAttached {
-            lifecycleScope.launch {
-                NotificationIconContainerViewBinder.bind(
-                    view = view,
-                    viewModel = viewModel,
-                    configuration = configuration,
-                    systemBarUtilsState = systemBarUtilsState,
-                    failureTracker = failureTracker,
-                    viewStore = viewStore,
-                )
+        return traceSection("NICStatusBar#bindWhileAttached") {
+            view.repeatWhenAttached {
+                lifecycleScope.launch {
+                    NotificationIconContainerViewBinder.bind(
+                        view = view,
+                        viewModel = viewModel,
+                        configuration = configuration,
+                        systemBarUtilsState = systemBarUtilsState,
+                        failureTracker = failureTracker,
+                        viewStore = viewStore,
+                    )
+                }
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt
index 8fe0022..ae77288 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt
@@ -24,6 +24,8 @@
 import androidx.annotation.ColorInt
 import androidx.collection.ArrayMap
 import androidx.lifecycle.lifecycleScope
+import com.android.app.tracing.traceSection
+import com.android.internal.R as RInternal
 import com.android.internal.statusbar.StatusBarIcon
 import com.android.internal.util.ContrastColorUtil
 import com.android.systemui.common.ui.ConfigurationState
@@ -48,8 +50,10 @@
 import kotlinx.coroutines.Job
 import kotlinx.coroutines.coroutineScope
 import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.mapNotNull
+import kotlinx.coroutines.flow.stateIn
 import kotlinx.coroutines.launch
 
 /** Binds a view-model to a [NotificationIconContainer]. */
@@ -65,8 +69,8 @@
     ): Unit = coroutineScope {
         launch {
             val contrastColorUtil = ContrastColorUtil.getInstance(view.context)
-            val iconColors: Flow<NotificationIconColors> =
-                viewModel.iconColors.mapNotNull { it.iconColors(view.viewBounds) }
+            val iconColors: StateFlow<NotificationIconColors> =
+                viewModel.iconColors.mapNotNull { it.iconColors(view.viewBounds) }.stateIn(this)
             viewModel.icons.bindIcons(
                 view,
                 configuration,
@@ -111,6 +115,14 @@
     ): Unit = coroutineScope {
         view.setUseIncreasedIconScale(true)
         launch {
+            // Collect state shared across all icon views, so that we are not duplicating collects
+            // for each individual icon.
+            val color: StateFlow<Int> =
+                configuration
+                    .getColorAttr(R.attr.wallpaperTextColor, DEFAULT_AOD_ICON_COLOR)
+                    .stateIn(this)
+            val tintAlpha = viewModel.tintAlpha.stateIn(this)
+            val animsEnabled = viewModel.areIconAnimationsEnabled.stateIn(this)
             viewModel.icons.bindIcons(
                 view,
                 configuration,
@@ -118,30 +130,16 @@
                 notifyBindingFailures = { failureTracker.aodFailures = it },
                 viewStore,
             ) { _, sbiv ->
-                viewModel.bindAodStatusBarIconView(sbiv, configuration)
+                coroutineScope {
+                    launch { StatusBarIconViewBinder.bindColor(sbiv, color) }
+                    launch { StatusBarIconViewBinder.bindTintAlpha(sbiv, tintAlpha) }
+                    launch { StatusBarIconViewBinder.bindAnimationsEnabled(sbiv, animsEnabled) }
+                }
             }
         }
         launch { viewModel.areContainerChangesAnimated.bindAnimationsEnabled(view) }
     }
 
-    private suspend fun NotificationIconContainerAlwaysOnDisplayViewModel.bindAodStatusBarIconView(
-        sbiv: StatusBarIconView,
-        configuration: ConfigurationState,
-    ) {
-        coroutineScope {
-            launch {
-                val color: Flow<Int> =
-                    configuration.getColorAttr(
-                        R.attr.wallpaperTextColor,
-                        DEFAULT_AOD_ICON_COLOR,
-                    )
-                StatusBarIconViewBinder.bindColor(sbiv, color)
-            }
-            launch { StatusBarIconViewBinder.bindTintAlpha(sbiv, tintAlpha) }
-            launch { StatusBarIconViewBinder.bindAnimationsEnabled(sbiv, areIconAnimationsEnabled) }
-        }
-    }
-
     /** Binds to [NotificationIconContainer.setAnimationsEnabled] */
     private suspend fun Flow<Boolean>.bindAnimationsEnabled(view: NotificationIconContainer) {
         collect(view::setAnimationsEnabled)
@@ -184,21 +182,20 @@
         notifyBindingFailures: (Collection<String>) -> Unit,
         viewStore: IconViewStore,
         bindIcon: suspend (iconKey: String, view: StatusBarIconView) -> Unit = { _, _ -> },
-    ) {
+    ): Unit = coroutineScope {
         val iconSizeFlow: Flow<Int> =
-            configuration.getDimensionPixelSize(
-                com.android.internal.R.dimen.status_bar_icon_size_sp,
-            )
+            configuration.getDimensionPixelSize(RInternal.dimen.status_bar_icon_size_sp)
         val iconHorizontalPaddingFlow: Flow<Int> =
             configuration.getDimensionPixelSize(R.dimen.status_bar_icon_horizontal_margin)
         val layoutParams: Flow<FrameLayout.LayoutParams> =
             combine(iconSizeFlow, iconHorizontalPaddingFlow, systemBarUtilsState.statusBarHeight) {
-                iconSize,
-                iconHPadding,
-                statusBarHeight,
-                ->
-                FrameLayout.LayoutParams(iconSize + 2 * iconHPadding, statusBarHeight)
-            }
+                    iconSize,
+                    iconHPadding,
+                    statusBarHeight,
+                    ->
+                    FrameLayout.LayoutParams(iconSize + 2 * iconHPadding, statusBarHeight)
+                }
+                .stateIn(this)
         try {
             bindIcons(view, layoutParams, notifyBindingFailures, viewStore, bindIcon)
         } finally {
@@ -217,7 +214,7 @@
         val failedBindings = mutableSetOf<String>()
         val boundViewsByNotifKey = ArrayMap<String, Pair<StatusBarIconView, Job>>()
         var prevIcons = NotificationIconsViewData()
-        collect { iconsData: NotificationIconsViewData ->
+        collectTracingEach("NotifIconContainer#bindIcons") { iconsData: NotificationIconsViewData ->
             val iconsDiff = NotificationIconsViewData.computeDifference(iconsData, prevIcons)
             prevIcons = iconsData
 
@@ -231,13 +228,15 @@
                 for (notifKey in iconsDiff.removed) {
                     failedBindings.remove(notifKey)
                     val (child, job) = boundViewsByNotifKey.remove(notifKey) ?: continue
-                    view.removeView(child)
-                    job.cancel()
+                    traceSection("removeIcon") {
+                        view.removeView(child)
+                        job.cancel()
+                    }
                 }
 
                 // Add and bind.
                 val toAdd: Sequence<String> = iconsDiff.added.asSequence() + failedBindings.toList()
-                for ((idx, notifKey) in toAdd.withIndex()) {
+                for (notifKey in toAdd) {
                     // Lookup the StatusBarIconView from the store.
                     val sbiv = viewStore.iconView(notifKey)
                     if (sbiv == null) {
@@ -245,27 +244,32 @@
                         continue
                     }
                     failedBindings.remove(notifKey)
-                    (sbiv.parent as? ViewGroup)?.run {
-                        if (this !== view) {
-                            Log.wtf(TAG, "StatusBarIconView($notifKey) has an unexpected parent")
+                    traceSection("addIcon") {
+                        (sbiv.parent as? ViewGroup)?.run {
+                            if (this !== view) {
+                                Log.wtf(
+                                    TAG,
+                                    "StatusBarIconView($notifKey) has an unexpected parent",
+                                )
+                            }
+                            // If the container was re-inflated and re-bound, then SBIVs might still
+                            // be attached to the prior view.
+                            removeView(sbiv)
+                            // The view might still be transiently added if it was just removed and
+                            // added again.
+                            removeTransientView(sbiv)
                         }
-                        // If the container was re-inflated and re-bound, then SBIVs might still be
-                        // attached to the prior view.
-                        removeView(sbiv)
-                        // The view might still be transiently added if it was just removed and
-                        // added again.
-                        removeTransientView(sbiv)
+                        view.addView(sbiv)
+                        boundViewsByNotifKey.remove(notifKey)?.second?.cancel()
+                        boundViewsByNotifKey[notifKey] =
+                            Pair(
+                                sbiv,
+                                launch {
+                                    launch { layoutParams.collect { sbiv.layoutParams = it } }
+                                    bindIcon(notifKey, sbiv)
+                                },
+                            )
                     }
-                    view.addView(sbiv, idx)
-                    boundViewsByNotifKey.remove(notifKey)?.second?.cancel()
-                    boundViewsByNotifKey[notifKey] =
-                        Pair(
-                            sbiv,
-                            launch {
-                                launch { layoutParams.collect { sbiv.layoutParams = it } }
-                                bindIcon(notifKey, sbiv)
-                            },
-                        )
                 }
 
                 // Set the maximum number of icons to show in the container. Any icons over this
@@ -273,10 +277,10 @@
                 val maxIconsAmount: Int =
                     when (iconsData.limitType) {
                         LimitType.MaximumIndex -> {
-                            iconsData.visibleIcons
-                                .asSequence()
-                                .take(iconsData.iconLimit)
-                                .count { info -> info.notifKey in boundViewsByNotifKey }
+                            iconsData.visibleIcons.asSequence().take(iconsData.iconLimit).count {
+                                info ->
+                                info.notifKey in boundViewsByNotifKey
+                            }
                         }
                         LimitType.MaximumAmount -> {
                             iconsData.iconLimit
@@ -289,19 +293,21 @@
 
                 // Re-sort notification icons
                 view.changeViewPositions {
-                    val expectedChildren: List<StatusBarIconView> =
-                        iconsData.visibleIcons.mapNotNull {
-                            boundViewsByNotifKey[it.notifKey]?.first
+                    traceSection("re-sort") {
+                        val expectedChildren: List<StatusBarIconView> =
+                            iconsData.visibleIcons.mapNotNull {
+                                boundViewsByNotifKey[it.notifKey]?.first
+                            }
+                        val childCount = view.childCount
+                        for (i in 0 until childCount) {
+                            val actual = view.getChildAt(i)
+                            val expected = expectedChildren[i]
+                            if (actual === expected) {
+                                continue
+                            }
+                            view.removeView(expected)
+                            view.addView(expected, i)
                         }
-                    val childCount = view.childCount
-                    for (i in 0 until childCount) {
-                        val actual = view.getChildAt(i)
-                        val expected = expectedChildren[i]
-                        if (actual === expected) {
-                            continue
-                        }
-                        view.removeView(expected)
-                        view.addView(expected, i)
                     }
                 }
             }
@@ -362,3 +368,7 @@
             /* bottom = */ top + height,
         )
     }
+
+private suspend fun <T> Flow<T>.collectTracingEach(tag: String, collector: (T) -> Unit) {
+    collect { traceSection(tag) { collector(it) } }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModel.kt
index 9cb60d5..d903f06 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModel.kt
@@ -17,6 +17,7 @@
 
 import android.content.res.Resources
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
@@ -25,8 +26,10 @@
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
 import com.android.systemui.statusbar.notification.icon.domain.interactor.AlwaysOnDisplayNotificationIconsInteractor
 import javax.inject.Inject
+import kotlin.coroutines.CoroutineContext
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.flowOn
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.onStart
 
@@ -35,46 +38,57 @@
 class NotificationIconContainerAlwaysOnDisplayViewModel
 @Inject
 constructor(
+    @Background bgContext: CoroutineContext,
     iconsInteractor: AlwaysOnDisplayNotificationIconsInteractor,
     keyguardInteractor: KeyguardInteractor,
     keyguardTransitionInteractor: KeyguardTransitionInteractor,
     @Main resources: Resources,
     shadeInteractor: ShadeInteractor,
 ) {
-
     private val maxIcons = resources.getInteger(R.integer.max_notif_icons_on_aod)
 
     /** Are changes to the icon container animated? */
     val areContainerChangesAnimated: Flow<Boolean> =
         combine(
-            shadeInteractor.isShadeTouchable,
-            keyguardInteractor.isKeyguardVisible,
-        ) { panelTouchesEnabled, isKeyguardVisible ->
-            panelTouchesEnabled && isKeyguardVisible
-        }
+                shadeInteractor.isShadeTouchable,
+                keyguardInteractor.isKeyguardVisible,
+            ) { panelTouchesEnabled, isKeyguardVisible ->
+                panelTouchesEnabled && isKeyguardVisible
+            }
+            .flowOn(bgContext)
 
     /** Amount of a "white" tint to be applied to the icons. */
     val tintAlpha: Flow<Float> =
         combine(
-            keyguardTransitionInteractor.transitionValue(KeyguardState.AOD).onStart { emit(0f) },
-            keyguardTransitionInteractor.transitionValue(KeyguardState.DOZING).onStart { emit(0f) },
-        ) { aodAmt, dozeAmt ->
-            aodAmt + dozeAmt // If transitioning between them, they should sum to 1f
-        }
+                keyguardTransitionInteractor.transitionValue(KeyguardState.AOD).onStart {
+                    emit(0f)
+                },
+                keyguardTransitionInteractor.transitionValue(KeyguardState.DOZING).onStart {
+                    emit(0f)
+                },
+            ) { aodAmt, dozeAmt ->
+                aodAmt + dozeAmt // If transitioning between them, they should sum to 1f
+            }
+            .flowOn(bgContext)
 
     /** Are notification icons animated (ex: animated gif)? */
     val areIconAnimationsEnabled: Flow<Boolean> =
-        keyguardTransitionInteractor.isFinishedInStateWhere {
-            // Don't animate icons when we're on AOD / dozing
-            it != KeyguardState.AOD && it != KeyguardState.DOZING
-        }
+        keyguardTransitionInteractor
+            .isFinishedInStateWhere {
+                // Don't animate icons when we're on AOD / dozing
+                it != KeyguardState.AOD && it != KeyguardState.DOZING
+            }
+            .flowOn(bgContext)
+            .onStart { emit(true) }
 
     /** [NotificationIconsViewData] indicating which icons to display in the view. */
     val icons: Flow<NotificationIconsViewData> =
-        iconsInteractor.aodNotifs.map { entries ->
-            NotificationIconsViewData(
-                visibleIcons = entries.mapNotNull { it.toIconInfo(it.aodIcon) },
-                iconLimit = maxIcons,
-            )
-        }
+        iconsInteractor.aodNotifs
+            .map { entries ->
+                NotificationIconsViewData(
+                    visibleIcons = entries.mapNotNull { it.toIconInfo(it.aodIcon) },
+                    iconLimit = maxIcons,
+                )
+            }
+            .flowOn(bgContext)
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerShelfViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerShelfViewModel.kt
index 8484fdc..3574828 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerShelfViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerShelfViewModel.kt
@@ -15,38 +15,45 @@
  */
 package com.android.systemui.statusbar.notification.icon.ui.viewmodel
 
+import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.statusbar.notification.icon.domain.interactor.NotificationIconsInteractor
 import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconsViewData.LimitType
 import javax.inject.Inject
+import kotlin.coroutines.CoroutineContext
 import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flowOn
 import kotlinx.coroutines.flow.map
 
 /** View-model for the overflow row of notification icons displayed in the notification shade. */
 class NotificationIconContainerShelfViewModel
 @Inject
 constructor(
+    @Background bgContext: CoroutineContext,
     interactor: NotificationIconsInteractor,
 ) {
     /** [NotificationIconsViewData] indicating which icons to display in the view. */
     val icons: Flow<NotificationIconsViewData> =
-        interactor.filteredNotifSet().map { entries ->
-            var firstAmbient = 0
-            val visibleKeys = buildList {
-                for (entry in entries) {
-                    entry.toIconInfo(entry.shelfIcon)?.let { info ->
-                        add(info)
-                        // NOTE: we assume that all ambient notifications will be at the end of the
-                        // list
-                        if (!entry.isAmbient) {
-                            firstAmbient++
+        interactor
+            .filteredNotifSet()
+            .map { entries ->
+                var firstAmbient = 0
+                val visibleKeys = buildList {
+                    for (entry in entries) {
+                        entry.toIconInfo(entry.shelfIcon)?.let { info ->
+                            add(info)
+                            // NOTE: we assume that all ambient notifications will be at the end of
+                            // the list
+                            if (!entry.isAmbient) {
+                                firstAmbient++
+                            }
                         }
                     }
                 }
+                NotificationIconsViewData(
+                    visibleKeys,
+                    iconLimit = firstAmbient,
+                    limitType = LimitType.MaximumIndex,
+                )
             }
-            NotificationIconsViewData(
-                visibleKeys,
-                iconLimit = firstAmbient,
-                limitType = LimitType.MaximumIndex,
-            )
-        }
+            .flowOn(bgContext)
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModel.kt
index d00cd1f..38921c2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModel.kt
@@ -17,12 +17,12 @@
 
 import android.content.res.Resources
 import android.graphics.Rect
+import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.plugins.DarkIconDispatcher
 import com.android.systemui.res.R
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
-import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
 import com.android.systemui.statusbar.notification.domain.interactor.HeadsUpNotificationIconInteractor
 import com.android.systemui.statusbar.notification.icon.domain.interactor.StatusBarNotificationIconsInteractor
 import com.android.systemui.statusbar.phone.domain.interactor.DarkIconInteractor
@@ -32,21 +32,23 @@
 import com.android.systemui.util.ui.AnimatedValue
 import com.android.systemui.util.ui.toAnimatedValueFlow
 import javax.inject.Inject
+import kotlin.coroutines.CoroutineContext
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.flowOn
 import kotlinx.coroutines.flow.map
 
 /** View-model for the row of notification icons displayed in the status bar, */
 class NotificationIconContainerStatusBarViewModel
 @Inject
 constructor(
+    @Background bgContext: CoroutineContext,
     darkIconInteractor: DarkIconInteractor,
     iconsInteractor: StatusBarNotificationIconsInteractor,
     headsUpIconInteractor: HeadsUpNotificationIconInteractor,
     keyguardInteractor: KeyguardInteractor,
-    notificationsInteractor: ActiveNotificationsInteractor,
     @Main resources: Resources,
     shadeInteractor: ShadeInteractor,
 ) {
@@ -56,37 +58,36 @@
     /** Are changes to the icon container animated? */
     val animationsEnabled: Flow<Boolean> =
         combine(
-            shadeInteractor.isShadeTouchable,
-            keyguardInteractor.isKeyguardShowing,
-        ) { panelTouchesEnabled, isKeyguardShowing ->
-            panelTouchesEnabled && !isKeyguardShowing
-        }
+                shadeInteractor.isShadeTouchable,
+                keyguardInteractor.isKeyguardShowing,
+            ) { panelTouchesEnabled, isKeyguardShowing ->
+                panelTouchesEnabled && !isKeyguardShowing
+            }
+            .flowOn(bgContext)
 
     /** The colors with which to display the notification icons. */
     val iconColors: Flow<NotificationIconColorLookup> =
-        combine(
-            darkIconInteractor.tintAreas,
-            darkIconInteractor.tintColor,
-            // Included so that tints are re-applied after entries are changed.
-            notificationsInteractor.topLevelRepresentativeNotifications,
-        ) { areas, tint, _ ->
-            NotificationIconColorLookup { viewBounds: Rect ->
-                if (DarkIconDispatcher.isInAreas(areas, viewBounds)) {
-                    IconColorsImpl(tint, areas)
-                } else {
-                    null
+        combine(darkIconInteractor.tintAreas, darkIconInteractor.tintColor) { areas, tint ->
+                NotificationIconColorLookup { viewBounds: Rect ->
+                    if (DarkIconDispatcher.isInAreas(areas, viewBounds)) {
+                        IconColorsImpl(tint, areas)
+                    } else {
+                        null
+                    }
                 }
             }
-        }
+            .flowOn(bgContext)
 
     /** [NotificationIconsViewData] indicating which icons to display in the view. */
     val icons: Flow<NotificationIconsViewData> =
-        iconsInteractor.statusBarNotifs.map { entries ->
-            NotificationIconsViewData(
-                visibleIcons = entries.mapNotNull { it.toIconInfo(it.statusBarIcon) },
-                iconLimit = maxIcons,
-            )
-        }
+        iconsInteractor.statusBarNotifs
+            .map { entries ->
+                NotificationIconsViewData(
+                    visibleIcons = entries.mapNotNull { it.toIconInfo(it.statusBarIcon) },
+                    iconLimit = maxIcons,
+                )
+            }
+            .flowOn(bgContext)
 
     /** An Icon to show "isolated" in the IconContainer. */
     val isolatedIcon: Flow<AnimatedValue<NotificationIconInfo?>> =
@@ -97,6 +98,7 @@
                 }
             }
             .distinctUntilChanged()
+            .flowOn(bgContext)
             .pairwise(initialValue = null)
             .sample(shadeInteractor.shadeExpansion) { (prev, iconInfo), shadeExpansion ->
                 val animate =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 31ca106..e200e65 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -273,12 +273,15 @@
     private final RefactorFlag mInlineReplyAnimation =
             RefactorFlag.forView(Flags.NOTIFICATION_INLINE_REPLY_ANIMATION);
 
-    private static final boolean mSimulateSlowMeasure = Compile.IS_DEBUG && RefactorFlag.forView(
-            Flags.ENABLE_NOTIFICATIONS_SIMULATE_SLOW_MEASURE).isEnabled();
+    private static boolean shouldSimulateSlowMeasure() {
+        return Compile.IS_DEBUG && RefactorFlag.forView(
+                Flags.ENABLE_NOTIFICATIONS_SIMULATE_SLOW_MEASURE).isEnabled();
+    }
+
     private static final String SLOW_MEASURE_SIMULATE_DELAY_PROPERTY =
             "persist.notifications.extra_measure_delay_ms";
-    private static final int SLOW_MEASURE_SIMULATE_DELAY_MS = mSimulateSlowMeasure ?
-            SystemProperties.getInt(SLOW_MEASURE_SIMULATE_DELAY_PROPERTY, 150) : 0;
+    private static final int SLOW_MEASURE_SIMULATE_DELAY_MS =
+            SystemProperties.getInt(SLOW_MEASURE_SIMULATE_DELAY_PROPERTY, 150);
 
     // Listener will be called when receiving a long click event.
     // Use #setLongPressPosition to optionally assign positional data with the long press.
@@ -1886,7 +1889,7 @@
         }
         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
 
-        if (Compile.IS_DEBUG && mSimulateSlowMeasure) {
+        if (shouldSimulateSlowMeasure()) {
             simulateExtraMeasureDelay();
         }
         Trace.endSection();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
index f6431a2..7ea9b14 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
@@ -32,6 +32,8 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
+import com.android.internal.util.ContrastColorUtil;
+import com.android.settingslib.Utils;
 import com.android.systemui.Dumpable;
 import com.android.systemui.res.R;
 
@@ -58,11 +60,19 @@
     private int mExpandAnimationWidth = -1;
     private int mExpandAnimationHeight = -1;
     private int mDrawableAlpha = 255;
+    private final ColorStateList mLightColoredStatefulColors;
+    private final ColorStateList mDarkColoredStatefulColors;
+    private final int mNormalColor;
 
     public NotificationBackgroundView(Context context, AttributeSet attrs) {
         super(context, attrs);
-        mDontModifyCorners = getResources().getBoolean(
-                R.bool.config_clipNotificationsToOutline);
+        mDontModifyCorners = getResources().getBoolean(R.bool.config_clipNotificationsToOutline);
+        mLightColoredStatefulColors = getResources().getColorStateList(
+                R.color.notification_state_color_light);
+        mDarkColoredStatefulColors = getResources().getColorStateList(
+                R.color.notification_state_color_dark);
+        mNormalColor = Utils.getColorAttrDefaultColor(mContext,
+                com.android.internal.R.attr.materialColorSurfaceContainerHigh);
     }
 
     @Override
@@ -122,6 +132,18 @@
     }
 
     /**
+     * Stateful colors are colors that will overlay on the notification original color when one of
+     * hover states, pressed states or other similar states is activated.
+     */
+    private void setStatefulColors() {
+        if (mTintColor != mNormalColor) {
+            ColorStateList newColor = ContrastColorUtil.isColorDark(mTintColor)
+                    ? mDarkColoredStatefulColors : mLightColoredStatefulColors;
+            ((GradientDrawable) getStatefulBackgroundLayer().mutate()).setColor(newColor);
+        }
+    }
+
+    /**
      * Sets a background drawable. As we need to change our bounds independently of layout, we need
      * the notion of a background independently of the regular View background..
      */
@@ -149,21 +171,20 @@
         setCustomBackground(d);
     }
 
-    public void setTint(int tintColor) {
-        if (tintColor != 0) {
-            ColorStateList stateList = new ColorStateList(new int[][]{
-                    new int[]{com.android.internal.R.attr.state_pressed},
-                    new int[]{com.android.internal.R.attr.state_hovered},
-                    new int[]{}},
+    private Drawable getBaseBackgroundLayer() {
+        return ((LayerDrawable) mBackground).getDrawable(0);
+    }
 
-                    new int[]{tintColor, 0, tintColor}
-            );
-            mBackground.setTintMode(PorterDuff.Mode.SRC_ATOP);
-            mBackground.setTintList(stateList);
-        } else {
-            mBackground.setTintList(null);
-        }
+    private Drawable getStatefulBackgroundLayer() {
+        return ((LayerDrawable) mBackground).getDrawable(1);
+    }
+
+    public void setTint(int tintColor) {
+        Drawable baseLayer = getBaseBackgroundLayer();
+        baseLayer.mutate().setTintMode(PorterDuff.Mode.SRC_ATOP);
+        baseLayer.setTint(tintColor);
         mTintColor = tintColor;
+        setStatefulColors();
         invalidate();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractor.kt
index 4b89615..9fdd0bc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractor.kt
@@ -18,7 +18,7 @@
 
 import android.os.PowerManager
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepository
+import com.android.systemui.deviceentry.data.repository.DeviceEntryFaceAuthRepository
 import com.android.systemui.keyguard.data.repository.KeyguardRepository
 import com.android.systemui.power.domain.interactor.PowerInteractor
 import com.android.systemui.statusbar.LockscreenShadeTransitionController
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt
index 5ab4d4e..819527e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar.notification.shelf.ui.viewbinder
 
+import com.android.app.tracing.traceSection
 import com.android.systemui.plugins.FalsingManager
 import com.android.systemui.statusbar.NotificationShelf
 import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerShelfViewBinder
@@ -38,10 +39,12 @@
     ): Unit = coroutineScope {
         ActivatableNotificationViewBinder.bind(viewModel, shelf, falsingManager)
         shelf.apply {
-            if (NotificationIconContainerRefactor.isEnabled) {
-                launch { nicBinder.bind(shelfIcons) }
-            } else {
-                notificationIconAreaController.setShelfIcons(shelfIcons)
+            traceSection("NotifShelf#bindShelfIcons") {
+                if (NotificationIconContainerRefactor.isEnabled) {
+                    launch { nicBinder.bind(shelfIcons) }
+                } else {
+                    notificationIconAreaController.setShelfIcons(shelfIcons)
+                }
             }
             launch {
                 viewModel.canModifyColorOfNotifications.collect(::setCanModifyColorOfNotifications)
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 0f640c9..ea414d2 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
@@ -4455,9 +4455,13 @@
 
         mSectionsManager.setHeaderForegroundColors(onSurface, onSurfaceVariant);
 
-        mFooterView.updateColors();
+        if (mFooterView != null) {
+            mFooterView.updateColors();
+        }
 
-        mEmptyShadeView.setTextColors(onSurface, onSurfaceVariant);
+        if (mEmptyShadeView != null) {
+            mEmptyShadeView.setTextColors(onSurface, onSurfaceVariant);
+        }
     }
 
     void goToFullShade(long delay) {
@@ -4865,10 +4869,6 @@
 
     public void generateHeadsUpAnimation(NotificationEntry entry, boolean isHeadsUp) {
         ExpandableNotificationRow row = entry.getHeadsUpAnimationView();
-        generateHeadsUpAnimation(row, isHeadsUp);
-    }
-
-    public void generateHeadsUpAnimation(ExpandableNotificationRow row, boolean isHeadsUp) {
         final boolean add = mAnimationsEnabled && (isHeadsUp || mHeadsUpGoingAwayAnimationsAllowed);
         if (SPEW) {
             Log.v(TAG, "generateHeadsUpAnimation:"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index 6f5058c..a30c294 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -69,6 +69,7 @@
 import com.android.systemui.flags.FeatureFlagsClassic;
 import com.android.systemui.flags.Flags;
 import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository;
+import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl;
 import com.android.systemui.keyguard.shared.model.KeyguardState;
 import com.android.systemui.keyguard.shared.model.TransitionStep;
 import com.android.systemui.media.controls.ui.KeyguardMediaController;
@@ -366,8 +367,7 @@
 
                 @Override
                 public void onStatePostChange() {
-                    mView.updateSensitiveness(mStatusBarStateController.goingToFullShade(),
-                            mLockscreenUserManager.isAnyProfilePublicMode());
+                    updateSensitivenessWithAnimation(mStatusBarStateController.goingToFullShade());
                     mView.onStatePostChange(mStatusBarStateController.fromShadeLocked());
                     if (!FooterViewRefactor.isEnabled()) {
                         updateImportantForAccessibility();
@@ -378,7 +378,7 @@
     private final UserChangedListener mLockscreenUserChangeListener = new UserChangedListener() {
         @Override
         public void onUserChanged(int userId) {
-            mView.updateSensitiveness(false, mLockscreenUserManager.isAnyProfilePublicMode());
+            updateSensitivenessWithAnimation(false);
             mHistoryEnabled = null;
             updateFooter();
         }
@@ -388,7 +388,11 @@
      * Recalculate sensitiveness without animation; called when waking up while keyguard occluded.
      */
     public void updateSensitivenessForOccludedWakeup() {
-        mView.updateSensitiveness(false, mLockscreenUserManager.isAnyProfilePublicMode());
+        updateSensitivenessWithAnimation(false);
+    }
+
+    private void updateSensitivenessWithAnimation(boolean animate) {
+        mView.updateSensitiveness(animate, mLockscreenUserManager.isAnyProfilePublicMode());
     }
 
     /**
@@ -1492,14 +1496,10 @@
         return mView.getFirstChildNotGone();
     }
 
-    private void generateHeadsUpAnimation(NotificationEntry entry, boolean isHeadsUp) {
+    public void generateHeadsUpAnimation(NotificationEntry entry, boolean isHeadsUp) {
         mView.generateHeadsUpAnimation(entry, isHeadsUp);
     }
 
-    public void generateHeadsUpAnimation(ExpandableNotificationRow row, boolean isHeadsUp) {
-        mView.generateHeadsUpAnimation(row, isHeadsUp);
-    }
-
     public void setMaxTopPadding(int padding) {
         mView.setMaxTopPadding(padding);
     }
@@ -1958,18 +1958,34 @@
                     mView.dispatchDownEventToScroller(ev);
                 }
             }
-            boolean scrollerWantsIt = false;
-            if (mLongPressedView == null && mView.isExpanded() && !mSwipeHelper.isSwiping()
-                    && !expandingNotification && !mView.getDisallowScrollingInThisMotion()) {
-                scrollerWantsIt = mView.onScrollTouch(ev);
-            }
             boolean horizontalSwipeWantsIt = false;
-            if (mLongPressedView == null && !mView.isBeingDragged()
-                    && !expandingNotification
-                    && !mView.getExpandedInThisMotion()
-                    && !onlyScrollingInThisMotion
-                    && !mView.getDisallowDismissInThisMotion()) {
-                horizontalSwipeWantsIt = mSwipeHelper.onTouchEvent(ev);
+            boolean scrollerWantsIt = false;
+            if (KeyguardShadeMigrationNssl.isEnabled()) {
+                // Reverse the order relative to the else statement. onScrollTouch will reset on an
+                // UP event, causing horizontalSwipeWantsIt to be set to true on vertical swipes.
+                if (mLongPressedView == null && !mView.isBeingDragged()
+                        && !expandingNotification
+                        && !mView.getExpandedInThisMotion()
+                        && !onlyScrollingInThisMotion
+                        && !mView.getDisallowDismissInThisMotion()) {
+                    horizontalSwipeWantsIt = mSwipeHelper.onTouchEvent(ev);
+                }
+                if (mLongPressedView == null && mView.isExpanded() && !mSwipeHelper.isSwiping()
+                        && !expandingNotification && !mView.getDisallowScrollingInThisMotion()) {
+                    scrollerWantsIt = mView.onScrollTouch(ev);
+                }
+            } else {
+                if (mLongPressedView == null && mView.isExpanded() && !mSwipeHelper.isSwiping()
+                        && !expandingNotification && !mView.getDisallowScrollingInThisMotion()) {
+                    scrollerWantsIt = mView.onScrollTouch(ev);
+                }
+                if (mLongPressedView == null && !mView.isBeingDragged()
+                        && !expandingNotification
+                        && !mView.getExpandedInThisMotion()
+                        && !onlyScrollingInThisMotion
+                        && !mView.getDisallowDismissInThisMotion()) {
+                    horizontalSwipeWantsIt = mSwipeHelper.onTouchEvent(ev);
+                }
             }
 
             // Check if we need to clear any snooze leavebehinds
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt
index 625fdc1..4b8fb1e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt
@@ -18,12 +18,15 @@
 package com.android.systemui.statusbar.notification.stack.domain.interactor
 
 import android.content.Context
+import com.android.systemui.Flags.centralizedStatusBarDimensRefactor
 import com.android.systemui.common.ui.data.repository.ConfigurationRepository
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.res.R
+import com.android.systemui.shade.LargeScreenHeaderHelper
 import com.android.systemui.statusbar.policy.SplitShadeStateController
+import dagger.Lazy
 import javax.inject.Inject
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableSharedFlow
@@ -45,6 +48,7 @@
     private val splitShadeStateController: SplitShadeStateController,
     keyguardInteractor: KeyguardInteractor,
     deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor,
+    largeScreenHeaderHelperLazy: Lazy<LargeScreenHeaderHelper>,
 ) {
 
     private val _topPosition = MutableStateFlow(0f)
@@ -72,7 +76,11 @@
                             getDimensionPixelSize(R.dimen.notification_panel_margin_bottom),
                         marginTop = getDimensionPixelSize(R.dimen.notification_panel_margin_top),
                         marginTopLargeScreen =
-                            getDimensionPixelSize(R.dimen.large_screen_shade_header_height),
+                            if (centralizedStatusBarDimensRefactor()) {
+                                largeScreenHeaderHelperLazy.get().getLargeScreenHeaderHeight()
+                            } else {
+                                getDimensionPixelSize(R.dimen.large_screen_shade_header_height)
+                            },
                         keyguardSplitShadeTopMargin =
                             getDimensionPixelSize(R.dimen.keyguard_split_shade_top_margin),
                     )
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
index eff91e5..5ee38be 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
@@ -25,6 +25,8 @@
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.StatusBarState.SHADE_LOCKED
+import com.android.systemui.keyguard.shared.model.TransitionState.RUNNING
+import com.android.systemui.keyguard.shared.model.TransitionState.STARTED
 import com.android.systemui.keyguard.ui.viewmodel.LockscreenToOccludedTransitionViewModel
 import com.android.systemui.keyguard.ui.viewmodel.OccludedToLockscreenTransitionViewModel
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
@@ -71,6 +73,20 @@
             KeyguardState.PRIMARY_BOUNCER
         )
 
+    private val lockscreenToOccludedRunning =
+        keyguardTransitionInteractor
+            .transition(KeyguardState.LOCKSCREEN, KeyguardState.OCCLUDED)
+            .map { it.transitionState == STARTED || it.transitionState == RUNNING }
+            .distinctUntilChanged()
+            .onStart { emit(false) }
+
+    private val occludedToLockscreenRunning =
+        keyguardTransitionInteractor
+            .transition(KeyguardState.OCCLUDED, KeyguardState.LOCKSCREEN)
+            .map { it.transitionState == STARTED || it.transitionState == RUNNING }
+            .distinctUntilChanged()
+            .onStart { emit(false) }
+
     val shadeCollapseFadeInComplete = MutableStateFlow(false)
 
     val configurationBasedDimensions: Flow<ConfigurationBasedDimensions> =
@@ -122,7 +138,11 @@
             ) { isKeyguard, isShadeVisible, qsExpansion ->
                 isKeyguard && !(isShadeVisible || qsExpansion)
             }
-            .distinctUntilChanged()
+            .stateIn(
+                scope = applicationScope,
+                started = SharingStarted.Eagerly,
+                initialValue = false,
+            )
 
     /** Fade in only for use after the shade collapses */
     val shadeCollpaseFadeIn: Flow<Boolean> =
@@ -182,26 +202,37 @@
             )
 
     val alpha: Flow<Float> =
-        isOnLockscreenWithoutShade
-            .flatMapLatest { isOnLockscreenWithoutShade ->
-                combineTransform(
-                    merge(
-                        occludedToLockscreenTransitionViewModel.lockscreenAlpha,
-                        lockscreenToOccludedTransitionViewModel.lockscreenAlpha,
-                        keyguardInteractor.keyguardAlpha,
-                    ),
-                    shadeCollpaseFadeIn,
-                ) { alpha, shadeCollpaseFadeIn ->
-                    if (isOnLockscreenWithoutShade) {
-                        if (!shadeCollpaseFadeIn) {
-                            emit(alpha)
-                        }
+        // Due to issues with the legacy shade, some shade expansion events are sent incorrectly,
+        // such as when the shade resets. This can happen while the LOCKSCREEN<->OCCLUDED transition
+        // is running. Therefore use a series of flatmaps to prevent unwanted interruptions while
+        // those transitions are in progress. Without this, the alpha value will produce a visible
+        // flicker.
+        lockscreenToOccludedRunning.flatMapLatest { isLockscreenToOccludedRunning ->
+            if (isLockscreenToOccludedRunning) {
+                lockscreenToOccludedTransitionViewModel.lockscreenAlpha
+            } else {
+                occludedToLockscreenRunning.flatMapLatest { isOccludedToLockscreenRunning ->
+                    if (isOccludedToLockscreenRunning) {
+                        occludedToLockscreenTransitionViewModel.lockscreenAlpha.onStart { emit(0f) }
                     } else {
-                        emit(1f)
+                        isOnLockscreenWithoutShade.flatMapLatest { isOnLockscreenWithoutShade ->
+                            combineTransform(
+                                keyguardInteractor.keyguardAlpha,
+                                shadeCollpaseFadeIn,
+                            ) { alpha, shadeCollpaseFadeIn ->
+                                if (isOnLockscreenWithoutShade) {
+                                    if (!shadeCollpaseFadeIn) {
+                                        emit(alpha)
+                                    }
+                                } else {
+                                    emit(1f)
+                                }
+                            }
+                        }
                     }
                 }
             }
-            .distinctUntilChanged()
+        }
 
     /**
      * Under certain scenarios, such as swiping up on the lockscreen, the container will need to be
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 57d49b2..6e3aabf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -31,6 +31,7 @@
 import static com.android.systemui.charging.WirelessChargingAnimation.UNKNOWN_BATTERY_LEVEL;
 import static com.android.systemui.statusbar.NotificationLockscreenUserManager.PERMISSION_SELF;
 import static com.android.systemui.statusbar.StatusBarState.SHADE;
+import static com.android.systemui.Flags.predictiveBackSysui;
 
 import android.annotation.Nullable;
 import android.app.ActivityOptions;
@@ -836,7 +837,7 @@
         mLightRevealScrim = lightRevealScrim;
 
         // Based on teamfood flag, turn predictive back dispatch on at runtime.
-        if (mFeatureFlags.isEnabled(Flags.WM_ENABLE_PREDICTIVE_BACK_SYSUI)) {
+        if (predictiveBackSysui()) {
             mContext.getApplicationInfo().setEnableOnBackInvokedCallback(true);
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ComponentSystemUIDialog.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ComponentSystemUIDialog.kt
index 38a6d39..13d7924 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ComponentSystemUIDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ComponentSystemUIDialog.kt
@@ -34,7 +34,6 @@
 import androidx.savedstate.setViewTreeSavedStateRegistryOwner
 import com.android.systemui.animation.DialogLaunchAnimator
 import com.android.systemui.broadcast.BroadcastDispatcher
-import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.model.SysUiState
 
 /**
@@ -53,7 +52,6 @@
     context: Context,
     theme: Int,
     dismissOnDeviceLock: Boolean,
-    featureFlags: FeatureFlags,
     dialogManager: SystemUIDialogManager,
     sysUiState: SysUiState,
     broadcastDispatcher: BroadcastDispatcher,
@@ -63,7 +61,6 @@
         context,
         theme,
         dismissOnDeviceLock,
-        featureFlags,
         dialogManager,
         sysUiState,
         broadcastDispatcher,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt
index 97cb45a..6e8ad2e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt
@@ -21,13 +21,17 @@
 import android.os.LocaleList
 import android.view.View.LAYOUT_DIRECTION_RTL
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener
 import javax.inject.Inject
 
 @SysUISingleton
-class ConfigurationControllerImpl @Inject constructor(context: Context) : ConfigurationController {
+class ConfigurationControllerImpl @Inject constructor(
+        @Application context: Context,
+        ) : ConfigurationController {
 
-    private val listeners: MutableList<ConfigurationController.ConfigurationListener> = ArrayList()
+    private val listeners: MutableList<ConfigurationListener> = ArrayList()
     private val lastConfig = Configuration()
     private var density: Int = 0
     private var smallestScreenWidth: Int = 0
@@ -143,14 +147,12 @@
         }
     }
 
-
-
-    override fun addCallback(listener: ConfigurationController.ConfigurationListener) {
+    override fun addCallback(listener: ConfigurationListener) {
         listeners.add(listener)
         listener.onDensityOrFontScaleChanged()
     }
 
-    override fun removeCallback(listener: ConfigurationController.ConfigurationListener) {
+    override fun removeCallback(listener: ConfigurationListener) {
         listeners.remove(listener)
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerModule.kt
new file mode 100644
index 0000000..fc3456a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerModule.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone
+
+import com.android.systemui.CoreStartable
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.ClassKey
+import dagger.multibindings.IntoMap
+
+@Module
+interface ConfigurationControllerModule {
+
+    /** Starts [ConfigurationControllerStartable] */
+    @Binds
+    @IntoMap
+    @ClassKey(ConfigurationControllerStartable::class)
+    fun bindConfigControllerStartable(impl: ConfigurationControllerStartable): CoreStartable
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerStartable.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerStartable.kt
new file mode 100644
index 0000000..90ebaf2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerStartable.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone
+
+import com.android.systemui.CoreStartable
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener
+import javax.inject.Inject
+
+@SysUISingleton
+class ConfigurationControllerStartable
+@Inject
+constructor(
+    private val configurationController: ConfigurationController,
+    private val listeners: Set<@JvmSuppressWildcards ConfigurationListener>
+) : CoreStartable {
+    override fun start() {
+        listeners.forEach { configurationController.addCallback(it) }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
index 600d4af..45005cb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
@@ -55,11 +55,12 @@
 import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
 import com.android.systemui.util.Assert;
 
+import dagger.Lazy;
+
 import java.util.ArrayList;
 
 import javax.inject.Inject;
 
-import dagger.Lazy;
 import kotlinx.coroutines.ExperimentalCoroutinesApi;
 
 /**
@@ -175,6 +176,16 @@
         }
     }
 
+    /**
+     * Notify the registered callback about SPFS fingerprint acquisition started event.
+     */
+    public void fireSideFpsAcquisitionStarted() {
+        Assert.isMainThread();
+        for (int i = 0; i < mCallbacks.size(); i++) {
+            mCallbacks.get(i).onSideFingerprintAcquisitionStarted();
+        }
+    }
+
     void fireNotificationPulse(NotificationEntry entry) {
         Runnable pulseSuppressedListener = () -> {
             if (NotificationIconContainerRefactor.isEnabled()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaViewController.kt
index 0bad47e..8a45ec1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaViewController.kt
@@ -18,12 +18,23 @@
 
 import com.android.systemui.flags.FeatureFlagsClassic
 import com.android.systemui.flags.Flags
+import com.android.systemui.Flags.smartspaceRelocateToBottom
+import android.view.View
+import android.view.ViewGroup
+import android.widget.LinearLayout
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController
 import com.android.systemui.util.ViewController
 import javax.inject.Inject
 
 class KeyguardBottomAreaViewController
-    @Inject constructor(view: KeyguardBottomAreaView, featureFlags: FeatureFlagsClassic) :
-    ViewController<KeyguardBottomAreaView> (view) {
+    @Inject constructor(
+            view: KeyguardBottomAreaView,
+            private val smartspaceController: LockscreenSmartspaceController,
+            featureFlags: FeatureFlagsClassic
+) : ViewController<KeyguardBottomAreaView> (view) {
+
+    private var smartspaceView: View? = null
 
     init {
         view.setIsLockscreenLandscapeEnabled(
@@ -31,6 +42,14 @@
     }
 
     override fun onViewAttached() {
+        if (!smartspaceRelocateToBottom() || !smartspaceController.isEnabled()) {
+            return
+        }
+
+        val ambientIndicationArea = mView.findViewById<View>(R.id.ambient_indication_container)
+        ambientIndicationArea?.visibility = View.GONE
+
+        addSmartspaceView()
     }
 
     override fun onViewDetached() {
@@ -40,4 +59,24 @@
         // TODO: remove this method.
         return mView
     }
+
+    private fun addSmartspaceView() {
+        if (!smartspaceRelocateToBottom()) {
+            return
+        }
+
+        val smartspaceContainer = mView.findViewById<View>(R.id.smartspace_container)
+        smartspaceContainer!!.visibility = View.VISIBLE
+
+        smartspaceView = smartspaceController.buildAndConnectView(smartspaceContainer as ViewGroup)
+        val lp = LinearLayout.LayoutParams(
+                ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
+        (smartspaceContainer as ViewGroup).addView(smartspaceView, 0, lp)
+        val startPadding = context.resources.getDimensionPixelSize(
+                R.dimen.below_clock_padding_start)
+        val endPadding = context.resources.getDimensionPixelSize(
+                R.dimen.below_clock_padding_end)
+        smartspaceView?.setPaddingRelative(startPadding, 0, endPadding, 0)
+//        mKeyguardUnlockAnimationController.lockscreenSmartspace = smartspaceView
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
index 0a03af7..ca3e3c6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -16,10 +16,12 @@
 
 package com.android.systemui.statusbar.phone;
 
+import static com.android.systemui.Flags.centralizedStatusBarDimensRefactor;
 import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset;
 import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInScale;
 import static com.android.systemui.statusbar.notification.NotificationUtils.interpolate;
 
+import android.content.Context;
 import android.content.res.Resources;
 import android.util.MathUtils;
 
@@ -30,6 +32,7 @@
 import com.android.systemui.log.core.Logger;
 import com.android.systemui.log.dagger.KeyguardClockLog;
 import com.android.systemui.res.R;
+import com.android.systemui.shade.LargeScreenHeaderHelper;
 import com.android.systemui.shade.ShadeViewController;
 import com.android.systemui.statusbar.policy.KeyguardUserSwitcherListView;
 
@@ -160,14 +163,14 @@
         mLogger = new Logger(logBuffer, TAG);
     }
 
-    /**
-     * Refreshes the dimension values.
-     */
-    public void loadDimens(Resources res) {
-        mStatusViewBottomMargin = res.getDimensionPixelSize(
-                R.dimen.keyguard_status_view_bottom_margin);
+    /** Refreshes the dimension values. */
+    public void loadDimens(Context context, Resources res) {
+        mStatusViewBottomMargin =
+                res.getDimensionPixelSize(R.dimen.keyguard_status_view_bottom_margin);
         mSplitShadeTopNotificationsMargin =
-                res.getDimensionPixelSize(R.dimen.large_screen_shade_header_height);
+                centralizedStatusBarDimensRefactor()
+                        ? LargeScreenHeaderHelper.getLargeScreenHeaderHeight(context)
+                        : res.getDimensionPixelSize(R.dimen.large_screen_shade_header_height);
         mSplitShadeTargetTopMargin =
                 res.getDimensionPixelSize(R.dimen.keyguard_split_shade_top_margin);
 
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 32b3ac2..9f08633 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt
@@ -28,7 +28,7 @@
 import com.android.systemui.Dumpable
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dump.DumpManager
-import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor
 import com.android.systemui.util.Assert
@@ -43,13 +43,13 @@
  */
 @SysUISingleton
 class KeyguardLiftController @Inject constructor(
-    private val context: Context,
-    private val statusBarStateController: StatusBarStateController,
-    private val asyncSensorManager: AsyncSensorManager,
-    private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
-    private val keyguardFaceAuthInteractor: KeyguardFaceAuthInteractor,
-    private val dumpManager: DumpManager,
-    private val selectedUserInteractor: SelectedUserInteractor,
+        private val context: Context,
+        private val statusBarStateController: StatusBarStateController,
+        private val asyncSensorManager: AsyncSensorManager,
+        private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
+        private val deviceEntryFaceAuthInteractor: DeviceEntryFaceAuthInteractor,
+        private val dumpManager: DumpManager,
+        private val selectedUserInteractor: SelectedUserInteractor,
 ) : Dumpable, CoreStartable {
 
     private val pickupSensor = asyncSensorManager.getDefaultSensor(Sensor.TYPE_PICK_UP_GESTURE)
@@ -75,7 +75,7 @@
             // Not listening anymore since trigger events unregister themselves
             isListening = false
             updateListeningState()
-            keyguardFaceAuthInteractor.onDeviceLifted()
+            deviceEntryFaceAuthInteractor.onDeviceLifted()
             keyguardUpdateMonitor.requestActiveUnlock(
                 ActiveUnlockConfig.ActiveUnlockRequestOrigin.WAKE,
                 "KeyguardLiftController")
@@ -113,7 +113,7 @@
         val onKeyguard = keyguardUpdateMonitor.isKeyguardVisible &&
                 !statusBarStateController.isDozing
 
-        val isFaceEnabled = keyguardFaceAuthInteractor.isFaceAuthEnabledAndEnrolled()
+        val isFaceEnabled = deviceEntryFaceAuthInteractor.isFaceAuthEnabledAndEnrolled()
         val shouldListen = (onKeyguard || bouncerVisible) && isFaceEnabled
         if (shouldListen != isListening) {
             isListening = shouldListen
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
index 7cc0888..7691459 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar.phone;
 
+import static com.android.systemui.Flags.centralizedStatusBarDimensRefactor;
 import static com.android.systemui.ScreenDecorations.DisplayCutoutView.boundsFromDirection;
 import static com.android.systemui.util.Utils.getStatusBarHeaderHeightKeyguard;
 
@@ -130,6 +131,9 @@
         mUserSwitcherContainer = findViewById(R.id.user_switcher_container);
         mIsPrivacyDotEnabled = mContext.getResources().getBoolean(R.bool.config_enablePrivacyDot);
         loadDimens();
+        if (!centralizedStatusBarDimensRefactor()) {
+            setGravity(Gravity.CENTER_VERTICAL);
+        }
     }
 
     /**
@@ -307,7 +311,8 @@
         final int minRight = (!isLayoutRtl() && mIsPrivacyDotEnabled)
                 ? Math.max(mMinDotWidth, mPadding.right) : mPadding.right;
 
-        setPadding(minLeft, waterfallTop, minRight, 0);
+        int top = centralizedStatusBarDimensRefactor() ? waterfallTop + mPadding.top : waterfallTop;
+        setPadding(minLeft, top, minRight, 0);
     }
 
     private boolean updateLayoutParamsNoCutout() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileControllerImpl.java
index abdf827..56ea00c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileControllerImpl.java
@@ -104,7 +104,8 @@
     }
 
     private void notifyManagedProfileRemoved() {
-        for (Callback callback : mCallbacks) {
+        ArrayList<Callback> copy = new ArrayList<>(mCallbacks);
+        for (Callback callback : copy) {
             callback.onManagedProfileRemoved();
         }
     }
@@ -148,7 +149,8 @@
         @Override
         public void onUserChanged(int newUser, @NonNull Context userContext) {
             reloadManagedProfiles();
-            for (Callback callback : mCallbacks) {
+            ArrayList<Callback> copy = new ArrayList<>(mCallbacks);
+            for (Callback callback : copy) {
                 callback.onManagedProfileChanged();
             }
         }
@@ -156,7 +158,8 @@
         @Override
         public void onProfilesChanged(@NonNull List<UserInfo> profiles) {
             reloadManagedProfiles();
-            for (Callback callback : mCallbacks) {
+            ArrayList<Callback> copy = new ArrayList<>(mCallbacks);
+            for (Callback callback : copy) {
                 callback.onManagedProfileChanged();
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
index 9ae4195..d7cbe5d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
@@ -14,6 +14,7 @@
 
 package com.android.systemui.statusbar.phone;
 
+import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_BINDABLE;
 import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_ICON;
 import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_MOBILE_NEW;
 import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_WIFI_NEW;
@@ -40,11 +41,13 @@
 import com.android.systemui.statusbar.StatusBarIconView;
 import com.android.systemui.statusbar.StatusIconDisplayable;
 import com.android.systemui.statusbar.connectivity.ui.MobileContextProvider;
+import com.android.systemui.statusbar.phone.StatusBarIconHolder.BindableIconHolder;
 import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.CallIndicatorIconState;
 import com.android.systemui.statusbar.pipeline.mobile.ui.MobileUiAdapter;
 import com.android.systemui.statusbar.pipeline.mobile.ui.binder.MobileIconsBinder;
 import com.android.systemui.statusbar.pipeline.mobile.ui.view.ModernStatusBarMobileView;
 import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsViewModel;
+import com.android.systemui.statusbar.pipeline.shared.ui.view.ModernStatusBarView;
 import com.android.systemui.statusbar.pipeline.wifi.ui.WifiUiAdapter;
 import com.android.systemui.statusbar.pipeline.wifi.ui.view.ModernStatusBarWifiView;
 import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.LocationBasedWifiViewModel;
@@ -432,6 +435,10 @@
 
                 case TYPE_MOBILE_NEW:
                     return addNewMobileIcon(index, slot, holder.getTag());
+
+                case TYPE_BINDABLE:
+                    // Safe cast, since only BindableIconHolders can set this tag on themselves
+                    return addBindableIcon((BindableIconHolder) holder, index);
             }
 
             return null;
@@ -446,6 +453,18 @@
             return view;
         }
 
+        /**
+         * ModernStatusBarViews can be created and bound, and thus do not need to update their
+         *  drawable by sending multiple calls to setIcon. Instead, by using a bindable
+         * icon view, we can simply create the icon when requested and allow the
+         * ViewBinder to control its visual state.
+         */
+        protected StatusIconDisplayable addBindableIcon(BindableIconHolder holder, int index) {
+            ModernStatusBarView view = holder.getInitializer().createAndBind(mContext);
+            mGroup.addView(view, index, onCreateLayoutParams());
+            return view;
+        }
+
         protected StatusIconDisplayable addNewWifiIcon(int index, String slot) {
             ModernStatusBarWifiView view = onCreateModernStatusBarWifiView(slot);
             mGroup.addView(view, index, onCreateLayoutParams());
@@ -530,6 +549,7 @@
                     return;
                 case TYPE_MOBILE_NEW:
                 case TYPE_WIFI_NEW:
+                case TYPE_BINDABLE:
                     // Nothing, the new icons update themselves
                     return;
                 default:
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
index 0f4d68c..4f148f1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
@@ -38,8 +38,11 @@
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.StatusIconDisplayable;
+import com.android.systemui.statusbar.phone.StatusBarIconHolder.BindableIconHolder;
 import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.CallIndicatorIconState;
 import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags;
+import com.android.systemui.statusbar.pipeline.icons.shared.BindableIconsRegistry;
+import com.android.systemui.statusbar.pipeline.icons.shared.model.BindableIcon;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
 import com.android.systemui.tuner.TunerService;
@@ -83,7 +86,8 @@
             TunerService tunerService,
             DumpManager dumpManager,
             StatusBarIconList statusBarIconList,
-            StatusBarPipelineFlags statusBarPipelineFlags
+            StatusBarPipelineFlags statusBarPipelineFlags,
+            BindableIconsRegistry modernIconsRegistry
     ) {
         mStatusBarIconList = statusBarIconList;
         mContext = context;
@@ -94,6 +98,28 @@
         tunerService.addTunable(this, ICON_HIDE_LIST);
         demoModeController.addCallback(this);
         dumpManager.registerDumpable(getClass().getSimpleName(), this);
+
+        addModernBindableIcons(modernIconsRegistry);
+    }
+
+    /**
+     * BindableIcons will always produce ModernStatusBarViews, which will be initialized and bound
+     * upon being added to any icon group. Because their view policy does not require subsequent
+     * calls to setIcon(), we can simply register them all statically here and not have to build
+     * CoreStartables for each modern icon.
+     *
+     * @param registry a statically defined provider of the modern icons
+     */
+    private void addModernBindableIcons(BindableIconsRegistry registry) {
+        List<BindableIcon> icons = registry.getBindableIcons();
+
+        // Initialization point for the bindable (modern) icons. These icons get their own slot
+        // allocated immediately, and are required to control their own display properties
+        for (BindableIcon i : icons) {
+            if (i.getShouldBindIcon()) {
+                addBindableIcon(i);
+            }
+        }
     }
 
     /** */
@@ -182,6 +208,17 @@
         mIconGroups.forEach(l -> l.onIconAdded(viewIndex, slot, hidden, holder));
     }
 
+    void addBindableIcon(BindableIcon icon) {
+        StatusBarIconHolder existingHolder = mStatusBarIconList.getIconHolder(icon.getSlot(), 0);
+        // Expected to be null
+        if (existingHolder == null) {
+            BindableIconHolder bindableIcon = new BindableIconHolder(icon.getInitializer());
+            setIcon(icon.getSlot(), bindableIcon);
+        } else {
+            Log.e(TAG, "addBindableIcon called, but icon has already been added. Ignoring");
+        }
+    }
+
     /** */
     @Override
     public void setIcon(String slot, int resourceId, CharSequence contentDescription) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.java
deleted file mode 100644
index 7048a78..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.java
+++ /dev/null
@@ -1,184 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.phone;
-
-import android.annotation.IntDef;
-import android.annotation.Nullable;
-import android.content.Context;
-import android.graphics.drawable.Icon;
-import android.os.UserHandle;
-
-import com.android.internal.statusbar.StatusBarIcon;
-import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.CallIndicatorIconState;
-import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconViewModel;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * Wraps {@link com.android.internal.statusbar.StatusBarIcon} so we can still have a uniform list
- */
-public class StatusBarIconHolder {
-    public static final int TYPE_ICON = 0;
-    /**
-     * TODO (b/249790733): address this once the new pipeline is in place
-     * This type exists so that the new pipeline (see {@link MobileIconViewModel}) can be used
-     * to inform the old view system about changes to the data set (the list of mobile icons). The
-     * design of the new pipeline should allow for removal of this icon holder type, and obsolete
-     * the need for this entire class.
-     *
-     * @deprecated This field only exists so the new status bar pipeline can interface with the
-     * view holder system.
-     */
-    @Deprecated
-    public static final int TYPE_MOBILE_NEW = 3;
-
-    /**
-     * TODO (b/238425913): address this once the new pipeline is in place
-     * This type exists so that the new wifi pipeline can be used to inform the old view system
-     * about the existence of the wifi icon. The design of the new pipeline should allow for removal
-     * of this icon holder type, and obsolete the need for this entire class.
-     *
-     * @deprecated This field only exists so the new status bar pipeline can interface with the
-     * view holder system.
-     */
-    @Deprecated
-    public static final int TYPE_WIFI_NEW = 4;
-
-    @IntDef({
-            TYPE_ICON,
-            TYPE_MOBILE_NEW,
-            TYPE_WIFI_NEW
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    @interface IconType {}
-
-    private StatusBarIcon mIcon;
-    private @IconType int mType = TYPE_ICON;
-    private int mTag = 0;
-
-    /** Returns a human-readable string representing the given type. */
-    public static String getTypeString(@IconType int type) {
-        switch(type) {
-            case TYPE_ICON: return "ICON";
-            case TYPE_MOBILE_NEW: return "MOBILE_NEW";
-            case TYPE_WIFI_NEW: return "WIFI_NEW";
-            default: return "UNKNOWN";
-        }
-    }
-
-    private StatusBarIconHolder() {
-    }
-
-    public static StatusBarIconHolder fromIcon(StatusBarIcon icon) {
-        StatusBarIconHolder wrapper = new StatusBarIconHolder();
-        wrapper.mIcon = icon;
-
-        return wrapper;
-    }
-
-    /** Creates a new holder with for the new wifi icon. */
-    public static StatusBarIconHolder forNewWifiIcon() {
-        StatusBarIconHolder holder = new StatusBarIconHolder();
-        holder.mType = TYPE_WIFI_NEW;
-        return holder;
-    }
-
-    /**
-     * ONLY for use with the new connectivity pipeline, where we only need a subscriptionID to
-     * determine icon ordering and building the correct view model
-     */
-    public static StatusBarIconHolder fromSubIdForModernMobileIcon(int subId) {
-        StatusBarIconHolder holder = new StatusBarIconHolder();
-        holder.mType = TYPE_MOBILE_NEW;
-        holder.mTag = subId;
-
-        return holder;
-    }
-
-    /**
-     * Creates a new StatusBarIconHolder from a CallIndicatorIconState.
-     */
-    public static StatusBarIconHolder fromCallIndicatorState(
-            Context context,
-            CallIndicatorIconState state) {
-        StatusBarIconHolder holder = new StatusBarIconHolder();
-        int resId = state.isNoCalling ? state.noCallingResId : state.callStrengthResId;
-        String contentDescription = state.isNoCalling
-                ? state.noCallingDescription : state.callStrengthDescription;
-        holder.mIcon = new StatusBarIcon(UserHandle.SYSTEM, context.getPackageName(),
-                Icon.createWithResource(context, resId), 0, 0, contentDescription);
-        holder.mTag = state.subId;
-        return holder;
-    }
-
-    public @IconType int getType() {
-        return mType;
-    }
-
-    @Nullable
-    public StatusBarIcon getIcon() {
-        return mIcon;
-    }
-
-    public void setIcon(StatusBarIcon icon) {
-        mIcon = icon;
-    }
-
-    public boolean isVisible() {
-        switch (mType) {
-            case TYPE_ICON:
-                return mIcon.visible;
-            case TYPE_MOBILE_NEW:
-            case TYPE_WIFI_NEW:
-                // The new pipeline controls visibilities via the view model and view binder, so
-                // this is effectively an unused return value.
-                return true;
-            default:
-                return true;
-        }
-    }
-
-    public void setVisible(boolean visible) {
-        if (isVisible() == visible) {
-            return;
-        }
-
-        switch (mType) {
-            case TYPE_ICON:
-                mIcon.visible = visible;
-                break;
-
-            case TYPE_MOBILE_NEW:
-            case TYPE_WIFI_NEW:
-                // The new pipeline controls visibilities via the view model and view binder, so
-                // ignore setVisible.
-                break;
-        }
-    }
-
-    public int getTag() {
-        return mTag;
-    }
-
-    @Override
-    public String toString() {
-        return "StatusBarIconHolder(type=" + getTypeString(mType)
-                + " tag=" + getTag()
-                + " visible=" + isVisible() + ")";
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.kt
new file mode 100644
index 0000000..bef0b28
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.kt
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.statusbar.phone
+
+import android.annotation.IntDef
+import android.content.Context
+import android.graphics.drawable.Icon
+import android.os.UserHandle
+import com.android.internal.statusbar.StatusBarIcon
+import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.CallIndicatorIconState
+import com.android.systemui.statusbar.pipeline.icons.shared.model.ModernStatusBarViewCreator
+
+/** Wraps [com.android.internal.statusbar.StatusBarIcon] so we can still have a uniform list */
+open class StatusBarIconHolder private constructor() {
+    @IntDef(TYPE_ICON, TYPE_MOBILE_NEW, TYPE_WIFI_NEW, TYPE_BINDABLE)
+    @Retention(AnnotationRetention.SOURCE)
+    internal annotation class IconType
+
+    var icon: StatusBarIcon? = null
+
+    @IconType
+    open var type = TYPE_ICON
+        internal set
+
+    var tag = 0
+        private set
+
+    open var isVisible: Boolean
+        get() =
+            when (type) {
+                TYPE_ICON -> icon!!.visible
+
+                // The new pipeline controls visibilities via the view model and
+                // view binder, so
+                // this is effectively an unused return value.
+                TYPE_BINDABLE,
+                TYPE_MOBILE_NEW,
+                TYPE_WIFI_NEW -> true
+                else -> true
+            }
+        set(visible) {
+            if (isVisible == visible) {
+                return
+            }
+            when (type) {
+                TYPE_ICON -> icon!!.visible = visible
+                TYPE_BINDABLE,
+                TYPE_MOBILE_NEW,
+                TYPE_WIFI_NEW -> {}
+            }
+        }
+
+    override fun toString(): String {
+        return ("StatusBarIconHolder(type=${getTypeString(type)}" +
+            " tag=$tag" +
+            " visible=$isVisible)")
+    }
+
+    companion object {
+        const val TYPE_ICON = 0
+
+        /**
+         * TODO (b/249790733): address this once the new pipeline is in place This type exists so
+         * that the new pipeline (see [MobileIconViewModel]) can be used to inform the old view
+         * system about changes to the data set (the list of mobile icons). The design of the new
+         * pipeline should allow for removal of this icon holder type, and obsolete the need for
+         * this entire class.
+         */
+        @Deprecated(
+            """This field only exists so the new status bar pipeline can interface with the
+      view holder system."""
+        )
+        const val TYPE_MOBILE_NEW = 3
+
+        /**
+         * TODO (b/238425913): address this once the new pipeline is in place This type exists so
+         * that the new wifi pipeline can be used to inform the old view system about the existence
+         * of the wifi icon. The design of the new pipeline should allow for removal of this icon
+         * holder type, and obsolete the need for this entire class.
+         */
+        @Deprecated(
+            """This field only exists so the new status bar pipeline can interface with the
+      view holder system."""
+        )
+        const val TYPE_WIFI_NEW = 4
+
+        /** Only applicable to [BindableIconHolder] */
+        const val TYPE_BINDABLE = 5
+
+        /** Returns a human-readable string representing the given type. */
+        fun getTypeString(@IconType type: Int): String {
+            return when (type) {
+                TYPE_ICON -> "ICON"
+                TYPE_MOBILE_NEW -> "MOBILE_NEW"
+                TYPE_WIFI_NEW -> "WIFI_NEW"
+                else -> "UNKNOWN"
+            }
+        }
+
+        @JvmStatic
+        fun fromIcon(icon: StatusBarIcon?): StatusBarIconHolder {
+            val wrapper = StatusBarIconHolder()
+            wrapper.icon = icon
+            return wrapper
+        }
+
+        /** Creates a new holder with for the new wifi icon. */
+        @JvmStatic
+        fun forNewWifiIcon(): StatusBarIconHolder {
+            val holder = StatusBarIconHolder()
+            holder.type = TYPE_WIFI_NEW
+            return holder
+        }
+
+        /**
+         * ONLY for use with the new connectivity pipeline, where we only need a subscriptionID to
+         * determine icon ordering and building the correct view model
+         */
+        @JvmStatic
+        fun fromSubIdForModernMobileIcon(subId: Int): StatusBarIconHolder {
+            val holder = StatusBarIconHolder()
+            holder.type = TYPE_MOBILE_NEW
+            holder.tag = subId
+            return holder
+        }
+
+        /** Creates a new StatusBarIconHolder from a CallIndicatorIconState. */
+        @JvmStatic
+        fun fromCallIndicatorState(
+            context: Context,
+            state: CallIndicatorIconState,
+        ): StatusBarIconHolder {
+            val holder = StatusBarIconHolder()
+            val resId = if (state.isNoCalling) state.noCallingResId else state.callStrengthResId
+            val contentDescription =
+                if (state.isNoCalling) state.noCallingDescription else state.callStrengthDescription
+            holder.icon =
+                StatusBarIcon(
+                    UserHandle.SYSTEM,
+                    context.packageName,
+                    Icon.createWithResource(context, resId),
+                    0,
+                    0,
+                    contentDescription,
+                )
+            holder.tag = state.subId
+            return holder
+        }
+    }
+
+    /**
+     * Subclass of StatusBarIconHolder that is responsible only for the registration of an icon into
+     * the [StatusBarIconList]. A bindable icon takes care of its own display, including hiding
+     * itself under the correct conditions.
+     *
+     * StatusBarIconController will register all available bindable icons on init (see
+     * [BindableIconsRepository]), and will ignore any call to setIcon for these.
+     *
+     * [initializer] a view creator that can bind the relevant view models to the created view.
+     */
+    class BindableIconHolder(val initializer: ModernStatusBarViewCreator) : StatusBarIconHolder() {
+        override var type: Int = TYPE_BINDABLE
+
+        /** This is unused, as bindable icons use their own view binders to control visibility */
+        override var isVisible: Boolean = true
+
+        override fun toString(): String {
+            return ("StatusBarIconHolder(type=BINDABLE)")
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 4999123..88347ab 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -18,6 +18,7 @@
 
 import static android.view.WindowInsets.Type.navigationBars;
 
+import static com.android.systemui.Flags.predictiveBackAnimateBouncer;
 import static com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants.EXPANSION_HIDDEN;
 import static com.android.systemui.plugins.ActivityStarter.OnDismissAction;
 import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK;
@@ -400,8 +401,7 @@
         mFoldAodAnimationController = sysUIUnfoldComponent
                 .map(SysUIUnfoldComponent::getFoldAodAnimationController).orElse(null);
         mAlternateBouncerInteractor = alternateBouncerInteractor;
-        mIsBackAnimationEnabled =
-                featureFlags.isEnabled(Flags.WM_ENABLE_PREDICTIVE_BACK_BOUNCER_ANIM);
+        mIsBackAnimationEnabled = predictiveBackAnimateBouncer();
         mUdfpsOverlayInteractor = udfpsOverlayInteractor;
         mActivityStarter = activityStarter;
         mKeyguardTransitionInteractor = keyguardTransitionInteractor;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
index 30a445f..703b3c6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
@@ -118,15 +118,25 @@
 
     private void updateVpn() {
         boolean vpnVisible = mSecurityController.isVpnEnabled();
-        int vpnIconId = currentVpnIconId(mSecurityController.isVpnBranded());
+        int vpnIconId = currentVpnIconId(
+                mSecurityController.isVpnBranded(),
+                mSecurityController.isVpnValidated());
 
         mIconController.setIcon(mSlotVpn, vpnIconId,
                 mContext.getResources().getString(R.string.accessibility_vpn_on));
         mIconController.setIconVisibility(mSlotVpn, vpnVisible);
     }
 
-    private int currentVpnIconId(boolean isBranded) {
-        return isBranded ? R.drawable.stat_sys_branded_vpn : R.drawable.stat_sys_vpn_ic;
+    private int currentVpnIconId(boolean isBranded, boolean isValidated) {
+        if (isBranded) {
+            return isValidated
+                    ? R.drawable.stat_sys_branded_vpn
+                    : R.drawable.stat_sys_no_internet_branded_vpn;
+        } else {
+            return isValidated
+                    ? R.drawable.stat_sys_vpn_ic
+                    : R.drawable.stat_sys_no_internet_vpn_ic;
+        }
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
index af6da3f..390d2c9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.statusbar.phone;
 
+import static com.android.systemui.Flags.predictiveBackAnimateDialogs;
+
 import android.app.AlertDialog;
 import android.app.Dialog;
 import android.content.BroadcastReceiver;
@@ -45,8 +47,6 @@
 import com.android.systemui.animation.DialogLaunchAnimator;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.dagger.qualifiers.Application;
-import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
 import com.android.systemui.model.SysUiState;
 import com.android.systemui.res.R;
 import com.android.systemui.shared.system.QuickStepContract;
@@ -78,7 +78,6 @@
     public static final boolean DEFAULT_DISMISS_ON_DEVICE_LOCK = true;
 
     private final Context mContext;
-    private final FeatureFlags mFeatureFlags;
     private final DialogDelegate<SystemUIDialog> mDelegate;
     @Nullable private final DismissReceiver mDismissReceiver;
     private final Handler mHandler = new Handler();
@@ -110,7 +109,6 @@
         // SystemUIDialogFactory and make all other dialogs create a SystemUIDialog to which we set
         // the content and attach listeners.
         this(context, theme, dismissOnDeviceLock,
-                Dependency.get(FeatureFlags.class),
                 Dependency.get(SystemUIDialogManager.class),
                 Dependency.get(SysUiState.class),
                 Dependency.get(BroadcastDispatcher.class),
@@ -119,7 +117,6 @@
 
     public static class Factory {
         private final Context mContext;
-        private final FeatureFlags mFeatureFlags;
         private final SystemUIDialogManager mSystemUIDialogManager;
         private final SysUiState mSysUiState;
         private final BroadcastDispatcher mBroadcastDispatcher;
@@ -128,13 +125,11 @@
         @Inject
         public Factory(
                 @Application Context context,
-                FeatureFlags featureFlags,
                 SystemUIDialogManager systemUIDialogManager,
                 SysUiState sysUiState,
                 BroadcastDispatcher broadcastDispatcher,
                 DialogLaunchAnimator dialogLaunchAnimator) {
             mContext = context;
-            mFeatureFlags = featureFlags;
             mSystemUIDialogManager = systemUIDialogManager;
             mSysUiState = sysUiState;
             mBroadcastDispatcher = broadcastDispatcher;
@@ -149,6 +144,14 @@
             return create(new DialogDelegate<>(){}, mContext);
         }
 
+        /** Creates a new instance of {@link SystemUIDialog} with no customized behavior.
+         *
+         * When you just need a dialog created with a specific {@link Context}, call this.
+         */
+        public SystemUIDialog create(Context context) {
+            return create(new DialogDelegate<>(){}, context);
+        }
+
         /**
          * Creates a new instance of {@link SystemUIDialog} with {@code delegate} as the {@link
          * Delegate}.
@@ -169,7 +172,6 @@
                     context,
                     DEFAULT_THEME,
                     DEFAULT_DISMISS_ON_DEVICE_LOCK,
-                    mFeatureFlags,
                     mSystemUIDialogManager,
                     mSysUiState,
                     mBroadcastDispatcher,
@@ -182,7 +184,6 @@
             Context context,
             int theme,
             boolean dismissOnDeviceLock,
-            FeatureFlags featureFlags,
             SystemUIDialogManager dialogManager,
             SysUiState sysUiState,
             BroadcastDispatcher broadcastDispatcher,
@@ -191,7 +192,6 @@
                 context,
                 theme,
                 dismissOnDeviceLock,
-                featureFlags,
                 dialogManager,
                 sysUiState,
                 broadcastDispatcher,
@@ -203,7 +203,6 @@
             Context context,
             int theme,
             boolean dismissOnDeviceLock,
-            FeatureFlags featureFlags,
             SystemUIDialogManager dialogManager,
             SysUiState sysUiState,
             BroadcastDispatcher broadcastDispatcher,
@@ -213,7 +212,6 @@
                 context,
                 theme,
                 dismissOnDeviceLock,
-                featureFlags,
                 dialogManager,
                 sysUiState,
                 broadcastDispatcher,
@@ -225,7 +223,6 @@
             Context context,
             int theme,
             boolean dismissOnDeviceLock,
-            FeatureFlags featureFlags,
             SystemUIDialogManager dialogManager,
             SysUiState sysUiState,
             BroadcastDispatcher broadcastDispatcher,
@@ -233,7 +230,6 @@
             DialogDelegate<SystemUIDialog> delegate) {
         super(context, theme);
         mContext = context;
-        mFeatureFlags = featureFlags;
         mDelegate = delegate;
 
         applyFlags(this);
@@ -261,7 +257,7 @@
         for (int i = 0; i < mOnCreateRunnables.size(); i++) {
             mOnCreateRunnables.get(i).run();
         }
-        if (mFeatureFlags.isEnabled(Flags.WM_ENABLE_PREDICTIVE_BACK_QS_DIALOG_ANIM)) {
+        if (predictiveBackAnimateDialogs()) {
             DialogKt.registerAnimationOnBackInvoked(
                     /* dialog = */ this,
                     /* targetView = */ getWindow().getDecorView()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialogFactory.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialogFactory.kt
index d91ca92..f3e8f62d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialogFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialogFactory.kt
@@ -20,7 +20,6 @@
 import com.android.systemui.animation.DialogLaunchAnimator
 import com.android.systemui.broadcast.BroadcastDispatcher
 import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.flags.FeatureFlagsClassic
 import com.android.systemui.model.SysUiState
 import com.android.systemui.util.Assert
 import javax.inject.Inject
@@ -30,7 +29,6 @@
 @Inject
 constructor(
     @Application val applicationContext: Context,
-    private val featureFlags: FeatureFlagsClassic,
     private val dialogManager: SystemUIDialogManager,
     private val sysUiState: SysUiState,
     private val broadcastDispatcher: BroadcastDispatcher,
@@ -57,7 +55,6 @@
             context,
             theme,
             dismissOnDeviceLock,
-            featureFlags,
             dialogManager,
             sysUiState,
             broadcastDispatcher,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
index 11456ff..617e107 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
@@ -20,6 +20,7 @@
 import android.database.ContentObserver;
 import android.os.Bundle;
 import android.os.Parcelable;
+import android.os.Trace;
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.telephony.SubscriptionManager;
@@ -451,6 +452,7 @@
 
     /** Initializes views related to the notification icon area. */
     public void initNotificationIconArea() {
+        Trace.beginSection("CollapsedStatusBarFragment#initNotifIconArea");
         ViewGroup notificationIconArea = mStatusBar.requireViewById(R.id.notification_icon_area);
         if (NotificationIconContainerRefactor.isEnabled()) {
             LayoutInflater.from(getContext())
@@ -470,6 +472,7 @@
         }
 
         updateNotificationIconAreaAndCallChip(/* animate= */ false);
+        Trace.endSection();
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
index e1fd37f..89a2fb7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
@@ -29,6 +29,8 @@
 import com.android.systemui.statusbar.pipeline.airplane.data.repository.AirplaneModeRepositoryImpl
 import com.android.systemui.statusbar.pipeline.airplane.ui.viewmodel.AirplaneModeViewModel
 import com.android.systemui.statusbar.pipeline.airplane.ui.viewmodel.AirplaneModeViewModelImpl
+import com.android.systemui.statusbar.pipeline.icons.shared.BindableIconsRegistry
+import com.android.systemui.statusbar.pipeline.icons.shared.BindableIconsRegistryImpl
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.CarrierConfigCoreStartable
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepository
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileRepositorySwitcher
@@ -42,6 +44,8 @@
 import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxyImpl
 import com.android.systemui.statusbar.pipeline.mobile.util.SubscriptionManagerProxy
 import com.android.systemui.statusbar.pipeline.mobile.util.SubscriptionManagerProxyImpl
+import com.android.systemui.statusbar.pipeline.satellite.data.DeviceBasedSatelliteRepository
+import com.android.systemui.statusbar.pipeline.satellite.data.prod.DeviceBasedSatelliteRepositoryImpl
 import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepository
 import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepositoryImpl
 import com.android.systemui.statusbar.pipeline.shared.ui.binder.CollapsedStatusBarViewBinder
@@ -76,8 +80,16 @@
     abstract fun airplaneModeViewModel(impl: AirplaneModeViewModelImpl): AirplaneModeViewModel
 
     @Binds
+    abstract fun bindableIconsRepository(impl: BindableIconsRegistryImpl): BindableIconsRegistry
+
+    @Binds
     abstract fun connectivityRepository(impl: ConnectivityRepositoryImpl): ConnectivityRepository
 
+    @Binds
+    abstract fun deviceBasedSatelliteRepository(
+        impl: DeviceBasedSatelliteRepositoryImpl
+    ): DeviceBasedSatelliteRepository
+
     @Binds abstract fun wifiRepository(impl: WifiRepositorySwitcher): WifiRepository
 
     @Binds abstract fun wifiInteractor(impl: WifiInteractorImpl): WifiInteractor
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/icons/shared/BindableIconsRegistry.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/icons/shared/BindableIconsRegistry.kt
new file mode 100644
index 0000000..e3c3139
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/icons/shared/BindableIconsRegistry.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.icons.shared
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.pipeline.icons.shared.model.BindableIcon
+import javax.inject.Inject
+
+/**
+ * Bindable status bar icons represent icon descriptions which can be registered with
+ * StatusBarIconController and can also create their own bindings. A bound icon is responsible for
+ * its own updates via the [repeatWhenAttached] view lifecycle utility. Thus,
+ * StatusBarIconController can (and will) ignore any call to setIcon.
+ *
+ * In other words, these icons are bound once (at controller init) and they will control their
+ * visibility on their own (while their overall appearance remains at the discretion of
+ * StatusBarIconController, via the ModernStatusBarViewBinding interface).
+ */
+interface BindableIconsRegistry {
+    val bindableIcons: List<BindableIcon>
+}
+
+@SysUISingleton
+class BindableIconsRegistryImpl
+@Inject
+constructor(
+/** Bindables go here */
+) : BindableIconsRegistry {
+    /**
+     * Adding the injected bindables to this list will get them registered with
+     * StatusBarIconController
+     */
+    override val bindableIcons: List<BindableIcon> = listOf()
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/icons/shared/model/BindableIcon.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/icons/shared/model/BindableIcon.kt
new file mode 100644
index 0000000..9d0d838
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/icons/shared/model/BindableIcon.kt
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.pipeline.icons.shared.model
+
+/**
+ * A BindableIcon describes a status bar icon that can be housed in the [ModernStatusBarView]
+ * created by [initializer]. They can be registered statically for [BindableIconsRepositoryImpl].
+ *
+ * Typical usage would be to create an (@SysUISingleton) adapter class that implements the
+ * interface. For example:
+ * ```
+ * @SysuUISingleton
+ * class MyBindableIconAdapter
+ * @Inject constructor(
+ *     // deps
+ *     val viewModel: MyViewModel
+ * ) : BindableIcon {
+ *     override val slot = "icon_slot_name"
+ *
+ *     override val initializer = ModernStatusBarViewCreator() {
+ *         SingleBindableStatusBarIconView.createView(context).also { iconView ->
+ *             MyIconViewBinder.bind(iconView, viewModel)
+ *         }
+ *     }
+ *
+ *     override fun shouldBind() = Flags.myFlag()
+ * }
+ * ```
+ *
+ * By defining this adapter (and injecting it into the repository), we get our icon registered with
+ * the legacy StatusBarIconController while proxying all updates to the view binder that is created
+ * elsewhere.
+ *
+ * Note that the initializer block defines a closure that can pull in the viewModel dependency
+ * without us having to store it directly in the icon controller.
+ */
+interface BindableIcon {
+    val slot: String
+    val initializer: ModernStatusBarViewCreator
+    val shouldBindIcon: Boolean
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/icons/shared/model/ModernStatusBarViewCreator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/icons/shared/model/ModernStatusBarViewCreator.kt
new file mode 100644
index 0000000..dbd5c1d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/icons/shared/model/ModernStatusBarViewCreator.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.pipeline.icons.shared.model
+
+import android.content.Context
+import com.android.systemui.statusbar.pipeline.shared.ui.view.ModernStatusBarView
+
+/**
+ * Defined as an interface (as opposed to a typealias) to simplify calling from java.
+ * [ModernStatusBarViewCreator.createAndBind] should return a constructed and bound
+ * [ModernStatusBarView].
+ */
+fun interface ModernStatusBarViewCreator {
+    fun createAndBind(context: Context): ModernStatusBarView
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
index be2c21b..f44401b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
@@ -325,8 +325,13 @@
     override val cdmaRoaming: StateFlow<Boolean> =
         telephonyPollingEvent
             .mapLatest {
-                val cdmaEri = telephonyManager.cdmaEnhancedRoamingIndicatorDisplayNumber
-                cdmaEri == ERI_ON || cdmaEri == ERI_FLASH
+                try {
+                    val cdmaEri = telephonyManager.cdmaEnhancedRoamingIndicatorDisplayNumber
+                    cdmaEri == ERI_ON || cdmaEri == ERI_FLASH
+                } catch (e: UnsupportedOperationException) {
+                    // Handles the same as a function call failure
+                    false
+                }
             }
             .stateIn(scope, SharingStarted.WhileSubscribed(), false)
 
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 dad4093..39135c7 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
@@ -71,6 +71,12 @@
     /** List of subscriptions, potentially filtered for CBRS */
     val filteredSubscriptions: Flow<List<SubscriptionModel>>
 
+    /**
+     * The current list of [MobileIconInteractor]s associated with the current list of
+     * [filteredSubscriptions]
+     */
+    val icons: StateFlow<List<MobileIconInteractor>>
+
     /** True if the active mobile data subscription has data enabled */
     val activeDataConnectionHasDataEnabled: StateFlow<Boolean>
 
@@ -259,6 +265,13 @@
         }
     }
 
+    override val icons =
+        filteredSubscriptions
+            .mapLatest { subs ->
+                subs.map { getMobileConnectionInteractorForSubId(it.subscriptionId) }
+            }
+            .stateIn(scope, SharingStarted.WhileSubscribed(), emptyList())
+
     /**
      * Copied from the old pipeline. We maintain a 2s period of time where we will keep the
      * validated bit from the old active network (A) while data is changing to the new one (B).
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/DeviceBasedSatelliteRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/DeviceBasedSatelliteRepository.kt
new file mode 100644
index 0000000..ad8b810
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/DeviceBasedSatelliteRepository.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.satellite.data
+
+import com.android.systemui.statusbar.pipeline.satellite.shared.model.SatelliteConnectionState
+import kotlinx.coroutines.flow.Flow
+
+/**
+ * Device-based satellite refers to the capability of a device to connect directly to a satellite
+ * network. This is in contrast to carrier-based satellite connectivity, which is a property of a
+ * given mobile data subscription.
+ */
+interface DeviceBasedSatelliteRepository {
+    /** See [SatelliteConnectionState] for available states */
+    val connectionState: Flow<SatelliteConnectionState>
+
+    /** 0-4 level (similar to wifi and mobile) */
+    // @IntRange(from = 0, to = 4)
+    val signalStrength: Flow<Int>
+
+    /** Clients must observe this property, as device-based satellite is location-dependent */
+    val isSatelliteAllowedForCurrentLocation: Flow<Boolean>
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt
new file mode 100644
index 0000000..de46a5e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt
@@ -0,0 +1,268 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.pipeline.satellite.data.prod
+
+import android.os.OutcomeReceiver
+import android.telephony.satellite.NtnSignalStrengthCallback
+import android.telephony.satellite.SatelliteManager
+import android.telephony.satellite.SatelliteModemStateCallback
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.statusbar.pipeline.satellite.data.DeviceBasedSatelliteRepository
+import com.android.systemui.statusbar.pipeline.satellite.data.prod.SatelliteSupport.Companion.whenSupported
+import com.android.systemui.statusbar.pipeline.satellite.data.prod.SatelliteSupport.NotSupported
+import com.android.systemui.statusbar.pipeline.satellite.data.prod.SatelliteSupport.Supported
+import com.android.systemui.statusbar.pipeline.satellite.data.prod.SatelliteSupport.Unknown
+import com.android.systemui.statusbar.pipeline.satellite.shared.model.SatelliteConnectionState
+import com.android.systemui.util.kotlin.getOrNull
+import com.android.systemui.util.time.SystemClock
+import java.util.Optional
+import javax.inject.Inject
+import kotlin.coroutines.resume
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.asExecutor
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.collectLatest
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.suspendCancellableCoroutine
+import kotlinx.coroutines.withContext
+
+/**
+ * A SatelliteManager that has responded that it has satellite support. Use [SatelliteSupport] to
+ * get one
+ */
+private typealias SupportedSatelliteManager = SatelliteManager
+
+/**
+ * "Supported" here means supported by the device. The value of this should be stable during the
+ * process lifetime.
+ */
+private sealed interface SatelliteSupport {
+    /** Not yet fetched */
+    data object Unknown : SatelliteSupport
+
+    /**
+     * SatelliteManager says that this mode is supported. Note that satellite manager can never be
+     * null now
+     */
+    data class Supported(val satelliteManager: SupportedSatelliteManager) : SatelliteSupport
+
+    /**
+     * Either we were told that there is no support for this feature, or the manager is null, or
+     * some other exception occurred while querying for support.
+     */
+    data object NotSupported : SatelliteSupport
+
+    @OptIn(ExperimentalCoroutinesApi::class)
+    companion object {
+        /** Convenience function to switch to the supported flow */
+        fun <T> Flow<SatelliteSupport>.whenSupported(
+            supported: (SatelliteManager) -> Flow<T>,
+            orElse: Flow<T>,
+        ): Flow<T> = flatMapLatest {
+            when (it) {
+                is Supported -> supported(it.satelliteManager)
+                else -> orElse
+            }
+        }
+    }
+}
+
+/**
+ * Basically your everyday run-of-the-mill system service listener, with three notable exceptions.
+ *
+ * First, there is an availability bit that we are tracking via [SatelliteManager]. See
+ * [isSatelliteAllowedForCurrentLocation] for the implementation details. The thing to note about
+ * this bit is that there is no callback that exists. Therefore we implement a simple polling
+ * mechanism here. Since the underlying bit is location-dependent, we simply poll every hour (see
+ * [POLLING_INTERVAL_MS]) and see what the current state is.
+ *
+ * Secondly, there are cases when simply requesting information from SatelliteManager can fail. See
+ * [SatelliteSupport] for details on how we track the state. What's worth noting here is that
+ * SUPPORTED is a stronger guarantee than [satelliteManager] being null. Therefore, the fundamental
+ * data flows here ([connectionState], [signalStrength],...) are wrapped in the convenience method
+ * [SatelliteSupport.whenSupported]. By defining flows as simple functions based on a
+ * [SupportedSatelliteManager], we can guarantee that the manager is non-null AND that it has told
+ * us that satellite is supported. Therefore, we don't expect exceptions to be thrown.
+ *
+ * Lastly, this class is designed to wait a full minute of process uptime before making any requests
+ * to the satellite manager. The hope is that by waiting we don't have to retry due to a modem that
+ * is still booting up or anything like that. We can tune or remove this behavior in the future if
+ * necessary.
+ */
+@SysUISingleton
+class DeviceBasedSatelliteRepositoryImpl
+@Inject
+constructor(
+    satelliteManagerOpt: Optional<SatelliteManager>,
+    @Background private val bgDispatcher: CoroutineDispatcher,
+    @Application private val scope: CoroutineScope,
+    private val systemClock: SystemClock,
+) : DeviceBasedSatelliteRepository {
+
+    private val satelliteManager: SatelliteManager?
+
+    override val isSatelliteAllowedForCurrentLocation: MutableStateFlow<Boolean>
+
+    // Some calls into satellite manager will throw exceptions if it is not supported.
+    // This is never expected to change after boot, but may need to be retried in some cases
+    private val satelliteSupport: MutableStateFlow<SatelliteSupport> = MutableStateFlow(Unknown)
+
+    init {
+        satelliteManager = satelliteManagerOpt.getOrNull()
+
+        isSatelliteAllowedForCurrentLocation = MutableStateFlow(false)
+
+        if (satelliteManager != null) {
+            // First, check that satellite is supported on this device
+            scope.launch {
+                ensureMinUptime(systemClock, MIN_UPTIME)
+                satelliteSupport.value = satelliteManager.checkSatelliteSupported()
+
+                // We only need to check location availability if this mode is supported
+                if (satelliteSupport.value is Supported) {
+                    isSatelliteAllowedForCurrentLocation.subscriptionCount
+                        .map { it > 0 }
+                        .distinctUntilChanged()
+                        .collectLatest { hasSubscribers ->
+                            if (hasSubscribers) {
+                                /*
+                                 * As there is no listener available for checking satellite allowed,
+                                 * we must poll. Defaulting to polling at most once every hour while
+                                 * active. Subsequent OOS events will restart the job, so a flaky
+                                 * connection might cause more frequent checks.
+                                 */
+                                while (true) {
+                                    checkIsSatelliteAllowed()
+                                    delay(POLLING_INTERVAL_MS)
+                                }
+                            }
+                        }
+                }
+            }
+        } else {
+            satelliteSupport.value = NotSupported
+        }
+    }
+
+    override val connectionState =
+        satelliteSupport.whenSupported(
+            supported = ::connectionStateFlow,
+            orElse = flowOf(SatelliteConnectionState.Off)
+        )
+
+    // By using the SupportedSatelliteManager here, we expect registration never to fail
+    private fun connectionStateFlow(sm: SupportedSatelliteManager): Flow<SatelliteConnectionState> =
+        conflatedCallbackFlow {
+                val cb = SatelliteModemStateCallback { state ->
+                    trySend(SatelliteConnectionState.fromModemState(state))
+                }
+
+                sm.registerForSatelliteModemStateChanged(bgDispatcher.asExecutor(), cb)
+
+                awaitClose { sm.unregisterForSatelliteModemStateChanged(cb) }
+            }
+            .flowOn(bgDispatcher)
+
+    override val signalStrength =
+        satelliteSupport.whenSupported(supported = ::signalStrengthFlow, orElse = flowOf(0))
+
+    // By using the SupportedSatelliteManager here, we expect registration never to fail
+    private fun signalStrengthFlow(sm: SupportedSatelliteManager) =
+        conflatedCallbackFlow {
+                val cb = NtnSignalStrengthCallback { signalStrength ->
+                    trySend(signalStrength.level)
+                }
+
+                sm.registerForNtnSignalStrengthChanged(bgDispatcher.asExecutor(), cb)
+
+                awaitClose { sm.unregisterForNtnSignalStrengthChanged(cb) }
+            }
+            .flowOn(bgDispatcher)
+
+    /** Fire off a request to check for satellite availability. Always runs on the bg context */
+    private suspend fun checkIsSatelliteAllowed() =
+        withContext(bgDispatcher) {
+            satelliteManager?.requestIsSatelliteCommunicationAllowedForCurrentLocation(
+                bgDispatcher.asExecutor(),
+                object : OutcomeReceiver<Boolean, SatelliteManager.SatelliteException> {
+                    override fun onError(e: SatelliteManager.SatelliteException) {
+                        android.util.Log.e(TAG, "Found exception when checking for satellite: ", e)
+                        isSatelliteAllowedForCurrentLocation.value = false
+                    }
+
+                    override fun onResult(allowed: Boolean) {
+                        isSatelliteAllowedForCurrentLocation.value = allowed
+                    }
+                }
+            )
+        }
+
+    private suspend fun SatelliteManager.checkSatelliteSupported(): SatelliteSupport =
+        suspendCancellableCoroutine { continuation ->
+            val cb =
+                object : OutcomeReceiver<Boolean, SatelliteManager.SatelliteException> {
+                    override fun onResult(supported: Boolean) {
+                        continuation.resume(
+                            if (supported) {
+                                Supported(satelliteManager = this@checkSatelliteSupported)
+                            } else {
+                                NotSupported
+                            }
+                        )
+                    }
+
+                    override fun onError(error: SatelliteManager.SatelliteException) {
+                        // Assume that an error means it's not supported
+                        continuation.resume(NotSupported)
+                    }
+                }
+
+            requestIsSatelliteSupported(bgDispatcher.asExecutor(), cb)
+        }
+
+    companion object {
+        // TTL for satellite polling is one hour
+        const val POLLING_INTERVAL_MS: Long = 1000 * 60 * 60
+
+        // Let the system boot up and stabilize before we check for system support
+        const val MIN_UPTIME: Long = 1000 * 60
+
+        private const val TAG = "DeviceBasedSatelliteRepo"
+
+        /** If our process hasn't been up for at least MIN_UPTIME, delay until we reach that time */
+        private suspend fun ensureMinUptime(clock: SystemClock, uptime: Long) {
+            val timeTilMinUptime =
+                uptime - (clock.uptimeMillis() - android.os.Process.getStartUptimeMillis())
+            if (timeTilMinUptime > 0) {
+                delay(timeTilMinUptime)
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractor.kt
new file mode 100644
index 0000000..8779577
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractor.kt
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.satellite.domain.interactor
+
+import com.android.internal.telephony.flags.Flags
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractor
+import com.android.systemui.statusbar.pipeline.satellite.data.DeviceBasedSatelliteRepository
+import com.android.systemui.statusbar.pipeline.satellite.shared.model.SatelliteConnectionState
+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.combine
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
+
+@SysUISingleton
+class DeviceBasedSatelliteInteractor
+@Inject
+constructor(
+    val repo: DeviceBasedSatelliteRepository,
+    iconsInteractor: MobileIconsInteractor,
+    @Application scope: CoroutineScope,
+) {
+    /** Must be observed by any UI showing Satellite iconography */
+    val isSatelliteAllowed =
+        if (Flags.oemEnabledSatelliteFlag()) {
+                repo.isSatelliteAllowedForCurrentLocation
+            } else {
+                flowOf(false)
+            }
+            .stateIn(scope, SharingStarted.WhileSubscribed(), false)
+
+    /** See [SatelliteConnectionState] for relevant states */
+    val connectionState =
+        if (Flags.oemEnabledSatelliteFlag()) {
+                repo.connectionState
+            } else {
+
+                flowOf(SatelliteConnectionState.Off)
+            }
+            .stateIn(scope, SharingStarted.WhileSubscribed(), SatelliteConnectionState.Off)
+
+    /** 0-4 description of the connection strength */
+    val signalStrength =
+        if (Flags.oemEnabledSatelliteFlag()) {
+                repo.signalStrength
+            } else {
+                flowOf(0)
+            }
+            .stateIn(scope, SharingStarted.WhileSubscribed(), 0)
+
+    /** When all connections are considered OOS, satellite connectivity is potentially valid */
+    val areAllConnectionsOutOfService =
+        if (Flags.oemEnabledSatelliteFlag()) {
+                iconsInteractor.icons.aggregateOver(selector = { intr -> intr.isInService }) {
+                    isInServiceList ->
+                    isInServiceList.all { !it }
+                }
+            } else {
+                flowOf(false)
+            }
+            .stateIn(scope, SharingStarted.WhileSubscribed(), false)
+}
+
+/**
+ * aggregateOver allows us to combine over the leaf-nodes of successive lists emitted from the
+ * top-level flow. Re-emits if the list changes, or any of the intermediate values change.
+ *
+ * Provides a way to connect the reactivity of the top-level flow with the reactivity of an
+ * arbitrarily-defined relationship ([selector]) from R to the flow that R exposes.
+ */
+@OptIn(ExperimentalCoroutinesApi::class)
+private inline fun <R, reified S, T> Flow<List<R>>.aggregateOver(
+    crossinline selector: (R) -> Flow<S>,
+    crossinline transform: (Array<S>) -> T
+): Flow<T> {
+    return map { list -> list.map { selector(it) } }
+        .flatMapLatest { newFlows -> combine(newFlows) { newVals -> transform(newVals) } }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/shared/model/SatelliteConnectionState.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/shared/model/SatelliteConnectionState.kt
new file mode 100644
index 0000000..bfe2941
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/shared/model/SatelliteConnectionState.kt
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.pipeline.satellite.shared.model
+
+import android.telephony.satellite.SatelliteManager
+import android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED
+import android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_DATAGRAM_RETRYING
+import android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_DATAGRAM_TRANSFERRING
+import android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_IDLE
+import android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_LISTENING
+import android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED
+import android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_OFF
+import android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_UNAVAILABLE
+import android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_UNKNOWN
+
+enum class SatelliteConnectionState {
+    // State is unknown or undefined
+    Unknown,
+    // Radio is off
+    Off,
+    // Radio is on, but not yet connected
+    On,
+    // Radio is connected, aka satellite is available for use
+    Connected;
+
+    companion object {
+        // TODO(b/316635648): validate these states. We don't need the level of granularity that
+        //  SatelliteManager gives us.
+        fun fromModemState(@SatelliteManager.SatelliteModemState modemState: Int) =
+            when (modemState) {
+                // Transferring data is connected
+                SATELLITE_MODEM_STATE_CONNECTED,
+                SATELLITE_MODEM_STATE_DATAGRAM_TRANSFERRING,
+                SATELLITE_MODEM_STATE_DATAGRAM_RETRYING -> Connected
+
+                // Modem is on but not connected
+                SATELLITE_MODEM_STATE_IDLE,
+                SATELLITE_MODEM_STATE_LISTENING,
+                SATELLITE_MODEM_STATE_NOT_CONNECTED -> On
+
+                // Consider unavailable equivalent to Off
+                SATELLITE_MODEM_STATE_UNAVAILABLE,
+                SATELLITE_MODEM_STATE_OFF -> Off
+
+                // Else, we don't know what's up
+                SATELLITE_MODEM_STATE_UNKNOWN -> Unknown
+                else -> Unknown
+            }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
index 41ed76d..45078e3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
@@ -20,6 +20,7 @@
 import static android.os.BatteryManager.CHARGING_POLICY_DEFAULT;
 import static android.os.BatteryManager.EXTRA_CHARGING_STATUS;
 import static android.os.BatteryManager.EXTRA_PRESENT;
+
 import static com.android.settingslib.fuelgauge.BatterySaverLogging.SAVER_ENABLED_QS;
 import static com.android.systemui.util.DumpUtilsKt.asIndenting;
 
@@ -61,6 +62,7 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Consumer;
 
 import javax.annotation.concurrent.GuardedBy;
 
@@ -448,50 +450,38 @@
         firePowerSaveChanged();
     }
 
+    protected final void dispatchSafeChange(Consumer<BatteryStateChangeCallback> action) {
+        ArrayList<BatteryStateChangeCallback> copy;
+        synchronized (mChangeCallbacks) {
+            copy = new ArrayList<>(mChangeCallbacks);
+        }
+        final int n = copy.size();
+        for (int i = 0; i < n; i++) {
+            action.accept(copy.get(i));
+        }
+    }
+
     protected void fireBatteryLevelChanged() {
         mLogger.logBatteryLevelChangedCallback(mLevel, mPluggedIn, mCharging);
-        synchronized (mChangeCallbacks) {
-            final int N = mChangeCallbacks.size();
-            for (int i = 0; i < N; i++) {
-                mChangeCallbacks.get(i).onBatteryLevelChanged(mLevel, mPluggedIn, mCharging);
-            }
-        }
+        dispatchSafeChange(
+                (callback) -> callback.onBatteryLevelChanged(mLevel, mPluggedIn, mCharging));
     }
 
     private void fireBatteryUnknownStateChanged() {
-        synchronized (mChangeCallbacks) {
-            final int n = mChangeCallbacks.size();
-            for (int i = 0; i < n; i++) {
-                mChangeCallbacks.get(i).onBatteryUnknownStateChanged(mStateUnknown);
-            }
-        }
+        dispatchSafeChange((callback) -> callback.onBatteryUnknownStateChanged(mStateUnknown));
     }
 
     private void firePowerSaveChanged() {
-        synchronized (mChangeCallbacks) {
-            final int N = mChangeCallbacks.size();
-            for (int i = 0; i < N; i++) {
-                mChangeCallbacks.get(i).onPowerSaveChanged(mPowerSave);
-            }
-        }
+        dispatchSafeChange((callback) -> callback.onPowerSaveChanged(mPowerSave));
     }
 
     private void fireIsBatteryDefenderChanged() {
-        synchronized (mChangeCallbacks) {
-            final int n = mChangeCallbacks.size();
-            for (int i = 0; i < n; i++) {
-                mChangeCallbacks.get(i).onIsBatteryDefenderChanged(mIsBatteryDefender);
-            }
-        }
+        dispatchSafeChange((callback) -> callback.onIsBatteryDefenderChanged(mIsBatteryDefender));
     }
 
     private void fireIsIncompatibleChargingChanged() {
-        synchronized (mChangeCallbacks) {
-            final int n = mChangeCallbacks.size();
-            for (int i = 0; i < n; i++) {
-                mChangeCallbacks.get(i).onIsIncompatibleChargingChanged(mIsIncompatibleCharging);
-            }
-        }
+        dispatchSafeChange(
+                (callback) -> callback.onIsIncompatibleChargingChanged(mIsIncompatibleCharging));
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
index 53b343c..fc2f6e9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
@@ -436,6 +436,8 @@
     @Override
     public void onServiceDisconnected() {}
 
+    // IMPORTANT: This handler guarantees that any operations on the list of callbacks is
+    // sequential, so no concurrent exceptions
     private final class H extends Handler {
         private final ArrayList<BluetoothController.Callback> mCallbacks = new ArrayList<>();
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java
index b06ebe9..149c8fa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java
@@ -35,9 +35,9 @@
 import androidx.annotation.VisibleForTesting;
 
 import com.android.internal.annotations.GuardedBy;
-import com.android.systemui.res.R;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dump.DumpManager;
+import com.android.systemui.res.R;
 import com.android.systemui.util.Utils;
 
 import java.io.PrintWriter;
@@ -291,11 +291,12 @@
 
     @VisibleForTesting
     void fireOnCastDevicesChanged() {
+        final ArrayList<Callback> callbacks;
         synchronized (mCallbacks) {
-            for (Callback callback : mCallbacks) {
-                fireOnCastDevicesChanged(callback);
-            }
-
+            callbacks = new ArrayList<>(mCallbacks);
+        }
+        for (Callback callback : callbacks) {
+            fireOnCastDevicesChanged(callback);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DataSaverControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DataSaverControllerImpl.java
index 8207012..6319781 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DataSaverControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DataSaverControllerImpl.java
@@ -36,10 +36,12 @@
     }
 
     private void handleRestrictBackgroundChanged(boolean isDataSaving) {
+        ArrayList<DataSaverController.Listener> copy;
         synchronized (mListeners) {
-            for (int i = 0; i < mListeners.size(); i++) {
-                mListeners.get(i).onDataSaverChanged(isDataSaving);
-            }
+            copy = new ArrayList<>(mListeners);
+        }
+        for (int i = 0; i < copy.size(); i++) {
+            copy.get(i).onDataSaverChanged(isDataSaving);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightControllerImpl.java
index 5dcafb3..b98eff8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightControllerImpl.java
@@ -202,10 +202,12 @@
 
     private void dispatchListeners(int message, boolean argument) {
         synchronized (mListeners) {
-            final int N = mListeners.size();
+            final ArrayList<WeakReference<FlashlightController.FlashlightListener>> copy =
+                    new ArrayList<>(mListeners);
+            final int n = copy.size();
             boolean cleanup = false;
-            for (int i = 0; i < N; i++) {
-                FlashlightListener l = mListeners.get(i).get();
+            for (int i = 0; i < n; i++) {
+                FlashlightListener l = copy.get(i).get();
                 if (l != null) {
                     if (message == DISPATCH_ERROR) {
                         l.onFlashlightError();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java
index fffd839..87dfc99 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java
@@ -119,7 +119,8 @@
             mHardwareToggleState.put(sensor, enabled);
         }
 
-        for (Callback callback : mCallbacks) {
+        Set<Callback> copy = new ArraySet<>(mCallbacks);
+        for (Callback callback : copy) {
             callback.onSensorBlockedChanged(sensor, isSensorBlocked(sensor));
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
index 0c5472f..886010c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
@@ -41,15 +41,14 @@
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
-import com.android.systemui.log.core.LogLevel;
 import com.android.systemui.res.R;
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
 
 import dagger.Lazy;
 
 import java.io.PrintWriter;
-import java.util.ArrayList;
 import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.function.Consumer;
 
 import javax.inject.Inject;
@@ -63,7 +62,8 @@
     private static final boolean DEBUG_AUTH_WITH_ADB = false;
     private static final String AUTH_BROADCAST_KEY = "debug_trigger_auth";
 
-    private final ArrayList<Callback> mCallbacks = new ArrayList<>();
+    private final ConcurrentHashMap.KeySetView<Callback, Boolean> mCallbacks =
+            ConcurrentHashMap.<Callback>newKeySet();
     private final Context mContext;
     private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     private final LockPatternUtils mLockPatternUtils;
@@ -157,9 +157,7 @@
     @Override
     public void addCallback(@NonNull Callback callback) {
         Objects.requireNonNull(callback, "Callback must not be null. b/128895449");
-        if (!mCallbacks.contains(callback)) {
-            mCallbacks.add(callback);
-        }
+        mCallbacks.add(callback);
     }
 
     @Override
@@ -221,18 +219,7 @@
     }
 
     private void invokeForEachCallback(Consumer<Callback> consumer) {
-        // Copy the list to allow removal during callback.
-        ArrayList<Callback> copyOfCallbacks = new ArrayList<>(mCallbacks);
-        for (int i = 0; i < copyOfCallbacks.size(); i++) {
-            Callback callback = copyOfCallbacks.get(i);
-            // Temporary fix for b/315731775, callback is null even though only non-null callbacks
-            // are added to the list by addCallback
-            if (callback != null) {
-                consumer.accept(callback);
-            } else {
-                mLogger.log("KeyguardStateController callback is null", LogLevel.DEBUG);
-            }
-        }
+        mCallbacks.forEach(consumer);
     }
 
     private void notifyUnlockedChanged() {
@@ -506,5 +493,10 @@
         public void onEnabledTrustAgentsChanged(int userId) {
             update(false /* updateAlways */);
         }
+
+        @Override
+        public void onForceIsDismissibleChanged(boolean keepUnlockedOnFold) {
+            update(false /* updateAlways */);
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java
index e5f72eb..9eee5d0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java
@@ -356,6 +356,8 @@
         updateActiveLocationRequests();
     }
 
+    // IMPORTANT: This handler guarantees that any operations on the list of callbacks is
+    // sequential, so no concurrent exceptions
     private final class H extends Handler {
         private static final int MSG_LOCATION_SETTINGS_CHANGED = 1;
         private static final int MSG_LOCATION_ACTIVE_CHANGED = 2;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NextAlarmControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NextAlarmControllerImpl.java
index 63b9ff9..b7d8ee3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NextAlarmControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NextAlarmControllerImpl.java
@@ -124,9 +124,10 @@
     }
 
     private void fireNextAlarmChanged() {
-        int n = mChangeCallbacks.size();
+        ArrayList<NextAlarmChangeCallback> copy = new ArrayList<>(mChangeCallbacks);
+        int n = copy.size();
         for (int i = 0; i < n; i++) {
-            mChangeCallbacks.get(i).onNextAlarmChanged(mNextAlarm);
+            copy.get(i).onNextAlarmChanged(mNextAlarm);
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SafetyController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SafetyController.java
index f3d183c..0176abd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SafetyController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SafetyController.java
@@ -100,10 +100,13 @@
     }
 
     private void handleSafetyCenterEnableChange() {
+        final ArrayList<SafetyController.Listener> copy;
         synchronized (mListeners) {
-            for (int i = 0; i < mListeners.size(); i++) {
-                mListeners.get(i).onSafetyCenterEnableChanged(mSafetyCenterEnabled);
-            }
+            copy = new ArrayList<>(mListeners);
+        }
+        final int n = copy.size();
+        for (int i = 0; i < n; i++) {
+            copy.get(i).onSafetyCenterEnableChanged(mSafetyCenterEnabled);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java
index 3be14bc..10bf068 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java
@@ -48,6 +48,8 @@
     boolean isNetworkLoggingEnabled();
     boolean isVpnEnabled();
     boolean isVpnRestricted();
+    /** Whether the VPN network is validated. */
+    boolean isVpnValidated();
     /** Whether the VPN app should use branded VPN iconography.  */
     boolean isVpnBranded();
     String getPrimaryVpnName();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
index 4a4d4e1..9f4a906 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
@@ -15,6 +15,9 @@
  */
 package com.android.systemui.statusbar.policy;
 
+import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
+import static android.net.NetworkCapabilities.TRANSPORT_VPN;
+
 import android.annotation.Nullable;
 import android.app.admin.DeviceAdminInfo;
 import android.app.admin.DevicePolicyManager;
@@ -32,7 +35,9 @@
 import android.graphics.drawable.Drawable;
 import android.net.ConnectivityManager;
 import android.net.ConnectivityManager.NetworkCallback;
+import android.net.LinkProperties;
 import android.net.Network;
+import android.net.NetworkCapabilities;
 import android.net.NetworkRequest;
 import android.net.VpnManager;
 import android.os.Handler;
@@ -50,12 +55,12 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.net.LegacyVpnInfo;
 import com.android.internal.net.VpnConfig;
-import com.android.systemui.res.R;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dump.DumpManager;
+import com.android.systemui.res.R;
 import com.android.systemui.settings.UserTracker;
 
 import org.xmlpull.v1.XmlPullParserException;
@@ -76,7 +81,10 @@
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
     private static final NetworkRequest REQUEST =
-            new NetworkRequest.Builder().clearCapabilities().build();
+            new NetworkRequest.Builder()
+                    .clearCapabilities()
+                    .addTransportType(TRANSPORT_VPN)
+                    .build();
     private static final int NO_NETWORK = -1;
 
     private static final String VPN_BRANDED_META_DATA = "com.android.systemui.IS_BRANDED";
@@ -99,6 +107,8 @@
     private SparseArray<VpnConfig> mCurrentVpns = new SparseArray<>();
     private int mCurrentUserId;
     private int mVpnUserId;
+    @GuardedBy("mNetworkProperties")
+    private final SparseArray<NetworkProperties> mNetworkProperties = new SparseArray<>();
 
     // Key: userId, Value: whether the user has CACerts installed
     // Needs to be cached here since the query has to be asynchronous
@@ -162,6 +172,21 @@
             pw.print(mCurrentVpns.valueAt(i).user);
         }
         pw.println("}");
+        pw.print("  mNetworkProperties={");
+        synchronized (mNetworkProperties) {
+            for (int i = 0; i < mNetworkProperties.size(); ++i) {
+                if (i > 0) {
+                    pw.print(", ");
+                }
+                pw.print(mNetworkProperties.keyAt(i));
+                pw.print("={");
+                pw.print(mNetworkProperties.valueAt(i).interfaceName);
+                pw.print(", ");
+                pw.print(mNetworkProperties.valueAt(i).validated);
+                pw.print("}");
+            }
+        }
+        pw.println("}");
     }
 
     @Override
@@ -304,6 +329,26 @@
     }
 
     @Override
+    public boolean isVpnValidated() {
+        // Prioritize reporting the network status of the parent user.
+        final VpnConfig primaryVpnConfig = mCurrentVpns.get(mVpnUserId);
+        if (primaryVpnConfig != null) {
+            return getVpnValidationStatus(primaryVpnConfig);
+        }
+        // Identify any Unvalidated status in each active VPN network within other profiles.
+        for (int profileId : mUserManager.getEnabledProfileIds(mVpnUserId)) {
+            final VpnConfig vpnConfig = mCurrentVpns.get(profileId);
+            if (vpnConfig == null) {
+                continue;
+            }
+            if (!getVpnValidationStatus(vpnConfig)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    @Override
     public boolean hasCACertInCurrentUser() {
         Boolean hasCACerts = mHasCACerts.get(mCurrentUserId);
         return hasCACerts != null && hasCACerts.booleanValue();
@@ -429,10 +474,12 @@
     }
 
     private void fireCallbacks() {
+        final ArrayList<SecurityControllerCallback> copy;
         synchronized (mCallbacks) {
-            for (SecurityControllerCallback callback : mCallbacks) {
-                callback.onStateChanged();
-            }
+            copy = new ArrayList<>(mCallbacks);
+        }
+        for (SecurityControllerCallback callback : copy) {
+            callback.onStateChanged();
         }
     }
 
@@ -491,11 +538,74 @@
         @Override
         public void onLost(Network network) {
             if (DEBUG) Log.d(TAG, "onLost " + network.getNetId());
+            synchronized (mNetworkProperties) {
+                mNetworkProperties.delete(network.getNetId());
+            }
             updateState();
             fireCallbacks();
         };
+
+
+        @Override
+        public void onCapabilitiesChanged(Network network, NetworkCapabilities nc) {
+            if (DEBUG) Log.d(TAG, "onCapabilitiesChanged " + network.getNetId());
+            final NetworkProperties properties;
+            synchronized (mNetworkProperties) {
+                properties = mNetworkProperties.get(network.getNetId());
+            }
+            // When a new network appears, the system first notifies the application about
+            // its capabilities through onCapabilitiesChanged. This initial notification
+            // will be skipped because the interface information is included in the
+            // subsequent onLinkPropertiesChanged call. After validating the network, the
+            // system might send another onCapabilitiesChanged notification if the network
+            // becomes validated.
+            if (properties == null) {
+                return;
+            }
+            final boolean validated = nc.hasCapability(NET_CAPABILITY_VALIDATED);
+            if (properties.validated != validated) {
+                properties.validated = validated;
+                fireCallbacks();
+            }
+        }
+
+        @Override
+        public void onLinkPropertiesChanged(Network network, LinkProperties linkProperties) {
+            if (DEBUG) Log.d(TAG, "onLinkPropertiesChanged " + network.getNetId());
+            final String interfaceName = linkProperties.getInterfaceName();
+            if (interfaceName == null) {
+                Log.w(TAG, "onLinkPropertiesChanged event with null interface");
+                return;
+            }
+            synchronized (mNetworkProperties) {
+                final NetworkProperties properties = mNetworkProperties.get(network.getNetId());
+                if (properties == null) {
+                    mNetworkProperties.put(
+                            network.getNetId(),
+                            new NetworkProperties(interfaceName, false));
+                } else {
+                    properties.interfaceName = interfaceName;
+                }
+            }
+        }
     };
 
+    /**
+     *  Retrieve the validation status of the VPN network associated with the given VpnConfig.
+     */
+    private boolean getVpnValidationStatus(@NonNull VpnConfig vpnConfig) {
+        synchronized (mNetworkProperties) {
+            // Find the network has the same interface as the VpnConfig
+            for (int i = 0; i < mNetworkProperties.size(); ++i) {
+                if (mNetworkProperties.valueAt(i).interfaceName.equals(vpnConfig.interfaze)) {
+                    return mNetworkProperties.valueAt(i).validated;
+                }
+            }
+        }
+        // If no matching network is found, consider it validated.
+        return true;
+    }
+
     private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
         @Override public void onReceive(Context context, Intent intent) {
             if (KeyChain.ACTION_TRUST_STORE_CHANGED.equals(intent.getAction())) {
@@ -506,4 +616,17 @@
             }
         }
     };
+
+    /**
+     *  A data class to hold specific Network properties received through the NetworkCallback.
+     */
+    private static class NetworkProperties {
+        public String interfaceName;
+        public boolean validated;
+
+        NetworkProperties(@NonNull String interfaceName, boolean validated) {
+            this.interfaceName = interfaceName;
+            this.validated = validated;
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
index 66bf527..df210b0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
@@ -48,12 +48,12 @@
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.settings.UserTracker;
-import com.android.systemui.util.Utils;
 import com.android.systemui.util.settings.GlobalSettings;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Objects;
+import java.util.function.Consumer;
 
 import javax.inject.Inject;
 
@@ -243,46 +243,43 @@
     }
 
     private void fireNextAlarmChanged() {
-        synchronized (mCallbacksLock) {
-            Utils.safeForeach(mCallbacks, c -> c.onNextAlarmChanged());
-        }
+        fireSafeChange(Callback::onNextAlarmChanged);
     }
 
     private void fireEffectsSuppressorChanged() {
-        synchronized (mCallbacksLock) {
-            Utils.safeForeach(mCallbacks, c -> c.onEffectsSupressorChanged());
-        }
+        fireSafeChange(Callback::onEffectsSupressorChanged);
     }
 
     private void fireZenChanged(int zen) {
-        synchronized (mCallbacksLock) {
-            Utils.safeForeach(mCallbacks, c -> c.onZenChanged(zen));
-        }
+        fireSafeChange(c -> c.onZenChanged(zen));
     }
 
     private void fireZenAvailableChanged(boolean available) {
-        synchronized (mCallbacksLock) {
-            Utils.safeForeach(mCallbacks, c -> c.onZenAvailableChanged(available));
-        }
+        fireSafeChange(c -> c.onZenAvailableChanged(available));
     }
 
     private void fireManualRuleChanged(ZenRule rule) {
-        synchronized (mCallbacksLock) {
-            Utils.safeForeach(mCallbacks, c -> c.onManualRuleChanged(rule));
-        }
+        fireSafeChange(c -> c.onManualRuleChanged(rule));
     }
 
     private void fireConsolidatedPolicyChanged(NotificationManager.Policy policy) {
+        fireSafeChange(c -> c.onConsolidatedPolicyChanged(policy));
+    }
+
+    private void fireSafeChange(Consumer<Callback> action) {
+        final ArrayList<Callback> copy;
         synchronized (mCallbacksLock) {
-            Utils.safeForeach(mCallbacks, c -> c.onConsolidatedPolicyChanged(policy));
+            copy = new ArrayList<>(mCallbacks);
+        }
+        final int n = copy.size();
+        for (int i = 0; i < n; i++) {
+            action.accept(copy.get(i));
         }
     }
 
     @VisibleForTesting
     protected void fireConfigChanged(ZenModeConfig config) {
-        synchronized (mCallbacksLock) {
-            Utils.safeForeach(mCallbacks, c -> c.onConfigChanged(config));
-        }
+        fireSafeChange(c -> c.onConfigChanged(config));
     }
 
     @VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/toast/ToastModule.kt b/packages/SystemUI/src/com/android/systemui/toast/ToastModule.kt
new file mode 100644
index 0000000..6cd9993
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/toast/ToastModule.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.toast
+
+import com.android.systemui.CoreStartable
+import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.ClassKey
+import dagger.multibindings.IntoMap
+import dagger.multibindings.IntoSet
+
+@Module
+interface ToastModule {
+    /** Starts ToastUI. */
+    @Binds
+    @IntoMap
+    @ClassKey(ToastUI::class)
+    fun bindToastUIStartable(service: ToastUI): CoreStartable
+
+    /** Listen to config changes for ToastUI. */
+    @Binds @IntoSet fun bindToastUIConfigChanges(service: ToastUI): ConfigurationListener
+}
diff --git a/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java b/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java
index 27f8121..85a455d 100644
--- a/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java
+++ b/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java
@@ -42,6 +42,7 @@
 import com.android.systemui.CoreStartable;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.policy.ConfigurationController;
 
 import java.util.Objects;
 
@@ -51,7 +52,10 @@
  * Controls display of text toasts.
  */
 @SysUISingleton
-public class ToastUI implements CoreStartable, CommandQueue.Callbacks {
+public class ToastUI implements
+        CoreStartable,
+        ConfigurationController.ConfigurationListener,
+        CommandQueue.Callbacks {
     // values from NotificationManagerService#LONG_DELAY and NotificationManagerService#SHORT_DELAY
     private static final int TOAST_LONG_TIME = 3500; // 3.5 seconds
     private static final int TOAST_SHORT_TIME = 2000; // 2 seconds
@@ -187,7 +191,7 @@
     }
 
     @Override
-    public void onConfigurationChanged(Configuration newConfig) {
+    public void onConfigChanged(Configuration newConfig) {
         if (newConfig.orientation != mOrientation) {
             mOrientation = newConfig.orientation;
             if (mToast != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
index 8087a87..550a65c 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
@@ -48,6 +48,8 @@
 import com.android.systemui.statusbar.phone.SystemUIDialog;
 import com.android.systemui.util.leak.LeakDetector;
 
+import dagger.Lazy;
+
 import java.util.HashSet;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
@@ -87,6 +89,7 @@
     // Set of all tunables, used for leak detection.
     private final HashSet<Tunable> mTunables = LeakDetector.ENABLED ? new HashSet<>() : null;
     private final Context mContext;
+    private final Lazy<SystemUIDialog.Factory> mSystemUIDialogFactoryLazy;
     private final LeakDetector mLeakDetector;
     private final DemoModeController mDemoModeController;
 
@@ -104,9 +107,11 @@
             @Main Handler mainHandler,
             LeakDetector leakDetector,
             DemoModeController demoModeController,
-            UserTracker userTracker) {
+            UserTracker userTracker,
+            Lazy<SystemUIDialog.Factory> systemUIDialogFactoryLazy) {
         super(context);
         mContext = context;
+        mSystemUIDialogFactoryLazy = systemUIDialogFactoryLazy;
         mContentResolver = mContext.getContentResolver();
         mLeakDetector = leakDetector;
         mDemoModeController = demoModeController;
@@ -301,7 +306,7 @@
 
     @Override
     public void showResetRequest(Runnable onDisabled) {
-        SystemUIDialog dialog = new SystemUIDialog(mContext);
+        SystemUIDialog dialog = mSystemUIDialogFactoryLazy.get().create();
         dialog.setShowForAllUsers(true);
         dialog.setMessage(R.string.remove_from_settings_prompt);
         dialog.setButton(DialogInterface.BUTTON_NEGATIVE, mContext.getString(R.string.cancel),
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyLogger.kt b/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyLogger.kt
new file mode 100644
index 0000000..76f7609
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyLogger.kt
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.unfold
+
+import com.android.systemui.shared.system.SysUiStatsLog
+
+class DisplaySwitchLatencyLogger {
+
+    /**
+     * Based on data present in [displaySwitchLatencyEvent], logs metrics for atom
+     * [DisplaySwitchLatencyTracked]
+     */
+    fun log(displaySwitchLatencyEvent: DisplaySwitchLatencyTracker.DisplaySwitchLatencyEvent) {
+        with(displaySwitchLatencyEvent) {
+            SysUiStatsLog.write(
+                SysUiStatsLog.DISPLAY_SWITCH_LATENCY_TRACKED,
+                latencyMs,
+                fromFoldableDeviceState,
+                fromState,
+                fromFocusedAppUid,
+                fromPipAppUid,
+                fromVisibleAppsUid.toIntArray(),
+                fromDensityDpi,
+                toState,
+                toFoldableDeviceState,
+                toFocusedAppUid,
+                toPipAppUid,
+                toVisibleAppsUid.toIntArray(),
+                toDensityDpi,
+                notificationCount,
+                externalDisplayCount,
+                throttlingLevel,
+                vskinTemperatureC,
+                hallSensorToFirstHingeAngleChangeMs,
+                hallSensorToDeviceStateChangeMs,
+                onScreenTurningOnToOnDrawnMs,
+                onDrawnToOnScreenTurnedOnMs,
+            )
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyTracker.kt b/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyTracker.kt
new file mode 100644
index 0000000..92a64a6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyTracker.kt
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.unfold
+
+import android.content.Context
+import android.util.Log
+import com.android.app.tracing.TraceUtils.instantForTrack
+import com.android.app.tracing.TraceUtils.traceAsync
+import com.android.systemui.CoreStartable
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.display.data.repository.DeviceStateRepository
+import com.android.systemui.display.data.repository.DeviceStateRepository.DeviceState
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.power.shared.model.ScreenPowerState
+import com.android.systemui.power.shared.model.WakeSleepReason
+import com.android.systemui.shared.system.SysUiStatsLog
+import com.android.systemui.unfold.dagger.UnfoldSingleThreadBg
+import com.android.systemui.unfold.domain.interactor.UnfoldTransitionInteractor
+import com.android.systemui.util.Compile
+import com.android.systemui.util.Utils.isDeviceFoldable
+import com.android.systemui.util.animation.data.repository.AnimationStatusRepository
+import com.android.systemui.util.kotlin.pairwise
+import com.android.systemui.util.time.SystemClock
+import com.android.systemui.util.time.measureTimeMillis
+import java.util.concurrent.Executor
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.asCoroutineDispatcher
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.launch
+
+/**
+ * [DisplaySwitchLatencyTracker] tracks latency and related fields for display switch of a foldable
+ * device. This class populates [DisplaySwitchLatencyEvent] while an ongoing display switch event
+ */
+@SysUISingleton
+class DisplaySwitchLatencyTracker
+@Inject
+constructor(
+    private val context: Context,
+    private val deviceStateRepository: DeviceStateRepository,
+    private val powerInteractor: PowerInteractor,
+    private val unfoldTransitionInteractor: UnfoldTransitionInteractor,
+    private val animationStatusRepository: AnimationStatusRepository,
+    private val keyguardInteractor: KeyguardInteractor,
+    @UnfoldSingleThreadBg private val singleThreadBgExecutor: Executor,
+    @Application private val applicationScope: CoroutineScope,
+    private val displaySwitchLatencyLogger: DisplaySwitchLatencyLogger,
+    private val systemClock: SystemClock
+) : CoreStartable {
+
+    private val backgroundDispatcher = singleThreadBgExecutor.asCoroutineDispatcher()
+
+    @OptIn(ExperimentalCoroutinesApi::class)
+    override fun start() {
+        if (!isDeviceFoldable(context)) {
+            return
+        }
+        applicationScope.launch(backgroundDispatcher) {
+            deviceStateRepository.state
+                .pairwise()
+                .filter {
+                    // Start tracking only when the foldable device is
+                    //folding(UNFOLDED/HALF_FOLDED -> FOLDED) or
+                    //unfolding(FOLDED -> HALF_FOLD/UNFOLDED)
+                    foldableDeviceState ->
+                    foldableDeviceState.previousValue == DeviceState.FOLDED ||
+                        foldableDeviceState.newValue == DeviceState.FOLDED
+                }
+                .flatMapLatest { foldableDeviceState ->
+                    flow {
+                        var displaySwitchLatencyEvent = DisplaySwitchLatencyEvent()
+                        val toFoldableDeviceState = foldableDeviceState.newValue.toStatsInt()
+                        displaySwitchLatencyEvent =
+                            displaySwitchLatencyEvent.withBeforeFields(
+                                foldableDeviceState.previousValue.toStatsInt()
+                            )
+
+                        val displaySwitchTimeMs =
+                            measureTimeMillis(systemClock) {
+                                traceAsync(TAG, "displaySwitch") {
+                                    waitForDisplaySwitch(toFoldableDeviceState)
+                                }
+                            }
+
+                        displaySwitchLatencyEvent =
+                            displaySwitchLatencyEvent.withAfterFields(
+                                toFoldableDeviceState,
+                                displaySwitchTimeMs.toInt(),
+                                getCurrentState()
+                            )
+                        emit(displaySwitchLatencyEvent)
+                    }
+                }
+                .collect { displaySwitchLatencyLogger.log(it) }
+        }
+    }
+
+    private fun DeviceState.toStatsInt(): Int =
+        when (this) {
+            DeviceState.FOLDED -> FOLDABLE_DEVICE_STATE_CLOSED
+            DeviceState.HALF_FOLDED -> FOLDABLE_DEVICE_STATE_HALF_OPEN
+            DeviceState.UNFOLDED -> FOLDABLE_DEVICE_STATE_OPEN
+            DeviceState.CONCURRENT_DISPLAY -> FOLDABLE_DEVICE_STATE_FLIPPED
+            else -> FOLDABLE_DEVICE_STATE_UNKNOWN
+        }
+
+    private suspend fun waitForDisplaySwitch(toFoldableDeviceState: Int) {
+        val isTransitionEnabled =
+            unfoldTransitionInteractor.isAvailable &&
+                animationStatusRepository.areAnimationsEnabled().first()
+        if (shouldWaitForScreenOn(toFoldableDeviceState, isTransitionEnabled)) {
+            waitForScreenTurnedOn()
+        } else {
+            traceAsync(TAG, "waitForTransitionStart()") {
+                unfoldTransitionInteractor.waitForTransitionStart()
+            }
+        }
+    }
+
+    private fun shouldWaitForScreenOn(
+        toFoldableDeviceState: Int,
+        isTransitionEnabled: Boolean
+    ): Boolean = (toFoldableDeviceState == FOLDABLE_DEVICE_STATE_CLOSED || !isTransitionEnabled)
+
+    private suspend fun waitForScreenTurnedOn() {
+        traceAsync(TAG, "waitForScreenTurnedOn()") {
+            powerInteractor.screenPowerState.filter { it == ScreenPowerState.SCREEN_ON }.first()
+        }
+    }
+
+    private fun getCurrentState(): Int =
+        when {
+            isStateAod() -> SysUiStatsLog.DISPLAY_SWITCH_LATENCY_TRACKED__TO_STATE__AOD
+            else -> SysUiStatsLog.DISPLAY_SWITCH_LATENCY_TRACKED__TO_STATE__UNKNOWN
+        }
+
+    private fun isStateAod(): Boolean {
+        val lastWakefulnessEvent = powerInteractor.detailedWakefulness.value
+        val isAodEnabled = keyguardInteractor.isAodAvailable.value
+
+        return (lastWakefulnessEvent.isAsleep() &&
+            (lastWakefulnessEvent.lastSleepReason == WakeSleepReason.FOLD) &&
+            isAodEnabled)
+    }
+
+    private inline fun log(msg: () -> String) {
+        if (DEBUG) Log.d(TAG, msg())
+    }
+
+    private fun DisplaySwitchLatencyEvent.withBeforeFields(
+        fromFoldableDeviceState: Int
+    ): DisplaySwitchLatencyEvent {
+        log { "fromFoldableDeviceState=$fromFoldableDeviceState" }
+        instantForTrack(TAG, "fromFoldableDeviceState=$fromFoldableDeviceState")
+
+        return copy(fromFoldableDeviceState = fromFoldableDeviceState)
+    }
+
+    private fun DisplaySwitchLatencyEvent.withAfterFields(
+        toFoldableDeviceState: Int,
+        displaySwitchTimeMs: Int,
+        toState: Int
+    ): DisplaySwitchLatencyEvent {
+        log {
+            "toFoldableDeviceState=$toFoldableDeviceState, " +
+                "toState=$toState, " +
+                "latencyMs=$displaySwitchTimeMs"
+        }
+        instantForTrack(TAG, "toFoldableDeviceState=$toFoldableDeviceState, toState=$toState")
+
+        return copy(
+            toFoldableDeviceState = toFoldableDeviceState,
+            latencyMs = displaySwitchTimeMs,
+            toState = toState
+        )
+    }
+
+    /**
+     * Stores values corresponding to all respective [DisplaySwitchLatencyTrackedField] in a single
+     * event of display switch for foldable devices.
+     *
+     * Once the data is captured in this data class and appropriate to log, it is logged through
+     * [DisplaySwitchLatencyLogger]
+     */
+    data class DisplaySwitchLatencyEvent(
+        val latencyMs: Int = VALUE_UNKNOWN,
+        val fromFoldableDeviceState: Int = FOLDABLE_DEVICE_STATE_UNKNOWN,
+        val fromState: Int = SysUiStatsLog.DISPLAY_SWITCH_LATENCY_TRACKED__FROM_STATE__UNKNOWN,
+        val fromFocusedAppUid: Int = VALUE_UNKNOWN,
+        val fromPipAppUid: Int = VALUE_UNKNOWN,
+        val fromVisibleAppsUid: Set<Int> = setOf(),
+        val fromDensityDpi: Int = VALUE_UNKNOWN,
+        val toFoldableDeviceState: Int = FOLDABLE_DEVICE_STATE_UNKNOWN,
+        val toState: Int = SysUiStatsLog.DISPLAY_SWITCH_LATENCY_TRACKED__FROM_STATE__UNKNOWN,
+        val toFocusedAppUid: Int = VALUE_UNKNOWN,
+        val toPipAppUid: Int = VALUE_UNKNOWN,
+        val toVisibleAppsUid: Set<Int> = setOf(),
+        val toDensityDpi: Int = VALUE_UNKNOWN,
+        val notificationCount: Int = VALUE_UNKNOWN,
+        val externalDisplayCount: Int = VALUE_UNKNOWN,
+        val throttlingLevel: Int =
+            SysUiStatsLog.DISPLAY_SWITCH_LATENCY_TRACKED__THROTTLING_LEVEL__NONE,
+        val vskinTemperatureC: Int = VALUE_UNKNOWN,
+        val hallSensorToFirstHingeAngleChangeMs: Int = VALUE_UNKNOWN,
+        val hallSensorToDeviceStateChangeMs: Int = VALUE_UNKNOWN,
+        val onScreenTurningOnToOnDrawnMs: Int = VALUE_UNKNOWN,
+        val onDrawnToOnScreenTurnedOnMs: Int = VALUE_UNKNOWN
+    )
+
+    companion object {
+        private const val VALUE_UNKNOWN = -1
+        private const val TAG = "DisplaySwitchLatency"
+        private val DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.VERBOSE)
+
+        private const val FOLDABLE_DEVICE_STATE_UNKNOWN =
+            SysUiStatsLog.DISPLAY_SWITCH_LATENCY_TRACKED__FROM_FOLDABLE_DEVICE_STATE__STATE_UNKNOWN
+        const val FOLDABLE_DEVICE_STATE_CLOSED =
+            SysUiStatsLog.DISPLAY_SWITCH_LATENCY_TRACKED__FROM_FOLDABLE_DEVICE_STATE__STATE_CLOSED
+        const val FOLDABLE_DEVICE_STATE_HALF_OPEN =
+            SysUiStatsLog
+                .DISPLAY_SWITCH_LATENCY_TRACKED__FROM_FOLDABLE_DEVICE_STATE__STATE_HALF_OPENED
+        private const val FOLDABLE_DEVICE_STATE_OPEN =
+            SysUiStatsLog.DISPLAY_SWITCH_LATENCY_TRACKED__FROM_FOLDABLE_DEVICE_STATE__STATE_OPENED
+        private const val FOLDABLE_DEVICE_STATE_FLIPPED =
+            SysUiStatsLog.DISPLAY_SWITCH_LATENCY_TRACKED__FROM_FOLDABLE_DEVICE_STATE__STATE_FLIPPED
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTraceLogger.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTraceLogger.kt
index 94912bf8..adf50a1 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTraceLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTraceLogger.kt
@@ -22,8 +22,8 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.unfold.data.repository.FoldStateRepository
 import com.android.systemui.unfold.system.DeviceStateRepository
-import com.android.systemui.unfold.updates.FoldStateRepository
 import javax.inject.Inject
 import kotlin.coroutines.CoroutineContext
 import kotlinx.coroutines.CoroutineScope
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt
index 50515da..8bef53c 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt
@@ -27,6 +27,8 @@
 import com.android.systemui.unfold.config.UnfoldTransitionConfig
 import com.android.systemui.unfold.dagger.UnfoldBgProgressFlag
 import com.android.systemui.unfold.dagger.UnfoldMain
+import com.android.systemui.unfold.data.repository.FoldStateRepository
+import com.android.systemui.unfold.data.repository.FoldStateRepositoryImpl
 import com.android.systemui.unfold.data.repository.UnfoldTransitionRepository
 import com.android.systemui.unfold.data.repository.UnfoldTransitionRepositoryImpl
 import com.android.systemui.unfold.domain.interactor.UnfoldTransitionInteractor
@@ -168,6 +170,11 @@
     @Provides
     fun screenStatusProvider(impl: LifecycleScreenStatusProvider): ScreenStatusProvider = impl
 
+    @Provides
+    @Singleton
+    fun provideDisplaySwitchLatencyLogger(): DisplaySwitchLatencyLogger =
+        DisplaySwitchLatencyLogger()
+
     @Module
     interface Bindings {
         @Binds
@@ -178,6 +185,8 @@
         @Binds fun bindRepository(impl: UnfoldTransitionRepositoryImpl): UnfoldTransitionRepository
 
         @Binds fun bindInteractor(impl: UnfoldTransitionInteractorImpl): UnfoldTransitionInteractor
+
+        @Binds fun bindFoldStateRepository(impl: FoldStateRepositoryImpl): FoldStateRepository
     }
 }
 
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/FoldStateRepository.kt b/packages/SystemUI/src/com/android/systemui/unfold/data/repository/FoldStateRepository.kt
similarity index 84%
rename from packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/FoldStateRepository.kt
rename to packages/SystemUI/src/com/android/systemui/unfold/data/repository/FoldStateRepository.kt
index 61b0b40..04b00ca 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/FoldStateRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/data/repository/FoldStateRepository.kt
@@ -13,9 +13,15 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.systemui.unfold.updates
+package com.android.systemui.unfold.data.repository
 
-import com.android.systemui.unfold.updates.FoldStateRepository.FoldUpdate
+import com.android.systemui.unfold.data.repository.FoldStateRepository.FoldUpdate
+import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_CLOSED
+import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_FULL_OPEN
+import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_HALF_OPEN
+import com.android.systemui.unfold.updates.FOLD_UPDATE_START_CLOSING
+import com.android.systemui.unfold.updates.FOLD_UPDATE_START_OPENING
+import com.android.systemui.unfold.updates.FoldStateProvider
 import javax.inject.Inject
 import kotlinx.coroutines.channels.Channel
 import kotlinx.coroutines.channels.awaitClose
@@ -50,7 +56,7 @@
                     FOLD_UPDATE_FINISH_HALF_OPEN -> FINISH_HALF_OPEN
                     FOLD_UPDATE_FINISH_FULL_OPEN -> FINISH_FULL_OPEN
                     FOLD_UPDATE_FINISH_CLOSED -> FINISH_CLOSED
-                    else -> error("FoldUpdateNotFound")
+                    else -> error("Fold update with id $oldId is not supported")
                 }
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/domain/interactor/UnfoldTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/unfold/domain/interactor/UnfoldTransitionInteractor.kt
index a2e77af..3e2e564 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/domain/interactor/UnfoldTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/domain/interactor/UnfoldTransitionInteractor.kt
@@ -15,16 +15,26 @@
  */
 package com.android.systemui.unfold.domain.interactor
 
-import com.android.systemui.unfold.data.repository.UnfoldTransitionStatus.TransitionFinished
 import com.android.systemui.unfold.data.repository.UnfoldTransitionRepository
+import com.android.systemui.unfold.data.repository.UnfoldTransitionStatus.TransitionFinished
+import com.android.systemui.unfold.data.repository.UnfoldTransitionStatus.TransitionStarted
 import javax.inject.Inject
 import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.flow.first
 
+/**
+ * Contains business-logic related to fold-unfold transitions while interacting with
+ * [UnfoldTransitionRepository]
+ */
 interface UnfoldTransitionInteractor {
+    /** Returns availability of fold/unfold transitions on the device */
     val isAvailable: Boolean
 
+    /** Suspends and waits for a fold/unfold transition to finish */
     suspend fun waitForTransitionFinish()
+
+    /** Suspends and waits for a fold/unfold transition to start */
+    suspend fun waitForTransitionStart()
 }
 
 class UnfoldTransitionInteractorImpl
@@ -37,4 +47,8 @@
     override suspend fun waitForTransitionFinish() {
         repository.transitionStatus.filter { it is TransitionFinished }.first()
     }
+
+    override suspend fun waitForTransitionStart() {
+        repository.transitionStatus.filter { it is TransitionStarted }.first()
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/util/ReferenceExt.kt b/packages/SystemUI/src/com/android/systemui/util/ReferenceExt.kt
index ac04d31..4f7dce3 100644
--- a/packages/SystemUI/src/com/android/systemui/util/ReferenceExt.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/ReferenceExt.kt
@@ -2,6 +2,7 @@
 
 import java.lang.ref.SoftReference
 import java.lang.ref.WeakReference
+import java.util.concurrent.atomic.AtomicReference
 import kotlin.properties.ReadWriteProperty
 import kotlin.reflect.KProperty
 
@@ -48,3 +49,25 @@
         }
     }
 }
+
+/**
+ * Creates a nullable Kotlin idiomatic [AtomicReference].
+ *
+ * Usage:
+ * ```
+ * var atomicReferenceObj: Object? by nullableAtomicReference(null)
+ * atomicReferenceObj = Object()
+ * ```
+ */
+fun <T> nullableAtomicReference(obj: T? = null): ReadWriteProperty<Any?, T?> {
+    return object : ReadWriteProperty<Any?, T?> {
+        val t = AtomicReference(obj)
+        override fun getValue(thisRef: Any?, property: KProperty<*>): T? {
+            return t.get()
+        }
+
+        override fun setValue(thisRef: Any?, property: KProperty<*>, value: T?) {
+            t.set(value)
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/Utils.java b/packages/SystemUI/src/com/android/systemui/util/Utils.java
index fa6d055..7861ded 100644
--- a/packages/SystemUI/src/com/android/systemui/util/Utils.java
+++ b/packages/SystemUI/src/com/android/systemui/util/Utils.java
@@ -82,6 +82,14 @@
     }
 
     /**
+     * Returns {@code true} if the device is a foldable device
+     */
+    public static boolean isDeviceFoldable(Context context) {
+        return context.getResources()
+                .getIntArray(com.android.internal.R.array.config_foldedDeviceStates).length != 0;
+    }
+
+    /**
      * Allow the media player to be shown in the QS area, controlled by 2 flags.
      * On by default, but can be disabled by setting either flag to 0/false.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/util/service/ObservableServiceConnection.java b/packages/SystemUI/src/com/android/systemui/util/service/ObservableServiceConnection.java
index df5162a..3d724e1 100644
--- a/packages/SystemUI/src/com/android/systemui/util/service/ObservableServiceConnection.java
+++ b/packages/SystemUI/src/com/android/systemui/util/service/ObservableServiceConnection.java
@@ -22,12 +22,17 @@
 import android.content.Intent;
 import android.content.ServiceConnection;
 import android.os.IBinder;
+import android.util.IndentingPrintWriter;
 import android.util.Log;
 
+import androidx.annotation.NonNull;
+
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.settings.UserTracker;
+import com.android.systemui.util.DumpUtilsKt;
 import com.android.systemui.util.annotations.WeaklyReferencedCallback;
 
+import java.io.PrintWriter;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.ref.WeakReference;
@@ -244,6 +249,21 @@
         });
     }
 
+    void dump(@NonNull PrintWriter pw) {
+        IndentingPrintWriter ipw = DumpUtilsKt.asIndenting(pw);
+        ipw.println("ObservableServiceConnection state:");
+        DumpUtilsKt.withIncreasedIndent(ipw, () -> {
+            ipw.println("mServiceIntent: " + mServiceIntent);
+            ipw.println("mLastDisconnectReason: " + mLastDisconnectReason.orElse(-1));
+            ipw.println("Callbacks:");
+            DumpUtilsKt.withIncreasedIndent(ipw, () -> {
+                for (WeakReference<Callback<T>> cbRef : mCallbacks) {
+                    ipw.println(cbRef.get());
+                }
+            });
+        });
+    }
+
     private void applyToCallbacksLocked(Consumer<Callback<T>> applicator) {
         final Iterator<WeakReference<Callback<T>>> iterator = mCallbacks.iterator();
 
diff --git a/packages/SystemUI/src/com/android/systemui/util/service/PersistentConnectionManager.java b/packages/SystemUI/src/com/android/systemui/util/service/PersistentConnectionManager.java
index 6e19bed..9b72eb7 100644
--- a/packages/SystemUI/src/com/android/systemui/util/service/PersistentConnectionManager.java
+++ b/packages/SystemUI/src/com/android/systemui/util/service/PersistentConnectionManager.java
@@ -17,6 +17,7 @@
 package com.android.systemui.util.service;
 
 import static com.android.systemui.util.service.dagger.ObservableServiceModule.BASE_RECONNECT_DELAY_MS;
+import static com.android.systemui.util.service.dagger.ObservableServiceModule.DUMPSYS_NAME;
 import static com.android.systemui.util.service.dagger.ObservableServiceModule.MAX_RECONNECT_ATTEMPTS;
 import static com.android.systemui.util.service.dagger.ObservableServiceModule.MIN_CONNECTION_DURATION_MS;
 import static com.android.systemui.util.service.dagger.ObservableServiceModule.OBSERVER;
@@ -24,9 +25,15 @@
 
 import android.util.Log;
 
+import androidx.annotation.NonNull;
+
+import com.android.systemui.Dumpable;
+import com.android.systemui.dump.DumpManager;
 import com.android.systemui.util.concurrency.DelayableExecutor;
 import com.android.systemui.util.time.SystemClock;
 
+import java.io.PrintWriter;
+
 import javax.inject.Inject;
 import javax.inject.Named;
 
@@ -35,7 +42,7 @@
  * {@link ObservableServiceConnection}.
  * @param <T> The transformed connection type handled by the service.
  */
-public class PersistentConnectionManager<T> {
+public class PersistentConnectionManager<T> implements Dumpable {
     private static final String TAG = "PersistentConnManager";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
@@ -45,6 +52,8 @@
     private final int mMaxReconnectAttempts;
     private final int mMinConnectionDuration;
     private final Observer mObserver;
+    private final DumpManager mDumpManager;
+    private final String mDumpsysName;
 
     private int mReconnectAttempts = 0;
     private Runnable mCurrentReconnectCancelable;
@@ -89,6 +98,8 @@
     public PersistentConnectionManager(
             SystemClock clock,
             DelayableExecutor mainExecutor,
+            DumpManager dumpManager,
+            @Named(DUMPSYS_NAME) String dumpsysName,
             @Named(SERVICE_CONNECTION) ObservableServiceConnection<T> serviceConnection,
             @Named(MAX_RECONNECT_ATTEMPTS) int maxReconnectAttempts,
             @Named(BASE_RECONNECT_DELAY_MS) int baseReconnectDelayMs,
@@ -98,6 +109,8 @@
         mMainExecutor = mainExecutor;
         mConnection = serviceConnection;
         mObserver = observer;
+        mDumpManager = dumpManager;
+        mDumpsysName = TAG + "#" + dumpsysName;
 
         mMaxReconnectAttempts = maxReconnectAttempts;
         mBaseReconnectDelayMs = baseReconnectDelayMs;
@@ -108,6 +121,7 @@
      * Begins the {@link PersistentConnectionManager} by connecting to the associated service.
      */
     public void start() {
+        mDumpManager.registerCriticalDumpable(mDumpsysName, this);
         mConnection.addCallback(mConnectionCallback);
         mObserver.addCallback(mObserverCallback);
         initiateConnectionAttempt();
@@ -120,6 +134,32 @@
         mConnection.removeCallback(mConnectionCallback);
         mObserver.removeCallback(mObserverCallback);
         mConnection.unbind();
+        mDumpManager.unregisterDumpable(mDumpsysName);
+    }
+
+    /**
+     * Add a callback to the {@link ObservableServiceConnection}.
+     * @param callback The callback to add.
+     */
+    public void addConnectionCallback(ObservableServiceConnection.Callback<T> callback) {
+        mConnection.addCallback(callback);
+    }
+
+    /**
+     * Remove a callback from the {@link ObservableServiceConnection}.
+     * @param callback The callback to remove.
+     */
+    public void removeConnectionCallback(ObservableServiceConnection.Callback<T> callback) {
+        mConnection.removeCallback(callback);
+    }
+
+    @Override
+    public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
+        pw.println("mMaxReconnectAttempts: " + mMaxReconnectAttempts);
+        pw.println("mBaseReconnectDelayMs: " + mBaseReconnectDelayMs);
+        pw.println("mMinConnectionDuration: " + mMinConnectionDuration);
+        pw.println("mReconnectAttempts: " + mReconnectAttempts);
+        mConnection.dump(pw);
     }
 
     private void initiateConnectionAttempt() {
diff --git a/packages/SystemUI/src/com/android/systemui/util/service/dagger/ObservableServiceModule.java b/packages/SystemUI/src/com/android/systemui/util/service/dagger/ObservableServiceModule.java
index bcf34f8..c52c524 100644
--- a/packages/SystemUI/src/com/android/systemui/util/service/dagger/ObservableServiceModule.java
+++ b/packages/SystemUI/src/com/android/systemui/util/service/dagger/ObservableServiceModule.java
@@ -19,14 +19,14 @@
 
 import android.content.res.Resources;
 
-import com.android.systemui.res.R;
 import com.android.systemui.dagger.qualifiers.Main;
-
-import javax.inject.Named;
+import com.android.systemui.res.R;
 
 import dagger.Module;
 import dagger.Provides;
 
+import javax.inject.Named;
+
 /**
  * Module containing components and parameters for
  * {@link com.android.systemui.util.service.ObservableServiceConnection}
@@ -41,6 +41,7 @@
     public static final String MIN_CONNECTION_DURATION_MS = "min_connection_duration_ms";
     public static final String SERVICE_CONNECTION = "service_connection";
     public static final String OBSERVER = "observer";
+    public static final String DUMPSYS_NAME = "dumpsys_name";
 
     @Provides
     @Named(MAX_RECONNECT_ATTEMPTS)
diff --git a/packages/SystemUI/src/com/android/systemui/util/time/MeasureTimeUtil.kt b/packages/SystemUI/src/com/android/systemui/util/time/MeasureTimeUtil.kt
new file mode 100644
index 0000000..f131968
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/time/MeasureTimeUtil.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.time
+
+import kotlin.contracts.ExperimentalContracts
+import kotlin.contracts.InvocationKind
+import kotlin.contracts.contract
+
+/**
+ * Executes the given [block] and returns elapsed time using provided [systemClock] in milliseconds.
+ */
+@OptIn(ExperimentalContracts::class)
+inline fun measureTimeMillis(systemClock: SystemClock, block: () -> Unit): Long {
+    contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
+    val start = systemClock.currentTimeMillis()
+    block()
+    return systemClock.currentTimeMillis() - start
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
index 3451ae0..dc2b80c 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
@@ -22,16 +22,17 @@
 import android.util.Log;
 
 import com.android.systemui.CoreStartable;
-import com.android.systemui.res.R;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.qs.tiles.DndTile;
+import com.android.systemui.res.R;
+import com.android.systemui.statusbar.policy.ConfigurationController;
 
 import java.io.PrintWriter;
 
 import javax.inject.Inject;
 
 @SysUISingleton
-public class VolumeUI implements CoreStartable {
+public class VolumeUI implements CoreStartable, ConfigurationController.ConfigurationListener {
     private static final String TAG = "VolumeUI";
     private static boolean LOGD = Log.isLoggable(TAG, Log.DEBUG);
 
@@ -60,7 +61,7 @@
     }
 
     @Override
-    public void onConfigurationChanged(Configuration newConfig) {
+    public void onConfigChanged(Configuration newConfig) {
         if (!mEnabled) return;
         mVolumeComponent.onConfigurationChanged(newConfig);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java b/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
index 53217d4..497c4cb 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
@@ -21,6 +21,7 @@
 import android.os.Looper;
 
 import com.android.internal.jank.InteractionJankMonitor;
+import com.android.systemui.CoreStartable;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.media.dialog.MediaOutputDialogFactory;
 import com.android.systemui.plugins.ActivityStarter;
@@ -36,20 +37,45 @@
 import com.android.systemui.volume.VolumeDialogComponent;
 import com.android.systemui.volume.VolumeDialogImpl;
 import com.android.systemui.volume.VolumePanelFactory;
+import com.android.systemui.volume.VolumeUI;
+import com.android.systemui.volume.panel.dagger.VolumePanelComponent;
+import com.android.systemui.volume.panel.dagger.factory.VolumePanelComponentFactory;
 
 import dagger.Binds;
 import dagger.Lazy;
 import dagger.Module;
 import dagger.Provides;
+import dagger.multibindings.ClassKey;
+import dagger.multibindings.IntoMap;
+import dagger.multibindings.IntoSet;
 
 /** Dagger Module for code in the volume package. */
-@Module
+@Module(
+        subcomponents = {
+                VolumePanelComponent.class
+        }
+)
 public interface VolumeModule {
-    /** */
+    /** Starts VolumeUI. */
+    @Binds
+    @IntoMap
+    @ClassKey(VolumeUI.class)
+    CoreStartable bindVolumeUIStartable(VolumeUI impl);
+
+    /** Listen to config changes for VolumeUI. */
+    @Binds
+    @IntoSet
+    ConfigurationController.ConfigurationListener bindVolumeUIConfigChanges(VolumeUI impl);
+
+    /**  */
     @Binds
     VolumeComponent provideVolumeComponent(VolumeDialogComponent volumeDialogComponent);
 
-    /** */
+    /**  */
+    @Binds
+    VolumePanelComponentFactory bindVolumePanelComponentFactory(VolumePanelComponent.Factory impl);
+
+    /**  */
     @Provides
     static VolumeDialog provideVolumeDialog(
             Context context,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileUser.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/VolumePanelComponentKey.kt
similarity index 70%
copy from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileUser.kt
copy to packages/SystemUI/src/com/android/systemui/volume/panel/VolumePanelComponentKey.kt
index efc7431..22a74d2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileUser.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/VolumePanelComponentKey.kt
@@ -14,12 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.custom.di.bound
+package com.android.systemui.volume.panel
 
-import javax.inject.Qualifier
-
-/** User associated with current custom tile binding. */
-@Qualifier
-@MustBeDocumented
-@Retention(AnnotationRetention.RUNTIME)
-annotation class CustomTileUser
+/** Uniquely identifies the [com.android.systemui.volume.panel.ui.VolumePanelComponent]. */
+typealias VolumePanelComponentKey = String
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/dagger/CoroutineModule.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/dagger/CoroutineModule.kt
new file mode 100644
index 0000000..3ce0bac
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/dagger/CoroutineModule.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.panel.dagger
+
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
+import dagger.Module
+import dagger.Provides
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.SupervisorJob
+
+/** Provides Volume Panel coroutine tools. */
+@Module
+interface CoroutineModule {
+
+    companion object {
+
+        /**
+         * Provides a coroutine scope to use inside [VolumePanelScope].
+         * [com.android.systemui.volume.panel.ui.viewmodel.VolumePanelViewModel] manages the
+         * lifecycle of this scope. It's cancelled when the View Model is destroyed. This helps to
+         * free occupied resources when volume panel is not shown.
+         */
+        @VolumePanelScope
+        @Provides
+        fun provideCoroutineScope(@Application applicationScope: CoroutineScope): CoroutineScope =
+            CoroutineScope(applicationScope.coroutineContext + SupervisorJob())
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/dagger/DefaultMultibindsModule.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/dagger/DefaultMultibindsModule.kt
new file mode 100644
index 0000000..3660ac1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/dagger/DefaultMultibindsModule.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.panel.dagger
+
+import com.android.systemui.volume.panel.VolumePanelComponentKey
+import com.android.systemui.volume.panel.domain.ComponentAvailabilityCriteria
+import dagger.Module
+import dagger.multibindings.Multibinds
+
+/**
+ * Provides empty multibinding maps for [ComponentAvailabilityCriteria] and [VolumePanelComponent]
+ */
+@Module
+interface DefaultMultibindsModule {
+
+    @Multibinds fun criteriaMap(): Map<VolumePanelComponentKey, ComponentAvailabilityCriteria>
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/dagger/VolumePanelComponent.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/dagger/VolumePanelComponent.kt
new file mode 100644
index 0000000..0a057eb
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/dagger/VolumePanelComponent.kt
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.panel.dagger
+
+import com.android.systemui.volume.panel.dagger.factory.VolumePanelComponentFactory
+import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
+import com.android.systemui.volume.panel.domain.DomainModule
+import com.android.systemui.volume.panel.domain.interactor.ComponentsInteractor
+import com.android.systemui.volume.panel.ui.UiModule
+import com.android.systemui.volume.panel.ui.viewmodel.ComponentsLayoutManager
+import com.android.systemui.volume.panel.ui.viewmodel.VolumePanelViewModel
+import dagger.BindsInstance
+import dagger.Subcomponent
+import kotlinx.coroutines.CoroutineScope
+
+/**
+ * Core Volume Panel dagger component. It's managed by [VolumePanelViewModel] and lives alongside
+ * it.
+ */
+@VolumePanelScope
+@Subcomponent(
+    modules =
+        [
+            // Volume Panel infra modules
+            CoroutineModule::class,
+            DefaultMultibindsModule::class,
+            DomainModule::class,
+            UiModule::class,
+            // Components modules
+        ]
+)
+interface VolumePanelComponent {
+
+    fun coroutineScope(): CoroutineScope
+
+    fun componentsInteractor(): ComponentsInteractor
+
+    fun componentsLayoutManager(): ComponentsLayoutManager
+
+    @Subcomponent.Factory
+    interface Factory : VolumePanelComponentFactory {
+
+        override fun create(@BindsInstance viewModel: VolumePanelViewModel): VolumePanelComponent
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/dagger/factory/VolumePanelComponentFactory.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/dagger/factory/VolumePanelComponentFactory.kt
new file mode 100644
index 0000000..e470c3f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/dagger/factory/VolumePanelComponentFactory.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.panel.dagger.factory
+
+import com.android.systemui.volume.panel.dagger.VolumePanelComponent
+import com.android.systemui.volume.panel.ui.viewmodel.VolumePanelViewModel
+import dagger.BindsInstance
+
+/**
+ * Common interface for all [dagger.Subcomponent.Factory] providing [VolumePanelComponent].
+ * [VolumePanelViewModel] uses it to create a new instance of the class.
+ */
+interface VolumePanelComponentFactory {
+
+    fun create(@BindsInstance viewModel: VolumePanelViewModel): VolumePanelComponent
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileUser.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/dagger/scope/VolumePanelScope.kt
similarity index 65%
copy from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileUser.kt
copy to packages/SystemUI/src/com/android/systemui/volume/panel/dagger/scope/VolumePanelScope.kt
index efc7431..e597d11 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileUser.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/dagger/scope/VolumePanelScope.kt
@@ -14,12 +14,12 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.custom.di.bound
+package com.android.systemui.volume.panel.dagger.scope
 
-import javax.inject.Qualifier
+import javax.inject.Scope
 
-/** User associated with current custom tile binding. */
-@Qualifier
-@MustBeDocumented
-@Retention(AnnotationRetention.RUNTIME)
-annotation class CustomTileUser
+/**
+ * Volume Panel dependency injection scope. This scope is created alongside Volume Panel and
+ * destroyed when it's lo longer present.
+ */
+@MustBeDocumented @Retention(AnnotationRetention.RUNTIME) @Scope annotation class VolumePanelScope
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/domain/ComponentAvailabilityCriteria.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/domain/ComponentAvailabilityCriteria.kt
new file mode 100644
index 0000000..d9702e4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/domain/ComponentAvailabilityCriteria.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.panel.domain
+
+import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flowOf
+
+interface ComponentAvailabilityCriteria {
+
+    /**
+     * Checks if the controller is currently available. Can be used to filter out unwanted
+     * components. For example, hide components for the hardware that is temporarily unavailable.
+     */
+    fun isAvailable(): Flow<Boolean>
+}
+
+@VolumePanelScope
+class AlwaysAvailableCriteria @Inject constructor() : ComponentAvailabilityCriteria {
+
+    override fun isAvailable(): Flow<Boolean> = flowOf(true)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/domain/DomainModule.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/domain/DomainModule.kt
new file mode 100644
index 0000000..7817630
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/domain/DomainModule.kt
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.panel.domain
+
+import com.android.systemui.volume.panel.VolumePanelComponentKey
+import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
+import com.android.systemui.volume.panel.domain.interactor.ComponentsInteractor
+import com.android.systemui.volume.panel.domain.interactor.ComponentsInteractorImpl
+import dagger.Binds
+import dagger.Module
+import dagger.Provides
+
+/** Domain layer bindings module. */
+@Module
+interface DomainModule {
+
+    @Binds fun bindComponentsInteractor(impl: ComponentsInteractorImpl): ComponentsInteractor
+
+    @Binds
+    fun bindDefaultComponentAvailabilityCriteria(
+        impl: AlwaysAvailableCriteria
+    ): ComponentAvailabilityCriteria
+
+    companion object {
+
+        /**
+         * Enabled components collection. These are the components processed by Volume Panel logic
+         * and possibly shown in the UI.
+         *
+         * There should be a binding in [VolumePanelScope] for [ComponentAvailabilityCriteria] and
+         * [com.android.systemui.volume.panel.ui.VolumePanelComponent] for each component from this
+         * collection.
+         */
+        @Provides
+        @VolumePanelScope
+        fun provideEnabledComponents(): Collection<VolumePanelComponentKey> = setOf()
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/domain/interactor/ComponentsInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/domain/interactor/ComponentsInteractor.kt
new file mode 100644
index 0000000..e5b52ea
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/domain/interactor/ComponentsInteractor.kt
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.panel.domain.interactor
+
+import com.android.systemui.volume.panel.VolumePanelComponentKey
+import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
+import com.android.systemui.volume.panel.domain.ComponentAvailabilityCriteria
+import com.android.systemui.volume.panel.domain.model.ComponentModel
+import javax.inject.Inject
+import javax.inject.Provider
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.shareIn
+
+interface ComponentsInteractor {
+
+    /**
+     * Components collection for the UI layer. Uses [ComponentAvailabilityCriteria] to dynamically
+     * determine each component availability.
+     */
+    val components: Flow<Collection<ComponentModel>>
+}
+
+@VolumePanelScope
+class ComponentsInteractorImpl
+@Inject
+constructor(
+    enabledComponents: Collection<VolumePanelComponentKey>,
+    defaultCriteria: Provider<ComponentAvailabilityCriteria>,
+    @VolumePanelScope coroutineScope: CoroutineScope,
+    private val criteriaByKey:
+        Map<
+            VolumePanelComponentKey,
+            @JvmSuppressWildcards
+            Provider<@JvmSuppressWildcards ComponentAvailabilityCriteria>
+        >,
+) : ComponentsInteractor {
+
+    override val components: Flow<Collection<ComponentModel>> =
+        combine(
+                enabledComponents.map { componentKey ->
+                    val componentCriteria = (criteriaByKey[componentKey] ?: defaultCriteria).get()
+                    componentCriteria.isAvailable().map { isAvailable ->
+                        ComponentModel(componentKey, isAvailable = isAvailable)
+                    }
+                }
+            ) {
+                it.asList()
+            }
+            .shareIn(coroutineScope, SharingStarted.Eagerly, replay = 1)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/domain/model/ComponentModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/domain/model/ComponentModel.kt
new file mode 100644
index 0000000..9765713
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/domain/model/ComponentModel.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.panel.domain.model
+
+import com.android.systemui.volume.panel.VolumePanelComponentKey
+
+/**
+ * Represents a current state of the Volume Panel component.
+ *
+ * @property key identifies the component the entity represents.
+ * @property isAvailable is true when the component is supported by the device.
+ */
+data class ComponentModel(
+    val key: VolumePanelComponentKey,
+    val isAvailable: Boolean,
+)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileUser.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/UiModule.kt
similarity index 60%
copy from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileUser.kt
copy to packages/SystemUI/src/com/android/systemui/volume/panel/ui/UiModule.kt
index efc7431..bfa7ef2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileUser.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/UiModule.kt
@@ -14,12 +14,16 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.custom.di.bound
+package com.android.systemui.volume.panel.ui
 
-import javax.inject.Qualifier
+import com.android.systemui.volume.panel.ui.viewmodel.ComponentsLayoutManager
+import com.android.systemui.volume.panel.ui.viewmodel.DefaultComponentsLayoutManager
+import dagger.Binds
+import dagger.Module
 
-/** User associated with current custom tile binding. */
-@Qualifier
-@MustBeDocumented
-@Retention(AnnotationRetention.RUNTIME)
-annotation class CustomTileUser
+/** UI layer bindings module. */
+@Module
+interface UiModule {
+
+    @Binds fun bindSorter(impl: DefaultComponentsLayoutManager): ComponentsLayoutManager
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/ui/model/ComponentState.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/model/ComponentState.kt
new file mode 100644
index 0000000..0a226e2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/model/ComponentState.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.panel.ui.model
+
+import com.android.systemui.volume.panel.VolumePanelComponentKey
+
+/**
+ * State of the [VolumePanelComponent].
+ *
+ * @property key uniquely identifies this component
+ * @property component is an inflated component obtained be the View Model
+ * @property isVisible determines component visibility in the UI
+ */
+data class ComponentState(
+    val key: VolumePanelComponentKey,
+    val isVisible: Boolean,
+)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileUser.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/model/ComponentsLayout.kt
similarity index 70%
copy from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileUser.kt
copy to packages/SystemUI/src/com/android/systemui/volume/panel/ui/model/ComponentsLayout.kt
index efc7431..5690ac3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileUser.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/model/ComponentsLayout.kt
@@ -14,12 +14,10 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.custom.di.bound
+package com.android.systemui.volume.panel.ui.model
 
-import javax.inject.Qualifier
-
-/** User associated with current custom tile binding. */
-@Qualifier
-@MustBeDocumented
-@Retention(AnnotationRetention.RUNTIME)
-annotation class CustomTileUser
+/** Represents components grouping into the layout. */
+data class ComponentsLayout(
+    val contentComponents: List<ComponentState>,
+    val bottomBarComponent: ComponentState,
+)
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/ui/model/VolumePanelState.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/model/VolumePanelState.kt
new file mode 100644
index 0000000..399342f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/model/VolumePanelState.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.panel.ui.model
+
+import android.content.res.Configuration
+import android.content.res.Configuration.Orientation
+
+/**
+ * State of the Volume Panel itself.
+ *
+ * @property orientation is current Volume Panel orientation.
+ */
+data class VolumePanelState(
+    @Orientation val orientation: Int,
+    val isVisible: Boolean,
+) {
+    init {
+        require(
+            orientation == Configuration.ORIENTATION_PORTRAIT ||
+                orientation == Configuration.ORIENTATION_LANDSCAPE ||
+                orientation == Configuration.ORIENTATION_UNDEFINED ||
+                orientation == Configuration.ORIENTATION_SQUARE
+        ) {
+            "Unknown orientation: $orientation"
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/ui/viewmodel/ComponentsLayoutManager.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/viewmodel/ComponentsLayoutManager.kt
new file mode 100644
index 0000000..f45401a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/viewmodel/ComponentsLayoutManager.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.panel.ui.viewmodel
+
+import com.android.systemui.volume.panel.ui.model.ComponentState
+import com.android.systemui.volume.panel.ui.model.ComponentsLayout
+import com.android.systemui.volume.panel.ui.model.VolumePanelState
+
+/**
+ * Lays out components to [ComponentsLayout], that UI uses to render the Volume Panel.
+ *
+ * Vertical layout shows the list from top to bottom:
+ * ```
+ * -----
+ * | 1 |
+ * | 2 |
+ * | 3 |
+ * | 4 |
+ * -----
+ * ```
+ *
+ * Horizontal layout shows the list in a grid from, filling the columns first:
+ * ```
+ * ----------
+ * | 1 || 3 |
+ * | 2 || 4 |
+ * ----------
+ * ```
+ */
+interface ComponentsLayoutManager {
+
+    fun layout(
+        volumePanelState: VolumePanelState,
+        components: Collection<ComponentState>,
+    ): ComponentsLayout
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/ui/viewmodel/DefaultComponentsLayoutManager.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/viewmodel/DefaultComponentsLayoutManager.kt
new file mode 100644
index 0000000..cedfaf3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/viewmodel/DefaultComponentsLayoutManager.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.panel.ui.viewmodel
+
+import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
+import com.android.systemui.volume.panel.ui.model.ComponentState
+import com.android.systemui.volume.panel.ui.model.ComponentsLayout
+import com.android.systemui.volume.panel.ui.model.VolumePanelState
+import javax.inject.Inject
+
+/**
+ * Default [ComponentsLayoutManager]. It places [VolumePanelComponents.BOTTOM_BAR] to
+ * [ComponentsLayout.bottomBarComponent] and everything else to
+ * [ComponentsLayout.contentComponents].
+ */
+@VolumePanelScope
+class DefaultComponentsLayoutManager @Inject constructor() : ComponentsLayoutManager {
+
+    override fun layout(
+        volumePanelState: VolumePanelState,
+        components: Collection<ComponentState>
+    ): ComponentsLayout = TODO("Unimplemented yet")
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModel.kt
new file mode 100644
index 0000000..dda361a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModel.kt
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.panel.ui.viewmodel
+
+import android.content.Context
+import android.content.res.Resources
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.statusbar.policy.onConfigChanged
+import com.android.systemui.volume.panel.dagger.VolumePanelComponent
+import com.android.systemui.volume.panel.dagger.factory.VolumePanelComponentFactory
+import com.android.systemui.volume.panel.domain.interactor.ComponentsInteractor
+import com.android.systemui.volume.panel.ui.model.ComponentState
+import com.android.systemui.volume.panel.ui.model.ComponentsLayout
+import com.android.systemui.volume.panel.ui.model.VolumePanelState
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.cancel
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.shareIn
+import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.launch
+
+class VolumePanelViewModel(
+    resources: Resources,
+    daggerComponentFactory: VolumePanelComponentFactory,
+    configurationController: ConfigurationController,
+) : ViewModel() {
+
+    private val volumePanelComponent: VolumePanelComponent = daggerComponentFactory.create(this)
+
+    private val scope: CoroutineScope
+        get() = volumePanelComponent.coroutineScope()
+
+    private val componentsInteractor: ComponentsInteractor
+        get() = volumePanelComponent.componentsInteractor()
+
+    private val componentsLayoutManager: ComponentsLayoutManager
+        get() = volumePanelComponent.componentsLayoutManager()
+
+    private val mutablePanelVisibility = MutableStateFlow(true)
+
+    val volumePanelState: StateFlow<VolumePanelState> =
+        combine(
+                configurationController.onConfigChanged.distinctUntilChanged(),
+                mutablePanelVisibility,
+            ) { configuration, isVisible ->
+                VolumePanelState(orientation = configuration.orientation, isVisible = isVisible)
+            }
+            .stateIn(
+                volumePanelComponent.coroutineScope(),
+                SharingStarted.Eagerly,
+                VolumePanelState(
+                    orientation = resources.configuration.orientation,
+                    isVisible = mutablePanelVisibility.value,
+                ),
+            )
+    val mComponentsLayout: Flow<ComponentsLayout> =
+        combine(
+                componentsInteractor.components,
+                volumePanelState,
+            ) { components, scope ->
+                val componentStates =
+                    components.map { model ->
+                        ComponentState(
+                            model.key,
+                            model.isAvailable,
+                        )
+                    }
+                componentsLayoutManager.layout(scope, componentStates)
+            }
+            .shareIn(
+                volumePanelComponent.coroutineScope(),
+                SharingStarted.Eagerly,
+                replay = 1,
+            )
+
+    fun dismissPanel() {
+        scope.launch { mutablePanelVisibility.emit(false) }
+    }
+
+    override fun onCleared() {
+        scope.cancel()
+        super.onCleared()
+    }
+
+    class Factory
+    @Inject
+    constructor(
+        @Application private val context: Context,
+        private val daggerComponentFactory: VolumePanelComponentFactory,
+        private val configurationController: ConfigurationController,
+    ) : ViewModelProvider.Factory {
+
+        @Suppress("UNCHECKED_CAST")
+        override fun <T : ViewModel> create(modelClass: Class<T>): T {
+            check(modelClass == VolumePanelViewModel::class.java)
+            return VolumePanelViewModel(
+                context.resources,
+                daggerComponentFactory,
+                configurationController,
+            )
+                as T
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java
index 5558aa7..111492c 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java
@@ -43,7 +43,7 @@
 import com.android.systemui.classifier.FalsingCollector;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor;
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.res.R;
@@ -69,7 +69,7 @@
     private final Executor mExecutor;
     private final Handler mHandler;
     private final FalsingManager mFalsingManager;
-    private final KeyguardFaceAuthInteractor mKeyguardFaceAuthInteractor;
+    private final DeviceEntryFaceAuthInteractor mDeviceEntryFaceAuthInteractor;
     private FalsingCollector mFalsingCollector;
     private final UserTracker mUserTracker;
     private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@@ -94,7 +94,7 @@
             KeyguardUpdateMonitor keyguardUpdateMonitor,
             StatusBarKeyguardViewManager keyguardViewManager,
             UiEventLogger uiEventLogger,
-            KeyguardFaceAuthInteractor keyguardFaceAuthInteractor) {
+            DeviceEntryFaceAuthInteractor deviceEntryFaceAuthInteractor) {
         mKeyguardStateController = keyguardStateController;
         mKeyguardDismissUtil = keyguardDismissUtil;
         mActivityStarter = activityStarter;
@@ -106,7 +106,7 @@
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
         mKeyguardViewManager = keyguardViewManager;
         mUiEventLogger = uiEventLogger;
-        mKeyguardFaceAuthInteractor = keyguardFaceAuthInteractor;
+        mDeviceEntryFaceAuthInteractor = deviceEntryFaceAuthInteractor;
     }
 
     @Override
@@ -213,7 +213,7 @@
                 true,
                 Utils.getColorAttrDefaultColor(
                         this, com.android.internal.R.attr.colorAccentPrimary));
-        mKeyguardFaceAuthInteractor.onWalletLaunched();
+        mDeviceEntryFaceAuthInteractor.onWalletLaunched();
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java
index 20fef92..e57c0e7 100644
--- a/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java
@@ -210,7 +210,13 @@
             if (DEBUG) {
                 Log.i(TAG, "onSurfaceDestroyed");
             }
-            mSurfaceHolder = null;
+            mLongExecutor.execute(this::onSurfaceDestroyedSynchronized);
+        }
+
+        private void onSurfaceDestroyedSynchronized() {
+            synchronized (mLock) {
+                mSurfaceHolder = null;
+            }
         }
 
         @Override
@@ -241,7 +247,7 @@
 
         private void drawFrameInternal() {
             if (mSurfaceHolder == null) {
-                Log.e(TAG, "attempt to draw a frame without a valid surface");
+                Log.i(TAG, "attempt to draw a frame without a valid surface");
                 return;
             }
 
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt
index 3f76d30..8858d13 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt
@@ -51,6 +51,7 @@
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.`when`
 import org.mockito.MockitoAnnotations
+import dagger.Lazy
 
 @SmallTest
 class ActiveUnlockConfigTest : SysuiTestCase() {
@@ -60,6 +61,7 @@
     @Mock private lateinit var dumpManager: DumpManager
     @Mock private lateinit var selectedUserInteractor: SelectedUserInteractor
     @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
+    @Mock private lateinit var lazyKeyguardUpdateMonitor: Lazy<KeyguardUpdateMonitor>
     @Mock private lateinit var mockPrintWriter: PrintWriter
 
     @Captor private lateinit var settingsObserverCaptor: ArgumentCaptor<ContentObserver>
@@ -72,6 +74,7 @@
         MockitoAnnotations.initMocks(this)
 
         whenever(selectedUserInteractor.getSelectedUserId()).thenReturn(currentUser)
+        whenever(lazyKeyguardUpdateMonitor.get()).thenReturn(keyguardUpdateMonitor)
         secureSettings = FakeSettings()
         activeUnlockConfig =
             ActiveUnlockConfig(
@@ -79,6 +82,7 @@
                 secureSettings,
                 contentResolver,
                 selectedUserInteractor,
+                lazyKeyguardUpdateMonitor,
                 dumpManager
             )
     }
@@ -260,7 +264,6 @@
         updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL))
 
         // GIVEN fingerprint and face are NOT enrolled
-        activeUnlockConfig.keyguardUpdateMonitor = keyguardUpdateMonitor
         `when`(keyguardUpdateMonitor.isFaceEnabledAndEnrolled).thenReturn(false)
         `when`(keyguardUpdateMonitor.isUnlockWithFingerprintPossible(0)).thenReturn(false)
 
@@ -290,7 +293,6 @@
         updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL))
 
         // GIVEN fingerprint and face are both enrolled
-        activeUnlockConfig.keyguardUpdateMonitor = keyguardUpdateMonitor
         `when`(keyguardUpdateMonitor.isFaceEnabledAndEnrolled).thenReturn(true)
         `when`(keyguardUpdateMonitor.isUnlockWithFingerprintPossible(0)).thenReturn(true)
 
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextManagerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextManagerTest.java
index 9ce9bd6..08c1de1 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextManagerTest.java
@@ -55,10 +55,10 @@
 import android.text.TextUtils;
 
 import com.android.keyguard.logging.CarrierTextManagerLogger;
-import com.android.systemui.res.R;
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.dump.LogBufferHelperKt;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.log.LogBufferHelperKt;
+import com.android.systemui.res.R;
 import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository;
 import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel;
 import com.android.systemui.telephony.TelephonyListenerManager;
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java
index 7f20d9a..51ceda3 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java
@@ -37,11 +37,11 @@
 import com.android.internal.widget.LockPatternUtils;
 import com.android.keyguard.KeyguardAbsKeyInputView.KeyDownListener;
 import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
+import com.android.systemui.Flags;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.classifier.FalsingCollector;
 import com.android.systemui.classifier.FalsingCollectorFake;
 import com.android.systemui.flags.FakeFeatureFlags;
-import com.android.systemui.flags.Flags;
 import com.android.systemui.res.R;
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
 
@@ -98,7 +98,6 @@
                 .thenReturn(mKeyguardMessageArea);
         when(mAbsKeyInputView.getResources()).thenReturn(getContext().getResources());
         mFeatureFlags = new FakeFeatureFlags();
-        mFeatureFlags.set(Flags.REVAMPED_BOUNCER_MESSAGES, false);
         mKeyguardAbsKeyInputViewController = createTestObject();
         mKeyguardAbsKeyInputViewController.init();
         reset(mKeyguardMessageAreaController);  // Clear out implicit call to init.
@@ -127,7 +126,7 @@
 
     @Test
     public void withFeatureFlagOn_oldMessage_isHidden() {
-        mFeatureFlags.set(Flags.REVAMPED_BOUNCER_MESSAGES, true);
+        mSetFlagsRule.enableFlags(Flags.FLAG_REVAMPED_BOUNCER_MESSAGES);
         KeyguardAbsKeyInputViewController underTest = createTestObject();
 
         underTest.init();
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 1ab634c..d8eb05a 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -30,6 +30,7 @@
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
 import static com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_STATE_CANCELLING_RESTARTING;
+import static com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_STATE_STOPPED;
 import static com.android.keyguard.KeyguardUpdateMonitor.DEFAULT_CANCEL_SIGNAL_TIMEOUT;
 import static com.android.keyguard.KeyguardUpdateMonitor.HAL_POWER_PRESS_TIMEOUT;
 import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_OPENED;
@@ -109,6 +110,7 @@
 import android.text.TextUtils;
 
 import com.android.dx.mockito.inline.extended.ExtendedMockito;
+import com.android.internal.foldables.FoldGracePeriodProvider;
 import com.android.internal.jank.InteractionJankMonitor;
 import com.android.internal.logging.InstanceId;
 import com.android.internal.logging.UiEventLogger;
@@ -119,16 +121,19 @@
 import com.android.keyguard.KeyguardUpdateMonitor.BiometricAuthenticated;
 import com.android.keyguard.logging.KeyguardUpdateMonitorLogger;
 import com.android.settingslib.fuelgauge.BatteryStatus;
+import com.android.systemui.Flags;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.biometrics.AuthController;
 import com.android.systemui.biometrics.FingerprintInteractiveToAuthProvider;
 import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.deviceentry.data.repository.FaceWakeUpTriggersConfig;
+import com.android.systemui.deviceentry.data.repository.FaceWakeUpTriggersConfigImpl;
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor;
+import com.android.systemui.deviceentry.domain.interactor.FaceAuthenticationListener;
+import com.android.systemui.deviceentry.shared.model.ErrorFaceAuthenticationStatus;
+import com.android.systemui.deviceentry.shared.model.FaceDetectionStatus;
+import com.android.systemui.deviceentry.shared.model.FailedFaceAuthenticationStatus;
 import com.android.systemui.dump.DumpManager;
-import com.android.systemui.keyguard.domain.interactor.FaceAuthenticationListener;
-import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor;
-import com.android.systemui.keyguard.shared.model.ErrorFaceAuthenticationStatus;
-import com.android.systemui.keyguard.shared.model.FaceDetectionStatus;
-import com.android.systemui.keyguard.shared.model.FailedFaceAuthenticationStatus;
 import com.android.systemui.log.SessionTracker;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.settings.UserTracker;
@@ -218,6 +223,8 @@
     @Mock
     private BroadcastDispatcher mBroadcastDispatcher;
     @Mock
+    private FoldGracePeriodProvider mFoldGracePeriodProvider;
+    @Mock
     private TelephonyManager mTelephonyManager;
     @Mock
     private SensorPrivacyManager mSensorPrivacyManager;
@@ -260,7 +267,7 @@
     @Mock
     private SelectedUserInteractor mSelectedUserInteractor;
     @Mock
-    private KeyguardFaceAuthInteractor mFaceAuthInteractor;
+    private DeviceEntryFaceAuthInteractor mFaceAuthInteractor;
     @Captor
     private ArgumentCaptor<FaceAuthenticationListener> mFaceAuthenticationListener;
 
@@ -314,7 +321,7 @@
         mContext.getOrCreateTestableResources().addOverride(
                 com.android.systemui.res.R.integer.config_face_auth_supported_posture,
                 DEVICE_POSTURE_UNKNOWN);
-        mFaceWakeUpTriggersConfig = new FaceWakeUpTriggersConfig(
+        mFaceWakeUpTriggersConfig = new FaceWakeUpTriggersConfigImpl(
                 mContext.getResources(),
                 mGlobalSettings,
                 mDumpManager
@@ -334,6 +341,7 @@
                         anyInt());
 
         mKeyguardUpdateMonitor = new TestableKeyguardUpdateMonitor(mContext);
+        mKeyguardUpdateMonitor.mFoldGracePeriodProvider = mFoldGracePeriodProvider;
         setupBiometrics(mKeyguardUpdateMonitor);
         mKeyguardUpdateMonitor.setFaceAuthInteractor(mFaceAuthInteractor);
         verify(mFaceAuthInteractor).registerListener(mFaceAuthenticationListener.capture());
@@ -922,8 +930,7 @@
     @Test
     public void trustAgentHasTrust() {
         // WHEN user has trust
-        mKeyguardUpdateMonitor.onTrustChanged(true, true,
-                mSelectedUserInteractor.getSelectedUserId(), 0, null);
+        givenSelectedUserCanSkipBouncerFromTrustedState();
 
         // THEN user is considered as "having trust" and bouncer can be skipped
         Assert.assertTrue(mKeyguardUpdateMonitor.getUserHasTrust(
@@ -947,8 +954,7 @@
     @Test
     public void trustAgentHasTrust_fingerprintLockout() {
         // GIVEN user has trust
-        mKeyguardUpdateMonitor.onTrustChanged(true, true,
-                mSelectedUserInteractor.getSelectedUserId(), 0, null);
+        givenSelectedUserCanSkipBouncerFromTrustedState();
         Assert.assertTrue(mKeyguardUpdateMonitor.getUserHasTrust(
                 mSelectedUserInteractor.getSelectedUserId()));
 
@@ -1973,6 +1979,61 @@
     }
 
     @Test
+    public void detectFingerprint_onSuccess_biometricStateStopped() {
+        // GIVEN FP detection is running
+        givenDetectFingerprintWithClearingFingerprintManagerInvocations();
+
+        // WHEN detection is successful
+        ArgumentCaptor<FingerprintManager.FingerprintDetectionCallback> fpDetectCallbackCaptor =
+                ArgumentCaptor.forClass(FingerprintManager.FingerprintDetectionCallback.class);
+        verify(mFingerprintManager).detectFingerprint(
+                any(), fpDetectCallbackCaptor.capture(), any());
+        fpDetectCallbackCaptor.getValue().onFingerprintDetected(0, 0, true);
+        mTestableLooper.processAllMessages();
+
+        // THEN fingerprint detect state should immediately update to STOPPED
+        assertThat(mKeyguardUpdateMonitor.mFingerprintRunningState)
+                .isEqualTo(BIOMETRIC_STATE_STOPPED);
+    }
+
+    @Test
+    public void runFpDetectFlagDisabled_sideFps_keyguardDismissible_fingerprintAuthenticateRuns() {
+        mSetFlagsRule.disableFlags(Flags.FLAG_RUN_FINGERPRINT_DETECT_ON_DISMISSIBLE_KEYGUARD);
+
+        // Clear invocations, since previous setup (e.g. registering BiometricManager callbacks)
+        // will trigger updateBiometricListeningState();
+        clearInvocations(mFingerprintManager);
+        mKeyguardUpdateMonitor.resetBiometricListeningState();
+
+        // GIVEN the user can skip the bouncer
+        givenSelectedUserCanSkipBouncerFromTrustedState();
+        when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true);
+        mKeyguardUpdateMonitor.dispatchStartedGoingToSleep(0 /* why */);
+        mTestableLooper.processAllMessages();
+
+        // WHEN verify authenticate runs
+        verifyFingerprintAuthenticateCall();
+    }
+
+    @Test
+    public void sideFps_keyguardDismissible_fingerprintDetectRuns() {
+        mSetFlagsRule.enableFlags(Flags.FLAG_RUN_FINGERPRINT_DETECT_ON_DISMISSIBLE_KEYGUARD);
+        // Clear invocations, since previous setup (e.g. registering BiometricManager callbacks)
+        // will trigger updateBiometricListeningState();
+        clearInvocations(mFingerprintManager);
+        mKeyguardUpdateMonitor.resetBiometricListeningState();
+
+        // GIVEN the user can skip the bouncer
+        givenSelectedUserCanSkipBouncerFromTrustedState();
+        when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true);
+        mKeyguardUpdateMonitor.dispatchStartedGoingToSleep(0 /* why */);
+        mTestableLooper.processAllMessages();
+
+        // WHEN verify detect runs
+        verifyFingerprintDetectCall();
+    }
+
+    @Test
     public void testFingerprintSensorProperties() throws RemoteException {
         mFingerprintAuthenticatorsRegisteredCallback.onAllAuthenticatorsRegistered(
                 new ArrayList<>());
@@ -2077,6 +2138,35 @@
         verify(callback).onBiometricEnrollmentStateChanged(BiometricSourceType.FACE);
     }
 
+    private void givenSelectedUserCanSkipBouncerFromTrustedState() {
+        mKeyguardUpdateMonitor.onTrustChanged(true, true,
+                mSelectedUserInteractor.getSelectedUserId(), 0, null);
+    }
+
+    @Test
+    public void forceIsDismissibleKeyguard_foldingGracePeriodNotEnabled() {
+        when(mFoldGracePeriodProvider.isEnabled()).thenReturn(false);
+        primaryAuthNotRequiredByStrongAuthTracker();
+        mKeyguardUpdateMonitor.tryForceIsDismissibleKeyguard();
+        Assert.assertFalse(mKeyguardUpdateMonitor.forceIsDismissibleIsKeepingDeviceUnlocked());
+    }
+
+    @Test
+    public void forceIsDismissibleKeyguard() {
+        when(mFoldGracePeriodProvider.isEnabled()).thenReturn(true);
+        primaryAuthNotRequiredByStrongAuthTracker();
+        mKeyguardUpdateMonitor.tryForceIsDismissibleKeyguard();
+        Assert.assertTrue(mKeyguardUpdateMonitor.forceIsDismissibleIsKeepingDeviceUnlocked());
+    }
+
+    @Test
+    public void forceIsDismissibleKeyguard_respectsLockdown() {
+        when(mFoldGracePeriodProvider.isEnabled()).thenReturn(true);
+        userDeviceLockDown();
+        mKeyguardUpdateMonitor.tryForceIsDismissibleKeyguard();
+        Assert.assertFalse(mKeyguardUpdateMonitor.forceIsDismissibleIsKeepingDeviceUnlocked());
+    }
+
     private void verifyFingerprintAuthenticateNeverCalled() {
         verify(mFingerprintManager, never()).authenticate(any(), any(), any(), any(), any());
         verify(mFingerprintManager, never()).authenticate(any(), any(), any(), any(), anyInt(),
@@ -2283,6 +2373,7 @@
                     Optional.of(mInteractiveToAuthProvider),
                     mTaskStackChangeListeners, mSelectedUserInteractor, mActivityTaskManager);
             setStrongAuthTracker(KeyguardUpdateMonitorTest.this.mStrongAuthTracker);
+            start();
         }
 
         public boolean hasSimStateJustChanged() {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/mediator/ScreenOnCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/mediator/ScreenOnCoordinatorTest.kt
index 9fe32f1..b45c894 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/mediator/ScreenOnCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/mediator/ScreenOnCoordinatorTest.kt
@@ -16,16 +16,21 @@
 
 package com.android.keyguard.mediator
 
-import android.os.Handler
 import android.os.Looper
+import android.platform.test.flag.junit.SetFlagsRule
+import android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT
 import android.testing.AndroidTestingRunner
 import androidx.test.filters.SmallTest
+import com.android.systemui.Flags
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.unfold.FoldAodAnimationController
 import com.android.systemui.unfold.SysUIUnfoldComponent
 import com.android.systemui.unfold.UnfoldLightRevealOverlayAnimation
 import com.android.systemui.util.mockito.capture
+import com.android.systemui.utils.os.FakeHandler
+import com.android.systemui.utils.os.FakeHandler.Mode.QUEUEING
 import org.junit.Before
+import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.ArgumentCaptor
@@ -52,10 +57,13 @@
     @Captor
     private lateinit var readyCaptor: ArgumentCaptor<Runnable>
 
-    private val testHandler = Handler(Looper.getMainLooper())
+    private val testHandler = FakeHandler(Looper.getMainLooper()).apply { setMode(QUEUEING) }
 
     private lateinit var screenOnCoordinator: ScreenOnCoordinator
 
+    @get:Rule
+    val setFlagsRule = SetFlagsRule(DEVICE_DEFAULT)
+
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
@@ -77,7 +85,7 @@
 
         onUnfoldOverlayReady()
         onFoldAodReady()
-        waitHandlerIdle(testHandler)
+        waitHandlerIdle()
 
         // Should be called when both unfold overlay and keyguard drawn ready
         verify(runnable).run()
@@ -90,7 +98,7 @@
 
         onUnfoldOverlayReady()
         onFoldAodReady()
-        waitHandlerIdle(testHandler)
+        waitHandlerIdle()
 
         // Should be called when both unfold overlay and keyguard drawn ready
         verify(runnable).run()
@@ -104,7 +112,8 @@
 
         onUnfoldOverlayReady()
         onFoldAodReady()
-        waitHandlerIdle(testHandler)
+        waitHandlerIdle()
+
 
         // Should not be called because this screen turning on call is not valid anymore
         verify(runnable, never()).run()
@@ -112,13 +121,43 @@
 
     @Test
     fun testUnfoldTransitionDisabledDrawnTasksReady_onScreenTurningOn_callsDrawnCallback() {
+        setFlagsRule.disableFlags(Flags.FLAG_ENABLE_BACKGROUND_KEYGUARD_ONDRAWN_CALLBACK)
         // Recreate with empty unfoldComponent
         screenOnCoordinator = ScreenOnCoordinator(
             Optional.empty(),
             testHandler
         )
         screenOnCoordinator.onScreenTurningOn(runnable)
-        waitHandlerIdle(testHandler)
+        waitHandlerIdle()
+
+        // Should be called when only keyguard drawn
+        verify(runnable).run()
+    }
+    @Test
+    fun testUnfoldTransitionDisabledDrawnTasksReady_onScreenTurningOn_usesMainHandler() {
+        setFlagsRule.disableFlags(Flags.FLAG_ENABLE_BACKGROUND_KEYGUARD_ONDRAWN_CALLBACK)
+        // Recreate with empty unfoldComponent
+        screenOnCoordinator = ScreenOnCoordinator(
+                Optional.empty(),
+                testHandler
+        )
+        screenOnCoordinator.onScreenTurningOn(runnable)
+
+        // Never called as the main handler didn't schedule it yet.
+        verify(runnable, never()).run()
+    }
+
+    @Test
+    fun unfoldTransitionDisabledDrawnTasksReady_onScreenTurningOn_bgCallback_callsDrawnCallback() {
+        setFlagsRule.enableFlags(Flags.FLAG_ENABLE_BACKGROUND_KEYGUARD_ONDRAWN_CALLBACK)
+        // Recreate with empty unfoldComponent
+        screenOnCoordinator = ScreenOnCoordinator(
+                Optional.empty(),
+                testHandler
+        )
+        screenOnCoordinator.onScreenTurningOn(runnable)
+        // No need to wait for the handler to be idle, as it shouldn't be used
+        // waitHandlerIdle()
 
         // Should be called when only keyguard drawn
         verify(runnable).run()
@@ -134,7 +173,7 @@
         readyCaptor.value.run()
     }
 
-    private fun waitHandlerIdle(handler: Handler) {
-        handler.runWithScissors({},  /* timeout= */ 0)
+    private fun waitHandlerIdle() {
+        testHandler.dispatchQueuedMessages()
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/FaceScanningProviderFactoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/FaceScanningProviderFactoryTest.kt
index 2d5c2ab..46936d6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/FaceScanningProviderFactoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/FaceScanningProviderFactoryTest.kt
@@ -25,9 +25,10 @@
 import com.android.internal.R
 import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.systemui.biometrics.AuthController
+import com.android.systemui.biometrics.data.repository.FakeFacePropertyRepository
 import com.android.systemui.decor.FaceScanningProviderFactory
-import com.android.systemui.dump.logcatLogBuffer
 import com.android.systemui.log.ScreenDecorationsLogger
+import com.android.systemui.log.logcatLogBuffer
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth.assertThat
@@ -53,6 +54,8 @@
 
     @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
 
+    private val facePropertyRepository = FakeFacePropertyRepository()
+
     private val displayId = 2
 
     @Before
@@ -86,9 +89,10 @@
                 keyguardUpdateMonitor,
                 mock(Executor::class.java),
                 ScreenDecorationsLogger(logcatLogBuffer("FaceScanningProviderFactoryTest")),
+                facePropertyRepository,
             )
 
-        whenever(authController.faceSensorLocation).thenReturn(Point(10, 10))
+        facePropertyRepository.setSensorLocation(Point(10, 10))
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
index 639276e..c07148b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
@@ -21,7 +21,7 @@
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
-import static com.android.systemui.dump.LogBufferHelperKt.logcatLogBuffer;
+import static com.android.systemui.log.LogBufferHelperKt.logcatLogBuffer;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -54,6 +54,7 @@
 import android.content.res.TypedArray;
 import android.graphics.Path;
 import android.graphics.PixelFormat;
+import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.hardware.display.DisplayManager;
@@ -80,6 +81,7 @@
 
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.biometrics.AuthController;
+import com.android.systemui.biometrics.data.repository.FakeFacePropertyRepository;
 import com.android.systemui.decor.CornerDecorProvider;
 import com.android.systemui.decor.CutoutDecorProviderFactory;
 import com.android.systemui.decor.CutoutDecorProviderImpl;
@@ -101,6 +103,7 @@
 import com.android.systemui.statusbar.events.PrivacyDotViewController;
 import com.android.systemui.util.concurrency.FakeExecutor;
 import com.android.systemui.util.concurrency.FakeThreadFactory;
+import com.android.systemui.util.kotlin.JavaAdapter;
 import com.android.systemui.util.settings.FakeSettings;
 import com.android.systemui.util.settings.SecureSettings;
 import com.android.systemui.util.time.FakeSystemClock;
@@ -108,8 +111,6 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.mockito.invocation.InvocationOnMock;
@@ -169,8 +170,11 @@
     private PrivacyDotViewController.ShowingListener mPrivacyDotShowingListener;
     @Mock
     private CutoutDecorProviderFactory mCutoutFactory;
-    @Captor
-    private ArgumentCaptor<AuthController.Callback> mAuthControllerCallback;
+    @Mock
+    private JavaAdapter mJavaAdapter;
+
+    private FakeFacePropertyRepository mFakeFacePropertyRepository =
+            new FakeFacePropertyRepository();
     private List<DecorProvider> mMockCutoutList;
 
     @Before
@@ -227,20 +231,23 @@
         doAnswer(it -> !(mMockCutoutList.isEmpty())).when(mCutoutFactory).getHasProviders();
         doReturn(mMockCutoutList).when(mCutoutFactory).getProviders();
 
+        mFakeFacePropertyRepository.setSensorLocation(new Point(10, 10));
+
         mFaceScanningDecorProvider = spy(new FaceScanningOverlayProviderImpl(
                 BOUNDS_POSITION_TOP,
                 mAuthController,
                 mStatusBarStateController,
                 mKeyguardUpdateMonitor,
                 mExecutor,
-                new ScreenDecorationsLogger(logcatLogBuffer("TestLogBuffer"))));
+                new ScreenDecorationsLogger(logcatLogBuffer("TestLogBuffer")),
+                mFakeFacePropertyRepository));
 
         mScreenDecorations = spy(new ScreenDecorations(mContext, mSecureSettings,
                 mCommandRegistry, mUserTracker, mDisplayTracker, mDotViewController,
                 mThreadFactory,
                 mPrivacyDotDecorProviderFactory, mFaceScanningProviderFactory,
                 new ScreenDecorationsLogger(logcatLogBuffer("TestLogBuffer")),
-                mAuthController) {
+                mFakeFacePropertyRepository, mJavaAdapter) {
             @Override
             public void start() {
                 super.start();
@@ -248,8 +255,8 @@
             }
 
             @Override
-            public void onConfigurationChanged(Configuration newConfig) {
-                super.onConfigurationChanged(newConfig);
+            public void onConfigChanged(Configuration newConfig) {
+                super.onConfigChanged(newConfig);
                 mExecutor.runAllReady();
             }
 
@@ -892,7 +899,7 @@
         // Switch to long edge cutout(left).
         mMockCutoutList.set(0, new CutoutDecorProviderImpl(BOUNDS_POSITION_LEFT));
 
-        mScreenDecorations.onConfigurationChanged(new Configuration());
+        mScreenDecorations.onConfigChanged(new Configuration());
         verifyOverlaysExistAndAdded(true, false, false, false, View.VISIBLE);
     }
 
@@ -913,7 +920,7 @@
         // Switch to long edge cutout(left).
         mMockCutoutList.set(0, new CutoutDecorProviderImpl(BOUNDS_POSITION_LEFT));
 
-        mScreenDecorations.onConfigurationChanged(new Configuration());
+        mScreenDecorations.onConfigChanged(new Configuration());
         verifyOverlaysExistAndAdded(true, false, true, false, View.VISIBLE);
         verify(mDotViewController, times(2)).initialize(any(), any(), any(), any());
         verify(mDotViewController, times(2)).setShowingListener(null);
@@ -949,7 +956,7 @@
         // top cutout
         mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_TOP));
 
-        mScreenDecorations.onConfigurationChanged(new Configuration());
+        mScreenDecorations.onConfigChanged(new Configuration());
 
         // Only top windows should be added.
         verifyOverlaysExistAndAdded(false, true, false, false, View.VISIBLE);
@@ -976,7 +983,7 @@
         // top cutout
         mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_TOP));
 
-        mScreenDecorations.onConfigurationChanged(new Configuration());
+        mScreenDecorations.onConfigChanged(new Configuration());
 
         // Both top and bottom windows should be added with VISIBLE because of privacy dot and
         // cutout, but rounded corners visibility shall be gone because of no rounding.
@@ -1013,7 +1020,7 @@
         doReturn(2f).when(mScreenDecorations).getPhysicalPixelDisplaySizeRatio();
         mDisplayInfo.rotation = Surface.ROTATION_270;
 
-        mScreenDecorations.onConfigurationChanged(null);
+        mScreenDecorations.onConfigChanged(null);
 
         assertEquals(new Size(6, 6), resDelegate.getTopRoundedSize());
         assertEquals(new Size(8, 8), resDelegate.getBottomRoundedSize());
@@ -1145,7 +1152,7 @@
         assertThat(mScreenDecorations.mIsRegistered, is(false));
 
         doReturn(true).when(mScreenDecorations).hasOverlays();
-        mScreenDecorations.onConfigurationChanged(new Configuration());
+        mScreenDecorations.onConfigChanged(new Configuration());
         assertThat(mScreenDecorations.mIsRegistered, is(true));
     }
 
@@ -1156,7 +1163,7 @@
         mScreenDecorations.start();
         assertThat(mScreenDecorations.mIsRegistered, is(true));
 
-        mScreenDecorations.onConfigurationChanged(new Configuration());
+        mScreenDecorations.onConfigChanged(new Configuration());
         assertThat(mScreenDecorations.mIsRegistered, is(true));
     }
 
@@ -1168,7 +1175,7 @@
         assertThat(mScreenDecorations.mIsRegistered, is(true));
 
         doReturn(false).when(mScreenDecorations).hasOverlays();
-        mScreenDecorations.onConfigurationChanged(new Configuration());
+        mScreenDecorations.onConfigChanged(new Configuration());
         assertThat(mScreenDecorations.mIsRegistered, is(false));
     }
 
@@ -1235,9 +1242,9 @@
                 mSecureSettings, mCommandRegistry, mUserTracker, mDisplayTracker,
                 mDotViewController,
                 mThreadFactory, mPrivacyDotDecorProviderFactory, mFaceScanningProviderFactory,
-                new ScreenDecorationsLogger(logcatLogBuffer("TestLogBuffer")), mAuthController);
+                new ScreenDecorationsLogger(logcatLogBuffer("TestLogBuffer")),
+                mFakeFacePropertyRepository, mJavaAdapter);
         screenDecorations.start();
-        verify(mAuthController).addCallback(mAuthControllerCallback.capture());
         when(mContext.getDisplay()).thenReturn(mDisplay);
         when(mDisplay.getDisplayInfo(any())).thenAnswer(new Answer<Boolean>() {
             @Override
@@ -1252,9 +1259,9 @@
         });
         mExecutor.runAllReady();
         clearInvocations(mFaceScanningDecorProvider);
-
-        AuthController.Callback callback = mAuthControllerCallback.getValue();
-        callback.onFaceSensorLocationChanged();
+        final Point location = new Point();
+        mFakeFacePropertyRepository.setSensorLocation(location);
+        screenDecorations.onFaceSensorLocationChanged(location);
         mExecutor.runAllReady();
 
         verify(mFaceScanningDecorProvider).onReloadResAndMeasure(any(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SystemUIApplicationTest.kt b/packages/SystemUI/tests/src/com/android/systemui/SystemUIApplicationTest.kt
new file mode 100644
index 0000000..202d9ce
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/SystemUIApplicationTest.kt
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui
+
+import android.os.Looper
+import android.platform.test.flag.junit.SetFlagsRule
+import android.testing.TestableLooper.RunWithLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.dagger.GlobalRootComponent
+import com.android.systemui.dagger.SysUIComponent
+import com.android.systemui.dump.dumpManager
+import com.android.systemui.flags.systemPropertiesHelper
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.process.processWrapper
+import com.android.systemui.startable.Dependencies
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import javax.inject.Provider
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+@RunWithLooper
+class SystemUIApplicationTest : SysuiTestCase() {
+
+    private val app: SystemUIApplication = SystemUIApplication()
+    private lateinit var contextAvailableCallback:
+        SystemUIAppComponentFactoryBase.ContextAvailableCallback
+
+    @get:Rule val setFlagsRule = SetFlagsRule(SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT)
+
+    val kosmos = Kosmos()
+    @Mock private lateinit var initializer: SystemUIInitializer
+    @Mock private lateinit var rootComponent: GlobalRootComponent
+    @Mock private lateinit var sysuiComponent: SysUIComponent
+    @Mock private lateinit var bootCompleteCache: BootCompleteCacheImpl
+    @Mock private lateinit var initController: InitController
+
+    private val startableA = StartableA()
+    private val startableB = StartableB()
+    private val startableC = StartableC()
+    private val startableD = StartableD()
+    private val startableE = StartableE()
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+        app.attachBaseContext(context)
+        contextAvailableCallback =
+            SystemUIAppComponentFactoryBase.ContextAvailableCallback { initializer }
+        whenever(initializer.rootComponent).thenReturn(rootComponent)
+        whenever(initializer.sysUIComponent).thenReturn(sysuiComponent)
+        whenever(rootComponent.mainLooper).thenReturn(Looper.myLooper())
+        whenever(rootComponent.systemPropertiesHelper).thenReturn(kosmos.systemPropertiesHelper)
+        whenever(rootComponent.processWrapper).thenReturn(kosmos.processWrapper)
+        whenever(sysuiComponent.provideBootCacheImpl()).thenReturn(bootCompleteCache)
+        whenever(sysuiComponent.createDumpManager()).thenReturn(kosmos.dumpManager)
+        whenever(sysuiComponent.initController).thenReturn(initController)
+        kosmos.processWrapper.systemUser = true
+
+        app.setContextAvailableCallback(contextAvailableCallback)
+    }
+
+    @Test
+    fun testAppOnCreate() {
+        app.onCreate()
+    }
+
+    @Test
+    fun testStartServices_singleService() {
+        whenever(sysuiComponent.startables)
+            .thenReturn(mutableMapOf(StartableA::class.java to Provider { startableA }))
+        app.onCreate()
+        app.startServicesIfNeeded()
+        assertThat(startableA.started).isTrue()
+    }
+
+    @Test
+    fun testStartServices_twoServices() {
+        whenever(sysuiComponent.startables)
+            .thenReturn(
+                mutableMapOf(
+                    StartableA::class.java to Provider { startableA },
+                    StartableB::class.java to Provider { startableB }
+                )
+            )
+        app.onCreate()
+        app.startServicesIfNeeded()
+        assertThat(startableA.started).isTrue()
+        assertThat(startableB.started).isTrue()
+    }
+
+    @Test
+    fun testStartServices_simpleDependency() {
+        whenever(sysuiComponent.startables)
+            .thenReturn(
+                mutableMapOf(
+                    StartableC::class.java to Provider { startableC },
+                    StartableA::class.java to Provider { startableA },
+                    StartableB::class.java to Provider { startableB }
+                )
+            )
+        app.onCreate()
+        app.startServicesIfNeeded()
+        assertThat(startableA.started).isTrue()
+        assertThat(startableB.started).isTrue()
+        assertThat(startableC.started).isTrue()
+        assertThat(startableC.order).isGreaterThan(startableA.order)
+    }
+
+    @Test
+    fun testStartServices_complexDependency() {
+        whenever(sysuiComponent.startables)
+            .thenReturn(
+                mutableMapOf(
+                    StartableE::class.java to Provider { startableE },
+                    StartableC::class.java to Provider { startableC },
+                    StartableD::class.java to Provider { startableD },
+                    StartableA::class.java to Provider { startableA },
+                    StartableB::class.java to Provider { startableB }
+                )
+            )
+        app.onCreate()
+        app.startServicesIfNeeded()
+        assertThat(startableA.started).isTrue()
+        assertThat(startableB.started).isTrue()
+        assertThat(startableC.started).isTrue()
+        assertThat(startableD.started).isTrue()
+        assertThat(startableE.started).isTrue()
+        assertThat(startableC.order).isGreaterThan(startableA.order)
+        assertThat(startableD.order).isGreaterThan(startableA.order)
+        assertThat(startableD.order).isGreaterThan(startableB.order)
+        assertThat(startableE.order).isGreaterThan(startableB.order)
+        assertThat(startableE.order).isGreaterThan(startableD.order)
+    }
+
+    open class TestableStartable : CoreStartable {
+        companion object {
+            var startOrder = 0
+        }
+
+        var started = false
+        var order = -1
+
+        override fun start() {
+            started = true
+            order = startOrder
+            startOrder++
+        }
+    }
+
+    class StartableA : TestableStartable()
+    class StartableB : TestableStartable()
+
+    @Dependencies(StartableA::class) class StartableC : TestableStartable()
+
+    @Dependencies(StartableA::class, StartableB::class) class StartableD : TestableStartable()
+
+    @Dependencies(StartableD::class, StartableB::class) class StartableE : TestableStartable()
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java
index 837a130..2afb3a1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java
@@ -43,7 +43,6 @@
 import android.view.accessibility.IRemoteMagnificationAnimationCallback;
 import android.view.animation.AccelerateInterpolator;
 
-import androidx.test.filters.FlakyTest;
 import androidx.test.filters.LargeTest;
 
 import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
@@ -68,7 +67,6 @@
 
 @LargeTest
 @RunWith(AndroidTestingRunner.class)
-@FlakyTest(bugId = 308501761)
 public class WindowMagnificationAnimationControllerTest extends SysuiTestCase {
 
     @Rule
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/DismissAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationControllerTest.java
similarity index 84%
rename from packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/DismissAnimationControllerTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationControllerTest.java
index fd258e3..9bcab57 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/DismissAnimationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationControllerTest.java
@@ -40,12 +40,12 @@
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
 
-/** Tests for {@link DismissAnimationController}. */
+/** Tests for {@link DragToInteractAnimationController}. */
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper
-public class DismissAnimationControllerTest extends SysuiTestCase {
-    private DismissAnimationController mDismissAnimationController;
+public class DragToInteractAnimationControllerTest extends SysuiTestCase {
+    private DragToInteractAnimationController mDragToInteractAnimationController;
     private DismissView mDismissView;
 
     @Rule
@@ -65,19 +65,20 @@
                 stubMenuViewAppearance);
         mDismissView = spy(new DismissView(mContext));
         DismissViewUtils.setup(mDismissView);
-        mDismissAnimationController = new DismissAnimationController(mDismissView, stubMenuView);
+        mDragToInteractAnimationController = new DragToInteractAnimationController(
+                mDismissView, stubMenuView);
     }
 
     @Test
     public void showDismissView_success() {
-        mDismissAnimationController.showDismissView(true);
+        mDragToInteractAnimationController.showDismissView(true);
 
         verify(mDismissView).show();
     }
 
     @Test
     public void hideDismissView_success() {
-        mDismissAnimationController.showDismissView(false);
+        mDragToInteractAnimationController.showDismissView(false);
 
         verify(mDismissView).hide();
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java
index 7f12c05..9c8de30 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
@@ -62,7 +62,7 @@
     @Mock
     private SecureSettings mSecureSettings;
     @Mock
-    private DismissAnimationController.DismissCallback mStubDismissCallback;
+    private DragToInteractAnimationController.DismissCallback mStubDismissCallback;
 
     private RecyclerView mStubListView;
     private MenuView mMenuView;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java
index 9797f2a..e1522f5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java
@@ -68,7 +68,7 @@
     private MenuView mStubMenuView;
     private MenuListViewTouchHandler mTouchHandler;
     private MenuAnimationController mMenuAnimationController;
-    private DismissAnimationController mDismissAnimationController;
+    private DragToInteractAnimationController mDragToInteractAnimationController;
     private RecyclerView mStubListView;
     private DismissView mDismissView;
 
@@ -92,10 +92,10 @@
                 mStubMenuView, stubMenuViewAppearance));
         mDismissView = spy(new DismissView(mContext));
         DismissViewUtils.setup(mDismissView);
-        mDismissAnimationController =
-                spy(new DismissAnimationController(mDismissView, mStubMenuView));
+        mDragToInteractAnimationController =
+                spy(new DragToInteractAnimationController(mDismissView, mStubMenuView));
         mTouchHandler = new MenuListViewTouchHandler(mMenuAnimationController,
-                mDismissAnimationController);
+                mDragToInteractAnimationController);
         final AccessibilityTargetAdapter stubAdapter = new AccessibilityTargetAdapter(mStubTargets);
         mStubListView = (RecyclerView) mStubMenuView.getChildAt(0);
         mStubListView.setAdapter(stubAdapter);
@@ -115,7 +115,7 @@
 
     @Test
     public void onActionMoveEvent_notConsumedEvent_shouldMoveToPosition() {
-        doReturn(false).when(mDismissAnimationController).maybeConsumeMoveMotionEvent(
+        doReturn(false).when(mDragToInteractAnimationController).maybeConsumeMoveMotionEvent(
                 any(MotionEvent.class));
         final int offset = 100;
         final MotionEvent stubDownEvent =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuNotificationFactoryTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuNotificationFactoryTest.java
new file mode 100644
index 0000000..9dd337e
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuNotificationFactoryTest.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.accessibility.floatingmenu;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.Notification;
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidTestingRunner.class)
+@SmallTest
+public class MenuNotificationFactoryTest extends SysuiTestCase {
+    private MenuNotificationFactory mMenuNotificationFactory;
+
+    @Before
+    public void setUp() {
+        mMenuNotificationFactory = new MenuNotificationFactory(mContext);
+    }
+
+    @Test
+    public void createHiddenNotification_hasUndoAndDeleteAction() {
+        Notification notification = mMenuNotificationFactory.createHiddenNotification();
+
+        assertThat(notification.contentIntent.getIntent().getAction()).isEqualTo(
+                MenuNotificationFactory.ACTION_UNDO);
+        assertThat(notification.deleteIntent.getIntent().getAction()).isEqualTo(
+                MenuNotificationFactory.ACTION_DELETE);
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java
index be6f3ff..68879a5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java
@@ -21,15 +21,30 @@
 import static android.view.WindowInsets.Type.displayCutout;
 import static android.view.WindowInsets.Type.ime;
 import static android.view.WindowInsets.Type.systemBars;
+
+import static com.android.systemui.accessibility.floatingmenu.MenuNotificationFactory.ACTION_DELETE;
+import static com.android.systemui.accessibility.floatingmenu.MenuNotificationFactory.ACTION_UNDO;
 import static com.android.systemui.accessibility.floatingmenu.MenuViewLayer.LayerIndex;
+
 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.doReturn;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.accessibilityservice.AccessibilityServiceInfo;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.content.BroadcastReceiver;
 import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
@@ -40,6 +55,8 @@
 import android.os.UserHandle;
 import android.platform.test.annotations.DisableFlags;
 import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 import android.provider.Settings;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
@@ -53,16 +70,21 @@
 import androidx.dynamicanimation.animation.SpringAnimation;
 import androidx.test.filters.SmallTest;
 
+import com.android.internal.messages.nano.SystemMessageProto;
 import com.android.systemui.Flags;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.SysuiTestableContext;
 import com.android.systemui.util.settings.SecureSettings;
+import com.android.wm.shell.common.magnetictarget.MagnetizedObject;
 
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
+import org.mockito.Spy;
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
 
@@ -98,6 +120,12 @@
     @Rule
     public MockitoRule mockito = MockitoJUnit.rule();
 
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule =
+            DeviceFlagsValueProvider.createCheckFlagsRule();
+
+    @Spy
+    private SysuiTestableContext mSpyContext = getContext();
     @Mock
     private IAccessibilityFloatingMenu mFloatingMenu;
 
@@ -110,8 +138,12 @@
     @Mock
     private AccessibilityManager mStubAccessibilityManager;
 
+    private final NotificationManager mMockNotificationManager = mock(NotificationManager.class);
+
     @Before
     public void setUp() throws Exception {
+        mSpyContext.addMockSystemService(Context.NOTIFICATION_SERVICE, mMockNotificationManager);
+
         final Rect mDisplayBounds = new Rect();
         mDisplayBounds.set(/* left= */ 0, /* top= */ 0, DISPLAY_WINDOW_WIDTH,
                 DISPLAY_WINDOW_HEIGHT);
@@ -119,31 +151,31 @@
                 new WindowMetrics(mDisplayBounds, fakeDisplayInsets(), /* density = */ 0.0f));
         doReturn(mWindowMetrics).when(mStubWindowManager).getCurrentWindowMetrics();
 
-        mMenuViewLayer = new MenuViewLayer(mContext, mStubWindowManager, mStubAccessibilityManager,
-                mFloatingMenu, mSecureSettings);
+        mMenuViewLayer = new MenuViewLayer(mSpyContext, mStubWindowManager,
+                mStubAccessibilityManager, mFloatingMenu, mSecureSettings);
         mMenuView = (MenuView) mMenuViewLayer.getChildAt(LayerIndex.MENU_VIEW);
         mMenuAnimationController = mMenuView.getMenuAnimationController();
 
         mLastAccessibilityButtonTargets =
-                Settings.Secure.getStringForUser(mContext.getContentResolver(),
+                Settings.Secure.getStringForUser(mSpyContext.getContentResolver(),
                         Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, UserHandle.USER_CURRENT);
         mLastEnabledAccessibilityServices =
-                Settings.Secure.getStringForUser(mContext.getContentResolver(),
+                Settings.Secure.getStringForUser(mSpyContext.getContentResolver(),
                         Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, UserHandle.USER_CURRENT);
 
         mMenuViewLayer.onAttachedToWindow();
-        Settings.Secure.putStringForUser(mContext.getContentResolver(),
+        Settings.Secure.putStringForUser(mSpyContext.getContentResolver(),
                 Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, "", UserHandle.USER_CURRENT);
-        Settings.Secure.putStringForUser(mContext.getContentResolver(),
+        Settings.Secure.putStringForUser(mSpyContext.getContentResolver(),
                 Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, "", UserHandle.USER_CURRENT);
     }
 
     @After
     public void tearDown() throws Exception {
-        Settings.Secure.putStringForUser(mContext.getContentResolver(),
+        Settings.Secure.putStringForUser(mSpyContext.getContentResolver(),
                 Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, mLastAccessibilityButtonTargets,
                 UserHandle.USER_CURRENT);
-        Settings.Secure.putStringForUser(mContext.getContentResolver(),
+        Settings.Secure.putStringForUser(mSpyContext.getContentResolver(),
                 Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, mLastEnabledAccessibilityServices,
                 UserHandle.USER_CURRENT);
 
@@ -188,7 +220,7 @@
         setupEnabledAccessibilityServiceList();
 
         mMenuViewLayer.mDismissMenuAction.run();
-        final String value = Settings.Secure.getString(mContext.getContentResolver(),
+        final String value = Settings.Secure.getString(mSpyContext.getContentResolver(),
                 Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
 
         assertThat(value).isEqualTo("");
@@ -203,7 +235,7 @@
                 AccessibilityManager.ACCESSIBILITY_SHORTCUT_KEY)).thenReturn(stubShortcutTargets);
 
         mMenuViewLayer.mDismissMenuAction.run();
-        final String value = Settings.Secure.getString(mContext.getContentResolver(),
+        final String value = Settings.Secure.getString(mSpyContext.getContentResolver(),
                 Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
 
         assertThat(value).isEqualTo(TEST_SELECT_TO_SPEAK_COMPONENT_NAME.flattenToString());
@@ -278,9 +310,60 @@
         assertThat(mMenuView.getTranslationX()).isEqualTo(beforePosition.x);
         assertThat(mMenuView.getTranslationY()).isEqualTo(beforePosition.y);
     }
+    @Test
+    @EnableFlags(Flags.FLAG_FLOATING_MENU_DRAG_TO_HIDE)
+    public void onReleasedInTarget_hideMenuAndShowNotificationWithExpectedActions() {
+        dragMenuThenReleasedInTarget();
+
+        verify(mMockNotificationManager).notify(
+                eq(SystemMessageProto.SystemMessage.NOTE_A11Y_FLOATING_MENU_HIDDEN),
+                any(Notification.class));
+        ArgumentCaptor<IntentFilter> intentFilterCaptor = ArgumentCaptor.forClass(
+                IntentFilter.class);
+        verify(mSpyContext).registerReceiver(
+                any(BroadcastReceiver.class),
+                intentFilterCaptor.capture(),
+                anyInt());
+        assertThat(intentFilterCaptor.getValue().matchAction(ACTION_UNDO)).isTrue();
+        assertThat(intentFilterCaptor.getValue().matchAction(ACTION_DELETE)).isTrue();
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_FLOATING_MENU_DRAG_TO_HIDE)
+    public void receiveActionUndo_dismissNotificationAndMenuVisible() {
+        ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor = ArgumentCaptor.forClass(
+                BroadcastReceiver.class);
+        dragMenuThenReleasedInTarget();
+
+        verify(mSpyContext).registerReceiver(broadcastReceiverCaptor.capture(),
+                any(IntentFilter.class), anyInt());
+        broadcastReceiverCaptor.getValue().onReceive(mSpyContext, new Intent(ACTION_UNDO));
+
+        verify(mSpyContext).unregisterReceiver(broadcastReceiverCaptor.getValue());
+        verify(mMockNotificationManager).cancel(
+                SystemMessageProto.SystemMessage.NOTE_A11Y_FLOATING_MENU_HIDDEN);
+        assertThat(mMenuView.getVisibility()).isEqualTo(VISIBLE);
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_FLOATING_MENU_DRAG_TO_HIDE)
+    public void receiveActionDelete_dismissNotificationAndHideMenu() {
+        ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor = ArgumentCaptor.forClass(
+                BroadcastReceiver.class);
+        dragMenuThenReleasedInTarget();
+
+        verify(mSpyContext).registerReceiver(broadcastReceiverCaptor.capture(),
+                any(IntentFilter.class), anyInt());
+        broadcastReceiverCaptor.getValue().onReceive(mSpyContext, new Intent(ACTION_DELETE));
+
+        verify(mSpyContext).unregisterReceiver(broadcastReceiverCaptor.getValue());
+        verify(mMockNotificationManager).cancel(
+                SystemMessageProto.SystemMessage.NOTE_A11Y_FLOATING_MENU_HIDDEN);
+        verify(mFloatingMenu).hide();
+    }
 
     private void setupEnabledAccessibilityServiceList() {
-        Settings.Secure.putString(mContext.getContentResolver(),
+        Settings.Secure.putString(mSpyContext.getContentResolver(),
                 Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
                 TEST_SELECT_TO_SPEAK_COMPONENT_NAME.flattenToString());
 
@@ -344,6 +427,12 @@
                     springAnimation.skipToEnd();
                     springAnimation.doAnimationFrame(500);
                 });
+    }
 
+    private void dragMenuThenReleasedInTarget() {
+        MagnetizedObject.MagnetListener magnetListener =
+                mMenuViewLayer.getDragToInteractAnimationController().getMagnetListener();
+        magnetListener.onReleasedInTarget(
+                new MagnetizedObject.MagneticTarget(mock(View.class), 200));
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/fontscaling/FontScalingDialogDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/accessibility/fontscaling/FontScalingDialogDelegateTest.kt
index bfb5485..9b6c8cd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/fontscaling/FontScalingDialogDelegateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/fontscaling/FontScalingDialogDelegateTest.kt
@@ -25,14 +25,12 @@
 import android.widget.Button
 import android.widget.SeekBar
 import androidx.test.filters.SmallTest
-import com.android.systemui.res.R
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.animation.DialogLaunchAnimator
 import com.android.systemui.common.ui.view.SeekBarWithIconButtonsView
 import com.android.systemui.common.ui.view.SeekBarWithIconButtonsView.OnSeekBarWithIconButtonsChangeListener
-import com.android.systemui.flags.FakeFeatureFlags
-import com.android.systemui.flags.Flags
 import com.android.systemui.model.SysUiState
+import com.android.systemui.res.R
 import com.android.systemui.settings.UserTracker
 import com.android.systemui.statusbar.phone.SystemUIDialog
 import com.android.systemui.statusbar.phone.SystemUIDialog.DEFAULT_DISMISS_ON_DEVICE_LOCK
@@ -78,7 +76,6 @@
     @Mock private lateinit var dialogManager: SystemUIDialogManager
     @Mock private lateinit var dialogFactory: SystemUIDialog.Factory
     @Mock private lateinit var userTracker: UserTracker
-    private val featureFlags = FakeFeatureFlags()
     @Mock private lateinit var sysuiState: SysUiState
     @Mock private lateinit var dialogLaunchAnimator: DialogLaunchAnimator
 
@@ -88,7 +85,6 @@
         testableLooper = TestableLooper.get(this)
         val mainHandler = Handler(testableLooper.looper)
         systemSettings = FakeSettings()
-        featureFlags.set(Flags.WM_ENABLE_PREDICTIVE_BACK_QS_DIALOG_ANIM, true)
         // Guarantee that the systemSettings always starts with the default font scale.
         systemSettings.putFloatForUser(Settings.System.FONT_SCALE, 1.0f, userTracker.userId)
         secureSettings = FakeSettings()
@@ -96,31 +92,34 @@
         backgroundDelayableExecutor = FakeExecutor(systemClock)
         whenever(sysuiState.setFlag(anyInt(), anyBoolean())).thenReturn(sysuiState)
 
-        fontScalingDialogDelegate = spy(FontScalingDialogDelegate(
+        fontScalingDialogDelegate =
+            spy(
+                FontScalingDialogDelegate(
+                    mContext,
+                    dialogFactory,
+                    LayoutInflater.from(mContext),
+                    systemSettings,
+                    secureSettings,
+                    systemClock,
+                    userTracker,
+                    mainHandler,
+                    backgroundDelayableExecutor
+                )
+            )
+
+        dialog =
+            SystemUIDialog(
                 mContext,
-                dialogFactory,
-                LayoutInflater.from(mContext),
-                systemSettings,
-                secureSettings,
-                systemClock,
-                userTracker,
-                mainHandler,
-                backgroundDelayableExecutor
-            ))
+                0,
+                DEFAULT_DISMISS_ON_DEVICE_LOCK,
+                dialogManager,
+                sysuiState,
+                fakeBroadcastDispatcher,
+                dialogLaunchAnimator,
+                fontScalingDialogDelegate
+            )
 
-        dialog = SystemUIDialog(
-            mContext,
-            0,
-            DEFAULT_DISMISS_ON_DEVICE_LOCK,
-            featureFlags,
-            dialogManager,
-            sysuiState,
-            fakeBroadcastDispatcher,
-            dialogLaunchAnimator,
-            fontScalingDialogDelegate
-        )
-
-        whenever(dialogFactory.create(any())).thenReturn(dialog)
+        whenever(dialogFactory.create(any(), any())).thenReturn(dialog)
     }
 
     @Test
@@ -299,11 +298,7 @@
         // Default seekbar progress for font size is 1, simulate dragging to 0 without
         // releasing the finger
         changeListener.onStartTrackingTouch(seekBar)
-        changeListener.onProgressChanged(
-            seekBar,
-            /* progress= */ 0,
-            /* fromUser= */ false
-        )
+        changeListener.onProgressChanged(seekBar, /* progress= */ 0, /* fromUser= */ false)
         backgroundDelayableExecutor.advanceClockToNext()
         backgroundDelayableExecutor.runAllReady()
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt
index 8693d5c..7c626a1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt
@@ -16,6 +16,9 @@
 
 package com.android.systemui.back.domain.interactor
 
+import android.platform.test.annotations.RequiresFlagsDisabled
+import android.platform.test.annotations.RequiresFlagsEnabled
+import android.platform.test.flag.junit.DeviceFlagsValueProvider
 import android.view.ViewRootImpl
 import android.window.BackEvent
 import android.window.BackEvent.EDGE_LEFT
@@ -26,9 +29,8 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.internal.statusbar.IStatusBarService
+import com.android.systemui.Flags
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.flags.FakeFeatureFlags
-import com.android.systemui.flags.Flags
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
@@ -72,7 +74,6 @@
 @OptIn(ExperimentalCoroutinesApi::class)
 class BackActionInteractorTest : SysuiTestCase() {
     private val testScope = TestScope()
-    private val featureFlags = FakeFeatureFlags()
     private val executor = FakeExecutor(FakeSystemClock())
 
     @JvmField @Rule var mockitoRule = MockitoJUnit.rule()
@@ -107,17 +108,17 @@
                 statusBarKeyguardViewManager,
                 shadeController,
                 notificationShadeWindowController,
-                windowRootViewVisibilityInteractor,
-                featureFlags,
+                windowRootViewVisibilityInteractor
             )
             .apply { this.setup(qsController, shadeViewController) }
     }
 
     private val powerInteractor = PowerInteractorFactory.create().powerInteractor
 
+    @get:Rule val checkFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
+
     @Before
     fun setUp() {
-        featureFlags.set(Flags.WM_SHADE_ANIMATE_BACK_GESTURE, false)
         whenever(notificationShadeWindowController.windowRootView).thenReturn(windowRootView)
         whenever(windowRootView.viewRootImpl).thenReturn(viewRootImpl)
         whenever(viewRootImpl.onBackInvokedDispatcher).thenReturn(onBackInvokedDispatcher)
@@ -229,9 +230,9 @@
     }
 
     @Test
+    @RequiresFlagsDisabled(Flags.FLAG_PREDICTIVE_BACK_ANIMATE_SHADE)
     fun animationFlagOff_onBackInvoked_keyguardNotified() {
         backActionInteractor.start()
-        featureFlags.set(Flags.WM_SHADE_ANIMATE_BACK_GESTURE, false)
         windowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(true)
         powerInteractor.setAwakeForTest()
         val callback = getBackInvokedCallback()
@@ -243,8 +244,8 @@
     }
 
     @Test
+    @RequiresFlagsEnabled(Flags.FLAG_PREDICTIVE_BACK_ANIMATE_SHADE)
     fun animationFlagOn_onBackInvoked_keyguardNotified() {
-        featureFlags.set(Flags.WM_SHADE_ANIMATE_BACK_GESTURE, true)
         backActionInteractor.start()
         windowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(true)
         powerInteractor.setAwakeForTest()
@@ -257,8 +258,8 @@
     }
 
     @Test
+    @RequiresFlagsEnabled(Flags.FLAG_PREDICTIVE_BACK_ANIMATE_SHADE)
     fun animationFlagOn_callbackIsAnimationCallback() {
-        featureFlags.set(Flags.WM_SHADE_ANIMATE_BACK_GESTURE, true)
         backActionInteractor.start()
         windowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(true)
         powerInteractor.setAwakeForTest()
@@ -269,8 +270,8 @@
     }
 
     @Test
+    @RequiresFlagsEnabled(Flags.FLAG_PREDICTIVE_BACK_ANIMATE_SHADE)
     fun onBackProgressed_shadeCannotBeCollapsed_shadeViewControllerNotNotified() {
-        featureFlags.set(Flags.WM_SHADE_ANIMATE_BACK_GESTURE, true)
         backActionInteractor.start()
         windowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(true)
         powerInteractor.setAwakeForTest()
@@ -284,8 +285,8 @@
     }
 
     @Test
+    @RequiresFlagsEnabled(Flags.FLAG_PREDICTIVE_BACK_ANIMATE_SHADE)
     fun onBackProgressed_shadeCanBeCollapsed_shadeViewControllerNotified() {
-        featureFlags.set(Flags.WM_SHADE_ANIMATE_BACK_GESTURE, true)
         backActionInteractor.start()
         windowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(true)
         powerInteractor.setAwakeForTest()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
index 4c510ee..a47e288 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
@@ -29,7 +29,8 @@
 import com.android.keyguard.logging.KeyguardLogger
 import com.android.systemui.Flags.FLAG_LIGHT_REVEAL_MIGRATION
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.dump.logcatLogBuffer
+import com.android.systemui.biometrics.data.repository.FakeFacePropertyRepository
+import com.android.systemui.log.logcatLogBuffer
 import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.keyguard.WakefulnessLifecycle
 import com.android.systemui.plugins.statusbar.StatusBarStateController
@@ -93,6 +94,7 @@
     @Mock
     private lateinit var fpSensorProp: FingerprintSensorPropertiesInternal
 
+    private val facePropertyRepository = FakeFacePropertyRepository()
     private val displayMetrics = DisplayMetrics()
 
     @Captor
@@ -123,10 +125,10 @@
             udfpsControllerProvider,
             statusBarStateController,
             displayMetrics,
-            featureFlags,
             KeyguardLogger(logcatLogBuffer(AuthRippleController.TAG)),
             biometricUnlockController,
             lightRevealScrim,
+            facePropertyRepository,
             rippleView,
         )
         controller.init()
@@ -203,7 +205,7 @@
 
     @Test
     fun testNullFaceSensorLocationDoesNothing() {
-        `when`(authController.faceSensorLocation).thenReturn(null)
+        facePropertyRepository.setSensorLocation(null)
         controller.onViewAttached()
 
         val captor = ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback::class.java)
@@ -271,7 +273,7 @@
     fun testAnimatorRunWhenWakeAndUnlock_faceUdfpsFingerDown() {
         mSetFlagsRule.disableFlags(FLAG_LIGHT_REVEAL_MIGRATION)
         val faceLocation = Point(5, 5)
-        `when`(authController.faceSensorLocation).thenReturn(faceLocation)
+        facePropertyRepository.setSensorLocation(faceLocation)
         controller.onViewAttached()
         `when`(keyguardStateController.isShowing).thenReturn(true)
         `when`(biometricUnlockController.isWakeAndUnlock).thenReturn(true)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/FaceAuthAccessibilityDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/FaceAuthAccessibilityDelegateTest.kt
index 86b9b84..9b0e58d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/FaceAuthAccessibilityDelegateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/FaceAuthAccessibilityDelegateTest.kt
@@ -22,7 +22,7 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.argumentCaptor
 import com.android.systemui.util.mockito.whenever
@@ -42,7 +42,7 @@
 class FaceAuthAccessibilityDelegateTest : SysuiTestCase() {
 
     @Mock private lateinit var hostView: View
-    @Mock private lateinit var faceAuthInteractor: KeyguardFaceAuthInteractor
+    @Mock private lateinit var faceAuthInteractor: DeviceEntryFaceAuthInteractor
     private lateinit var underTest: FaceAuthAccessibilityDelegate
 
     @Before
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsUtilsTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsUtilsTest.java
index 2aeba9a..3dcb3f8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsUtilsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsUtilsTest.java
@@ -20,6 +20,7 @@
 
 import android.content.res.Resources;
 import android.graphics.Rect;
+import android.hardware.fingerprint.FingerprintSensorProperties;
 import android.view.Surface;
 
 import androidx.test.filters.SmallTest;
@@ -63,28 +64,32 @@
         assertThat(
                 mUdfpsUtils.onTouchOutsideOfSensorArea(true, mContext,
                         0 /* touchX */, 0/* touchY */,
-                        new UdfpsOverlayParams(new Rect(), new Rect(), 0, 0, 1f, rotation)
+                        new UdfpsOverlayParams(new Rect(), new Rect(), 0, 0, 1f, rotation,
+                                FingerprintSensorProperties.TYPE_UDFPS_OPTICAL)
                 )
         ).isEqualTo(mTouchHints[0]);
         // touch at 90 degrees
         assertThat(
                 mUdfpsUtils.onTouchOutsideOfSensorArea(true, mContext,
                         0 /* touchX */, -1/* touchY */,
-                        new UdfpsOverlayParams(new Rect(), new Rect(), 0, 0, 1f, rotation)
+                        new UdfpsOverlayParams(new Rect(), new Rect(), 0, 0, 1f, rotation,
+                                FingerprintSensorProperties.TYPE_UDFPS_OPTICAL)
                 )
         ).isEqualTo(mTouchHints[1]);
         // touch at 180 degrees
         assertThat(
                 mUdfpsUtils.onTouchOutsideOfSensorArea(true, mContext,
                         -1 /* touchX */, 0/* touchY */,
-                        new UdfpsOverlayParams(new Rect(), new Rect(), 0, 0, 1f, rotation)
+                        new UdfpsOverlayParams(new Rect(), new Rect(), 0, 0, 1f, rotation,
+                                FingerprintSensorProperties.TYPE_UDFPS_OPTICAL)
                 )
         ).isEqualTo(mTouchHints[2]);
         // touch at 270 degrees
         assertThat(
                 mUdfpsUtils.onTouchOutsideOfSensorArea(true, mContext,
                         0 /* touchX */, 1/* touchY */,
-                        new UdfpsOverlayParams(new Rect(), new Rect(), 0, 0, 1f, rotation)
+                        new UdfpsOverlayParams(new Rect(), new Rect(), 0, 0, 1f, rotation,
+                                FingerprintSensorProperties.TYPE_UDFPS_OPTICAL)
                 )
         ).isEqualTo(mTouchHints[3]);
     }
@@ -97,28 +102,32 @@
         assertThat(
                 mUdfpsUtils.onTouchOutsideOfSensorArea(true, mContext,
                         0 /* touchX */, 0 /* touchY */,
-                        new UdfpsOverlayParams(new Rect(), new Rect(), 0, 0, 1f, rotation)
+                        new UdfpsOverlayParams(new Rect(), new Rect(), 0, 0, 1f, rotation,
+                                FingerprintSensorProperties.TYPE_UDFPS_OPTICAL)
                 )
         ).isEqualTo(mTouchHints[1]);
         // touch at 90 degrees -> 180 degrees
         assertThat(
                 mUdfpsUtils.onTouchOutsideOfSensorArea(true, mContext,
                         0 /* touchX */, -1 /* touchY */,
-                        new UdfpsOverlayParams(new Rect(), new Rect(), 0, 0, 1f, rotation)
+                        new UdfpsOverlayParams(new Rect(), new Rect(), 0, 0, 1f, rotation,
+                                FingerprintSensorProperties.TYPE_UDFPS_OPTICAL)
                 )
         ).isEqualTo(mTouchHints[2]);
         // touch at 180 degrees -> 270 degrees
         assertThat(
                 mUdfpsUtils.onTouchOutsideOfSensorArea(true, mContext,
                         -1 /* touchX */, 0 /* touchY */,
-                        new UdfpsOverlayParams(new Rect(), new Rect(), 0, 0, 1f, rotation)
+                        new UdfpsOverlayParams(new Rect(), new Rect(), 0, 0, 1f, rotation,
+                                FingerprintSensorProperties.TYPE_UDFPS_OPTICAL)
                 )
         ).isEqualTo(mTouchHints[3]);
         // touch at 270 degrees -> 0 degrees
         assertThat(
                 mUdfpsUtils.onTouchOutsideOfSensorArea(true, mContext,
                         0 /* touchX */, 1/* touchY */,
-                        new UdfpsOverlayParams(new Rect(), new Rect(), 0, 0, 1f, rotation)
+                        new UdfpsOverlayParams(new Rect(), new Rect(), 0, 0, 1f, rotation,
+                                FingerprintSensorProperties.TYPE_UDFPS_OPTICAL)
                 )
         ).isEqualTo(mTouchHints[0]);
     }
@@ -131,28 +140,32 @@
         assertThat(
                 mUdfpsUtils.onTouchOutsideOfSensorArea(true, mContext,
                         0 /* touchX */, 0/* touchY */,
-                        new UdfpsOverlayParams(new Rect(), new Rect(), 0, 0, 1f, rotation)
+                        new UdfpsOverlayParams(new Rect(), new Rect(), 0, 0, 1f, rotation,
+                                FingerprintSensorProperties.TYPE_UDFPS_OPTICAL)
                 )
         ).isEqualTo(mTouchHints[3]);
         // touch at 90 degrees -> 0 degrees
         assertThat(
                 mUdfpsUtils.onTouchOutsideOfSensorArea(true, mContext,
                         0 /* touchX */, -1/* touchY */,
-                        new UdfpsOverlayParams(new Rect(), new Rect(), 0, 0, 1f, rotation)
+                        new UdfpsOverlayParams(new Rect(), new Rect(), 0, 0, 1f, rotation,
+                                FingerprintSensorProperties.TYPE_UDFPS_OPTICAL)
                 )
         ).isEqualTo(mTouchHints[0]);
         // touch at 180 degrees -> 90 degrees
         assertThat(
                 mUdfpsUtils.onTouchOutsideOfSensorArea(true, mContext,
                         -1 /* touchX */, 0/* touchY */,
-                        new UdfpsOverlayParams(new Rect(), new Rect(), 0, 0, 1f, rotation)
+                        new UdfpsOverlayParams(new Rect(), new Rect(), 0, 0, 1f, rotation,
+                                FingerprintSensorProperties.TYPE_UDFPS_OPTICAL)
                 )
         ).isEqualTo(mTouchHints[1]);
         // touch at 270 degrees -> 180 degrees
         assertThat(
                 mUdfpsUtils.onTouchOutsideOfSensorArea(true, mContext,
                         0 /* touchX */, 1/* touchY */,
-                        new UdfpsOverlayParams(new Rect(), new Rect(), 0, 0, 1f, rotation)
+                        new UdfpsOverlayParams(new Rect(), new Rect(), 0, 0, 1f, rotation,
+                                FingerprintSensorProperties.TYPE_UDFPS_OPTICAL)
                 )
         ).isEqualTo(mTouchHints[2]);
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/DisplayStateRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/DisplayStateRepositoryTest.kt
index 834179bf..a84778a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/DisplayStateRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/DisplayStateRepositoryTest.kt
@@ -19,6 +19,7 @@
 import android.hardware.devicestate.DeviceStateManager
 import android.hardware.display.DisplayManager
 import android.os.Handler
+import android.util.Size
 import android.view.Display
 import android.view.DisplayInfo
 import android.view.Surface
@@ -147,6 +148,40 @@
             displayListenerCaptor.value.onDisplayChanged(Surface.ROTATION_180)
             assertThat(currentRotation).isEqualTo(DisplayRotation.ROTATION_180)
         }
+
+    @Test
+    fun updatesCurrentSize_whenDisplayStateChanges() =
+        testScope.runTest {
+            val currentSize by collectLastValue(underTest.currentDisplaySize)
+            runCurrent()
+
+            verify(displayManager)
+                .registerDisplayListener(
+                    displayListenerCaptor.capture(),
+                    same(handler),
+                    eq(DisplayManager.EVENT_FLAG_DISPLAY_CHANGED)
+                )
+
+            whenever(display.getDisplayInfo(any())).then {
+                val info = it.getArgument<DisplayInfo>(0)
+                info.rotation = Surface.ROTATION_0
+                info.logicalWidth = 100
+                info.logicalHeight = 200
+                return@then true
+            }
+            displayListenerCaptor.value.onDisplayChanged(Surface.ROTATION_0)
+            assertThat(currentSize).isEqualTo(Size(100, 200))
+
+            whenever(display.getDisplayInfo(any())).then {
+                val info = it.getArgument<DisplayInfo>(0)
+                info.rotation = Surface.ROTATION_90
+                info.logicalWidth = 100
+                info.logicalHeight = 200
+                return@then true
+            }
+            displayListenerCaptor.value.onDisplayChanged(Surface.ROTATION_180)
+            assertThat(currentSize).isEqualTo(Size(200, 100))
+        }
 }
 
 private fun DeviceStateManager.captureCallback() =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FacePropertyRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FacePropertyRepositoryImplTest.kt
index c14ad6a..9f24d5d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FacePropertyRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FacePropertyRepositoryImplTest.kt
@@ -17,10 +17,12 @@
 
 package com.android.systemui.biometrics.data.repository
 
+import android.graphics.Point
 import android.hardware.biometrics.BiometricConstants.BIOMETRIC_LOCKOUT_NONE
 import android.hardware.biometrics.BiometricConstants.BIOMETRIC_LOCKOUT_PERMANENT
 import android.hardware.biometrics.BiometricConstants.BIOMETRIC_LOCKOUT_TIMED
 import android.hardware.biometrics.SensorProperties
+import android.hardware.camera2.CameraManager
 import android.hardware.face.FaceManager
 import android.hardware.face.FaceSensorPropertiesInternal
 import android.hardware.face.IFaceAuthenticatorsRegisteredCallback
@@ -28,9 +30,12 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.biometrics.shared.model.LockoutMode
 import com.android.systemui.biometrics.shared.model.SensorStrength
+import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
 import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.res.R
 import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth.assertThat
+import java.util.concurrent.Executor
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.StandardTestDispatcher
 import kotlinx.coroutines.test.TestDispatcher
@@ -45,6 +50,7 @@
 import org.mockito.ArgumentCaptor
 import org.mockito.Captor
 import org.mockito.Mock
+import org.mockito.Mockito.any
 import org.mockito.Mockito.verify
 import org.mockito.junit.MockitoJUnit
 import org.mockito.junit.MockitoRule
@@ -53,23 +59,56 @@
 @SmallTest
 @RunWith(JUnit4::class)
 class FacePropertyRepositoryImplTest : SysuiTestCase() {
+    companion object {
+        private const val LOGICAL_CAMERA_ID_OUTER_FRONT = "0"
+        private const val LOGICAL_CAMERA_ID_INNER_FRONT = "1"
+        private const val PHYSICAL_CAMERA_ID_OUTER_FRONT = "5"
+        private const val PHYSICAL_CAMERA_ID_INNER_FRONT = "6"
+        private val OUTER_FRONT_SENSOR_LOCATION = intArrayOf(100, 10, 20)
+        private val INNER_FRONT_SENSOR_LOCATION = intArrayOf(200, 20, 30)
+    }
+
     @JvmField @Rule val mockitoRule: MockitoRule = MockitoJUnit.rule()
 
     private lateinit var underTest: FacePropertyRepository
     private lateinit var dispatcher: TestDispatcher
     private lateinit var testScope: TestScope
 
+    private val displayStateRepository = FakeDisplayStateRepository()
+    private val configurationRepository = FakeConfigurationRepository()
+
     @Captor private lateinit var callback: ArgumentCaptor<IFaceAuthenticatorsRegisteredCallback>
     @Mock private lateinit var faceManager: FaceManager
+    @Captor private lateinit var cameraCallback: ArgumentCaptor<CameraManager.AvailabilityCallback>
+    @Mock private lateinit var cameraManager: CameraManager
     @Before
     fun setup() {
+        overrideResource(R.string.config_protectedCameraId, LOGICAL_CAMERA_ID_OUTER_FRONT)
+        overrideResource(R.string.config_protectedPhysicalCameraId, PHYSICAL_CAMERA_ID_OUTER_FRONT)
+        overrideResource(R.string.config_protectedInnerCameraId, LOGICAL_CAMERA_ID_INNER_FRONT)
+        overrideResource(
+            R.string.config_protectedInnerPhysicalCameraId,
+            PHYSICAL_CAMERA_ID_INNER_FRONT
+        )
+        overrideResource(R.array.config_face_auth_props, OUTER_FRONT_SENSOR_LOCATION)
+        overrideResource(R.array.config_inner_face_auth_props, INNER_FRONT_SENSOR_LOCATION)
+
         dispatcher = StandardTestDispatcher()
         testScope = TestScope(dispatcher)
         underTest = createRepository(faceManager)
     }
 
     private fun createRepository(manager: FaceManager? = faceManager) =
-        FacePropertyRepositoryImpl(testScope.backgroundScope, dispatcher, manager)
+        FacePropertyRepositoryImpl(
+            context,
+            context.mainExecutor,
+            testScope.backgroundScope,
+            dispatcher,
+            manager,
+            cameraManager,
+            displayStateRepository,
+            configurationRepository,
+        )
 
     @Test
     fun whenFaceManagerIsNotPresentIsNull() =
@@ -129,6 +168,75 @@
             assertThat(underTest.getLockoutMode(userId)).isEqualTo(LockoutMode.NONE)
         }
 
+    @Test
+    fun providesTheSensorLocationOfOuterCameraFromOnPhysicalCameraAvailable() {
+        testScope.runTest {
+            runCurrent()
+            collectLastValue(underTest.sensorLocation)
+
+            verify(faceManager).addAuthenticatorsRegisteredCallback(callback.capture())
+            callback.value.onAllAuthenticatorsRegistered(
+                listOf(createSensorProperties(1, SensorProperties.STRENGTH_STRONG))
+            )
+            runCurrent()
+            verify(cameraManager)
+                .registerAvailabilityCallback(any(Executor::class.java), cameraCallback.capture())
+
+            cameraCallback.value.onPhysicalCameraAvailable("1", PHYSICAL_CAMERA_ID_OUTER_FRONT)
+            runCurrent()
+
+            val sensorLocation by collectLastValue(underTest.sensorLocation)
+            assertThat(sensorLocation)
+                .isEqualTo(Point(OUTER_FRONT_SENSOR_LOCATION[0], OUTER_FRONT_SENSOR_LOCATION[1]))
+        }
+    }
+
+    @Test
+    fun providesTheSensorLocationOfInnerCameraFromOnPhysicalCameraAvailable() {
+        testScope.runTest {
+            runCurrent()
+            collectLastValue(underTest.sensorLocation)
+
+            verify(faceManager).addAuthenticatorsRegisteredCallback(callback.capture())
+            callback.value.onAllAuthenticatorsRegistered(
+                listOf(createSensorProperties(1, SensorProperties.STRENGTH_STRONG))
+            )
+            runCurrent()
+            verify(cameraManager)
+                .registerAvailabilityCallback(any(Executor::class.java), cameraCallback.capture())
+
+            cameraCallback.value.onPhysicalCameraAvailable("1", PHYSICAL_CAMERA_ID_INNER_FRONT)
+            runCurrent()
+
+            val sensorLocation by collectLastValue(underTest.sensorLocation)
+            assertThat(sensorLocation)
+                .isEqualTo(Point(INNER_FRONT_SENSOR_LOCATION[0], INNER_FRONT_SENSOR_LOCATION[1]))
+        }
+    }
+
+    @Test
+    fun providesTheSensorLocationOfCameraFromOnPhysicalCameraUnavailable() {
+        testScope.runTest {
+            runCurrent()
+            collectLastValue(underTest.sensorLocation)
+
+            verify(faceManager).addAuthenticatorsRegisteredCallback(callback.capture())
+            callback.value.onAllAuthenticatorsRegistered(
+                listOf(createSensorProperties(1, SensorProperties.STRENGTH_STRONG))
+            )
+            runCurrent()
+            verify(cameraManager)
+                .registerAvailabilityCallback(any(Executor::class.java), cameraCallback.capture())
+
+            cameraCallback.value.onPhysicalCameraUnavailable("1", PHYSICAL_CAMERA_ID_INNER_FRONT)
+            runCurrent()
+
+            val sensorLocation by collectLastValue(underTest.sensorLocation)
+            assertThat(sensorLocation)
+                .isEqualTo(Point(OUTER_FRONT_SENSOR_LOCATION[0], OUTER_FRONT_SENSOR_LOCATION[1]))
+        }
+    }
+
     private fun createSensorProperties(id: Int, strength: Int) =
         FaceSensorPropertiesInternal(id, strength, 0, emptyList(), 1, false, false, false)
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderTest.kt
index b4ae00d..54dbd04 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderTest.kt
@@ -57,16 +57,16 @@
 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
 import com.android.systemui.bouncer.ui.BouncerView
 import com.android.systemui.classifier.FalsingCollector
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
 import com.android.systemui.display.data.repository.FakeDisplayRepository
-import com.android.systemui.dump.logcatLogBuffer
 import com.android.systemui.keyguard.DismissCallbackRegistry
 import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
 import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
 import com.android.systemui.keyguard.data.repository.FakeTrustRepository
 import com.android.systemui.keyguard.domain.interactor.DeviceEntrySideFpsOverlayInteractor
-import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor
 import com.android.systemui.keyguard.ui.viewmodel.SideFpsProgressBarViewModel
 import com.android.systemui.log.SideFpsLogger
+import com.android.systemui.log.logcatLogBuffer
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.res.R
 import com.android.systemui.shared.Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
@@ -75,6 +75,7 @@
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
 import com.android.systemui.util.time.FakeSystemClock
 import java.util.Optional
@@ -82,6 +83,7 @@
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.test.StandardTestDispatcher
 import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
@@ -108,7 +110,7 @@
     @JvmField @Rule var mockitoRule: MockitoRule = MockitoJUnit.rule()
     @Mock private lateinit var activityTaskManager: ActivityTaskManager
     @Mock private lateinit var displayManager: DisplayManager
-    @Mock private lateinit var faceAuthInteractor: KeyguardFaceAuthInteractor
+    @Mock private lateinit var faceAuthInteractor: DeviceEntryFaceAuthInteractor
     @Mock
     private lateinit var fingerprintInteractiveToAuthProvider: FingerprintInteractiveToAuthProvider
     @Mock private lateinit var fpsUnlockTracker: FpsUnlockTracker
@@ -217,6 +219,7 @@
 
         deviceEntrySideFpsOverlayInteractor =
             DeviceEntrySideFpsOverlayInteractor(
+                testScope.backgroundScope,
                 mContext,
                 deviceEntryFingerprintAuthRepository,
                 primaryBouncerInteractor,
@@ -234,15 +237,18 @@
                 windowManager,
                 displayStateInteractor,
                 Optional.of(fingerprintInteractiveToAuthProvider),
+                mock(),
                 SideFpsLogger(logcatLogBuffer("SfpsLogger"))
             )
 
         sideFpsProgressBarViewModel =
             SideFpsProgressBarViewModel(
                 mContext,
-                deviceEntryFingerprintAuthRepository,
+                mock(),
                 sfpsSensorInteractor,
+                mock(),
                 displayStateInteractor,
+                UnconfinedTestDispatcher(),
                 testScope.backgroundScope,
             )
 
@@ -260,14 +266,14 @@
             SideFpsOverlayViewBinder(
                 testScope.backgroundScope,
                 mContext,
-                biometricStatusInteractor,
-                displayStateInteractor,
-                deviceEntrySideFpsOverlayInteractor,
-                fpsUnlockTracker,
-                layoutInflater,
-                sideFpsProgressBarViewModel,
-                sfpsSensorInteractor,
-                windowManager
+                { biometricStatusInteractor },
+                { displayStateInteractor },
+                { deviceEntrySideFpsOverlayInteractor },
+                { fpsUnlockTracker },
+                { layoutInflater },
+                { sideFpsProgressBarViewModel },
+                { sfpsSensorInteractor },
+                { windowManager }
             )
 
         context.addMockSystemService(DisplayManager::class.java, displayManager)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelTest.kt
index 2267bdc..1fa60fc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelTest.kt
@@ -55,16 +55,16 @@
 import com.android.systemui.bouncer.ui.BouncerView
 import com.android.systemui.classifier.FalsingCollector
 import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
 import com.android.systemui.display.data.repository.FakeDisplayRepository
-import com.android.systemui.dump.logcatLogBuffer
 import com.android.systemui.keyguard.DismissCallbackRegistry
 import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
 import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
 import com.android.systemui.keyguard.data.repository.FakeTrustRepository
 import com.android.systemui.keyguard.domain.interactor.DeviceEntrySideFpsOverlayInteractor
-import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor
 import com.android.systemui.keyguard.ui.viewmodel.SideFpsProgressBarViewModel
 import com.android.systemui.log.SideFpsLogger
+import com.android.systemui.log.logcatLogBuffer
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.res.R
 import com.android.systemui.shared.Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
@@ -72,6 +72,7 @@
 import com.android.systemui.unfold.compat.ScreenSizeFoldProvider
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor
 import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
 import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
@@ -100,7 +101,7 @@
     @JvmField @Rule var mockitoRule: MockitoRule = MockitoJUnit.rule()
 
     @Mock private lateinit var activityTaskManager: ActivityTaskManager
-    @Mock private lateinit var faceAuthInteractor: KeyguardFaceAuthInteractor
+    @Mock private lateinit var faceAuthInteractor: DeviceEntryFaceAuthInteractor
     @Mock
     private lateinit var fingerprintInteractiveToAuthProvider: FingerprintInteractiveToAuthProvider
     @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
@@ -220,6 +221,7 @@
 
         deviceEntrySideFpsOverlayInteractor =
             DeviceEntrySideFpsOverlayInteractor(
+                testScope.backgroundScope,
                 mContext,
                 deviceEntryFingerprintAuthRepository,
                 primaryBouncerInteractor,
@@ -237,15 +239,18 @@
                 windowManager,
                 displayStateInteractor,
                 Optional.of(fingerprintInteractiveToAuthProvider),
+                mock(),
                 SideFpsLogger(logcatLogBuffer("SfpsLogger"))
             )
 
         sideFpsProgressBarViewModel =
             SideFpsProgressBarViewModel(
                 mContext,
-                deviceEntryFingerprintAuthRepository,
+                mock(),
                 sfpsSensorInteractor,
+                mock(),
                 displayStateInteractor,
+                StandardTestDispatcher(),
                 testScope.backgroundScope,
             )
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/BroadcastDialogDelegateTest.java b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/BroadcastDialogDelegateTest.java
index 4022d43..7d5aec6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/BroadcastDialogDelegateTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/BroadcastDialogDelegateTest.java
@@ -17,7 +17,9 @@
 package com.android.systemui.bluetooth;
 
 import static com.android.systemui.statusbar.phone.SystemUIDialog.DEFAULT_DISMISS_ON_DEVICE_LOCK;
+
 import static com.google.common.truth.Truth.assertThat;
+
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.any;
@@ -28,8 +30,6 @@
 
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
-import android.view.LayoutInflater;
-import android.view.View;
 import android.widget.Button;
 import android.widget.TextView;
 
@@ -42,8 +42,6 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.animation.DialogLaunchAnimator;
 import com.android.systemui.broadcast.BroadcastSender;
-import com.android.systemui.flags.FakeFeatureFlags;
-import com.android.systemui.flags.Flags;
 import com.android.systemui.media.dialog.MediaOutputDialogFactory;
 import com.android.systemui.model.SysUiState;
 import com.android.systemui.res.R;
@@ -74,7 +72,6 @@
             LocalBluetoothLeBroadcast.class);
     private final BroadcastSender mBroadcastSender = mock(BroadcastSender.class);
     private BroadcastDialogDelegate mBroadcastDialogDelegate;
-    private FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags();
     @Mock SystemUIDialog.Factory mSystemUIDialogFactory;
     @Mock SystemUIDialogManager mDialogManager;
     @Mock SysUiState mSysUiState;
@@ -93,9 +90,8 @@
         when(mLocalBluetoothManager.getProfileManager()).thenReturn(mLocalBluetoothProfileManager);
         when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn(null);
 
-        mFeatureFlags.set(Flags.WM_ENABLE_PREDICTIVE_BACK_QS_DIALOG_ANIM, true);
         when(mSysUiState.setFlag(anyInt(), anyBoolean())).thenReturn(mSysUiState);
-        when(mSystemUIDialogFactory.create(any())).thenReturn(mDialog);
+        when(mSystemUIDialogFactory.create(any(), any())).thenReturn(mDialog);
 
         mBroadcastDialogDelegate = new BroadcastDialogDelegate(
                 mContext,
@@ -112,7 +108,6 @@
                 mContext,
                 0,
                 DEFAULT_DISMISS_ON_DEVICE_LOCK,
-                mFeatureFlags,
                 mDialogManager,
                 mSysUiState,
                 getFakeBroadcastDispatcher(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractorTest.kt
index aa0d7b6..e796303 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractorTest.kt
@@ -25,6 +25,7 @@
 import com.android.keyguard.KeyguardSecurityModel
 import com.android.keyguard.KeyguardSecurityModel.SecurityMode.PIN
 import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.systemui.Flags
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.biometrics.data.repository.FaceSensorInfo
 import com.android.systemui.biometrics.data.repository.FakeFacePropertyRepository
@@ -35,15 +36,13 @@
 import com.android.systemui.bouncer.ui.BouncerView
 import com.android.systemui.classifier.FalsingCollector
 import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.flags.FakeFeatureFlagsClassic
-import com.android.systemui.flags.Flags
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
 import com.android.systemui.flags.SystemPropertiesHelper
 import com.android.systemui.keyguard.DismissCallbackRegistry
 import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
 import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFaceAuthRepository
 import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
 import com.android.systemui.keyguard.data.repository.FakeTrustRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor
 import com.android.systemui.keyguard.shared.model.AuthenticationFlags
 import com.android.systemui.res.R.string.kg_too_many_failed_attempts_countdown
 import com.android.systemui.res.R.string.kg_trust_agent_disabled
@@ -107,8 +106,7 @@
 
     suspend fun TestScope.init() {
         userRepository.setSelectedUserInfo(PRIMARY_USER)
-        val featureFlags =
-            FakeFeatureFlagsClassic().apply { set(Flags.REVAMPED_BOUNCER_MESSAGES, true) }
+        mSetFlagsRule.enableFlags(Flags.FLAG_REVAMPED_BOUNCER_MESSAGES)
         primaryBouncerInteractor =
             PrimaryBouncerInteractor(
                 bouncerRepository,
@@ -124,14 +122,13 @@
                 fakeTrustRepository,
                 testScope.backgroundScope,
                 mSelectedUserInteractor,
-                mock(KeyguardFaceAuthInteractor::class.java),
+                mock(DeviceEntryFaceAuthInteractor::class.java),
             )
         underTest =
             BouncerMessageInteractor(
                 repository = repository,
                 userRepository = userRepository,
                 countDownTimerUtil = countDownTimerUtil,
-                featureFlags = featureFlags,
                 updateMonitor = updateMonitor,
                 biometricSettingsRepository = biometricSettingsRepository,
                 applicationScope = this.backgroundScope,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorTest.kt
index dacf23a..ee46f76 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorTest.kt
@@ -32,9 +32,9 @@
 import com.android.systemui.bouncer.ui.BouncerView
 import com.android.systemui.bouncer.ui.BouncerViewDelegate
 import com.android.systemui.classifier.FalsingCollector
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
 import com.android.systemui.keyguard.DismissCallbackRegistry
 import com.android.systemui.keyguard.data.repository.FakeTrustRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.res.R
 import com.android.systemui.shared.Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
@@ -73,7 +73,7 @@
     @Mock private lateinit var dismissCallbackRegistry: DismissCallbackRegistry
     @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
     @Mock private lateinit var mSelectedUserInteractor: SelectedUserInteractor
-    @Mock private lateinit var faceAuthInteractor: KeyguardFaceAuthInteractor
+    @Mock private lateinit var faceAuthInteractor: DeviceEntryFaceAuthInteractor
     private lateinit var mainHandler: FakeHandler
     private lateinit var underTest: PrimaryBouncerInteractor
     private lateinit var resources: TestableResources
diff --git a/packages/SystemUI/tests/src/com/android/systemui/contrast/ContrastDialogDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/contrast/ContrastDialogDelegateTest.kt
index 65f68f9..35ac2ae 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/contrast/ContrastDialogDelegateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/contrast/ContrastDialogDelegateTest.kt
@@ -31,6 +31,7 @@
 import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.model.SysUiState
 import com.android.systemui.settings.UserTracker
+import com.android.systemui.statusbar.phone.DialogDelegate
 import com.android.systemui.statusbar.phone.SystemUIDialog
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.mockito.any
@@ -69,7 +70,8 @@
         mDependency.injectTestDependency(SysUiState::class.java, sysuiState)
         mDependency.injectMockDependency(DialogLaunchAnimator::class.java)
         whenever(sysuiState.setFlag(any(), any())).thenReturn(sysuiState)
-        whenever(sysuiDialogFactory.create(any())).thenReturn(sysuiDialog)
+        whenever(sysuiDialogFactory.create(any(SystemUIDialog.Delegate::class.java)))
+            .thenReturn(sysuiDialog)
         whenever(sysuiDialog.layoutInflater).thenReturn(LayoutInflater.from(mContext))
 
         whenever(mockUserTracker.userId).thenReturn(context.userId)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/PanelConfirmationDialogFactoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/PanelConfirmationDialogFactoryTest.kt
index 4e8f866..7f0ea9a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/PanelConfirmationDialogFactoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/PanelConfirmationDialogFactoryTest.kt
@@ -17,34 +17,48 @@
 
 package com.android.systemui.controls.management
 
+import android.content.Context
 import android.content.DialogInterface
 import android.testing.AndroidTestingRunner
 import androidx.test.filters.SmallTest
-import com.android.systemui.res.R
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.res.R
 import com.android.systemui.statusbar.phone.SystemUIDialog
+import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.argumentCaptor
 import com.android.systemui.util.mockito.capture
-import com.android.systemui.util.mockito.eq
-import com.android.systemui.util.mockito.mock
 import com.google.common.truth.Truth.assertThat
+import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.Mock
 import org.mockito.Mockito.verify
-import org.mockito.Mockito.`when`
+import org.mockito.MockitoAnnotations
+import org.mockito.Mockito.`when` as whenever
 
 @SmallTest
 @RunWith(AndroidTestingRunner::class)
 class PanelConfirmationDialogFactoryTest : SysuiTestCase() {
 
+    @Mock private lateinit var mockDialog : SystemUIDialog
+    @Mock private lateinit var mockDialogFactory : SystemUIDialog.Factory
+    private lateinit var factory : PanelConfirmationDialogFactory
+    @Before
+    fun setup() {
+        MockitoAnnotations.initMocks(this)
+
+        whenever(mockDialogFactory.create(any(Context::class.java))).thenReturn(mockDialog)
+        whenever(mockDialog.context).thenReturn(mContext)
+        factory = PanelConfirmationDialogFactory(mockDialogFactory)
+    }
+
     @Test
     fun testDialogHasCorrectInfo() {
-        val mockDialog: SystemUIDialog = mock() { `when`(context).thenReturn(mContext) }
-        val factory = PanelConfirmationDialogFactory { mockDialog }
         val appName = "appName"
 
-        factory.createConfirmationDialog(context, appName) {}
+        factory.createConfirmationDialog(mContext, appName) {}
 
         verify(mockDialog).setCanceledOnTouchOutside(true)
         verify(mockDialog)
@@ -55,12 +69,9 @@
 
     @Test
     fun testDialogPositiveButton() {
-        val mockDialog: SystemUIDialog = mock() { `when`(context).thenReturn(mContext) }
-        val factory = PanelConfirmationDialogFactory { mockDialog }
-
         var response: Boolean? = null
 
-        factory.createConfirmationDialog(context, "") { response = it }
+        factory.createConfirmationDialog(mContext,"") { response = it }
 
         val captor: ArgumentCaptor<DialogInterface.OnClickListener> = argumentCaptor()
         verify(mockDialog).setPositiveButton(eq(R.string.controls_dialog_ok), capture(captor))
@@ -72,12 +83,9 @@
 
     @Test
     fun testDialogNeutralButton() {
-        val mockDialog: SystemUIDialog = mock() { `when`(context).thenReturn(mContext) }
-        val factory = PanelConfirmationDialogFactory { mockDialog }
-
         var response: Boolean? = null
 
-        factory.createConfirmationDialog(context, "") { response = it }
+        factory.createConfirmationDialog(mContext, "") { response = it }
 
         val captor: ArgumentCaptor<DialogInterface.OnClickListener> = argumentCaptor()
         verify(mockDialog).setNeutralButton(eq(R.string.cancel), capture(captor))
@@ -89,12 +97,9 @@
 
     @Test
     fun testDialogCancel() {
-        val mockDialog: SystemUIDialog = mock() { `when`(context).thenReturn(mContext) }
-        val factory = PanelConfirmationDialogFactory { mockDialog }
-
         var response: Boolean? = null
 
-        factory.createConfirmationDialog(context, "") { response = it }
+        factory.createConfirmationDialog(mContext, "") { response = it }
 
         val captor: ArgumentCaptor<DialogInterface.OnCancelListener> = argumentCaptor()
         verify(mockDialog).setOnCancelListener(capture(captor))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/start/ControlsStartableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/start/ControlsStartableTest.kt
index 8f65fc8..bcef67e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/start/ControlsStartableTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/start/ControlsStartableTest.kt
@@ -24,19 +24,28 @@
 import android.content.IntentFilter
 import android.content.pm.ApplicationInfo
 import android.content.pm.ServiceInfo
+import android.os.UserHandle
 import android.os.UserManager
 import android.testing.AndroidTestingRunner
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.common.data.repository.fakePackageChangeRepository
+import com.android.systemui.common.data.repository.packageChangeRepository
+import com.android.systemui.common.data.shared.model.PackageChangeModel
 import com.android.systemui.controls.ControlsServiceInfo
 import com.android.systemui.controls.controller.ControlsController
 import com.android.systemui.controls.dagger.ControlsComponent
 import com.android.systemui.controls.management.ControlsListingController
 import com.android.systemui.controls.panels.AuthorizedPanelsRepository
 import com.android.systemui.controls.panels.FakeSelectedComponentRepository
+import com.android.systemui.controls.panels.SelectedComponentRepository
 import com.android.systemui.controls.ui.SelectedItem
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
 import com.android.systemui.settings.UserTracker
+import com.android.systemui.testKosmos
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.argumentCaptor
@@ -48,6 +57,9 @@
 import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
 import java.util.Optional
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -61,10 +73,13 @@
 import org.mockito.Mockito.`when`
 import org.mockito.MockitoAnnotations
 
+@OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
 @RunWith(AndroidTestingRunner::class)
 class ControlsStartableTest : SysuiTestCase() {
 
+    private val kosmos = testKosmos()
+
     @Mock private lateinit var controlsController: ControlsController
     @Mock private lateinit var controlsListingController: ControlsListingController
     @Mock private lateinit var userTracker: UserTracker
@@ -72,7 +87,7 @@
     @Mock private lateinit var userManager: UserManager
     @Mock private lateinit var broadcastDispatcher: BroadcastDispatcher
 
-    private val preferredPanelsRepository = FakeSelectedComponentRepository()
+    private lateinit var preferredPanelsRepository: FakeSelectedComponentRepository
 
     private lateinit var fakeExecutor: FakeExecutor
 
@@ -81,8 +96,10 @@
         MockitoAnnotations.initMocks(this)
         whenever(authorizedPanelsRepository.getPreferredPackages()).thenReturn(setOf())
         whenever(userManager.isUserUnlocked(anyInt())).thenReturn(true)
+        whenever(userTracker.userHandle).thenReturn(UserHandle.of(1))
 
         fakeExecutor = FakeExecutor(FakeSystemClock())
+        preferredPanelsRepository = FakeSelectedComponentRepository()
     }
 
     @Test
@@ -306,6 +323,100 @@
         verify(controlsController, never()).setPreferredSelection(any())
     }
 
+    @Test
+    fun testSelectedComponentIsUninstalled() =
+        with(kosmos) {
+            testScope.runTest {
+                val selectedComponent =
+                    SelectedComponentRepository.SelectedComponent(
+                        "panel",
+                        TEST_COMPONENT_PANEL,
+                        isPanel = true
+                    )
+                preferredPanelsRepository.setSelectedComponent(selectedComponent)
+                val activeUser = UserHandle.of(100)
+                whenever(userTracker.userHandle).thenReturn(activeUser)
+
+                createStartable(enabled = true).onBootCompleted()
+                fakeExecutor.runAllReady()
+                runCurrent()
+
+                assertThat(preferredPanelsRepository.getSelectedComponent())
+                    .isEqualTo(selectedComponent)
+                fakePackageChangeRepository.notifyChange(
+                    PackageChangeModel.Uninstalled(
+                        packageName = TEST_PACKAGE_PANEL,
+                        packageUid = UserHandle.getUid(100, 1)
+                    )
+                )
+                runCurrent()
+
+                assertThat(preferredPanelsRepository.getSelectedComponent()).isNull()
+            }
+        }
+
+    @Test
+    fun testSelectedComponentIsChanged() =
+        with(kosmos) {
+            testScope.runTest {
+                val selectedComponent =
+                    SelectedComponentRepository.SelectedComponent(
+                        "panel",
+                        TEST_COMPONENT_PANEL,
+                        isPanel = true
+                    )
+                preferredPanelsRepository.setSelectedComponent(selectedComponent)
+                val activeUser = UserHandle.of(100)
+                whenever(userTracker.userHandle).thenReturn(activeUser)
+
+                createStartable(enabled = true).onBootCompleted()
+                fakeExecutor.runAllReady()
+                runCurrent()
+
+                fakePackageChangeRepository.notifyChange(
+                    PackageChangeModel.Changed(
+                        packageName = TEST_PACKAGE_PANEL,
+                        packageUid = UserHandle.getUid(100, 1)
+                    )
+                )
+                runCurrent()
+
+                assertThat(preferredPanelsRepository.getSelectedComponent())
+                    .isEqualTo(selectedComponent)
+            }
+        }
+
+    @Test
+    fun testOtherPackageIsUninstalled() =
+        with(kosmos) {
+            testScope.runTest {
+                val selectedComponent =
+                    SelectedComponentRepository.SelectedComponent(
+                        "panel",
+                        TEST_COMPONENT_PANEL,
+                        isPanel = true
+                    )
+                preferredPanelsRepository.setSelectedComponent(selectedComponent)
+                val activeUser = UserHandle.of(100)
+                whenever(userTracker.userHandle).thenReturn(activeUser)
+
+                createStartable(enabled = true).onBootCompleted()
+                fakeExecutor.runAllReady()
+                runCurrent()
+
+                fakePackageChangeRepository.notifyChange(
+                    PackageChangeModel.Uninstalled(
+                        packageName = TEST_PACKAGE,
+                        packageUid = UserHandle.getUid(100, 1)
+                    )
+                )
+                runCurrent()
+
+                assertThat(preferredPanelsRepository.getSelectedComponent())
+                    .isEqualTo(selectedComponent)
+            }
+        }
+
     private fun setUpControlsListingControls(listings: List<ControlsServiceInfo>) {
         doAnswer { doReturn(listings).`when`(controlsListingController).getCurrentServices() }
             .`when`(controlsListingController)
@@ -326,11 +437,14 @@
                 }
             }
         return ControlsStartable(
+            kosmos.applicationCoroutineScope,
+            kosmos.testDispatcher,
             fakeExecutor,
             component,
             userTracker,
             authorizedPanelsRepository,
             preferredPanelsRepository,
+            kosmos.packageChangeRepository,
             userManager,
             broadcastDispatcher,
         )
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsDialogsFactoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsDialogsFactoryTest.kt
index 8eebcee..38c6a0e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsDialogsFactoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsDialogsFactoryTest.kt
@@ -17,17 +17,23 @@
 
 package com.android.systemui.controls.ui
 
+import android.content.Context
 import android.testing.AndroidTestingRunner
 import androidx.test.filters.SmallTest
 import com.android.systemui.res.R
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.phone.SystemUIDialog
 import com.android.systemui.util.FakeSystemUIDialogController
+import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth.assertThat
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.any
 import org.mockito.Mockito.eq
 import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
 
 @SmallTest
 @RunWith(AndroidTestingRunner::class)
@@ -37,18 +43,24 @@
         const val APP_NAME = "Test App"
     }
 
-    private val fakeDialogController = FakeSystemUIDialogController()
+    @Mock
+    private lateinit var mockDialogFactory : SystemUIDialog.Factory
+
+    private val fakeDialogController = FakeSystemUIDialogController(mContext)
 
     private lateinit var underTest: ControlsDialogsFactory
 
     @Before
     fun setup() {
-        underTest = ControlsDialogsFactory { fakeDialogController.dialog }
+        MockitoAnnotations.initMocks(this)
+        whenever(mockDialogFactory.create(any(Context::class.java)))
+            .thenReturn(fakeDialogController.dialog)
+        underTest = ControlsDialogsFactory(mockDialogFactory)
     }
 
     @Test
     fun testCreatesRemoveAppDialog() {
-        val dialog = underTest.createRemoveAppDialog(context, APP_NAME) {}
+        val dialog = underTest.createRemoveAppDialog(mContext, APP_NAME) {}
 
         verify(dialog)
             .setTitle(
@@ -60,7 +72,7 @@
     @Test
     fun testPositiveClickRemoveAppDialogWorks() {
         var dialogResult: Boolean? = null
-        underTest.createRemoveAppDialog(context, APP_NAME) { dialogResult = it }
+        underTest.createRemoveAppDialog(mContext, APP_NAME) { dialogResult = it }
 
         fakeDialogController.clickPositive()
 
@@ -70,7 +82,7 @@
     @Test
     fun testNeutralClickRemoveAppDialogWorks() {
         var dialogResult: Boolean? = null
-        underTest.createRemoveAppDialog(context, APP_NAME) { dialogResult = it }
+        underTest.createRemoveAppDialog(mContext, APP_NAME) { dialogResult = it }
 
         fakeDialogController.clickNeutral()
 
@@ -80,7 +92,7 @@
     @Test
     fun testCancelRemoveAppDialogWorks() {
         var dialogResult: Boolean? = null
-        underTest.createRemoveAppDialog(context, APP_NAME) { dialogResult = it }
+        underTest.createRemoveAppDialog(mContext, APP_NAME) { dialogResult = it }
 
         fakeDialogController.cancel()
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsUiControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsUiControllerImplTest.kt
index 11bd9cb..36ae0c7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsUiControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsUiControllerImplTest.kt
@@ -51,6 +51,7 @@
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.res.R
 import com.android.systemui.settings.UserTracker
+import com.android.systemui.statusbar.phone.SystemUIDialog
 import com.android.systemui.statusbar.policy.KeyguardStateController
 import com.android.systemui.util.FakeSystemUIDialogController
 import com.android.systemui.util.concurrency.FakeExecutor
@@ -97,9 +98,10 @@
     @Mock lateinit var authorizedPanelsRepository: AuthorizedPanelsRepository
     @Mock lateinit var featureFlags: FeatureFlags
     @Mock lateinit var packageManager: PackageManager
+    @Mock lateinit var systemUIDialogFactory: SystemUIDialog.Factory
 
     private val preferredPanelRepository = FakeSelectedComponentRepository()
-    private val fakeDialogController = FakeSystemUIDialogController()
+    private lateinit var fakeDialogController: FakeSystemUIDialogController
     private val uiExecutor = FakeExecutor(FakeSystemClock())
     private val bgExecutor = FakeExecutor(FakeSystemClock())
 
@@ -114,6 +116,9 @@
     fun setup() {
         MockitoAnnotations.initMocks(this)
 
+        fakeDialogController = FakeSystemUIDialogController(mContext)
+        whenever(systemUIDialogFactory.create(any(Context::class.java)))
+            .thenReturn(fakeDialogController.dialog)
         controlsSettingsRepository = FakeControlsSettingsRepository()
 
         // This way, it won't be cloned every time `LayoutInflater.fromContext` is called, but we
@@ -146,10 +151,7 @@
                 authorizedPanelsRepository,
                 preferredPanelRepository,
                 featureFlags,
-                ControlsDialogsFactory {
-                    isRemoveAppDialogCreated = true
-                    fakeDialogController.dialog
-                },
+                ControlsDialogsFactory(systemUIDialogFactory),
                 dumpManager,
             )
         `when`(userTracker.userId).thenReturn(0)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsInteractorTest.kt
index 59bcf01..d5c3641 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsInteractorTest.kt
@@ -24,13 +24,13 @@
 import com.android.systemui.biometrics.shared.model.SensorStrength
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.deviceentry.domain.interactor.deviceEntryHapticsInteractor
+import com.android.systemui.deviceentry.shared.model.FailedFaceAuthenticationStatus
 import com.android.systemui.keyevent.data.repository.fakeKeyEventRepository
 import com.android.systemui.keyguard.data.repository.biometricSettingsRepository
 import com.android.systemui.keyguard.data.repository.deviceEntryFingerprintAuthRepository
 import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFaceAuthRepository
 import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
 import com.android.systemui.keyguard.shared.model.FailFingerprintAuthenticationStatus
-import com.android.systemui.keyguard.shared.model.FailedFaceAuthenticationStatus
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.power.data.repository.powerRepository
 import com.android.systemui.power.shared.model.WakeSleepReason
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/FaceWakeUpTriggersConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/FaceWakeUpTriggersConfigTest.kt
similarity index 91%
rename from packages/SystemUI/tests/src/com/android/keyguard/FaceWakeUpTriggersConfigTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/FaceWakeUpTriggersConfigTest.kt
index 94c34a5..e9b4bbb 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/FaceWakeUpTriggersConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/FaceWakeUpTriggersConfigTest.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.keyguard
+package com.android.systemui.deviceentry.data.repository
 
 import android.os.PowerManager
 import android.testing.AndroidTestingRunner
@@ -71,6 +71,6 @@
             wakeUpTriggers
         )
 
-        return FaceWakeUpTriggersConfig(mContext.getResources(), globalSettings, dumpManager)
+        return FaceWakeUpTriggersConfigImpl(mContext.resources, globalSettings, dumpManager)
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorTest.kt
similarity index 93%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorTest.kt
index 6eb95bd..368d1d9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorTest.kt
@@ -1,21 +1,20 @@
 /*
- *  Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
  *
- *  Licensed under the Apache License, Version 2.0 (the "License");
- *  you may not use this file except in compliance with the License.
- *  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.interactor
+package com.android.systemui.deviceentry.domain.interactor
 
 import android.app.trust.TrustManager
 import android.content.pm.UserInfo
@@ -25,8 +24,6 @@
 import android.os.PowerManager
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.keyguard.FaceAuthUiEvent
-import com.android.keyguard.FaceWakeUpTriggersConfig
 import com.android.keyguard.KeyguardSecurityModel
 import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.systemui.SysuiTestCase
@@ -42,7 +39,9 @@
 import com.android.systemui.bouncer.ui.BouncerView
 import com.android.systemui.classifier.FalsingCollector
 import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.dump.logcatLogBuffer
+import com.android.systemui.deviceentry.data.repository.FaceWakeUpTriggersConfig
+import com.android.systemui.deviceentry.shared.FaceAuthUiEvent
+import com.android.systemui.deviceentry.shared.model.ErrorFaceAuthenticationStatus
 import com.android.systemui.keyguard.DismissCallbackRegistry
 import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
 import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFaceAuthRepository
@@ -50,11 +49,13 @@
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
 import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.data.repository.FakeTrustRepository
-import com.android.systemui.keyguard.shared.model.ErrorFaceAuthenticationStatus
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.keyguard.shared.model.TransitionStep
 import com.android.systemui.log.FaceAuthenticationLogger
+import com.android.systemui.log.logcatLogBuffer
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.power.domain.interactor.PowerInteractor
 import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest
@@ -87,9 +88,9 @@
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
 @RunWith(AndroidJUnit4::class)
-class KeyguardFaceAuthInteractorTest : SysuiTestCase() {
+class DeviceEntryFaceAuthInteractorTest : SysuiTestCase() {
 
-    private lateinit var underTest: SystemUIKeyguardFaceAuthInteractor
+    private lateinit var underTest: SystemUIDeviceEntryFaceAuthInteractor
     private lateinit var testScope: TestScope
     private lateinit var bouncerRepository: FakeKeyguardBouncerRepository
     private lateinit var keyguardTransitionRepository: FakeKeyguardTransitionRepository
@@ -133,7 +134,7 @@
         fakeBiometricSettingsRepository = FakeBiometricSettingsRepository()
 
         underTest =
-            SystemUIKeyguardFaceAuthInteractor(
+            SystemUIDeviceEntryFaceAuthInteractor(
                 mContext,
                 testScope.backgroundScope,
                 dispatcher,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/ui/viewmodel/UdfpsAccessibilityOverlayViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/ui/viewmodel/UdfpsAccessibilityOverlayViewModelTest.kt
index 93ce86a..e158e47 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/ui/viewmodel/UdfpsAccessibilityOverlayViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/ui/viewmodel/UdfpsAccessibilityOverlayViewModelTest.kt
@@ -23,7 +23,7 @@
 import com.android.systemui.biometrics.data.repository.fingerprintPropertyRepository
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
-import com.android.systemui.deviceentry.data.ui.viewmodel.udfpsAccessibilityOverlayViewModel
+import com.android.systemui.deviceentry.data.ui.viewmodel.deviceEntryUdfpsAccessibilityOverlayViewModel
 import com.android.systemui.flags.Flags.FULL_SCREEN_USER_SWITCHER
 import com.android.systemui.flags.fakeFeatureFlagsClassic
 import com.android.systemui.keyguard.data.repository.deviceEntryFingerprintAuthRepository
@@ -57,7 +57,7 @@
     private val deviceEntryFingerprintAuthRepository = kosmos.deviceEntryFingerprintAuthRepository
     private val deviceEntryRepository = kosmos.fakeDeviceEntryRepository
     private val shadeRepository = kosmos.fakeShadeRepository
-    private val underTest = kosmos.udfpsAccessibilityOverlayViewModel
+    private val underTest = kosmos.deviceEntryUdfpsAccessibilityOverlayViewModel
 
     @Test
     fun visible() =
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/FaceAuthReasonTest.kt b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/shared/FaceAuthReasonTest.kt
similarity index 92%
rename from packages/SystemUI/tests/src/com/android/keyguard/FaceAuthReasonTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/deviceentry/shared/FaceAuthReasonTest.kt
index 68d0f41..ef89752 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/FaceAuthReasonTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/shared/FaceAuthReasonTest.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -13,7 +13,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.keyguard
+
+package com.android.systemui.deviceentry.shared
 
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
diff --git a/packages/SystemUI/tests/src/com/android/systemui/haptics/slider/SeekableSliderEventProducerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/haptics/slider/SeekableSliderEventProducerTest.kt
index 71a56cd..c22d35c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/haptics/slider/SeekableSliderEventProducerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/haptics/slider/SeekableSliderEventProducerTest.kt
@@ -123,4 +123,25 @@
 
             assertEquals(SliderEvent(SliderEventType.STOPPED_TRACKING_TOUCH, 0.5F), latest)
         }
+
+    @Test
+    fun onArrowUp_afterStartTrackingTouch_ArrowUpProduced() = runTest {
+        val latest by collectLastValue(eventFlow)
+
+        eventProducer.onStartTrackingTouch(seekBar)
+        eventProducer.onArrowUp()
+
+        assertEquals(SliderEvent(SliderEventType.ARROW_UP, 0f), latest)
+    }
+
+    @Test
+    fun onArrowUp_afterChangeByProgram_ArrowUpProduced_withProgress() = runTest {
+        val progress = 50
+        val latest by collectLastValue(eventFlow)
+
+        eventProducer.onProgressChanged(seekBar, progress, false)
+        eventProducer.onArrowUp()
+
+        assertEquals(SliderEvent(SliderEventType.ARROW_UP, 0.5f), latest)
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/haptics/slider/SeekableSliderTrackerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/haptics/slider/SeekableSliderTrackerTest.kt
index 8d12e49..db04962 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/haptics/slider/SeekableSliderTrackerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/haptics/slider/SeekableSliderTrackerTest.kt
@@ -528,6 +528,194 @@
         verifyNoMoreInteractions(sliderStateListener)
     }
 
+    @Test
+    fun onProgressChangeByProgram_atTheMiddle_onIdle_movesToArrowHandleMovedOnce() = runTest {
+        // GIVEN an initialized tracker in the IDLE state
+        initTracker(testScheduler)
+
+        // GIVEN a progress due to an external source that lands at the middle of the slider
+        val progress = 0.5f
+        sliderEventProducer.sendEvent(
+            SliderEvent(SliderEventType.PROGRESS_CHANGE_BY_PROGRAM, progress)
+        )
+
+        // THEN the state moves to ARROW_HANDLE_MOVED_ONCE and the listener is called to play
+        // haptics
+        assertThat(mSeekableSliderTracker.currentState)
+            .isEqualTo(SliderState.ARROW_HANDLE_MOVED_ONCE)
+        verify(sliderStateListener).onSelectAndArrow(progress)
+    }
+
+    @Test
+    fun onProgressChangeByProgram_atUpperBookend_onIdle_movesToIdle() = runTest {
+        // GIVEN an initialized tracker in the IDLE state
+        val config = SeekableSliderTrackerConfig()
+        initTracker(testScheduler, config)
+
+        // GIVEN a progress due to an external source that lands at the upper bookend
+        val progress = config.upperBookendThreshold + 0.01f
+        sliderEventProducer.sendEvent(
+            SliderEvent(SliderEventType.PROGRESS_CHANGE_BY_PROGRAM, progress)
+        )
+
+        // THEN the tracker executes upper bookend haptics before moving back to IDLE
+        verify(sliderStateListener).onUpperBookend()
+        assertThat(mSeekableSliderTracker.currentState).isEqualTo(SliderState.IDLE)
+    }
+
+    @Test
+    fun onProgressChangeByProgram_atLowerBookend_onIdle_movesToIdle() = runTest {
+        // GIVEN an initialized tracker in the IDLE state
+        val config = SeekableSliderTrackerConfig()
+        initTracker(testScheduler, config)
+
+        // WHEN a progress is recorded due to an external source that lands at the lower bookend
+        val progress = config.lowerBookendThreshold - 0.01f
+        sliderEventProducer.sendEvent(
+            SliderEvent(SliderEventType.PROGRESS_CHANGE_BY_PROGRAM, progress)
+        )
+
+        // THEN the tracker executes lower bookend haptics before moving to IDLE
+        verify(sliderStateListener).onLowerBookend()
+        assertThat(mSeekableSliderTracker.currentState).isEqualTo(SliderState.IDLE)
+    }
+
+    @Test
+    fun onArrowUp_onArrowMovedOnce_movesToIdle() = runTest {
+        // GIVEN an initialized tracker in the ARROW_HANDLE_MOVED_ONCE state
+        initTracker(testScheduler)
+        mSeekableSliderTracker.setState(SliderState.ARROW_HANDLE_MOVED_ONCE)
+
+        // WHEN the external stimulus is released
+        val progress = 0.5f
+        sliderEventProducer.sendEvent(SliderEvent(SliderEventType.ARROW_UP, progress))
+
+        // THEN the tracker moves back to IDLE and there are no haptics
+        assertThat(mSeekableSliderTracker.currentState).isEqualTo(SliderState.IDLE)
+        verifyZeroInteractions(sliderStateListener)
+    }
+
+    @Test
+    fun onStartTrackingTouch_onArrowMovedOnce_movesToWait() = runTest {
+        // GIVEN an initialized tracker in the ARROW_HANDLE_MOVED_ONCE state
+        initTracker(testScheduler)
+        mSeekableSliderTracker.setState(SliderState.ARROW_HANDLE_MOVED_ONCE)
+
+        // WHEN the slider starts tracking touch
+        val progress = 0.5f
+        sliderEventProducer.sendEvent(SliderEvent(SliderEventType.STARTED_TRACKING_TOUCH, progress))
+
+        // THEN the tracker moves back to WAIT and starts the waiting job. Also, there are no
+        // haptics
+        assertThat(mSeekableSliderTracker.currentState).isEqualTo(SliderState.WAIT)
+        assertThat(mSeekableSliderTracker.isWaiting).isTrue()
+        verifyZeroInteractions(sliderStateListener)
+    }
+
+    @Test
+    fun onProgressChangeByProgram_onArrowMovedOnce_movesToArrowMovesContinuously() = runTest {
+        // GIVEN an initialized tracker in the ARROW_HANDLE_MOVED_ONCE state
+        initTracker(testScheduler)
+        mSeekableSliderTracker.setState(SliderState.ARROW_HANDLE_MOVED_ONCE)
+
+        // WHEN the slider gets an external progress change
+        val progress = 0.5f
+        sliderEventProducer.sendEvent(
+            SliderEvent(SliderEventType.PROGRESS_CHANGE_BY_PROGRAM, progress)
+        )
+
+        // THEN the tracker moves to ARROW_HANDLE_MOVES_CONTINUOUSLY and calls the appropriate
+        // haptics
+        assertThat(mSeekableSliderTracker.currentState)
+            .isEqualTo(SliderState.ARROW_HANDLE_MOVES_CONTINUOUSLY)
+        verify(sliderStateListener).onProgress(progress)
+    }
+
+    @Test
+    fun onArrowUp_onArrowMovesContinuously_movesToIdle() = runTest {
+        // GIVEN an initialized tracker in the ARROW_HANDLE_MOVES_CONTINUOUSLY state
+        initTracker(testScheduler)
+        mSeekableSliderTracker.setState(SliderState.ARROW_HANDLE_MOVES_CONTINUOUSLY)
+
+        // WHEN the external stimulus is released
+        val progress = 0.5f
+        sliderEventProducer.sendEvent(SliderEvent(SliderEventType.ARROW_UP, progress))
+
+        // THEN the tracker moves to IDLE and no haptics are played
+        assertThat(mSeekableSliderTracker.currentState).isEqualTo(SliderState.IDLE)
+        verifyZeroInteractions(sliderStateListener)
+    }
+
+    @Test
+    fun onStartTrackingTouch_onArrowMovesContinuously_movesToWait() = runTest {
+        // GIVEN an initialized tracker in the ARROW_HANDLE_MOVES_CONTINUOUSLY state
+        initTracker(testScheduler)
+        mSeekableSliderTracker.setState(SliderState.ARROW_HANDLE_MOVES_CONTINUOUSLY)
+
+        // WHEN the slider starts tracking touch
+        val progress = 0.5f
+        sliderEventProducer.sendEvent(SliderEvent(SliderEventType.STARTED_TRACKING_TOUCH, progress))
+
+        // THEN the tracker moves to WAIT and the wait job starts.
+        assertThat(mSeekableSliderTracker.currentState).isEqualTo(SliderState.WAIT)
+        assertThat(mSeekableSliderTracker.isWaiting).isTrue()
+        verifyZeroInteractions(sliderStateListener)
+    }
+
+    @Test
+    fun onProgressChangeByProgram_onArrowMovesContinuously_preservesState() = runTest {
+        // GIVEN an initialized tracker in the ARROW_HANDLE_MOVES_CONTINUOUSLY state
+        initTracker(testScheduler)
+        mSeekableSliderTracker.setState(SliderState.ARROW_HANDLE_MOVES_CONTINUOUSLY)
+
+        // WHEN the slider changes progress programmatically at the middle
+        val progress = 0.5f
+        sliderEventProducer.sendEvent(
+            SliderEvent(SliderEventType.PROGRESS_CHANGE_BY_PROGRAM, progress)
+        )
+
+        // THEN the tracker stays in the same state and haptics are delivered appropriately
+        assertThat(mSeekableSliderTracker.currentState)
+            .isEqualTo(SliderState.ARROW_HANDLE_MOVES_CONTINUOUSLY)
+        verify(sliderStateListener).onProgress(progress)
+    }
+
+    @Test
+    fun onProgramProgress_atLowerBookend_onArrowMovesContinuously_movesToIdle() = runTest {
+        // GIVEN an initialized tracker in the ARROW_HANDLE_MOVES_CONTINUOUSLY state
+        val config = SeekableSliderTrackerConfig()
+        initTracker(testScheduler, config)
+        mSeekableSliderTracker.setState(SliderState.ARROW_HANDLE_MOVES_CONTINUOUSLY)
+
+        // WHEN the slider reaches the lower bookend programmatically
+        val progress = config.lowerBookendThreshold - 0.01f
+        sliderEventProducer.sendEvent(
+            SliderEvent(SliderEventType.PROGRESS_CHANGE_BY_PROGRAM, progress)
+        )
+
+        // THEN the tracker executes lower bookend haptics before moving to IDLE
+        verify(sliderStateListener).onLowerBookend()
+        assertThat(mSeekableSliderTracker.currentState).isEqualTo(SliderState.IDLE)
+    }
+
+    @Test
+    fun onProgramProgress_atUpperBookend_onArrowMovesContinuously_movesToIdle() = runTest {
+        // GIVEN an initialized tracker in the ARROW_HANDLE_MOVES_CONTINUOUSLY state
+        val config = SeekableSliderTrackerConfig()
+        initTracker(testScheduler, config)
+        mSeekableSliderTracker.setState(SliderState.ARROW_HANDLE_MOVES_CONTINUOUSLY)
+
+        // WHEN the slider reaches the lower bookend programmatically
+        val progress = config.upperBookendThreshold + 0.01f
+        sliderEventProducer.sendEvent(
+            SliderEvent(SliderEventType.PROGRESS_CHANGE_BY_PROGRAM, progress)
+        )
+
+        // THEN the tracker executes upper bookend haptics before moving to IDLE
+        verify(sliderStateListener).onUpperBookend()
+        assertThat(mSeekableSliderTracker.currentState).isEqualTo(SliderState.IDLE)
+    }
+
     @OptIn(ExperimentalCoroutinesApi::class)
     private fun initTracker(
         scheduler: TestCoroutineScheduler,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProviderTest.kt
index ab6bc2c..66fdf53 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProviderTest.kt
@@ -19,7 +19,6 @@
 import android.os.VibrationAttributes
 import android.os.VibrationEffect
 import android.view.VelocityTracker
-import android.view.animation.AccelerateInterpolator
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
@@ -51,8 +50,6 @@
     private val lowTickDuration = 12 // Mocked duration of a low tick
     private val dragTextureThresholdMillis =
         lowTickDuration * config.numberOfLowTicks + config.deltaMillisForDragInterval
-    private val progressInterpolator = AccelerateInterpolator(config.progressInterpolatorFactor)
-    private val velocityInterpolator = AccelerateInterpolator(config.velocityInterpolatorFactor)
     private lateinit var sliderHapticFeedbackProvider: SliderHapticFeedbackProvider
 
     @Before
@@ -60,7 +57,9 @@
         MockitoAnnotations.initMocks(this)
         whenever(vibratorHelper.getPrimitiveDurations(any()))
             .thenReturn(intArrayOf(lowTickDuration))
-        whenever(velocityTracker.xVelocity).thenReturn(config.maxVelocityToScale)
+        whenever(velocityTracker.isAxisSupported(config.velocityAxis)).thenReturn(true)
+        whenever(velocityTracker.getAxisVelocity(config.velocityAxis))
+            .thenReturn(config.maxVelocityToScale)
         sliderHapticFeedbackProvider =
             SliderHapticFeedbackProvider(vibratorHelper, velocityTracker, config, clock)
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
index b38c9ec..b57cf53 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -72,6 +72,7 @@
 
 import androidx.test.filters.SmallTest;
 
+import com.android.internal.foldables.FoldGracePeriodProvider;
 import com.android.internal.jank.InteractionJankMonitor;
 import com.android.internal.logging.InstanceId;
 import com.android.internal.logging.UiEventLogger;
@@ -324,6 +325,54 @@
 
     @Test
     @TestableLooper.RunWithLooper(setAsMainLooper = true)
+    public void showKeyguardAfterKeyguardNotEnabled() {
+        // GIVEN feature is enabled
+        final FoldGracePeriodProvider mockedFoldGracePeriodProvider =
+                mock(FoldGracePeriodProvider.class);
+        mViewMediator.mFoldGracePeriodProvider = mockedFoldGracePeriodProvider;
+        when(mockedFoldGracePeriodProvider.isEnabled()).thenReturn(true);
+
+        // GIVEN keyguard is not enabled and isn't showing
+        mViewMediator.onSystemReady();
+        mViewMediator.setKeyguardEnabled(false);
+        TestableLooper.get(this).processAllMessages();
+        captureKeyguardUpdateMonitorCallback();
+        assertFalse(mViewMediator.isShowingAndNotOccluded());
+
+        // WHEN showKeyguard is requested
+        mViewMediator.showDismissibleKeyguard();
+
+        // THEN keyguard is shown
+        TestableLooper.get(this).processAllMessages();
+        assertTrue(mViewMediator.isShowingAndNotOccluded());
+    }
+
+    @Test
+    @TestableLooper.RunWithLooper(setAsMainLooper = true)
+    public void showKeyguardAfterKeyguardNotEnabled_featureNotEnabled() {
+        // GIVEN feature is NOT enabled
+        final FoldGracePeriodProvider mockedFoldGracePeriodProvider =
+                mock(FoldGracePeriodProvider.class);
+        mViewMediator.mFoldGracePeriodProvider = mockedFoldGracePeriodProvider;
+        when(mockedFoldGracePeriodProvider.isEnabled()).thenReturn(false);
+
+        // GIVEN keyguard is not enabled and isn't showing
+        mViewMediator.onSystemReady();
+        mViewMediator.setKeyguardEnabled(false);
+        TestableLooper.get(this).processAllMessages();
+        captureKeyguardUpdateMonitorCallback();
+        assertFalse(mViewMediator.isShowingAndNotOccluded());
+
+        // WHEN showKeyguard is requested
+        mViewMediator.showDismissibleKeyguard();
+
+        // THEN keyguard is still NOT shown
+        TestableLooper.get(this).processAllMessages();
+        assertFalse(mViewMediator.isShowingAndNotOccluded());
+    }
+
+    @Test
+    @TestableLooper.RunWithLooper(setAsMainLooper = true)
     public void doNotHideKeyguard_whenLockdown_onKeyguardNotEnabledExternally() {
         // GIVEN keyguard is enabled and lockdown occurred so the keyguard is showing
         mViewMediator.onSystemReady();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorTest.kt
index 0616a34..c4df27c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorTest.kt
@@ -29,6 +29,7 @@
 import com.android.systemui.bouncer.ui.BouncerView
 import com.android.systemui.classifier.FalsingCollector
 import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
 import com.android.systemui.keyguard.DismissCallbackRegistry
 import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
 import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
@@ -62,7 +63,7 @@
 class DeviceEntrySideFpsOverlayInteractorTest : SysuiTestCase() {
     @JvmField @Rule var mockitoRule: MockitoRule = MockitoJUnit.rule()
 
-    @Mock private lateinit var faceAuthInteractor: KeyguardFaceAuthInteractor
+    @Mock private lateinit var faceAuthInteractor: DeviceEntryFaceAuthInteractor
     @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
     @Mock private lateinit var mSelectedUserInteractor: SelectedUserInteractor
 
@@ -109,6 +110,7 @@
             )
         underTest =
             DeviceEntrySideFpsOverlayInteractor(
+                testScope.backgroundScope,
                 mContext,
                 FakeDeviceEntryFingerprintAuthRepository(),
                 primaryBouncerInteractor,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorTest.kt
index 8e81185..809947d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorTest.kt
@@ -63,6 +63,8 @@
                 transitionRepository = super.transitionRepository,
                 transitionInteractor = super.transitionInteractor,
                 scope = super.testScope.backgroundScope,
+                bgDispatcher = super.testDispatcher,
+                mainDispatcher = super.testDispatcher,
                 keyguardInteractor = super.keyguardInteractor,
                 flags = FakeFeatureFlags(),
                 keyguardSecurityModel = mock(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
index b8a8bdf..e531d44 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
@@ -20,7 +20,6 @@
 import androidx.test.filters.SmallTest
 import com.android.keyguard.KeyguardSecurityModel
 import com.android.keyguard.KeyguardSecurityModel.SecurityMode.PIN
-import com.android.keyguard.TestScopeProvider
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
 import com.android.systemui.flags.FakeFeatureFlags
@@ -51,6 +50,7 @@
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.cancelChildren
+import kotlinx.coroutines.test.StandardTestDispatcher
 import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.advanceUntilIdle
 import kotlinx.coroutines.test.runCurrent
@@ -109,7 +109,8 @@
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
-        testScope = TestScopeProvider.getTestScope()
+        val testDispatcher = StandardTestDispatcher()
+        testScope = TestScope(testDispatcher)
 
         keyguardRepository = FakeKeyguardRepository()
         bouncerRepository = FakeKeyguardBouncerRepository()
@@ -139,6 +140,8 @@
         fromLockscreenTransitionInteractor =
             FromLockscreenTransitionInteractor(
                     scope = testScope,
+                    bgDispatcher = testDispatcher,
+                    mainDispatcher = testDispatcher,
                     keyguardInteractor = keyguardInteractor,
                     transitionRepository = transitionRepository,
                     transitionInteractor = transitionInteractor,
@@ -160,6 +163,8 @@
         fromPrimaryBouncerTransitionInteractor =
             FromPrimaryBouncerTransitionInteractor(
                     scope = testScope,
+                    bgDispatcher = testDispatcher,
+                    mainDispatcher = testDispatcher,
                     keyguardInteractor = keyguardInteractor,
                     transitionRepository = transitionRepository,
                     transitionInteractor = transitionInteractor,
@@ -173,6 +178,8 @@
         fromDreamingTransitionInteractor =
             FromDreamingTransitionInteractor(
                     scope = testScope,
+                    bgDispatcher = testDispatcher,
+                    mainDispatcher = testDispatcher,
                     keyguardInteractor = keyguardInteractor,
                     transitionRepository = transitionRepository,
                     transitionInteractor = transitionInteractor,
@@ -182,6 +189,8 @@
         fromDreamingLockscreenHostedTransitionInteractor =
             FromDreamingLockscreenHostedTransitionInteractor(
                     scope = testScope,
+                    bgDispatcher = testDispatcher,
+                    mainDispatcher = testDispatcher,
                     keyguardInteractor = keyguardInteractor,
                     transitionRepository = transitionRepository,
                     transitionInteractor = transitionInteractor,
@@ -191,6 +200,8 @@
         fromAodTransitionInteractor =
             FromAodTransitionInteractor(
                     scope = testScope,
+                    bgDispatcher = testDispatcher,
+                    mainDispatcher = testDispatcher,
                     keyguardInteractor = keyguardInteractor,
                     transitionRepository = transitionRepository,
                     transitionInteractor = transitionInteractor,
@@ -200,6 +211,8 @@
         fromGoneTransitionInteractor =
             FromGoneTransitionInteractor(
                     scope = testScope,
+                    bgDispatcher = testDispatcher,
+                    mainDispatcher = testDispatcher,
                     keyguardInteractor = keyguardInteractor,
                     transitionRepository = transitionRepository,
                     transitionInteractor = transitionInteractor,
@@ -210,6 +223,8 @@
         fromDozingTransitionInteractor =
             FromDozingTransitionInteractor(
                     scope = testScope,
+                    bgDispatcher = testDispatcher,
+                    mainDispatcher = testDispatcher,
                     keyguardInteractor = keyguardInteractor,
                     transitionRepository = transitionRepository,
                     transitionInteractor = transitionInteractor,
@@ -220,6 +235,8 @@
         fromOccludedTransitionInteractor =
             FromOccludedTransitionInteractor(
                     scope = testScope,
+                    bgDispatcher = testDispatcher,
+                    mainDispatcher = testDispatcher,
                     keyguardInteractor = keyguardInteractor,
                     transitionRepository = transitionRepository,
                     transitionInteractor = transitionInteractor,
@@ -230,6 +247,8 @@
         fromAlternateBouncerTransitionInteractor =
             FromAlternateBouncerTransitionInteractor(
                     scope = testScope,
+                    bgDispatcher = testDispatcher,
+                    mainDispatcher = testDispatcher,
                     keyguardInteractor = keyguardInteractor,
                     transitionRepository = transitionRepository,
                     transitionInteractor = transitionInteractor,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/OccludingAppDeviceEntryInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/OccludingAppDeviceEntryInteractorTest.kt
index 1f245f1..7358b9d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/OccludingAppDeviceEntryInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/OccludingAppDeviceEntryInteractorTest.kt
@@ -147,7 +147,7 @@
                     trustRepository,
                     testScope.backgroundScope,
                     mSelectedUserInteractor,
-                    keyguardFaceAuthInteractor = mock(),
+                    deviceEntryFaceAuthInteractor = mock(),
                 ),
                 AlternateBouncerInteractor(
                     statusBarStateController = mock(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlowTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlowTest.kt
index edd781d..2d9d5ed 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlowTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlowTest.kt
@@ -20,14 +20,16 @@
 import com.android.app.animation.Interpolators.EMPHASIZED_ACCELERATE
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.coroutines.collectValues
+import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.keyguard.shared.model.TransitionStep
-import com.android.systemui.util.mockito.mock
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlin.time.Duration.Companion.milliseconds
-import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
 import org.junit.Test
@@ -37,23 +39,21 @@
 @SmallTest
 @RunWith(JUnit4::class)
 class KeyguardTransitionAnimationFlowTest : SysuiTestCase() {
-    private lateinit var underTest: KeyguardTransitionAnimationFlow.SharedFlowBuilder
-    private lateinit var repository: FakeKeyguardTransitionRepository
-    private lateinit var testScope: TestScope
+    val kosmos = testKosmos()
+    val testScope = kosmos.testScope
+    val animationFlow = kosmos.keyguardTransitionAnimationFlow
+    val repository = kosmos.fakeKeyguardTransitionRepository
+
+    private lateinit var underTest: KeyguardTransitionAnimationFlow.FlowBuilder
 
     @Before
     fun setUp() {
-        testScope = TestScope()
-        repository = FakeKeyguardTransitionRepository()
         underTest =
-            KeyguardTransitionAnimationFlow(
-                    testScope.backgroundScope,
-                    mock(),
-                )
-                .setup(
-                    duration = 1000.milliseconds,
-                    stepFlow = repository.transitions,
-                )
+            animationFlow.setup(
+                duration = 1000.milliseconds,
+                from = KeyguardState.GONE,
+                to = KeyguardState.DREAMING,
+            )
     }
 
     @Test(expected = IllegalArgumentException::class)
@@ -83,6 +83,8 @@
                     onFinish = { 10f },
                 )
             var animationValues = collectLastValue(flow)
+            runCurrent()
+
             repository.sendTransitionStep(step(1f, TransitionState.FINISHED), validateStep = false)
             assertThat(animationValues()).isEqualTo(10f)
         }
@@ -97,6 +99,8 @@
                     onCancel = { 100f },
                 )
             var animationValues = collectLastValue(flow)
+            runCurrent()
+
             repository.sendTransitionStep(step(0.5f, TransitionState.CANCELED))
             assertThat(animationValues()).isEqualTo(100f)
         }
@@ -111,6 +115,8 @@
                     onStep = { it },
                 )
             var animationValues = collectLastValue(flow)
+            runCurrent()
+
             repository.sendTransitionStep(step(0f, TransitionState.STARTED))
             assertThat(animationValues()).isEqualTo(0f)
 
@@ -137,6 +143,8 @@
                     onStep = { it },
                 )
             var animationValues = collectLastValue(flow)
+            runCurrent()
+
             repository.sendTransitionStep(step(0f, TransitionState.STARTED))
             assertFloat(animationValues(), EMPHASIZED_ACCELERATE.getInterpolation(0f))
             repository.sendTransitionStep(step(0.5f, TransitionState.RUNNING))
@@ -157,17 +165,56 @@
                     duration = 1000.milliseconds,
                     onStep = { it * 2 },
                 )
-            var animationValues = collectLastValue(flow)
+            val animationValues by collectLastValue(flow)
+            runCurrent()
+
             repository.sendTransitionStep(step(0f, TransitionState.STARTED))
-            assertFloat(animationValues(), 0f)
+            assertFloat(animationValues, 0f)
             repository.sendTransitionStep(step(0.3f, TransitionState.RUNNING))
-            assertFloat(animationValues(), 0.6f)
+            assertFloat(animationValues, 0.6f)
             repository.sendTransitionStep(step(0.6f, TransitionState.RUNNING))
-            assertFloat(animationValues(), 1.2f)
+            assertFloat(animationValues, 1.2f)
             repository.sendTransitionStep(step(0.8f, TransitionState.RUNNING))
-            assertFloat(animationValues(), 1.6f)
+            assertFloat(animationValues, 1.6f)
             repository.sendTransitionStep(step(1f, TransitionState.RUNNING))
-            assertFloat(animationValues(), 2f)
+            assertFloat(animationValues, 2f)
+        }
+
+    @Test
+    fun sameFloatValueWithTheSameTransitionStateDoesNotEmitTwice() =
+        testScope.runTest {
+            val flow =
+                underTest.sharedFlow(
+                    duration = 1000.milliseconds,
+                    onStep = { it },
+                )
+            val values by collectValues(flow)
+            runCurrent()
+
+            repository.sendTransitionStep(step(0.3f, TransitionState.RUNNING))
+            repository.sendTransitionStep(step(0.3f, TransitionState.RUNNING))
+
+            assertThat(values.size).isEqualTo(1)
+            assertThat(values[0]).isEqualTo(0.3f)
+        }
+
+    @Test
+    fun sameFloatValueWithADifferentTransitionStateDoesEmitTwice() =
+        testScope.runTest {
+            val flow =
+                underTest.sharedFlow(
+                    duration = 1000.milliseconds,
+                    onStep = { it },
+                )
+            val values by collectValues(flow)
+            runCurrent()
+
+            repository.sendTransitionStep(step(0.3f, TransitionState.STARTED))
+            repository.sendTransitionStep(step(0.3f, TransitionState.RUNNING))
+
+            assertThat(values.size).isEqualTo(2)
+            assertThat(values[0]).isEqualTo(0.3f)
+            assertThat(values[0]).isEqualTo(0.3f)
         }
 
     private fun assertFloat(actual: Float?, expected: Float) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt
index dc0d9c5..070a0cc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt
@@ -17,21 +17,26 @@
 
 package com.android.systemui.keyguard.ui.view.layout.sections
 
+import android.content.pm.PackageManager
+import android.content.res.Resources
 import androidx.constraintlayout.widget.ConstraintSet
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
-import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.policy.SplitShadeStateController
 import com.android.systemui.util.Utils
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth.assertThat
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.anyString
 import org.mockito.Mock
 import org.mockito.MockitoAnnotations
 
@@ -40,17 +45,10 @@
 class ClockSectionTest : SysuiTestCase() {
     @Mock private lateinit var keyguardClockInteractor: KeyguardClockInteractor
     @Mock private lateinit var keyguardClockViewModel: KeyguardClockViewModel
-    @Mock private lateinit var smartspaceViewModel: KeyguardSmartspaceViewModel
     @Mock private lateinit var splitShadeStateController: SplitShadeStateController
 
     private lateinit var underTest: ClockSection
 
-    // smartspaceViewModel.getDimen("date_weather_view_height")
-    private val SMART_SPACE_DATE_WEATHER_HEIGHT = 10
-
-    // smartspaceViewModel.getDimen("enhanced_smartspace_height")
-    private val ENHANCED_SMART_SPACE_HEIGHT = 11
-
     private val SMALL_CLOCK_TOP_SPLIT_SHADE =
         context.resources.getDimensionPixelSize(R.dimen.keyguard_split_shade_top_margin)
 
@@ -75,15 +73,41 @@
     @Before
     fun setup() {
         MockitoAnnotations.initMocks(this)
-        whenever(smartspaceViewModel.getDimen("date_weather_view_height"))
-            .thenReturn(SMART_SPACE_DATE_WEATHER_HEIGHT)
-        whenever(smartspaceViewModel.getDimen("enhanced_smartspace_height"))
-            .thenReturn(ENHANCED_SMART_SPACE_HEIGHT)
+        val remoteResources = mock<Resources>()
+        whenever(
+                remoteResources.getIdentifier(
+                    anyString(),
+                    eq("dimen"),
+                    anyString(),
+                )
+            )
+            .then { invocation ->
+                val name = invocation.arguments[0] as String
+                val index = DIMENSION_BY_IDENTIFIER_NAME.indexOfFirst { (key, _) -> key == name }
+                if (index == -1) {
+                    error(
+                        "No entry for a dimension named \"$name\", please add it to the list above."
+                    )
+                }
+                index
+            }
+        whenever(
+                remoteResources.getDimensionPixelSize(
+                    anyInt(),
+                )
+            )
+            .then { invocation ->
+                val id = invocation.arguments[0] as Int
+                DIMENSION_BY_IDENTIFIER_NAME[id].second
+            }
+        val packageManager = mock<PackageManager>()
+        whenever(packageManager.getResourcesForApplication(anyString())).thenReturn(remoteResources)
+        mContext.setMockPackageManager(packageManager)
+
         underTest =
             ClockSection(
                 keyguardClockInteractor,
                 keyguardClockViewModel,
-                smartspaceViewModel,
                 mContext,
                 splitShadeStateController,
             )
@@ -164,4 +188,14 @@
         assertThat(smallClockConstraint.layout.topToTop).isEqualTo(ConstraintSet.PARENT_ID)
         assertThat(smallClockConstraint.layout.topMargin).isEqualTo(expectedSmallClockTopMargin)
     }
+
+    companion object {
+        private val SMART_SPACE_DATE_WEATHER_HEIGHT = 10
+        private val ENHANCED_SMART_SPACE_HEIGHT = 11
+        private val DIMENSION_BY_IDENTIFIER_NAME =
+            listOf(
+                "date_weather_view_height" to SMART_SPACE_DATE_WEATHER_HEIGHT,
+                "enhanced_smartspace_height" to ENHANCED_SMART_SPACE_HEIGHT,
+            )
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSectionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSectionTest.kt
index 8dd33d5..1205dce 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSectionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSectionTest.kt
@@ -21,11 +21,11 @@
 import androidx.constraintlayout.widget.ConstraintLayout
 import androidx.constraintlayout.widget.ConstraintSet
 import androidx.test.filters.SmallTest
-import com.android.systemui.res.R
-import com.android.systemui.SysuiTestCase
 import com.android.systemui.Flags as AConfigFlags
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.keyguard.ui.viewmodel.AodAlphaViewModel
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardIndicationAreaViewModel
-import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
+import com.android.systemui.res.R
 import com.android.systemui.statusbar.KeyguardIndicationController
 import com.google.common.truth.Truth.assertThat
 import org.junit.Before
@@ -38,8 +38,9 @@
 @RunWith(JUnit4::class)
 @SmallTest
 class DefaultIndicationAreaSectionTest : SysuiTestCase() {
+
     @Mock private lateinit var keyguardIndicationAreaViewModel: KeyguardIndicationAreaViewModel
-    @Mock private lateinit var keyguardRootViewModel: KeyguardRootViewModel
+    @Mock private lateinit var aodAlphaViewModel: AodAlphaViewModel
     @Mock private lateinit var indicationController: KeyguardIndicationController
 
     private lateinit var underTest: DefaultIndicationAreaSection
@@ -51,7 +52,7 @@
             DefaultIndicationAreaSection(
                 context,
                 keyguardIndicationAreaViewModel,
-                keyguardRootViewModel,
+                aodAlphaViewModel,
                 indicationController,
             )
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSectionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSectionTest.kt
index 740110b4..28da957 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSectionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSectionTest.kt
@@ -29,7 +29,9 @@
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel
 import com.android.systemui.res.R
+import com.android.systemui.shared.R as sharedR
 import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController
+import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.flow.StateFlow
@@ -50,9 +52,9 @@
     @Mock private lateinit var keyguardUnlockAnimationController: KeyguardUnlockAnimationController
     @Mock private lateinit var hasCustomWeatherDataDisplay: StateFlow<Boolean>
 
-    private val smartspaceView = View(mContext).also { it.id = View.generateViewId() }
-    private val weatherView = View(mContext).also { it.id = View.generateViewId() }
-    private val dateView = View(mContext).also { it.id = View.generateViewId() }
+    private val smartspaceView = View(mContext).also { it.id = sharedR.id.bc_smartspace_view }
+    private val weatherView = View(mContext).also { it.id = sharedR.id.weather_smartspace_view }
+    private val dateView = View(mContext).also { it.id = sharedR.id.date_smartspace_view }
     private lateinit var constraintLayout: ConstraintLayout
     private lateinit var constraintSet: ConstraintSet
 
@@ -69,13 +71,13 @@
                 keyguardUnlockAnimationController,
             )
         constraintLayout = ConstraintLayout(mContext)
-        whenever(keyguardSmartspaceViewModel.smartspaceView).thenReturn(smartspaceView)
-        whenever(keyguardSmartspaceViewModel.weatherView).thenReturn(weatherView)
-        whenever(keyguardSmartspaceViewModel.dateView).thenReturn(dateView)
+        whenever(lockscreenSmartspaceController.buildAndConnectView(any()))
+            .thenReturn(smartspaceView)
+        whenever(lockscreenSmartspaceController.buildAndConnectWeatherView(any()))
+            .thenReturn(weatherView)
+        whenever(lockscreenSmartspaceController.buildAndConnectDateView(any())).thenReturn(dateView)
         whenever(keyguardClockViewModel.hasCustomWeatherDataDisplay)
             .thenReturn(hasCustomWeatherDataDisplay)
-        whenever(keyguardSmartspaceViewModel.smartspaceController)
-            .thenReturn(lockscreenSmartspaceController)
         constraintSet = ConstraintSet()
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelTest.kt
index d959872..87391cc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelTest.kt
@@ -31,6 +31,7 @@
 import com.google.common.collect.Range
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -50,6 +51,7 @@
     fun transitionToAlternateBouncer_scrimAlphaUpdate() =
         testScope.runTest {
             val scrimAlphas by collectValues(underTest.scrimAlpha)
+            runCurrent()
 
             transitionRepository.sendTransitionSteps(
                 listOf(
@@ -69,17 +71,17 @@
     fun transitionFromAlternateBouncer_scrimAlphaUpdate() =
         testScope.runTest {
             val scrimAlphas by collectValues(underTest.scrimAlpha)
+            runCurrent()
 
             transitionRepository.sendTransitionSteps(
                 listOf(
-                    stepToAlternateBouncer(0f, TransitionState.STARTED),
-                    stepToAlternateBouncer(.4f),
-                    stepToAlternateBouncer(.6f),
-                    stepToAlternateBouncer(1f),
+                    stepFromAlternateBouncer(0f, TransitionState.STARTED),
+                    stepFromAlternateBouncer(.4f),
+                    stepFromAlternateBouncer(.6f),
+                    stepFromAlternateBouncer(1f),
                 ),
                 testScope,
             )
-
             assertThat(scrimAlphas.size).isEqualTo(4)
             scrimAlphas.forEach { assertThat(it).isIn(Range.closed(0f, 1f)) }
         }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelTest.kt
index af8d8a8..795e68d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelTest.kt
@@ -30,6 +30,7 @@
 import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -63,6 +64,7 @@
             fingerprintPropertyRepository.supportsUdfps()
             val deviceEntryBackgroundViewAlpha by
                 collectLastValue(underTest.deviceEntryBackgroundViewAlpha)
+            runCurrent()
 
             // fade in
             repository.sendTransitionStep(step(0f, TransitionState.STARTED))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsTest.kt
index daafe12..75994da 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsTest.kt
@@ -39,6 +39,7 @@
 import com.google.common.truth.Truth.assertThat
 import kotlin.time.Duration.Companion.milliseconds
 import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
 import org.junit.Test
@@ -78,6 +79,8 @@
     fun scrimAlpha_runDimissFromKeyguard_shadeExpanded() =
         testScope.runTest {
             val values by collectValues(underTest.scrimAlpha(500.milliseconds, PRIMARY_BOUNCER))
+            runCurrent()
+
             shadeRepository.setLockscreenShadeExpansion(1f)
             whenever(primaryBouncerInteractor.willRunDismissFromKeyguard()).thenReturn(true)
 
@@ -101,6 +104,8 @@
     fun scrimAlpha_runDimissFromKeyguard_shadeNotExpanded() =
         testScope.runTest {
             val values by collectValues(underTest.scrimAlpha(500.milliseconds, PRIMARY_BOUNCER))
+            runCurrent()
+
             shadeRepository.setLockscreenShadeExpansion(0f)
 
             whenever(primaryBouncerInteractor.willRunDismissFromKeyguard()).thenReturn(true)
@@ -123,6 +128,7 @@
     fun scrimBehindAlpha_leaveShadeOpen() =
         testScope.runTest {
             val values by collectValues(underTest.scrimAlpha(500.milliseconds, PRIMARY_BOUNCER))
+            runCurrent()
 
             sysuiStatusBarStateController.setLeaveOpenOnKeyguardHide(true)
 
@@ -146,6 +152,8 @@
     fun scrimBehindAlpha_doNotLeaveShadeOpen() =
         testScope.runTest {
             val values by collectValues(underTest.scrimAlpha(500.milliseconds, PRIMARY_BOUNCER))
+            runCurrent()
+
             keyguardTransitionRepository.sendTransitionSteps(
                 listOf(
                     step(0f, TransitionState.STARTED),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModelTest.kt
index dd542d4..471029b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModelTest.kt
@@ -20,18 +20,15 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectValues
-import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
+import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.keyguard.shared.model.TransitionStep
-import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
-import com.android.systemui.util.mockito.mock
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.runTest
-import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
 
@@ -39,29 +36,10 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class DozingToLockscreenTransitionViewModelTest : SysuiTestCase() {
-    private lateinit var testScope: TestScope
-    private lateinit var underTest: DozingToLockscreenTransitionViewModel
-    private lateinit var repository: FakeKeyguardTransitionRepository
-
-    @Before
-    fun setUp() {
-        testScope = TestScope()
-        repository = FakeKeyguardTransitionRepository()
-        underTest =
-            DozingToLockscreenTransitionViewModel(
-                interactor =
-                    KeyguardTransitionInteractorFactory.create(
-                            scope = testScope.backgroundScope,
-                            repository = repository,
-                        )
-                        .keyguardTransitionInteractor,
-                animationFlow =
-                    KeyguardTransitionAnimationFlow(
-                        scope = testScope.backgroundScope,
-                        logger = mock()
-                    ),
-            )
-    }
+    val kosmos = testKosmos()
+    val testScope = kosmos.testScope
+    val repository = kosmos.fakeKeyguardTransitionRepository
+    val underTest = kosmos.dozingToLockscreenTransitionViewModel
 
     @Test
     fun deviceEntryParentViewShows() =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModelTest.kt
index a105008..1c9c942 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModelTest.kt
@@ -31,6 +31,7 @@
 import com.google.common.collect.Range
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -52,6 +53,7 @@
             val pixels = -100f
             val enterFromTopTranslationY by
                 collectLastValue(underTest.enterFromTopTranslationY(pixels.toInt()))
+            runCurrent()
 
             // The animation should only start > .4f way through
             repository.sendTransitionStep(step(0f, TransitionState.STARTED))
@@ -72,6 +74,7 @@
     fun enterFromTopAnimationAlpha() =
         testScope.runTest {
             val enterFromTopAnimationAlpha by collectLastValue(underTest.enterFromTopAnimationAlpha)
+            runCurrent()
 
             // The animation should only start > .4f way through
             repository.sendTransitionStep(step(0f, TransitionState.STARTED))
@@ -92,6 +95,7 @@
         testScope.runTest {
             val deviceEntryBackgroundViewAlpha by
                 collectLastValue(underTest.deviceEntryBackgroundViewAlpha)
+            runCurrent()
 
             // immediately 0f
             repository.sendTransitionStep(step(0f, TransitionState.STARTED))
@@ -113,6 +117,7 @@
             fingerprintPropertyRepository.supportsUdfps()
             biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true)
             val deviceEntryParentViewAlpha by collectLastValue(underTest.deviceEntryParentViewAlpha)
+            runCurrent()
 
             // animation doesn't start until the end
             repository.sendTransitionStep(step(0f, TransitionState.STARTED))
@@ -137,6 +142,7 @@
             fingerprintPropertyRepository.supportsRearFps()
             biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true)
             val deviceEntryParentViewAlpha by collectLastValue(underTest.deviceEntryParentViewAlpha)
+            runCurrent()
 
             // animation doesn't start until the end
             repository.sendTransitionStep(step(0f, TransitionState.STARTED))
@@ -161,6 +167,7 @@
             fingerprintPropertyRepository.supportsUdfps()
             biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(false)
             val deviceEntryParentViewAlpha by collectLastValue(underTest.deviceEntryParentViewAlpha)
+            runCurrent()
 
             // animation doesn't start until the end
             repository.sendTransitionStep(step(0f, TransitionState.STARTED))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelTest.kt
index d421004..1b4573d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelTest.kt
@@ -34,6 +34,7 @@
 import com.android.systemui.plugins.clocks.ClockFaceConfig
 import com.android.systemui.plugins.clocks.ClockFaceController
 import com.android.systemui.shared.clocks.ClockRegistry
+import com.android.systemui.statusbar.policy.SplitShadeStateController
 import com.android.systemui.util.mockito.whenever
 import com.android.systemui.util.settings.FakeSettings
 import com.google.common.truth.Truth.assertThat
@@ -66,6 +67,8 @@
     @Mock private lateinit var largeClock: ClockFaceController
     @Mock private lateinit var clockFaceConfig: ClockFaceConfig
     @Mock private lateinit var eventController: ClockEventController
+    @Mock private lateinit var splitShadeStateController: SplitShadeStateController
+
     @Before
     fun setup() {
         MockitoAnnotations.initMocks(this)
@@ -92,6 +95,7 @@
                 keyguardInteractor,
                 keyguardClockInteractor,
                 scope.backgroundScope,
+                splitShadeStateController,
             )
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
deleted file mode 100644
index ee1be10..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
+++ /dev/null
@@ -1,498 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-@file:OptIn(ExperimentalCoroutinesApi::class)
-
-package com.android.systemui.keyguard.ui.viewmodel
-
-import android.view.View
-import androidx.test.filters.SmallTest
-import com.android.systemui.Flags as AConfigFlags
-import com.android.systemui.Flags.FLAG_NEW_AOD_TRANSITION
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
-import com.android.systemui.common.ui.domain.interactor.configurationInteractor
-import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
-import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
-import com.android.systemui.flags.featureFlagsClassic
-import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
-import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
-import com.android.systemui.keyguard.domain.interactor.BurnInInteractor
-import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
-import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
-import com.android.systemui.keyguard.shared.model.BurnInModel
-import com.android.systemui.keyguard.shared.model.KeyguardState
-import com.android.systemui.keyguard.shared.model.TransitionState
-import com.android.systemui.keyguard.shared.model.TransitionStep
-import com.android.systemui.kosmos.testScope
-import com.android.systemui.plugins.clocks.ClockController
-import com.android.systemui.statusbar.notification.data.repository.fakeNotificationsKeyguardViewStateRepository
-import com.android.systemui.statusbar.notification.stack.domain.interactor.notificationsKeyguardInteractor
-import com.android.systemui.statusbar.phone.dozeParameters
-import com.android.systemui.statusbar.phone.screenOffAnimationController
-import com.android.systemui.testKosmos
-import com.android.systemui.util.mockito.whenever
-import com.android.systemui.util.ui.isAnimating
-import com.android.systemui.util.ui.stopAnimating
-import com.android.systemui.util.ui.value
-import com.google.common.truth.Truth.assertThat
-import javax.inject.Provider
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.emptyFlow
-import kotlinx.coroutines.test.runCurrent
-import kotlinx.coroutines.test.runTest
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
-import org.mockito.Answers
-import org.mockito.Mock
-import org.mockito.Mockito.RETURNS_DEEP_STUBS
-import org.mockito.Mockito.anyInt
-import org.mockito.MockitoAnnotations
-
-@SmallTest
-@RunWith(JUnit4::class)
-class KeyguardRootViewModelTest : SysuiTestCase() {
-    private val kosmos = testKosmos()
-    private val testScope = kosmos.testScope
-    private val repository = kosmos.fakeKeyguardRepository
-    private val configurationRepository = kosmos.fakeConfigurationRepository
-    private val keyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository
-    private val screenOffAnimationController = kosmos.screenOffAnimationController
-    private val deviceEntryRepository = kosmos.fakeDeviceEntryRepository
-    private val notificationsKeyguardInteractor = kosmos.notificationsKeyguardInteractor
-    private val fakeNotificationsKeyguardViewStateRepository =
-        kosmos.fakeNotificationsKeyguardViewStateRepository
-    private val dozeParameters = kosmos.dozeParameters
-    private lateinit var underTest: KeyguardRootViewModel
-
-    @Mock private lateinit var burnInInteractor: BurnInInteractor
-    private val burnInFlow = MutableStateFlow(BurnInModel())
-
-    @Mock private lateinit var goneToAodTransitionViewModel: GoneToAodTransitionViewModel
-    private val enterFromTopAnimationAlpha = MutableStateFlow(0f)
-
-    @Mock
-    private lateinit var aodToLockscreenTransitionViewModel: AodToLockscreenTransitionViewModel
-    @Mock
-    private lateinit var occludedToLockscreenTransitionViewModel:
-        OccludedToLockscreenTransitionViewModel
-    private val occludedToLockscreenTranslationY = MutableStateFlow(0f)
-    private val occludedToLockscreenAlpha = MutableStateFlow(0f)
-
-    @Mock(answer = Answers.RETURNS_DEEP_STUBS) private lateinit var clockController: ClockController
-
-    @Before
-    fun setUp() {
-        MockitoAnnotations.initMocks(this)
-
-        mSetFlagsRule.enableFlags(AConfigFlags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR)
-        mSetFlagsRule.enableFlags(FLAG_NEW_AOD_TRANSITION)
-        mSetFlagsRule.disableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
-
-        whenever(goneToAodTransitionViewModel.enterFromTopTranslationY(anyInt()))
-            .thenReturn(emptyFlow<Float>())
-        whenever(goneToAodTransitionViewModel.enterFromTopAnimationAlpha)
-            .thenReturn(enterFromTopAnimationAlpha)
-
-        whenever(burnInInteractor.keyguardBurnIn).thenReturn(burnInFlow)
-
-        whenever(occludedToLockscreenTransitionViewModel.lockscreenTranslationY)
-            .thenReturn(occludedToLockscreenTranslationY)
-        whenever(occludedToLockscreenTransitionViewModel.lockscreenAlpha)
-            .thenReturn(occludedToLockscreenAlpha)
-
-        underTest =
-            KeyguardRootViewModel(
-                configurationInteractor = kosmos.configurationInteractor,
-                deviceEntryInteractor = kosmos.deviceEntryInteractor,
-                dozeParameters = kosmos.dozeParameters,
-                keyguardInteractor = kosmos.keyguardInteractor,
-                keyguardTransitionInteractor = kosmos.keyguardTransitionInteractor,
-                notificationsKeyguardInteractor = kosmos.notificationsKeyguardInteractor,
-                burnInInteractor = burnInInteractor,
-                keyguardClockViewModel = kosmos.keyguardClockViewModel,
-                goneToAodTransitionViewModel = goneToAodTransitionViewModel,
-                aodToLockscreenTransitionViewModel = aodToLockscreenTransitionViewModel,
-                occludedToLockscreenTransitionViewModel = occludedToLockscreenTransitionViewModel,
-                screenOffAnimationController = screenOffAnimationController,
-                // TODO(b/310989341): remove after change to aconfig
-                featureFlags = kosmos.featureFlagsClassic
-            )
-
-        underTest.clockControllerProvider = Provider { clockController }
-    }
-
-    @Test
-    fun alpha() =
-        testScope.runTest {
-            val alpha by collectLastValue(underTest.alpha)
-
-            keyguardTransitionRepository.sendTransitionSteps(
-                from = KeyguardState.OFF,
-                to = KeyguardState.LOCKSCREEN,
-                testScope = testScope,
-            )
-
-            repository.setKeyguardAlpha(0.1f)
-            assertThat(alpha).isEqualTo(0.1f)
-            repository.setKeyguardAlpha(0.5f)
-            assertThat(alpha).isEqualTo(0.5f)
-            repository.setKeyguardAlpha(0.2f)
-            assertThat(alpha).isEqualTo(0.2f)
-            repository.setKeyguardAlpha(0f)
-            assertThat(alpha).isEqualTo(0f)
-            occludedToLockscreenAlpha.value = 0.8f
-            assertThat(alpha).isEqualTo(0.8f)
-        }
-
-    @Test
-    fun alphaWhenGoneEqualsZero() =
-        testScope.runTest {
-            val alpha by collectLastValue(underTest.alpha)
-
-            keyguardTransitionRepository.sendTransitionSteps(
-                from = KeyguardState.LOCKSCREEN,
-                to = KeyguardState.GONE,
-                testScope = testScope,
-            )
-
-            repository.setKeyguardAlpha(0.1f)
-            assertThat(alpha).isEqualTo(0f)
-            repository.setKeyguardAlpha(0.5f)
-            assertThat(alpha).isEqualTo(0f)
-            repository.setKeyguardAlpha(1f)
-            assertThat(alpha).isEqualTo(0f)
-        }
-
-    @Test
-    fun translationYInitialValueIsZero() =
-        testScope.runTest {
-            val translationY by collectLastValue(underTest.translationY)
-            assertThat(translationY).isEqualTo(0)
-        }
-
-    @Test
-    fun translationAndScaleFromBurnInNotDozing() =
-        testScope.runTest {
-            val translationX by collectLastValue(underTest.translationX)
-            val translationY by collectLastValue(underTest.translationY)
-            val scale by collectLastValue(underTest.scale)
-
-            // Set to not dozing (on lockscreen)
-            keyguardTransitionRepository.sendTransitionStep(
-                TransitionStep(
-                    from = KeyguardState.AOD,
-                    to = KeyguardState.LOCKSCREEN,
-                    value = 1f,
-                    transitionState = TransitionState.FINISHED
-                ),
-                validateStep = false,
-            )
-
-            // Trigger a change to the burn-in model
-            burnInFlow.value =
-                BurnInModel(
-                    translationX = 20,
-                    translationY = 30,
-                    scale = 0.5f,
-                )
-
-            assertThat(translationX).isEqualTo(0)
-            assertThat(translationY).isEqualTo(0)
-            assertThat(scale).isEqualTo(Pair(1f, true /* scaleClockOnly */))
-        }
-
-    @Test
-    fun translationAndScaleFromBurnFullyDozing() =
-        testScope.runTest {
-            val translationX by collectLastValue(underTest.translationX)
-            val translationY by collectLastValue(underTest.translationY)
-            val scale by collectLastValue(underTest.scale)
-
-            underTest.statusViewTop = 100
-
-            // Set to dozing (on AOD)
-            keyguardTransitionRepository.sendTransitionStep(
-                TransitionStep(
-                    from = KeyguardState.GONE,
-                    to = KeyguardState.AOD,
-                    value = 1f,
-                    transitionState = TransitionState.FINISHED
-                ),
-                validateStep = false,
-            )
-            // Trigger a change to the burn-in model
-            burnInFlow.value =
-                BurnInModel(
-                    translationX = 20,
-                    translationY = 30,
-                    scale = 0.5f,
-                )
-
-            assertThat(translationX).isEqualTo(20)
-            assertThat(translationY).isEqualTo(30)
-            assertThat(scale).isEqualTo(Pair(0.5f, true /* scaleClockOnly */))
-
-            // Set to the beginning of GONE->AOD transition
-            keyguardTransitionRepository.sendTransitionStep(
-                TransitionStep(
-                    from = KeyguardState.GONE,
-                    to = KeyguardState.AOD,
-                    value = 0f,
-                    transitionState = TransitionState.STARTED
-                ),
-                validateStep = false,
-            )
-            assertThat(translationX).isEqualTo(0)
-            assertThat(translationY).isEqualTo(0)
-            assertThat(scale).isEqualTo(Pair(1f, true /* scaleClockOnly */))
-        }
-
-    @Test
-    fun translationAndScaleFromBurnFullyDozingStaysOutOfTopInset() =
-        testScope.runTest {
-            val translationX by collectLastValue(underTest.translationX)
-            val translationY by collectLastValue(underTest.translationY)
-            val scale by collectLastValue(underTest.scale)
-
-            underTest.statusViewTop = 100
-            underTest.topInset = 80
-
-            // Set to dozing (on AOD)
-            keyguardTransitionRepository.sendTransitionStep(
-                TransitionStep(
-                    from = KeyguardState.GONE,
-                    to = KeyguardState.AOD,
-                    value = 1f,
-                    transitionState = TransitionState.FINISHED
-                ),
-                validateStep = false,
-            )
-
-            // Trigger a change to the burn-in model
-            burnInFlow.value =
-                BurnInModel(
-                    translationX = 20,
-                    translationY = -30,
-                    scale = 0.5f,
-                )
-            assertThat(translationX).isEqualTo(20)
-            // -20 instead of -30, due to inset of 80
-            assertThat(translationY).isEqualTo(-20)
-            assertThat(scale).isEqualTo(Pair(0.5f, true /* scaleClockOnly */))
-
-            // Set to the beginning of GONE->AOD transition
-            keyguardTransitionRepository.sendTransitionStep(
-                TransitionStep(
-                    from = KeyguardState.GONE,
-                    to = KeyguardState.AOD,
-                    value = 0f,
-                    transitionState = TransitionState.STARTED
-                ),
-                validateStep = false,
-            )
-            assertThat(translationX).isEqualTo(0)
-            assertThat(translationY).isEqualTo(0)
-            assertThat(scale).isEqualTo(Pair(1f, true /* scaleClockOnly */))
-        }
-
-    @Test
-    fun translationAndScaleFromBurnInUseScaleOnly() =
-        testScope.runTest {
-            whenever(clockController.config.useAlternateSmartspaceAODTransition).thenReturn(true)
-
-            val translationX by collectLastValue(underTest.translationX)
-            val translationY by collectLastValue(underTest.translationY)
-            val scale by collectLastValue(underTest.scale)
-
-            // Set to dozing (on AOD)
-            keyguardTransitionRepository.sendTransitionStep(
-                TransitionStep(
-                    from = KeyguardState.GONE,
-                    to = KeyguardState.AOD,
-                    value = 1f,
-                    transitionState = TransitionState.FINISHED
-                ),
-                validateStep = false,
-            )
-
-            // Trigger a change to the burn-in model
-            burnInFlow.value =
-                BurnInModel(
-                    translationX = 20,
-                    translationY = 30,
-                    scale = 0.5f,
-                )
-
-            assertThat(translationX).isEqualTo(0)
-            assertThat(translationY).isEqualTo(0)
-            assertThat(scale).isEqualTo(Pair(0.5f, false /* scaleClockOnly */))
-        }
-
-    @Test
-    fun burnInLayerVisibility() =
-        testScope.runTest {
-            val burnInLayerVisibility by collectLastValue(underTest.burnInLayerVisibility)
-
-            keyguardTransitionRepository.sendTransitionStep(
-                TransitionStep(
-                    from = KeyguardState.LOCKSCREEN,
-                    to = KeyguardState.AOD,
-                    value = 0f,
-                    transitionState = TransitionState.STARTED
-                ),
-                validateStep = false,
-            )
-            assertThat(burnInLayerVisibility).isEqualTo(View.VISIBLE)
-        }
-
-    @Test
-    fun burnInLayerAlpha() =
-        testScope.runTest {
-            val burnInLayerAlpha by collectLastValue(underTest.burnInLayerAlpha)
-
-            enterFromTopAnimationAlpha.value = 0.2f
-            assertThat(burnInLayerAlpha).isEqualTo(0.2f)
-
-            enterFromTopAnimationAlpha.value = 1f
-            assertThat(burnInLayerAlpha).isEqualTo(1f)
-        }
-
-    @Test
-    fun iconContainer_isNotVisible_notOnKeyguard_dontShowAodIconsWhenShade() =
-        testScope.runTest {
-            val isVisible by collectLastValue(underTest.isNotifIconContainerVisible)
-            runCurrent()
-            keyguardTransitionRepository.sendTransitionSteps(
-                from = KeyguardState.OFF,
-                to = KeyguardState.GONE,
-                testScope,
-            )
-            whenever(screenOffAnimationController.shouldShowAodIconsWhenShade()).thenReturn(false)
-            runCurrent()
-
-            assertThat(isVisible?.value).isFalse()
-            assertThat(isVisible?.isAnimating).isFalse()
-        }
-
-    @Test
-    fun iconContainer_isVisible_bypassEnabled() =
-        testScope.runTest {
-            val isVisible by collectLastValue(underTest.isNotifIconContainerVisible)
-            runCurrent()
-            deviceEntryRepository.setBypassEnabled(true)
-            runCurrent()
-
-            assertThat(isVisible?.value).isTrue()
-        }
-
-    @Test
-    fun iconContainer_isNotVisible_pulseExpanding_notBypassing() =
-        testScope.runTest {
-            val isVisible by collectLastValue(underTest.isNotifIconContainerVisible)
-            runCurrent()
-            fakeNotificationsKeyguardViewStateRepository.setPulseExpanding(true)
-            deviceEntryRepository.setBypassEnabled(false)
-            runCurrent()
-
-            assertThat(isVisible?.value).isEqualTo(false)
-        }
-
-    @Test
-    fun iconContainer_isVisible_notifsFullyHidden_bypassEnabled() =
-        testScope.runTest {
-            val isVisible by collectLastValue(underTest.isNotifIconContainerVisible)
-            runCurrent()
-            fakeNotificationsKeyguardViewStateRepository.setPulseExpanding(false)
-            deviceEntryRepository.setBypassEnabled(true)
-            fakeNotificationsKeyguardViewStateRepository.setNotificationsFullyHidden(true)
-            runCurrent()
-
-            assertThat(isVisible?.value).isTrue()
-            assertThat(isVisible?.isAnimating).isTrue()
-        }
-
-    @Test
-    fun iconContainer_isVisible_notifsFullyHidden_bypassDisabled_aodDisabled() =
-        testScope.runTest {
-            val isVisible by collectLastValue(underTest.isNotifIconContainerVisible)
-            runCurrent()
-            fakeNotificationsKeyguardViewStateRepository.setPulseExpanding(false)
-            deviceEntryRepository.setBypassEnabled(false)
-            whenever(dozeParameters.alwaysOn).thenReturn(false)
-            fakeNotificationsKeyguardViewStateRepository.setNotificationsFullyHidden(true)
-            runCurrent()
-
-            assertThat(isVisible?.value).isTrue()
-            assertThat(isVisible?.isAnimating).isFalse()
-        }
-
-    @Test
-    fun iconContainer_isVisible_notifsFullyHidden_bypassDisabled_displayNeedsBlanking() =
-        testScope.runTest {
-            val isVisible by collectLastValue(underTest.isNotifIconContainerVisible)
-            runCurrent()
-            fakeNotificationsKeyguardViewStateRepository.setPulseExpanding(false)
-            deviceEntryRepository.setBypassEnabled(false)
-            whenever(dozeParameters.alwaysOn).thenReturn(true)
-            whenever(dozeParameters.displayNeedsBlanking).thenReturn(true)
-            fakeNotificationsKeyguardViewStateRepository.setNotificationsFullyHidden(true)
-            runCurrent()
-
-            assertThat(isVisible?.value).isTrue()
-            assertThat(isVisible?.isAnimating).isFalse()
-        }
-
-    @Test
-    fun iconContainer_isVisible_notifsFullyHidden_bypassDisabled() =
-        testScope.runTest {
-            val isVisible by collectLastValue(underTest.isNotifIconContainerVisible)
-            runCurrent()
-            fakeNotificationsKeyguardViewStateRepository.setPulseExpanding(false)
-            deviceEntryRepository.setBypassEnabled(false)
-            whenever(dozeParameters.alwaysOn).thenReturn(true)
-            whenever(dozeParameters.displayNeedsBlanking).thenReturn(false)
-            fakeNotificationsKeyguardViewStateRepository.setNotificationsFullyHidden(true)
-            runCurrent()
-
-            assertThat(isVisible?.value).isTrue()
-            assertThat(isVisible?.isAnimating).isTrue()
-        }
-
-    @Test
-    fun isIconContainerVisible_stopAnimation() =
-        testScope.runTest {
-            val isVisible by collectLastValue(underTest.isNotifIconContainerVisible)
-            runCurrent()
-            fakeNotificationsKeyguardViewStateRepository.setPulseExpanding(false)
-            deviceEntryRepository.setBypassEnabled(false)
-            whenever(dozeParameters.alwaysOn).thenReturn(true)
-            whenever(dozeParameters.displayNeedsBlanking).thenReturn(false)
-            fakeNotificationsKeyguardViewStateRepository.setNotificationsFullyHidden(true)
-            runCurrent()
-
-            assertThat(isVisible?.isAnimating).isEqualTo(true)
-            isVisible?.stopAnimating()
-            runCurrent()
-
-            assertThat(isVisible?.isAnimating).isEqualTo(false)
-        }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModelTest.kt
index 5e62317..1912987 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModelTest.kt
@@ -30,6 +30,7 @@
 import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -51,6 +52,7 @@
         testScope.runTest {
             val deviceEntryBackgroundViewAlpha by
                 collectLastValue(underTest.deviceEntryBackgroundViewAlpha)
+            runCurrent()
 
             // immediately 0f
             keyguardTransitionRepository.sendTransitionStep(step(0f, TransitionState.STARTED))
@@ -72,6 +74,7 @@
             fingerprintPropertyRepository.supportsUdfps()
             biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true)
             val deviceEntryParentViewAlpha by collectLastValue(underTest.deviceEntryParentViewAlpha)
+            runCurrent()
 
             // immediately 1f
             keyguardTransitionRepository.sendTransitionStep(step(0f, TransitionState.STARTED))
@@ -96,6 +99,7 @@
             fingerprintPropertyRepository.supportsRearFps()
             biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true)
             val deviceEntryParentViewAlpha by collectLastValue(underTest.deviceEntryParentViewAlpha)
+            runCurrent()
 
             // no updates
             keyguardTransitionRepository.sendTransitionStep(step(0f, TransitionState.STARTED))
@@ -120,6 +124,7 @@
             fingerprintPropertyRepository.supportsUdfps()
             biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(false)
             val deviceEntryParentViewAlpha by collectLastValue(underTest.deviceEntryParentViewAlpha)
+            runCurrent()
 
             // no updates
             keyguardTransitionRepository.sendTransitionStep(step(0f, TransitionState.STARTED))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModelTest.kt
index 9729022..c55c27c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModelTest.kt
@@ -30,6 +30,7 @@
 import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -54,6 +55,7 @@
             fingerprintPropertyRepository.supportsUdfps()
             val deviceEntryBackgroundViewAlpha by
                 collectLastValue(underTest.deviceEntryBackgroundViewAlpha)
+            runCurrent()
 
             // immediately 0f
             keyguardTransitionRepository.sendTransitionStep(step(0f, TransitionState.STARTED))
@@ -75,6 +77,7 @@
             fingerprintPropertyRepository.supportsUdfps()
             biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true)
             val deviceEntryParentViewAlpha by collectLastValue(underTest.deviceEntryParentViewAlpha)
+            runCurrent()
 
             keyguardTransitionRepository.sendTransitionStep(step(0f, TransitionState.STARTED))
 
@@ -92,6 +95,7 @@
             fingerprintPropertyRepository.supportsRearFps()
             biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true)
             val deviceEntryParentViewAlpha by collectLastValue(underTest.deviceEntryParentViewAlpha)
+            runCurrent()
 
             // animation doesn't start until the end
             keyguardTransitionRepository.sendTransitionStep(step(0f, TransitionState.STARTED))
@@ -116,6 +120,7 @@
             fingerprintPropertyRepository.supportsUdfps()
             biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(false)
             val deviceEntryParentViewAlpha by collectLastValue(underTest.deviceEntryParentViewAlpha)
+            runCurrent()
 
             keyguardTransitionRepository.sendTransitionStep(step(0f, TransitionState.STARTED))
             assertThat(deviceEntryParentViewAlpha).isNull()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModelTest.kt
index 2c6436e..0796af0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModelTest.kt
@@ -30,6 +30,7 @@
 import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -71,6 +72,7 @@
         testScope.runTest {
             fingerprintPropertyRepository.supportsUdfps()
             val bgViewAlpha by collectLastValue(underTest.deviceEntryBackgroundViewAlpha)
+            runCurrent()
 
             // immediately 1f
             keyguardTransitionRepository.sendTransitionStep(step(0f, TransitionState.STARTED))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataCombineLatestTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataCombineLatestTest.java
index 0a5b124..fb101dd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataCombineLatestTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataCombineLatestTest.java
@@ -78,7 +78,7 @@
         mMediaData = new MediaData(
                 USER_ID, true, APP, null, ARTIST, TITLE, null,
                 new ArrayList<>(), new ArrayList<>(), null, PACKAGE, null, null, null, true, null,
-                MediaData.PLAYBACK_LOCAL, false, KEY, false, false, false, 0L,
+                MediaData.PLAYBACK_LOCAL, false, KEY, false, false, false, 0L, 0L,
                 InstanceId.fakeInstanceId(-1), -1, false, null);
         mDeviceData = new MediaDeviceData(true, null, DEVICE_NAME, null, false);
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataFilterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataFilterTest.kt
index 8532ffe..94b9fa4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataFilterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataFilterTest.kt
@@ -55,6 +55,7 @@
 private const val KEY_ALT = "TEST_KEY_2"
 private const val USER_MAIN = 0
 private const val USER_GUEST = 10
+private const val PRIVATE_PROFILE = 12
 private const val PACKAGE = "PKG"
 private val INSTANCE_ID = InstanceId.fakeInstanceId(123)!!
 private const val APP_UID = 99
@@ -82,6 +83,7 @@
     private lateinit var mediaDataFilter: MediaDataFilter
     private lateinit var dataMain: MediaData
     private lateinit var dataGuest: MediaData
+    private lateinit var dataPrivateProfile: MediaData
     private val clock = FakeSystemClock()
 
     @Before
@@ -115,6 +117,7 @@
                 appUid = APP_UID
             )
         dataGuest = dataMain.copy(userId = USER_GUEST)
+        dataPrivateProfile = dataMain.copy(userId = PRIVATE_PROFILE)
 
         whenever(smartspaceData.targetId).thenReturn(SMARTSPACE_KEY)
         whenever(smartspaceData.isActive).thenReturn(true)
@@ -130,8 +133,19 @@
 
     private fun setUser(id: Int) {
         whenever(lockscreenUserManager.isCurrentProfile(anyInt())).thenReturn(false)
+        whenever(lockscreenUserManager.isProfileAvailable(anyInt())).thenReturn(false)
         whenever(lockscreenUserManager.isCurrentProfile(eq(id))).thenReturn(true)
-        mediaDataFilter.handleUserSwitched(id)
+        whenever(lockscreenUserManager.isProfileAvailable(eq(id))).thenReturn(true)
+        whenever(lockscreenUserManager.isProfileAvailable(eq(PRIVATE_PROFILE))).thenReturn(true)
+        mediaDataFilter.handleUserSwitched()
+    }
+
+    private fun setPrivateProfileUnavailable() {
+        whenever(lockscreenUserManager.isCurrentProfile(anyInt())).thenReturn(false)
+        whenever(lockscreenUserManager.isCurrentProfile(eq(USER_MAIN))).thenReturn(true)
+        whenever(lockscreenUserManager.isCurrentProfile(eq(PRIVATE_PROFILE))).thenReturn(true)
+        whenever(lockscreenUserManager.isProfileAvailable(eq(PRIVATE_PROFILE))).thenReturn(false)
+        mediaDataFilter.handleProfileChanged()
     }
 
     @Test
@@ -206,6 +220,20 @@
     }
 
     @Test
+    fun testOnProfileChanged_profileUnavailable_loadControls() {
+        // GIVEN that we had some media for both profiles
+        mediaDataFilter.onMediaDataLoaded(KEY, null, dataMain)
+        mediaDataFilter.onMediaDataLoaded(KEY_ALT, null, dataPrivateProfile)
+        reset(listener)
+
+        // and we change profile status
+        setPrivateProfileUnavailable()
+
+        // THEN we should add the private profile media
+        verify(listener).onMediaDataRemoved(eq(KEY_ALT))
+    }
+
+    @Test
     fun hasAnyMedia_noMediaSet_returnsFalse() {
         assertThat(mediaDataFilter.hasAnyMedia()).isFalse()
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
index ce999cb..72847a6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
@@ -71,13 +71,13 @@
 import com.android.settingslib.bluetooth.LocalBluetoothManager;
 import com.android.settingslib.media.LocalMediaManager;
 import com.android.settingslib.media.MediaDevice;
-import com.android.systemui.res.R;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.animation.ActivityLaunchAnimator;
 import com.android.systemui.animation.DialogLaunchAnimator;
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.media.nearby.NearbyMediaDevicesManager;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.res.R;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
@@ -584,7 +584,7 @@
     }
 
     @Test
-    public void addDeviceToPlayMedia_triggersFromLocalMediaManager() {
+    public void addDeviceToPlayMedia_callsLocalMediaManager() {
         MediaOutputController testMediaOutputController = new MediaOutputController(mSpyContext,
                 null,
                 mMediaSessionManager, mLocalBluetoothManager, mStarter,
@@ -592,16 +592,15 @@
                 mNearbyMediaDevicesManager, mAudioManager, mPowerExemptionManager,
                 mKeyguardManager, mFlags, mUserTracker);
 
-        LocalMediaManager testLocalMediaManager = spy(testMediaOutputController.mLocalMediaManager);
-        testMediaOutputController.mLocalMediaManager = testLocalMediaManager;
+        LocalMediaManager mockLocalMediaManager = mock(LocalMediaManager.class);
+        testMediaOutputController.mLocalMediaManager = mockLocalMediaManager;
 
         testMediaOutputController.addDeviceToPlayMedia(mMediaDevice2);
-
-        verify(testLocalMediaManager).addDeviceToPlayMedia(mMediaDevice2);
+        verify(mockLocalMediaManager).addDeviceToPlayMedia(mMediaDevice2);
     }
 
     @Test
-    public void removeDeviceFromPlayMedia_triggersFromLocalMediaManager() {
+    public void removeDeviceFromPlayMedia_callsLocalMediaManager() {
         MediaOutputController testMediaOutputController = new MediaOutputController(mSpyContext,
                 null,
                 mMediaSessionManager, mLocalBluetoothManager, mStarter,
@@ -609,12 +608,11 @@
                 mNearbyMediaDevicesManager, mAudioManager, mPowerExemptionManager,
                 mKeyguardManager, mFlags, mUserTracker);
 
-        LocalMediaManager testLocalMediaManager = spy(testMediaOutputController.mLocalMediaManager);
-        testMediaOutputController.mLocalMediaManager = testLocalMediaManager;
+        LocalMediaManager mockLocalMediaManager = mock(LocalMediaManager.class);
+        testMediaOutputController.mLocalMediaManager = mockLocalMediaManager;
 
         testMediaOutputController.removeDeviceFromPlayMedia(mMediaDevice2);
-
-        verify(testLocalMediaManager).removeDeviceFromPlayMedia(mMediaDevice2);
+        verify(mockLocalMediaManager).removeDeviceFromPlayMedia(mMediaDevice2);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/MediaProjectionTaskSwitcherCoreStartableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/MediaProjectionTaskSwitcherCoreStartableTest.kt
index bcbf666..16c92ec 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/MediaProjectionTaskSwitcherCoreStartableTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/MediaProjectionTaskSwitcherCoreStartableTest.kt
@@ -18,38 +18,27 @@
 
 import android.testing.AndroidTestingRunner
 import androidx.test.filters.SmallTest
+import com.android.systemui.Flags.FLAG_PSS_TASK_SWITCHER
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
 import com.android.systemui.mediaprojection.taskswitcher.ui.TaskSwitcherNotificationCoordinator
-import com.android.systemui.util.mockito.whenever
-import org.junit.Before
+import com.android.systemui.util.mockito.mock
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.mockito.Mock
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.verifyZeroInteractions
-import org.mockito.MockitoAnnotations
 
 @RunWith(AndroidTestingRunner::class)
 @SmallTest
 class MediaProjectionTaskSwitcherCoreStartableTest : SysuiTestCase() {
 
-    @Mock private lateinit var flags: FeatureFlags
-    @Mock private lateinit var coordinator: TaskSwitcherNotificationCoordinator
+    private val coordinator = mock<TaskSwitcherNotificationCoordinator>()
 
-    private lateinit var coreStartable: MediaProjectionTaskSwitcherCoreStartable
-
-    @Before
-    fun setUp() {
-        MockitoAnnotations.initMocks(this)
-
-        coreStartable = MediaProjectionTaskSwitcherCoreStartable(coordinator, flags)
-    }
+    private val coreStartable =
+        MediaProjectionTaskSwitcherCoreStartable(notificationCoordinatorLazy = { coordinator })
 
     @Test
     fun start_flagEnabled_startsCoordinator() {
-        whenever(flags.isEnabled(Flags.PARTIAL_SCREEN_SHARING_TASK_SWITCHER)).thenReturn(true)
+        mSetFlagsRule.enableFlags(FLAG_PSS_TASK_SWITCHER)
 
         coreStartable.start()
 
@@ -58,7 +47,7 @@
 
     @Test
     fun start_flagDisabled_doesNotStartCoordinator() {
-        whenever(flags.isEnabled(Flags.PARTIAL_SCREEN_SHARING_TASK_SWITCHER)).thenReturn(false)
+        mSetFlagsRule.disableFlags(FLAG_PSS_TASK_SWITCHER)
 
         coreStartable.start()
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java b/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java
index 6248bb1..1a303b0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java
@@ -55,6 +55,7 @@
 import com.android.systemui.broadcast.BroadcastSender;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.settings.UserTracker;
+import com.android.systemui.statusbar.phone.SystemUIDialog;
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.util.NotificationChannels;
 import com.android.systemui.util.settings.FakeGlobalSettings;
@@ -77,7 +78,6 @@
     public static final String FORMATTED_45M = "0h 45m";
     public static final String FORMATTED_HOUR = "1h 0m";
     private final NotificationManager mMockNotificationManager = mock(NotificationManager.class);
-    private final GlobalSettings mGlobalSettings = new FakeGlobalSettings();
     private PowerNotificationWarnings mPowerNotificationWarnings;
 
     @Mock
@@ -90,6 +90,10 @@
     private UserTracker mUserTracker;
     @Mock
     private View mView;
+    @Mock
+    private SystemUIDialog.Factory mSystemUIDialogFactory;
+    @Mock
+    private SystemUIDialog mSystemUIDialog;
 
     private BroadcastReceiver mReceiver;
 
@@ -113,9 +117,16 @@
         when(mUserTracker.getUserId()).thenReturn(ActivityManager.getCurrentUser());
         when(mUserTracker.getUserHandle()).thenReturn(
                 UserHandle.of(ActivityManager.getCurrentUser()));
-        mPowerNotificationWarnings = new PowerNotificationWarnings(wrapper, starter,
-                broadcastSender, () -> mBatteryController, mDialogLaunchAnimator, mUiEventLogger,
-                mGlobalSettings, mUserTracker);
+        when(mSystemUIDialogFactory.create()).thenReturn(mSystemUIDialog);
+        mPowerNotificationWarnings = new PowerNotificationWarnings(
+                wrapper,
+                starter,
+                broadcastSender,
+                () -> mBatteryController,
+                mDialogLaunchAnimator,
+                mUiEventLogger,
+                mUserTracker,
+                mSystemUIDialogFactory);
         BatteryStateSnapshot snapshot = new BatteryStateSnapshot(100, false, false, 1,
                 BatteryManager.BATTERY_HEALTH_GOOD, 5, 15);
         mPowerNotificationWarnings.updateSnapshot(snapshot);
@@ -251,7 +262,7 @@
 
         verify(mDialogLaunchAnimator, never()).showFromView(any(), any());
 
-        assertThat(mPowerNotificationWarnings.getSaverConfirmationDialog().isShowing()).isTrue();
+        verify(mPowerNotificationWarnings.getSaverConfirmationDialog()).show();
         mPowerNotificationWarnings.getSaverConfirmationDialog().dismiss();
     }
 
@@ -266,7 +277,7 @@
 
         verify(mDialogLaunchAnimator, never()).showFromView(any(), any());
 
-        assertThat(mPowerNotificationWarnings.getSaverConfirmationDialog().isShowing()).isTrue();
+        verify(mPowerNotificationWarnings.getSaverConfirmationDialog()).show();
         mPowerNotificationWarnings.getSaverConfirmationDialog().dismiss();
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/FgsManagerControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/FgsManagerControllerTest.java
index f5a3bec..698868d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/FgsManagerControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/FgsManagerControllerTest.java
@@ -27,6 +27,7 @@
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 import android.app.IActivityManager;
 import android.app.IForegroundServiceObserver;
@@ -53,6 +54,7 @@
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.settings.UserTracker;
+import com.android.systemui.statusbar.phone.SystemUIDialog;
 import com.android.systemui.util.DeviceConfigProxyFake;
 import com.android.systemui.util.concurrency.FakeExecutor;
 import com.android.systemui.util.time.FakeSystemClock;
@@ -95,6 +97,10 @@
     BroadcastDispatcher mBroadcastDispatcher;
     @Mock
     DumpManager mDumpManager;
+    @Mock
+    SystemUIDialog.Factory mSystemUIDialogFactory;
+    @Mock
+    SystemUIDialog mSystemUIDialog;
 
     private FgsManagerController mFmc;
 
@@ -114,6 +120,7 @@
         mSystemClock = new FakeSystemClock();
         mMainExecutor = new FakeExecutor(mSystemClock);
         mBackgroundExecutor = new FakeExecutor(mSystemClock);
+        when(mSystemUIDialogFactory.create()).thenReturn(mSystemUIDialog);
 
         mUserProfiles = new ArrayList<>();
         Mockito.doReturn(mUserProfiles).when(mUserTracker).getUserProfiles();
@@ -325,7 +332,8 @@
                 mDeviceConfigProxyFake,
                 mDialogLaunchAnimator,
                 mBroadcastDispatcher,
-                mDumpManager
+                mDumpManager,
+                mSystemUIDialogFactory
         );
         fmc.init();
         Assert.assertTrue(fmc.getIncludesUserVisibleJobs());
@@ -351,7 +359,8 @@
                 mDeviceConfigProxyFake,
                 mDialogLaunchAnimator,
                 mBroadcastDispatcher,
-                mDumpManager
+                mDumpManager,
+                mSystemUIDialogFactory
         );
         fmc.init();
         Assert.assertFalse(fmc.getIncludesUserVisibleJobs());
@@ -457,7 +466,8 @@
                 mDeviceConfigProxyFake,
                 mDialogLaunchAnimator,
                 mBroadcastDispatcher,
-                mDumpManager
+                mDumpManager,
+                mSystemUIDialogFactory
         );
         result.init();
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
index 018fa9e..a92111e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
@@ -394,6 +394,7 @@
     public void setTiles_differentTiles_extraTileRemoved() {
         when(mQSHost.getTiles()).thenReturn(List.of(mQSTile, mOtherTile));
         mController.setTiles();
+        assertEquals(2, mController.mRecords.size());
 
         clearInvocations(mQSPanel);
 
@@ -402,6 +403,7 @@
 
         verify(mQSPanel, times(1)).removeTile(any());
         verify(mQSPanel, never()).addTile(any());
+        assertEquals(1, mController.mRecords.size());
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DataSaverTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DataSaverTileTest.kt
index 51e95be..c109a1e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DataSaverTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DataSaverTileTest.kt
@@ -32,7 +32,9 @@
 import com.android.systemui.qs.QsEventLogger
 import com.android.systemui.qs.logging.QSLogger
 import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.android.systemui.statusbar.phone.SystemUIDialog
 import com.android.systemui.statusbar.policy.DataSaverController
+import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth.assertThat
 import org.junit.After
 import org.junit.Before
@@ -49,8 +51,6 @@
 
     @Mock private lateinit var mHost: QSHost
     @Mock private lateinit var mMetricsLogger: MetricsLogger
-    @Mock private lateinit var mStatusBarStateController: StatusBarStateController
-    @Mock private lateinit var mActivityStarter: ActivityStarter
     @Mock private lateinit var mQsLogger: QSLogger
     private val falsingManager = FalsingManagerFake()
     @Mock private lateinit var statusBarStateController: StatusBarStateController
@@ -58,6 +58,8 @@
     @Mock private lateinit var dataSaverController: DataSaverController
     @Mock private lateinit var dialogLaunchAnimator: DialogLaunchAnimator
     @Mock private lateinit var uiEventLogger: QsEventLogger
+    @Mock private lateinit var systemUIDialogFactory: SystemUIDialog.Factory
+    @Mock private lateinit var systemUIDialog: SystemUIDialog
 
     private lateinit var testableLooper: TestableLooper
     private lateinit var tile: DataSaverTile
@@ -67,7 +69,8 @@
         MockitoAnnotations.initMocks(this)
         testableLooper = TestableLooper.get(this)
 
-        Mockito.`when`(mHost.context).thenReturn(mContext)
+        whenever(mHost.context).thenReturn(mContext)
+        whenever(systemUIDialogFactory.create()).thenReturn(systemUIDialog)
 
         tile =
             DataSaverTile(
@@ -81,7 +84,8 @@
                 activityStarter,
                 mQsLogger,
                 dataSaverController,
-                dialogLaunchAnimator
+                dialogLaunchAnimator,
+                systemUIDialogFactory
             )
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImplTest.kt
index 02d40da..ea2b22c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImplTest.kt
@@ -115,7 +115,7 @@
                 .isEqualTo(
                     "test_spec:\n" +
                         "    QSTileState(" +
-                        "icon=() -> com.android.systemui.common.shared.model.Icon, " +
+                        "icon=() -> com.android.systemui.common.shared.model.Icon?, " +
                         "label=test_data, " +
                         "activationState=INACTIVE, " +
                         "secondaryLabel=null, " +
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/user/UserSwitchDialogControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/user/UserSwitchDialogControllerTest.kt
index 0a34810..945490f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/user/UserSwitchDialogControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/user/UserSwitchDialogControllerTest.kt
@@ -36,6 +36,7 @@
 import com.android.systemui.util.mockito.capture
 import com.android.systemui.util.mockito.eq
 import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -56,6 +57,8 @@
 class UserSwitchDialogControllerTest : SysuiTestCase() {
 
     @Mock
+    private lateinit var dialogFactory: SystemUIDialog.Factory
+    @Mock
     private lateinit var dialog: SystemUIDialog
     @Mock
     private lateinit var falsingManager: FalsingManager
@@ -80,7 +83,8 @@
     fun setUp() {
         MockitoAnnotations.initMocks(this)
 
-        `when`(dialog.context).thenReturn(mContext)
+        whenever(dialog.context).thenReturn(mContext)
+        whenever(dialogFactory.create()).thenReturn(dialog)
 
         controller = UserSwitchDialogController(
             { userDetailViewAdapter },
@@ -88,7 +92,7 @@
             falsingManager,
             dialogLaunchAnimator,
             uiEventLogger,
-            { dialog }
+            dialogFactory
         )
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayDialogControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayDialogControllerTest.java
index c108a80..dc211303 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayDialogControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayDialogControllerTest.java
@@ -18,25 +18,42 @@
 
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertNotSame;
-import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 import android.content.res.Configuration;
+import android.content.res.Resources;
 import android.hardware.devicestate.DeviceStateManager;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
+import android.view.LayoutInflater;
+import android.view.View;
 import android.widget.TextView;
 
 import androidx.test.filters.SmallTest;
 
-import com.android.systemui.res.R;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.res.R;
+import com.android.systemui.flags.FakeFeatureFlags;
+import com.android.systemui.flags.Flags;
+import com.android.systemui.model.SysUiState;
+import com.android.systemui.res.R;
 import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.phone.SystemUIDialog;
 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.ArgumentCaptor;
 import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
@@ -45,24 +62,48 @@
 
     @Mock
     private CommandQueue mCommandQueue;
+    @Mock
+    private SystemUIDialog.Factory mSystemUIDialogFactory;
+    @Mock
+    private SystemUIDialog mSystemUIDialog;
+    private final FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags();
+    @Mock
+    private SysUiState mSysUiState;
+    @Mock
+    private Resources mResources;
 
-    private FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
+    LayoutInflater mLayoutInflater = LayoutInflater.from(mContext);
 
+    private final FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
 
     private static final int CLOSED_BASE_STATE = 0;
     private static final int OPEN_BASE_STATE = 1;
 
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+
+        when(mSysUiState.setFlag(anyInt(), anyBoolean())).thenReturn(mSysUiState);
+        when(mSystemUIDialogFactory.create()).thenReturn(mSystemUIDialog);
+        when(mSystemUIDialog.getContext()).thenReturn(mContext);
+    }
     @Test
     public void testClosedDialogIsShown() {
-        RearDisplayDialogController controller = new RearDisplayDialogController(mContext,
-                mCommandQueue, mFakeExecutor);
+        RearDisplayDialogController controller = new RearDisplayDialogController(
+                mCommandQueue,
+                mFakeExecutor,
+                mResources,
+                mLayoutInflater,
+                mSystemUIDialogFactory);
         controller.setDeviceStateManagerCallback(new TestDeviceStateManagerCallback());
         controller.setFoldedStates(new int[]{0});
         controller.setAnimationRepeatCount(0);
 
         controller.showRearDisplayDialog(CLOSED_BASE_STATE);
-        assertTrue(controller.mRearDisplayEducationDialog.isShowing());
-        TextView deviceClosedTitleTextView = controller.mRearDisplayEducationDialog.findViewById(
+        verify(mSystemUIDialog).show();
+
+        View container = getDialogViewContainer();
+        TextView deviceClosedTitleTextView = container.findViewById(
                 R.id.rear_display_title_text_view);
         assertEquals(deviceClosedTitleTextView.getText().toString(),
                 getContext().getResources().getString(
@@ -71,20 +112,28 @@
 
     @Test
     public void testClosedDialogIsRefreshedOnConfigurationChange() {
-        RearDisplayDialogController controller = new RearDisplayDialogController(mContext,
-                mCommandQueue, mFakeExecutor);
+        RearDisplayDialogController controller = new RearDisplayDialogController(
+                mCommandQueue,
+                mFakeExecutor,
+                mResources,
+                mLayoutInflater,
+                mSystemUIDialogFactory);
         controller.setDeviceStateManagerCallback(new TestDeviceStateManagerCallback());
         controller.setFoldedStates(new int[]{0});
         controller.setAnimationRepeatCount(0);
 
         controller.showRearDisplayDialog(CLOSED_BASE_STATE);
-        assertTrue(controller.mRearDisplayEducationDialog.isShowing());
-        TextView deviceClosedTitleTextView = controller.mRearDisplayEducationDialog.findViewById(
+        verify(mSystemUIDialog).show();
+        View container = getDialogViewContainer();
+        TextView deviceClosedTitleTextView = container.findViewById(
                 R.id.rear_display_title_text_view);
 
-        controller.onConfigurationChanged(new Configuration());
-        assertTrue(controller.mRearDisplayEducationDialog.isShowing());
-        TextView deviceClosedTitleTextView2 = controller.mRearDisplayEducationDialog.findViewById(
+        reset(mSystemUIDialog);
+        when(mSystemUIDialog.isShowing()).thenReturn(true);
+        when(mSystemUIDialog.getContext()).thenReturn(mContext);
+
+        controller.onConfigChanged(new Configuration());
+        TextView deviceClosedTitleTextView2 = container.findViewById(
                 R.id.rear_display_title_text_view);
 
         assertNotSame(deviceClosedTitleTextView, deviceClosedTitleTextView2);
@@ -92,22 +141,33 @@
 
     @Test
     public void testOpenDialogIsShown() {
-        RearDisplayDialogController controller = new RearDisplayDialogController(mContext,
-                mCommandQueue, mFakeExecutor);
+        RearDisplayDialogController controller = new RearDisplayDialogController(
+                mCommandQueue,
+                mFakeExecutor,
+                mResources,
+                mLayoutInflater,
+                mSystemUIDialogFactory);
         controller.setDeviceStateManagerCallback(new TestDeviceStateManagerCallback());
         controller.setFoldedStates(new int[]{0});
         controller.setAnimationRepeatCount(0);
 
         controller.showRearDisplayDialog(OPEN_BASE_STATE);
 
-        assertTrue(controller.mRearDisplayEducationDialog.isShowing());
-        TextView deviceClosedTitleTextView = controller.mRearDisplayEducationDialog.findViewById(
+        verify(mSystemUIDialog).show();
+        View container = getDialogViewContainer();
+        TextView deviceClosedTitleTextView = container.findViewById(
                 R.id.rear_display_title_text_view);
         assertEquals(deviceClosedTitleTextView.getText().toString(),
                 getContext().getResources().getString(
                         R.string.rear_display_unfolded_bottom_sheet_title));
     }
 
+    private View getDialogViewContainer() {
+        ArgumentCaptor<View> viewCaptor = ArgumentCaptor.forClass(View.class);
+        verify(mSystemUIDialog).setView(viewCaptor.capture());
+
+        return viewCaptor.getValue();
+    }
     /**
      * Empty device state manager callbacks, so we can verify that the correct
      * dialogs are being created regardless of device state of the test device.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt
index 7ce51ae..86ab01c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt
@@ -105,7 +105,6 @@
             spy(
                 SystemUIDialog.Factory(
                     context,
-                    flags,
                     systemUIDialogManager,
                     sysuiState,
                     broadcastDispatcher,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java
index cb90cc5..0ba99f2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java
@@ -44,7 +44,6 @@
 import com.android.systemui.animation.DialogLaunchAnimator;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.flags.FakeFeatureFlags;
-import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.flags.Flags;
 import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger;
 import com.android.systemui.mediaprojection.SessionCreationSource;
@@ -113,7 +112,6 @@
 
         mDialogFactory = new TestSystemUIDialogFactory(
                 mContext,
-                mFeatureFlags,
                 Dependency.get(SystemUIDialogManager.class),
                 Dependency.get(SysUiState.class),
                 Dependency.get(BroadcastDispatcher.class),
@@ -313,14 +311,12 @@
 
         TestSystemUIDialogFactory(
                 Context context,
-                FeatureFlags featureFlags,
                 SystemUIDialogManager systemUIDialogManager,
                 SysUiState sysUiState,
                 BroadcastDispatcher broadcastDispatcher,
                 DialogLaunchAnimator dialogLaunchAnimator) {
             super(
                     context,
-                    featureFlags,
                     systemUIDialogManager,
                     sysUiState,
                     broadcastDispatcher,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogDelegateTest.kt
index 8f696e7..2399536 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogDelegateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogDelegateTest.kt
@@ -74,7 +74,6 @@
         val systemUIDialogFactory =
             SystemUIDialog.Factory(
                 context,
-                Dependency.get(FeatureFlags::class.java),
                 Dependency.get(SystemUIDialogManager::class.java),
                 Dependency.get(SysUiState::class.java),
                 Dependency.get(BroadcastDispatcher::class.java),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt
index c32d259..032ec74 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt
@@ -177,7 +177,7 @@
         verify(context)
                 .registerReceiverForAllUsers(eq(tracker), capture(captor), isNull(), eq(handler))
         with(captor.value) {
-            assertThat(countActions()).isEqualTo(7)
+            assertThat(countActions()).isEqualTo(11)
             assertThat(hasAction(Intent.ACTION_LOCALE_CHANGED)).isTrue()
             assertThat(hasAction(Intent.ACTION_USER_INFO_CHANGED)).isTrue()
             assertThat(hasAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE)).isTrue()
@@ -185,6 +185,10 @@
             assertThat(hasAction(Intent.ACTION_MANAGED_PROFILE_ADDED)).isTrue()
             assertThat(hasAction(Intent.ACTION_MANAGED_PROFILE_REMOVED)).isTrue()
             assertThat(hasAction(Intent.ACTION_MANAGED_PROFILE_UNLOCKED)).isTrue()
+            assertThat(hasAction(Intent.ACTION_PROFILE_ADDED)).isTrue()
+            assertThat(hasAction(Intent.ACTION_PROFILE_REMOVED)).isTrue()
+            assertThat(hasAction(Intent.ACTION_PROFILE_AVAILABLE)).isTrue()
+            assertThat(hasAction(Intent.ACTION_PROFILE_UNAVAILABLE)).isTrue()
         }
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt
index 88c728f..b94e483 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt
@@ -36,8 +36,12 @@
 import com.android.systemui.util.mockito.whenever
 import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
-import dagger.Lazy
+import kotlin.time.Duration.Companion.milliseconds
+import kotlinx.coroutines.FlowPreview
 import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.takeWhile
+import kotlinx.coroutines.flow.timeout
+import kotlinx.coroutines.test.runTest
 import org.junit.After
 import org.junit.Before
 import org.junit.Rule
@@ -59,7 +63,6 @@
     @Mock private lateinit var brightnessControllerFactory: BrightnessController.Factory
     @Mock private lateinit var brightnessController: BrightnessController
     @Mock private lateinit var accessibilityMgr: AccessibilityManagerWrapper
-    @Mock private lateinit var shadeInteractorLazy: Lazy<ShadeInteractor>
     @Mock private lateinit var shadeInteractor: ShadeInteractor
 
     private val clock = FakeSystemClock()
@@ -89,7 +92,6 @@
             .thenReturn(brightnessSliderController)
         `when`(brightnessSliderController.rootView).thenReturn(View(context))
         `when`(brightnessControllerFactory.create(any())).thenReturn(brightnessController)
-        whenever(shadeInteractorLazy.get()).thenReturn(shadeInteractor)
         whenever(shadeInteractor.isQsExpanded).thenReturn(MutableStateFlow(false))
     }
 
@@ -180,6 +182,22 @@
         assertThat(activityRule.activity.isFinishing()).isFalse()
     }
 
+    @OptIn(FlowPreview::class)
+    @Test
+    fun testFinishOnQSExpanded() = runTest {
+        val isQSExpanded = MutableStateFlow(false)
+        `when`(shadeInteractor.isQsExpanded).thenReturn(isQSExpanded)
+        activityRule.launchActivity(Intent(Intent.ACTION_SHOW_BRIGHTNESS_DIALOG))
+
+        assertThat(activityRule.activity.isFinishing()).isFalse()
+
+        isQSExpanded.value = true
+        // Observe the activity's state until is it finishing or the timeout is reached, whatever
+        // comes first. This fixes the flakiness seen when using advanceUntilIdle().
+        activityRule.activity.finishing.timeout(100.milliseconds).takeWhile { !it }.collect {}
+        assertThat(activityRule.activity.isFinishing()).isTrue()
+    }
+
     class TestDialog(
         brightnessSliderControllerFactory: BrightnessSliderController.Factory,
         brightnessControllerFactory: BrightnessController.Factory,
@@ -194,14 +212,14 @@
             accessibilityMgr,
             shadeInteractor
         ) {
-        private var finishing = false
+        var finishing = MutableStateFlow(false)
 
         override fun isFinishing(): Boolean {
-            return finishing
+            return finishing.value
         }
 
         override fun requestFinish() {
-            finishing = true
+            finishing.value = true
         }
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
index 5569ca9..b7a9ea7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.shade
 
+import android.os.PowerManager
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import android.testing.ViewUtils
@@ -43,6 +44,8 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mock
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
 
 @RunWith(AndroidTestingRunner::class)
@@ -52,6 +55,7 @@
     @Mock private lateinit var communalViewModel: CommunalViewModel
     @Mock private lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor
     @Mock private lateinit var shadeInteractor: ShadeInteractor
+    @Mock private lateinit var powerManager: PowerManager
 
     private lateinit var containerView: View
     private lateinit var testableLooper: TestableLooper
@@ -76,7 +80,8 @@
                 communalInteractor,
                 communalViewModel,
                 keyguardTransitionInteractor,
-                shadeInteractor
+                shadeInteractor,
+                powerManager
             )
         testableLooper = TestableLooper.get(this)
 
@@ -90,14 +95,14 @@
     }
 
     @Test
-    fun isEnabled_interactorEnabled_returnsTrue() {
+    fun isEnabled_interactorEnabled_interceptsTouches() {
         communalRepository.setIsCommunalEnabled(true)
 
         assertThat(underTest.isEnabled()).isTrue()
     }
 
     @Test
-    fun isEnabled_interactorDisabled_returnsFalse() {
+    fun isEnabled_interactorDisabled_doesNotIntercept() {
         communalRepository.setIsCommunalEnabled(false)
 
         assertThat(underTest.isEnabled()).isFalse()
@@ -120,7 +125,7 @@
     }
 
     @Test
-    fun onTouchEvent_touchInsideGestureRegion_returnsTrue() {
+    fun onTouchEvent_touchInsideGestureRegion_interceptsTouches() {
         // Communal is open.
         communalRepository.setDesiredScene(CommunalSceneKey.Communal)
 
@@ -131,7 +136,7 @@
     }
 
     @Test
-    fun onTouchEvent_subsequentTouchesAfterGestureStart_returnsTrue() {
+    fun onTouchEvent_subsequentTouchesAfterGestureStart_interceptsTouches() {
         // Communal is open.
         communalRepository.setDesiredScene(CommunalSceneKey.Communal)
 
@@ -146,7 +151,7 @@
     }
 
     @Test
-    fun onTouchEvent_communalOpen_returnsTrue() {
+    fun onTouchEvent_communalOpen_interceptsTouches() {
         // Communal is open.
         communalRepository.setDesiredScene(CommunalSceneKey.Communal)
 
@@ -155,10 +160,12 @@
 
         // Touch events are intercepted.
         assertThat(underTest.onTouchEvent(DOWN_EVENT)).isTrue()
+        // User activity sent to PowerManager.
+        verify(powerManager).userActivity(any(), any(), any())
     }
 
     @Test
-    fun onTouchEvent_communalAndBouncerShowing_returnsFalse() {
+    fun onTouchEvent_communalAndBouncerShowing_doesNotIntercept() {
         // Communal is open.
         communalRepository.setDesiredScene(CommunalSceneKey.Communal)
 
@@ -170,10 +177,12 @@
 
         // Touch events are not intercepted.
         assertThat(underTest.onTouchEvent(DOWN_EVENT)).isFalse()
+        // User activity is not sent to PowerManager.
+        verify(powerManager, times(0)).userActivity(any(), any(), any())
     }
 
     @Test
-    fun onTouchEvent_communalAndShadeShowing_returnsFalse() {
+    fun onTouchEvent_communalAndShadeShowing_doesNotIntercept() {
         // Communal is open.
         communalRepository.setDesiredScene(CommunalSceneKey.Communal)
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
index e572dcc..b239482 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
@@ -19,12 +19,10 @@
 import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
 
 import static com.android.keyguard.KeyguardClockSwitch.LARGE;
-import static com.android.systemui.dump.LogBufferHelperKt.logcatLogBuffer;
+import static com.android.systemui.log.LogBufferHelperKt.logcatLogBuffer;
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static kotlinx.coroutines.flow.FlowKt.emptyFlow;
-
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyFloat;
@@ -39,6 +37,8 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import static kotlinx.coroutines.flow.FlowKt.emptyFlow;
+
 import android.annotation.IdRes;
 import android.content.ContentResolver;
 import android.content.res.Configuration;
@@ -86,6 +86,7 @@
 import com.android.systemui.classifier.FalsingManagerFake;
 import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository;
 import com.android.systemui.common.ui.view.LongPressHandlingView;
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor;
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor;
 import com.android.systemui.doze.DozeLog;
 import com.android.systemui.dump.DumpManager;
@@ -98,7 +99,6 @@
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository;
 import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor;
 import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor;
-import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor;
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory;
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
@@ -152,7 +152,9 @@
 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
 import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
 import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinatorLogger;
+import com.android.systemui.statusbar.notification.data.repository.NotificationsKeyguardViewStateRepository;
 import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor;
+import com.android.systemui.statusbar.notification.domain.interactor.NotificationsKeyguardInteractor;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
 import com.android.systemui.statusbar.notification.stack.AmbientState;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
@@ -337,13 +339,14 @@
     protected ArgumentCaptor<NotificationStackScrollLayout.OnEmptySpaceClickListener>
             mEmptySpaceClickListenerCaptor;
     @Mock protected ActivityStarter mActivityStarter;
-    @Mock protected KeyguardFaceAuthInteractor mKeyguardFaceAuthInteractor;
+    @Mock protected DeviceEntryFaceAuthInteractor mDeviceEntryFaceAuthInteractor;
     @Mock private JavaAdapter mJavaAdapter;
     @Mock private CastController mCastController;
     @Mock private SharedNotificationContainerInteractor mSharedNotificationContainerInteractor;
-    @Mock private ActiveNotificationsInteractor mActiveNotificationsInteractor;
+    @Mock protected ActiveNotificationsInteractor mActiveNotificationsInteractor;
     @Mock private KeyguardClockPositionAlgorithm mKeyguardClockPositionAlgorithm;
     @Mock private NaturalScrollingSettingObserver mNaturalScrollingSettingObserver;
+    @Mock private LargeScreenHeaderHelper mLargeScreenHeaderHelper;
 
     protected final int mMaxUdfpsBurnInOffsetY = 5;
     protected FakeFeatureFlagsClassic mFeatureFlags = new FakeFeatureFlagsClassic();
@@ -382,7 +385,6 @@
     @Before
     public void setup() {
         MockitoAnnotations.initMocks(this);
-        mFeatureFlags.set(Flags.WM_SHADE_ANIMATE_BACK_GESTURE, false);
         mFeatureFlags.set(Flags.TRACKPAD_GESTURE_FEATURES, false);
         mFeatureFlags.set(Flags.LOCKSCREEN_ENABLE_LANDSCAPE, false);
         mFeatureFlags.set(Flags.QS_USER_DETAIL_SHORTCUT, false);
@@ -425,7 +427,8 @@
                                 mContext,
                                 new ResourcesSplitShadeStateController(),
                                 mKeyguardInteractor,
-                                deviceEntryUdfpsInteractor
+                                deviceEntryUdfpsInteractor,
+                                () -> mLargeScreenHeaderHelper
                         ),
                         mShadeRepository
                 )
@@ -587,6 +590,10 @@
         when(mPrimaryBouncerToGoneTransitionViewModel.getLockscreenAlpha())
                 .thenReturn(emptyFlow());
 
+        NotificationsKeyguardViewStateRepository notifsKeyguardViewStateRepository =
+                new NotificationsKeyguardViewStateRepository();
+        NotificationsKeyguardInteractor notifsKeyguardInteractor =
+                new NotificationsKeyguardInteractor(notifsKeyguardViewStateRepository);
         NotificationWakeUpCoordinator coordinator =
                 new NotificationWakeUpCoordinator(
                         mDumpManager,
@@ -597,7 +604,8 @@
                         mKeyguardBypassController,
                         mDozeParameters,
                         mScreenOffAnimationController,
-                        new NotificationWakeUpCoordinatorLogger(logcatLogBuffer()));
+                        new NotificationWakeUpCoordinatorLogger(logcatLogBuffer()),
+                        notifsKeyguardInteractor);
         mConfigurationController = new ConfigurationControllerImpl(mContext);
         PulseExpansionHandler expansionHandler = new PulseExpansionHandler(
                 mContext,
@@ -730,7 +738,7 @@
                 mActiveNotificationsInteractor,
                 mShadeAnimationInteractor,
                 mKeyguardViewConfigurator,
-                mKeyguardFaceAuthInteractor,
+                mDeviceEntryFaceAuthInteractor,
                 new ResourcesSplitShadeStateController(),
                 mPowerInteractor,
                 mKeyguardClockPositionAlgorithm,
@@ -800,13 +808,14 @@
                 mInteractionJankMonitor,
                 mShadeLog,
                 mDumpManager,
-                mKeyguardFaceAuthInteractor,
+                mDeviceEntryFaceAuthInteractor,
                 mShadeRepository,
                 mShadeInteractor,
                 mActiveNotificationsInteractor,
                 mJavaAdapter,
                 mCastController,
-                new ResourcesSplitShadeStateController()
+                new ResourcesSplitShadeStateController(),
+                () -> mLargeScreenHeaderHelper
         );
     }
 
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 28fe8e4..2e8d46a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
@@ -456,11 +456,13 @@
         enableSplitShade(/* enabled= */ true);
 
         when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2);
+        when(mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue()).thenReturn(true);
         mNotificationPanelViewController.updateResources();
         assertThat(getConstraintSetLayout(R.id.keyguard_status_view).endToEnd)
                 .isEqualTo(R.id.qs_edge_guideline);
 
         when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(0);
+        when(mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue()).thenReturn(false);
         mNotificationPanelViewController.updateResources();
         assertThat(getConstraintSetLayout(R.id.keyguard_status_view).endToEnd)
                 .isEqualTo(ConstraintSet.PARENT_ID);
@@ -469,6 +471,7 @@
     @Test
     public void keyguardStatusView_splitShade_dozing_alwaysDozingOn_isCentered() {
         when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2);
+        when(mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue()).thenReturn(true);
         mStatusBarStateController.setState(KEYGUARD);
         enableSplitShade(/* enabled= */ true);
 
@@ -480,6 +483,7 @@
     @Test
     public void keyguardStatusView_splitShade_dozing_alwaysDozingOff_isNotCentered() {
         when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2);
+        when(mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue()).thenReturn(true);
         mStatusBarStateController.setState(KEYGUARD);
         enableSplitShade(/* enabled= */ true);
 
@@ -491,6 +495,7 @@
     @Test
     public void keyguardStatusView_splitShade_notDozing_alwaysDozingOn_isNotCentered() {
         when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2);
+        when(mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue()).thenReturn(true);
         mStatusBarStateController.setState(KEYGUARD);
         enableSplitShade(/* enabled= */ true);
 
@@ -502,6 +507,7 @@
     @Test
     public void keyguardStatusView_splitShade_pulsing_isNotCentered() {
         when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2);
+        when(mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue()).thenReturn(true);
         when(mNotificationListContainer.hasPulsingNotifications()).thenReturn(true);
         mStatusBarStateController.setState(KEYGUARD);
         enableSplitShade(/* enabled= */ true);
@@ -514,6 +520,7 @@
     @Test
     public void keyguardStatusView_splitShade_notPulsing_isNotCentered() {
         when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2);
+        when(mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue()).thenReturn(true);
         when(mNotificationListContainer.hasPulsingNotifications()).thenReturn(false);
         mStatusBarStateController.setState(KEYGUARD);
         enableSplitShade(/* enabled= */ true);
@@ -529,6 +536,7 @@
         // The conditions below would make the clock NOT be centered on split shade.
         // On single shade it should always be centered though.
         when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2);
+        when(mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue()).thenReturn(true);
         when(mNotificationListContainer.hasPulsingNotifications()).thenReturn(false);
         mStatusBarStateController.setState(KEYGUARD);
         setDozing(/* dozing= */ false, /* dozingAlwaysOn= */ false);
@@ -539,6 +547,7 @@
     @Test
     public void keyguardStatusView_willPlayDelayedDoze_isCentered_thenNot() {
         when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2);
+        when(mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue()).thenReturn(true);
         mStatusBarStateController.setState(KEYGUARD);
         enableSplitShade(/* enabled= */ true);
 
@@ -553,6 +562,7 @@
     @Test
     public void keyguardStatusView_willPlayDelayedDoze_notifiesKeyguardMediaController() {
         when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2);
+        when(mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue()).thenReturn(true);
         mStatusBarStateController.setState(KEYGUARD);
         enableSplitShade(/* enabled= */ true);
 
@@ -564,6 +574,7 @@
     @Test
     public void keyguardStatusView_willPlayDelayedDoze_isCentered_thenStillCenteredIfNoNotifs() {
         when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(0);
+        when(mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue()).thenReturn(false);
         mStatusBarStateController.setState(KEYGUARD);
         enableSplitShade(/* enabled= */ true);
 
@@ -700,10 +711,12 @@
         mStatusBarStateController.setState(KEYGUARD);
 
         when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(0);
+        when(mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue()).thenReturn(false);
         triggerPositionClockAndNotifications();
         verify(mKeyguardStatusViewController).displayClock(LARGE, /* animate */ true);
 
         when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(1);
+        when(mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue()).thenReturn(true);
         triggerPositionClockAndNotifications();
         verify(mKeyguardStatusViewController).displayClock(SMALL, /* animate */ true);
     }
@@ -715,10 +728,12 @@
         clearInvocations(mKeyguardStatusViewController);
 
         when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(0);
+        when(mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue()).thenReturn(false);
         triggerPositionClockAndNotifications();
         verify(mKeyguardStatusViewController).displayClock(LARGE, /* animate */ true);
 
         when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(1);
+        when(mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue()).thenReturn(true);
         triggerPositionClockAndNotifications();
         verify(mKeyguardStatusViewController, times(2))
                 .displayClock(LARGE, /* animate */ true);
@@ -730,6 +745,7 @@
     public void testHasNotifications_switchesToLargeClockWhenEnteringSplitShade() {
         mStatusBarStateController.setState(KEYGUARD);
         when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(1);
+        when(mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue()).thenReturn(true);
 
         enableSplitShade(/* enabled= */ true);
 
@@ -740,6 +756,7 @@
     public void testNoNotifications_switchesToLargeClockWhenEnteringSplitShade() {
         mStatusBarStateController.setState(KEYGUARD);
         when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(0);
+        when(mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue()).thenReturn(false);
 
         enableSplitShade(/* enabled= */ true);
 
@@ -752,6 +769,7 @@
         enableSplitShade(/* enabled= */ true);
         clearInvocations(mKeyguardStatusViewController);
         when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(1);
+        when(mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue()).thenReturn(true);
 
         enableSplitShade(/* enabled= */ false);
 
@@ -764,6 +782,7 @@
         enableSplitShade(/* enabled= */ true);
         clearInvocations(mKeyguardStatusViewController);
         when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(0);
+        when(mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue()).thenReturn(false);
 
         enableSplitShade(/* enabled= */ false);
 
@@ -777,6 +796,7 @@
         enableSplitShade(/* enabled= */ true);
         when(mMediaDataManager.hasActiveMediaOrRecommendation()).thenReturn(true);
         when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2);
+        when(mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue()).thenReturn(true);
         clearInvocations(mKeyguardStatusViewController);
 
         mNotificationPanelViewController.setDozing(/* dozing= */ true, /* animate= */ false);
@@ -791,6 +811,7 @@
         enableSplitShade(/* enabled= */ true);
         when(mMediaDataManager.hasActiveMediaOrRecommendation()).thenReturn(true);
         when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2);
+        when(mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue()).thenReturn(true);
         clearInvocations(mKeyguardStatusViewController);
 
         mNotificationPanelViewController.setDozing(/* dozing= */ true, /* animate= */ false);
@@ -847,6 +868,7 @@
         clearInvocations(mKeyguardStatusViewController);
         when(mMediaDataManager.hasActiveMedia()).thenReturn(true);
         when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2);
+        when(mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue()).thenReturn(true);
 
         mNotificationPanelViewController.setDozing(true, false);
 
@@ -863,6 +885,7 @@
         when(mResources.getBoolean(R.bool.force_small_clock_on_lockscreen)).thenReturn(true);
         when(mMediaDataManager.hasActiveMedia()).thenReturn(false);
         when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(0);
+        when(mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue()).thenReturn(false);
         clearInvocations(mKeyguardStatusViewController);
 
         enableSplitShade(/* enabled= */ true);
@@ -881,6 +904,7 @@
         when(mResources.getBoolean(R.bool.force_small_clock_on_lockscreen)).thenReturn(true);
         when(mMediaDataManager.hasActiveMedia()).thenReturn(false);
         when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(0);
+        when(mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue()).thenReturn(false);
         clearInvocations(mKeyguardStatusViewController);
 
         enableSplitShade(/* enabled= */ true);
@@ -898,11 +922,13 @@
 
         // one notification + media player visible
         when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(1);
+        when(mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue()).thenReturn(true);
         triggerPositionClockAndNotifications();
         verify(mKeyguardStatusViewController).displayClock(SMALL, /* animate */ true);
 
         // only media player visible
         when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(0);
+        when(mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue()).thenReturn(false);
         triggerPositionClockAndNotifications();
         verify(mKeyguardStatusViewController, times(2)).displayClock(SMALL, true);
         verify(mKeyguardStatusViewController, never()).displayClock(LARGE, /* animate */ true);
@@ -1094,7 +1120,7 @@
         mTouchHandler.onTouch(mock(View.class), mDownMotionEvent);
         mEmptySpaceClickListenerCaptor.getValue().onEmptySpaceClicked(0, 0);
 
-        verify(mKeyguardFaceAuthInteractor).onNotificationPanelClicked();
+        verify(mDeviceEntryFaceAuthInteractor).onNotificationPanelClicked();
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
index 9d8b214..5d663d2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
@@ -143,6 +143,7 @@
     @Mock private SelectedUserInteractor mSelectedUserInteractor;
     @Mock private UserTracker mUserTracker;
     @Mock private SceneContainerFlags mSceneContainerFlags;
+    @Mock private LargeScreenHeaderHelper mLargeScreenHeaderHelper;
     @Captor private ArgumentCaptor<WindowManager.LayoutParams> mLayoutParameters;
     @Captor private ArgumentCaptor<StatusBarStateController.StateListener> mStateListener;
 
@@ -215,6 +216,8 @@
                 keyguardTransitionRepository,
                 keyguardTransitionInteractor,
                 mTestScope.getBackgroundScope(),
+                mUtils.getTestDispatcher(),
+                mUtils.getTestDispatcher(),
                 keyguardInteractor,
                 featureFlags,
                 shadeRepository,
@@ -233,6 +236,8 @@
                 keyguardTransitionRepository,
                 keyguardTransitionInteractor,
                 mTestScope.getBackgroundScope(),
+                mUtils.getTestDispatcher(),
+                mUtils.getTestDispatcher(),
                 keyguardInteractor,
                 featureFlags,
                 mKeyguardSecurityModel,
@@ -261,7 +266,8 @@
                                 mContext,
                                 new ResourcesSplitShadeStateController(),
                                 keyguardInteractor,
-                                deviceEntryUdfpsInteractor),
+                                deviceEntryUdfpsInteractor,
+                                () -> mLargeScreenHeaderHelper),
                         shadeRepository
                 )
         );
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
index 86d8d54..ee7c6c8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
@@ -48,10 +48,9 @@
 import com.android.systemui.compose.ComposeFacade.isComposeAvailable
 import com.android.systemui.dock.DockManager
 import com.android.systemui.dump.DumpManager
-import com.android.systemui.dump.logcatLogBuffer
+import com.android.systemui.log.logcatLogBuffer
 import com.android.systemui.flags.FakeFeatureFlagsClassic
 import com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED
-import com.android.systemui.flags.Flags.REVAMPED_BOUNCER_MESSAGES
 import com.android.systemui.flags.Flags.SPLIT_SHADE_SUBPIXEL_OPTIMIZATION
 import com.android.systemui.flags.Flags.TRACKPAD_GESTURE_COMMON
 import com.android.systemui.flags.Flags.TRACKPAD_GESTURE_FEATURES
@@ -63,16 +62,13 @@
 import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFaceAuthRepository
 import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
 import com.android.systemui.keyguard.data.repository.FakeTrustRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
 import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl
 import com.android.systemui.keyguard.shared.model.TransitionStep
-import com.android.systemui.keyguard.ui.SwipeUpAnywhereGestureHandler
-import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerUdfpsIconViewModel
-import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerViewModel
+import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerDependencies
 import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel
 import com.android.systemui.log.BouncerLogger
-import com.android.systemui.plugins.FalsingManager
 import com.android.systemui.res.R
 import com.android.systemui.shade.NotificationShadeWindowView.InteractionEventHandler
 import com.android.systemui.statusbar.DragDownHelper
@@ -81,7 +77,6 @@
 import com.android.systemui.statusbar.NotificationShadeDepthController
 import com.android.systemui.statusbar.NotificationShadeWindowController
 import com.android.systemui.statusbar.SysuiStatusBarStateController
-import com.android.systemui.statusbar.gesture.TapGestureDetector
 import com.android.systemui.statusbar.notification.data.repository.NotificationLaunchAnimationRepository
 import com.android.systemui.statusbar.notification.domain.interactor.NotificationLaunchAnimationInteractor
 import com.android.systemui.statusbar.notification.stack.AmbientState
@@ -101,7 +96,6 @@
 import com.android.systemui.util.mockito.eq
 import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
-import java.util.Optional
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.emptyFlow
 import kotlinx.coroutines.test.TestScope
@@ -116,8 +110,9 @@
 import org.mockito.Mockito.never
 import org.mockito.Mockito.times
 import org.mockito.Mockito.verify
-import org.mockito.Mockito.`when` as whenever
 import org.mockito.MockitoAnnotations
+import java.util.Optional
+import org.mockito.Mockito.`when` as whenever
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
@@ -197,9 +192,9 @@
         featureFlagsClassic.set(TRACKPAD_GESTURE_COMMON, true)
         featureFlagsClassic.set(TRACKPAD_GESTURE_FEATURES, false)
         featureFlagsClassic.set(SPLIT_SHADE_SUBPIXEL_OPTIMIZATION, true)
-        featureFlagsClassic.set(REVAMPED_BOUNCER_MESSAGES, true)
         featureFlagsClassic.set(LOCKSCREEN_WALLPAPER_DREAM_ENABLED, false)
         mSetFlagsRule.disableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
+        mSetFlagsRule.enableFlags(Flags.FLAG_REVAMPED_BOUNCER_MESSAGES)
 
         testScope = TestScope()
         fakeClock = FakeSystemClock()
@@ -242,7 +237,6 @@
                     repository = BouncerMessageRepositoryImpl(),
                     userRepository = FakeUserRepository(),
                     countDownTimerUtil = mock(CountDownTimerUtil::class.java),
-                    featureFlags = featureFlagsClassic,
                     updateMonitor = mock(KeyguardUpdateMonitor::class.java),
                     biometricSettingsRepository = FakeBiometricSettingsRepository(),
                     applicationScope = testScope.backgroundScope,
@@ -263,7 +257,7 @@
                             FakeTrustRepository(),
                             testScope.backgroundScope,
                             mSelectedUserInteractor,
-                            mock(KeyguardFaceAuthInteractor::class.java)
+                            mock(DeviceEntryFaceAuthInteractor::class.java)
                         ),
                     facePropertyRepository = FakeFacePropertyRepository(),
                     deviceEntryFingerprintAuthRepository =
@@ -278,11 +272,7 @@
                 alternateBouncerInteractor,
                 mSelectedUserInteractor,
                 { mock (JavaAdapter::class.java )},
-                { mock(AlternateBouncerViewModel::class.java) },
-                { mock(FalsingManager::class.java) },
-                { mock(SwipeUpAnywhereGestureHandler::class.java) },
-                { mock(TapGestureDetector::class.java) },
-                { mock(AlternateBouncerUdfpsIconViewModel::class.java) },
+                { mock(AlternateBouncerDependencies::class.java) },
             )
         underTest.setupExpandedStatusBar()
         underTest.setDragDownHelper(dragDownHelper)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
index d9ff892..33d60ea 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
@@ -28,6 +28,7 @@
 import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.keyguard.LockIconViewController
 import com.android.keyguard.dagger.KeyguardBouncerComponent
+import com.android.systemui.Flags as AConfigFlags
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.biometrics.data.repository.FakeFacePropertyRepository
 import com.android.systemui.bouncer.data.repository.BouncerMessageRepositoryImpl
@@ -43,7 +44,6 @@
 import com.android.systemui.classifier.FalsingCollectorFake
 import com.android.systemui.dock.DockManager
 import com.android.systemui.dump.DumpManager
-import com.android.systemui.dump.logcatLogBuffer
 import com.android.systemui.flags.FakeFeatureFlags
 import com.android.systemui.flags.Flags
 import com.android.systemui.flags.SystemPropertiesHelper
@@ -55,11 +55,10 @@
 import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
 import com.android.systemui.keyguard.data.repository.FakeTrustRepository
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
-import com.android.systemui.keyguard.ui.SwipeUpAnywhereGestureHandler
-import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerUdfpsIconViewModel
-import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerViewModel
+import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerDependencies
 import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel
 import com.android.systemui.log.BouncerLogger
+import com.android.systemui.log.logcatLogBuffer
 import com.android.systemui.plugins.FalsingManager
 import com.android.systemui.res.R
 import com.android.systemui.shade.NotificationShadeWindowView.InteractionEventHandler
@@ -69,7 +68,6 @@
 import com.android.systemui.statusbar.NotificationShadeDepthController
 import com.android.systemui.statusbar.NotificationShadeWindowController
 import com.android.systemui.statusbar.SysuiStatusBarStateController
-import com.android.systemui.statusbar.gesture.TapGestureDetector
 import com.android.systemui.statusbar.notification.data.repository.NotificationLaunchAnimationRepository
 import com.android.systemui.statusbar.notification.domain.interactor.NotificationLaunchAnimationInteractor
 import com.android.systemui.statusbar.notification.stack.AmbientState
@@ -189,9 +187,9 @@
         featureFlags.set(Flags.TRACKPAD_GESTURE_COMMON, true)
         featureFlags.set(Flags.TRACKPAD_GESTURE_FEATURES, false)
         featureFlags.set(Flags.SPLIT_SHADE_SUBPIXEL_OPTIMIZATION, true)
-        featureFlags.set(Flags.REVAMPED_BOUNCER_MESSAGES, true)
         featureFlags.set(Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED, false)
-        mSetFlagsRule.disableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
+        mSetFlagsRule.disableFlags(AConfigFlags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
+        mSetFlagsRule.enableFlags(AConfigFlags.FLAG_REVAMPED_BOUNCER_MESSAGES)
         testScope = TestScope()
         controller =
             NotificationShadeWindowViewController(
@@ -232,7 +230,6 @@
                     repository = BouncerMessageRepositoryImpl(),
                     userRepository = FakeUserRepository(),
                     countDownTimerUtil = Mockito.mock(CountDownTimerUtil::class.java),
-                    featureFlags = featureFlags,
                     updateMonitor = Mockito.mock(KeyguardUpdateMonitor::class.java),
                     biometricSettingsRepository = FakeBiometricSettingsRepository(),
                     applicationScope = testScope.backgroundScope,
@@ -268,11 +265,7 @@
                 alternateBouncerInteractor,
                 mSelectedUserInteractor,
                 { Mockito.mock(JavaAdapter::class.java) },
-                { Mockito.mock(AlternateBouncerViewModel::class.java) },
-                { Mockito.mock(FalsingManager::class.java) },
-                { Mockito.mock(SwipeUpAnywhereGestureHandler::class.java) },
-                { Mockito.mock(TapGestureDetector::class.java) },
-                { Mockito.mock(AlternateBouncerUdfpsIconViewModel::class.java) },
+                { Mockito.mock(AlternateBouncerDependencies::class.java) },
             )
 
         controller.setupExpandedStatusBar()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt
index 88a47eb..ea3caa3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt
@@ -42,6 +42,7 @@
 import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.mockito.capture
+import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
 import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
@@ -52,7 +53,6 @@
 import org.junit.runner.RunWith
 import org.mockito.ArgumentCaptor
 import org.mockito.Captor
-import org.mockito.Mock
 import org.mockito.Mockito
 import org.mockito.Mockito.RETURNS_DEEP_STUBS
 import org.mockito.Mockito.any
@@ -74,15 +74,16 @@
 @SmallTest
 class NotificationsQSContainerControllerLegacyTest : SysuiTestCase() {
 
-    @Mock lateinit var view: NotificationsQuickSettingsContainer
-    @Mock lateinit var navigationModeController: NavigationModeController
-    @Mock lateinit var overviewProxyService: OverviewProxyService
-    @Mock lateinit var shadeHeaderController: ShadeHeaderController
-    @Mock lateinit var shadeInteractor: ShadeInteractor
-    @Mock lateinit var fragmentService: FragmentService
-    @Mock lateinit var fragmentHostManager: FragmentHostManager
-    @Mock
-    lateinit var notificationStackScrollLayoutController: NotificationStackScrollLayoutController
+    private val view = mock<NotificationsQuickSettingsContainer>()
+    private val navigationModeController = mock<NavigationModeController>()
+    private val overviewProxyService = mock<OverviewProxyService>()
+    private val shadeHeaderController = mock<ShadeHeaderController>()
+    private val shadeInteractor = mock<ShadeInteractor>()
+    private val fragmentService = mock<FragmentService>()
+    private val fragmentHostManager = mock<FragmentHostManager>()
+    private val notificationStackScrollLayoutController =
+        mock<NotificationStackScrollLayoutController>()
+    private val largeScreenHeaderHelper = mock<LargeScreenHeaderHelper>()
 
     @Captor lateinit var navigationModeCaptor: ArgumentCaptor<ModeChangedListener>
     @Captor lateinit var taskbarVisibilityCaptor: ArgumentCaptor<OverviewProxyListener>
@@ -123,7 +124,8 @@
                 delayableExecutor,
                 featureFlags,
                 notificationStackScrollLayoutController,
-                ResourcesSplitShadeStateController()
+                ResourcesSplitShadeStateController(),
+                largeScreenHeaderHelperLazy = { largeScreenHeaderHelper }
             )
 
         overrideResource(R.dimen.split_shade_notifications_scrim_margin_bottom, SCRIM_MARGIN)
@@ -480,7 +482,8 @@
                 delayableExecutor,
                 featureFlags,
                 notificationStackScrollLayoutController,
-                ResourcesSplitShadeStateController()
+                ResourcesSplitShadeStateController(),
+                largeScreenHeaderHelperLazy = { largeScreenHeaderHelper }
             )
         controller.updateConstraints()
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerTest.kt
index 1f37ca0..c1bc303 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerTest.kt
@@ -43,6 +43,7 @@
 import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.mockito.capture
+import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
 import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
@@ -53,7 +54,6 @@
 import org.junit.runner.RunWith
 import org.mockito.ArgumentCaptor
 import org.mockito.Captor
-import org.mockito.Mock
 import org.mockito.Mockito
 import org.mockito.Mockito.RETURNS_DEEP_STUBS
 import org.mockito.Mockito.any
@@ -71,15 +71,16 @@
 @SmallTest
 class NotificationsQSContainerControllerTest : SysuiTestCase() {
 
-    @Mock lateinit var view: NotificationsQuickSettingsContainer
-    @Mock lateinit var navigationModeController: NavigationModeController
-    @Mock lateinit var overviewProxyService: OverviewProxyService
-    @Mock lateinit var shadeHeaderController: ShadeHeaderController
-    @Mock lateinit var shadeInteractor: ShadeInteractor
-    @Mock lateinit var fragmentService: FragmentService
-    @Mock lateinit var fragmentHostManager: FragmentHostManager
-    @Mock
-    lateinit var notificationStackScrollLayoutController: NotificationStackScrollLayoutController
+    private val view = mock<NotificationsQuickSettingsContainer>()
+    private val navigationModeController = mock<NavigationModeController>()
+    private val overviewProxyService = mock<OverviewProxyService>()
+    private val shadeHeaderController = mock<ShadeHeaderController>()
+    private val shadeInteractor = mock<ShadeInteractor>()
+    private val fragmentService = mock<FragmentService>()
+    private val fragmentHostManager = mock<FragmentHostManager>()
+    private val notificationStackScrollLayoutController =
+        mock<NotificationStackScrollLayoutController>()
+    private val largeScreenHeaderHelper = mock<LargeScreenHeaderHelper>()
 
     @Captor lateinit var navigationModeCaptor: ArgumentCaptor<ModeChangedListener>
     @Captor lateinit var taskbarVisibilityCaptor: ArgumentCaptor<OverviewProxyListener>
@@ -122,7 +123,8 @@
                 delayableExecutor,
                 featureFlags,
                 notificationStackScrollLayoutController,
-                ResourcesSplitShadeStateController()
+                ResourcesSplitShadeStateController(),
+                largeScreenHeaderHelperLazy = { largeScreenHeaderHelper }
             )
 
         overrideResource(R.dimen.split_shade_notifications_scrim_margin_bottom, SCRIM_MARGIN)
@@ -463,7 +465,8 @@
                 delayableExecutor,
                 featureFlags,
                 notificationStackScrollLayoutController,
-                ResourcesSplitShadeStateController()
+                ResourcesSplitShadeStateController(),
+                largeScreenHeaderHelperLazy = { largeScreenHeaderHelper }
             )
         controller.updateConstraints()
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
index eb5633b..982787b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
@@ -42,6 +42,7 @@
 import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository;
 import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository;
 import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor;
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor;
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.flags.FakeFeatureFlagsClassic;
@@ -55,7 +56,6 @@
 import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor;
 import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor;
 import com.android.systemui.keyguard.domain.interactor.InWindowLauncherUnlockAnimationInteractor;
-import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor;
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
 import com.android.systemui.media.controls.pipeline.MediaDataManager;
@@ -176,6 +176,7 @@
     @Mock protected CastController mCastController;
     @Mock protected UserSwitcherInteractor mUserSwitcherInteractor;
     @Mock protected SelectedUserInteractor mSelectedUserInteractor;
+    @Mock protected LargeScreenHeaderHelper mLargeScreenHeaderHelper;
 
     protected FakeDisableFlagsRepository mDisableFlagsRepository =
             new FakeDisableFlagsRepository();
@@ -250,6 +251,8 @@
                 keyguardTransitionRepository,
                 keyguardTransitionInteractor,
                 mTestScope.getBackgroundScope(),
+                mUtils.getTestDispatcher(),
+                mUtils.getTestDispatcher(),
                 keyguardInteractor,
                 featureFlags,
                 mShadeRepository,
@@ -268,6 +271,8 @@
                 keyguardTransitionRepository,
                 keyguardTransitionInteractor,
                 mTestScope.getBackgroundScope(),
+                mUtils.getTestDispatcher(),
+                mUtils.getTestDispatcher(),
                 keyguardInteractor,
                 featureFlags,
                 mock(KeyguardSecurityModel.class),
@@ -299,7 +304,8 @@
                                 mContext,
                                 splitShadeStateController,
                                 keyguardInteractor,
-                                deviceEntryUdfpsInteractor),
+                                deviceEntryUdfpsInteractor,
+                                () -> mLargeScreenHeaderHelper),
                         mShadeRepository
                 )
         );
@@ -378,13 +384,14 @@
                 mInteractionJankMonitor,
                 mShadeLogger,
                 mDumpManager,
-                mock(KeyguardFaceAuthInteractor.class),
+                mock(DeviceEntryFaceAuthInteractor.class),
                 mShadeRepository,
                 mShadeInteractor,
                 mActiveNotificationsInteractor,
                 new JavaAdapter(mTestScope.getBackgroundScope()),
                 mCastController,
-                splitShadeStateController
+                splitShadeStateController,
+                () -> mLargeScreenHeaderHelper
         );
         mQsController.init();
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
index ee27c5c..64fd80d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
@@ -35,6 +35,7 @@
 import com.android.systemui.plugins.PluginManager
 import com.android.systemui.util.mockito.argumentCaptor
 import com.android.systemui.util.mockito.eq
+import java.util.function.BiConsumer
 import junit.framework.Assert.assertEquals
 import junit.framework.Assert.fail
 import kotlinx.coroutines.CoroutineDispatcher
@@ -100,10 +101,7 @@
         override fun toString() = "Manager[$tag]"
         override fun getPackage(): String = mComponentName.getPackageName()
         override fun getComponentName(): ComponentName = mComponentName
-
-        private var isDebug: Boolean = false
-        override fun getIsDebug(): Boolean = isDebug
-        override fun setIsDebug(value: Boolean) { isDebug = value }
+        override fun setLogFunc(func: BiConsumer<String, String>) { }
 
         override fun loadPlugin() {
             if (!mIsLoaded) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/notifications/data/repository/NotificationSettingsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/notifications/data/repository/NotificationSettingsRepositoryTest.kt
index 80f8cf1..50349be 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/notifications/data/repository/NotificationSettingsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/notifications/data/repository/NotificationSettingsRepositoryTest.kt
@@ -58,13 +58,13 @@
         testScope.runTest {
             val showNotifs by collectLastValue(underTest.isShowNotificationsOnLockScreenEnabled)
 
-            secureSettingsRepository.set(
+            secureSettingsRepository.setInt(
                 name = Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS,
                 value = 1,
             )
             assertThat(showNotifs).isEqualTo(true)
 
-            secureSettingsRepository.set(
+            secureSettingsRepository.setInt(
                 name = Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS,
                 value = 0,
             )
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceTest.java
index 6eabf44..3defee9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceTest.java
@@ -17,13 +17,17 @@
 package com.android.systemui.shared.plugins;
 
 import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertNotNull;
 import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertTrue;
+import static junit.framework.Assert.fail;
 
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Log;
 
 import androidx.test.runner.AndroidJUnit4;
 
@@ -40,7 +44,11 @@
 
 import java.lang.ref.WeakReference;
 import java.util.Collections;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Supplier;
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
@@ -104,6 +112,7 @@
         mPluginInstance = mPluginInstanceFactory.create(
                 mContext, mAppInfo, TEST_PLUGIN_COMPONENT_NAME,
                 TestPlugin.class, mPluginListener);
+        mPluginInstance.setLogFunc((tag, msg) -> Log.d((String) tag, (String) msg));
         mPluginContext = new WeakReference<>(mPluginInstance.getPluginContext());
     }
 
@@ -158,7 +167,7 @@
 
     @Test
     public void testOnAttach_SkipLoad() {
-        mPluginListener.mAttachReturn = false;
+        mPluginListener.mOnAttach = () -> false;
         mPluginInstance.onCreate();
         assertEquals(1, mPluginListener.mAttachedCount);
         assertEquals(0, mPluginListener.mLoadCount);
@@ -166,6 +175,65 @@
         assertInstances(0, 0);
     }
 
+    @Test
+    public void testLoadUnloadSimultaneous_HoldsUnload() throws Exception {
+        final Semaphore loadLock = new Semaphore(1);
+        final Semaphore unloadLock = new Semaphore(1);
+
+        mPluginListener.mOnAttach = () -> false;
+        mPluginListener.mOnLoad = () -> {
+            assertNotNull(mPluginInstance.getPlugin());
+
+            // Allow the bg thread the opportunity to delete the plugin
+            loadLock.release();
+            Thread.yield();
+            boolean isLocked = getLock(unloadLock, 1000);
+
+            // Ensure the bg thread failed to do delete the plugin
+            assertNotNull(mPluginInstance.getPlugin());
+            // We expect that bgThread deadlocked holding the semaphore
+            assertFalse(isLocked);
+        };
+
+        AtomicBoolean isBgThreadFailed = new AtomicBoolean(false);
+        Thread bgThread = new Thread(() -> {
+            assertTrue(getLock(unloadLock, 10));
+            assertTrue(getLock(loadLock, 4000)); // Wait for the foreground thread
+            assertNotNull(mPluginInstance.getPlugin());
+            // Attempt to delete the plugin, this should block until the load completes
+            mPluginInstance.unloadPlugin();
+            assertNull(mPluginInstance.getPlugin());
+            unloadLock.release();
+            loadLock.release();
+        });
+
+        // This protects the test suite from crashing due to the uncaught exception.
+        bgThread.setUncaughtExceptionHandler((Thread t, Throwable ex) -> {
+            Log.e("testLoadUnloadSimultaneous_HoldsUnload", "Exception from BG Thread", ex);
+            isBgThreadFailed.set(true);
+        });
+
+        loadLock.acquire();
+        mPluginInstance.onCreate();
+
+        assertNull(mPluginInstance.getPlugin());
+        bgThread.start();
+        mPluginInstance.loadPlugin();
+
+        bgThread.join(5000);
+        assertFalse(isBgThreadFailed.get());
+        assertNull(mPluginInstance.getPlugin());
+    }
+
+    private boolean getLock(Semaphore lock, long millis) {
+        try {
+            return lock.tryAcquire(millis, TimeUnit.MILLISECONDS);
+        } catch (InterruptedException ex) {
+            fail();
+            return false;
+        }
+    }
+
     // This target class doesn't matter, it just needs to have a Requires to hit the flow where
     // the mock version info is called.
     @ProvidesInterface(action = TestPlugin.ACTION, version = TestPlugin.VERSION)
@@ -226,7 +294,10 @@
     }
 
     public class FakeListener implements PluginListener<TestPlugin> {
-        public boolean mAttachReturn = true;
+        public Supplier<Boolean> mOnAttach = null;
+        public Runnable mOnDetach = null;
+        public Runnable mOnLoad = null;
+        public Runnable mOnUnload = null;
         public int mAttachedCount = 0;
         public int mDetachedCount = 0;
         public int mLoadCount = 0;
@@ -236,13 +307,16 @@
         public boolean onPluginAttached(PluginLifecycleManager<TestPlugin> manager) {
             mAttachedCount++;
             assertEquals(PluginInstanceTest.this.mPluginInstance, manager);
-            return mAttachReturn;
+            return mOnAttach != null ? mOnAttach.get() : true;
         }
 
         @Override
         public void onPluginDetached(PluginLifecycleManager<TestPlugin> manager) {
             mDetachedCount++;
             assertEquals(PluginInstanceTest.this.mPluginInstance, manager);
+            if (mOnDetach != null) {
+                mOnDetach.run();
+            }
         }
 
         @Override
@@ -261,6 +335,9 @@
                 assertEquals(expectedContext, pluginContext);
             }
             assertEquals(PluginInstanceTest.this.mPluginInstance, manager);
+            if (mOnLoad != null) {
+                mOnLoad.run();
+            }
         }
 
         @Override
@@ -274,6 +351,9 @@
                 assertEquals(expectedPlugin, plugin);
             }
             assertEquals(PluginInstanceTest.this.mPluginInstance, manager);
+            if (mOnUnload != null) {
+                mOnUnload.run();
+            }
         }
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
index 76c4015..e1d9282 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
@@ -17,7 +17,7 @@
 
 package com.android.systemui.statusbar;
 
-import static com.android.systemui.dump.LogBufferHelperKt.logcatLogBuffer;
+import static com.android.systemui.log.LogBufferHelperKt.logcatLogBuffer;
 import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED;
 
 import static com.google.common.truth.Truth.assertThat;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
index 560ebc6..d3850be 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
@@ -16,7 +16,7 @@
 
 package com.android.systemui.statusbar;
 
-import static com.android.systemui.dump.LogBufferHelperKt.logcatLogBuffer;
+import static com.android.systemui.log.LogBufferHelperKt.logcatLogBuffer;
 
 import static junit.framework.Assert.assertTrue;
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
index f25ce0a..83590ee 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
@@ -27,7 +27,7 @@
 import com.android.systemui.classifier.FalsingCollectorFake
 import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
 import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
-import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor;
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
 import com.android.systemui.flags.FakeFeatureFlagsClassic
 import com.android.systemui.keyguard.data.repository.FakeCommandQueue
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
@@ -44,6 +44,7 @@
 import com.android.systemui.power.domain.interactor.PowerInteractor
 import com.android.systemui.scene.SceneTestUtils
 import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags
+import com.android.systemui.shade.LargeScreenHeaderHelper
 import com.android.systemui.shade.data.repository.FakeShadeRepository
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
 import com.android.systemui.shade.domain.interactor.ShadeInteractorImpl
@@ -65,7 +66,6 @@
 import org.mockito.ArgumentMatchers.anyFloat
 import org.mockito.ArgumentMatchers.anyInt
 import org.mockito.ArgumentMatchers.eq
-import org.mockito.Mock
 import org.mockito.Mockito
 import org.mockito.Mockito.mock
 import org.mockito.Mockito.verify
@@ -79,13 +79,15 @@
 
     private val utils = SceneTestUtils(this)
     private val testScope = utils.testScope
+    private val testDispatcher = utils.testDispatcher
     private lateinit var shadeInteractor: ShadeInteractor
     private lateinit var fromLockscreenTransitionInteractor: FromLockscreenTransitionInteractor
     private lateinit var fromPrimaryBouncerTransitionInteractor:
         FromPrimaryBouncerTransitionInteractor
-    @Mock lateinit var interactionJankMonitor: InteractionJankMonitor
-    @Mock lateinit var mockDarkAnimator: ObjectAnimator
-    @Mock lateinit var deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor
+    private val interactionJankMonitor = mock<InteractionJankMonitor>()
+    private val mockDarkAnimator = mock<ObjectAnimator>()
+    private val deviceEntryUdfpsInteractor = mock<DeviceEntryUdfpsInteractor>()
+    private val largeScreenHeaderHelper = mock<LargeScreenHeaderHelper>()
 
     private lateinit var controller: StatusBarStateControllerImpl
     private lateinit var uiEventLogger: UiEventLoggerFake
@@ -142,6 +144,8 @@
                 keyguardTransitionRepository,
                 keyguardTransitionInteractor,
                 testScope.backgroundScope,
+                testDispatcher,
+                testDispatcher,
                 keyguardInteractor,
                 featureFlags,
                 shadeRepository,
@@ -161,6 +165,8 @@
                 keyguardTransitionRepository,
                 keyguardTransitionInteractor,
                 testScope.backgroundScope,
+                testDispatcher,
+                testDispatcher,
                 keyguardInteractor,
                 featureFlags,
                 mock(),
@@ -189,6 +195,7 @@
                         ResourcesSplitShadeStateController(),
                         keyguardInteractor,
                         deviceEntryUdfpsInteractor,
+                        largeScreenHeaderHelperLazy = { largeScreenHeaderHelper }
                     ),
                     shadeRepository,
                 )
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorTest.kt
index 40edea2..039fef9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorTest.kt
@@ -22,12 +22,14 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.animation.AnimatorTestRule
 import com.android.systemui.dump.DumpManager
-import com.android.systemui.dump.logcatLogBuffer
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.log.logcatLogBuffer
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.shade.ShadeViewController.Companion.WAKEUP_ANIMATION_DELAY_MS
 import com.android.systemui.statusbar.StatusBarState
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
 import com.android.systemui.statusbar.notification.stack.StackStateAnimator.ANIMATION_DURATION_WAKEUP
+import com.android.systemui.statusbar.notification.stack.domain.interactor.notificationsKeyguardInteractor
 import com.android.systemui.statusbar.phone.DozeParameters
 import com.android.systemui.statusbar.phone.KeyguardBypassController
 import com.android.systemui.statusbar.phone.ScreenOffAnimationController
@@ -54,6 +56,8 @@
 
     @get:Rule val animatorTestRule = AnimatorTestRule()
 
+    private val kosmos = Kosmos()
+
     private val dumpManager: DumpManager = mock()
     private val headsUpManager: HeadsUpManager = mock()
     private val statusBarStateController: StatusBarStateController = mock()
@@ -100,6 +104,7 @@
                 dozeParameters,
                 screenOffAnimationController,
                 logger,
+                kosmos.notificationsKeyguardInteractor,
             )
         statusBarStateCallback = withArgCaptor {
             verify(statusBarStateController).addCallback(capture())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
index 104b751..2cf599a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
@@ -26,7 +26,7 @@
 import static android.service.notification.NotificationStats.DISMISSAL_SHADE;
 import static android.service.notification.NotificationStats.DISMISS_SENTIMENT_NEUTRAL;
 
-import static com.android.systemui.dump.LogBufferHelperKt.logcatLogBuffer;
+import static com.android.systemui.log.LogBufferHelperKt.logcatLogBuffer;
 import static com.android.systemui.statusbar.notification.collection.NotifCollection.REASON_NOT_CANCELED;
 import static com.android.systemui.statusbar.notification.collection.NotifCollection.REASON_UNKNOWN;
 import static com.android.systemui.statusbar.notification.collection.NotificationEntry.DismissState.DISMISSED;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
index 8cf64a5..4ff09d3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
@@ -16,7 +16,7 @@
 
 package com.android.systemui.statusbar.notification.collection;
 
-import static com.android.systemui.dump.LogBufferHelperKt.logcatLogBuffer;
+import static com.android.systemui.log.LogBufferHelperKt.logcatLogBuffer;
 import static com.android.systemui.statusbar.notification.collection.ListDumper.dumpTree;
 import static com.android.systemui.statusbar.notification.collection.ShadeListBuilder.MAX_CONSECUTIVE_REENTRANT_REBUILDS;
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerTest.java
index 3dcfcfa..b1180ae 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerTest.java
@@ -16,7 +16,7 @@
 
 package com.android.systemui.statusbar.notification.collection.coalescer;
 
-import static com.android.systemui.dump.LogBufferHelperKt.logcatLogBuffer;
+import static com.android.systemui.log.LogBufferHelperKt.logcatLogBuffer;
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyList;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinatorTest.kt
index 362da0b..a652ad6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinatorTest.kt
@@ -20,7 +20,7 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.dump.DumpManager
-import com.android.systemui.dump.logcatLogBuffer
+import com.android.systemui.log.logcatLogBuffer
 import com.android.systemui.statusbar.notification.collection.NotifPipeline
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt
index 4f1581c..a8be62b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt
@@ -21,7 +21,7 @@
 import android.testing.TestableLooper.RunWithLooper
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.dump.logcatLogBuffer
+import com.android.systemui.log.logcatLogBuffer
 import com.android.systemui.statusbar.NotificationRemoteInputManager
 import com.android.systemui.statusbar.notification.NotifPipelineFlags
 import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt
index 2ee016b..5ff7353 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt
@@ -24,7 +24,7 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.dump.DumpManager
-import com.android.systemui.dump.logcatLogBuffer
+import com.android.systemui.log.logcatLogBuffer
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
 import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.shared.model.KeyguardState
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java
index 548ecde..58eec2e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java
@@ -18,7 +18,7 @@
 
 import static android.provider.Settings.Secure.SHOW_NOTIFICATION_SNOOZE;
 
-import static com.android.systemui.dump.LogBufferHelperKt.logcatLogBuffer;
+import static com.android.systemui.log.LogBufferHelperKt.logcatLogBuffer;
 import static com.android.systemui.statusbar.notification.collection.GroupEntry.ROOT_ENTRY;
 
 import static org.junit.Assert.assertFalse;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ShadeEventCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ShadeEventCoordinatorTest.kt
index 069eec2..56f16f3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ShadeEventCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ShadeEventCoordinatorTest.kt
@@ -21,7 +21,7 @@
 import android.testing.TestableLooper.RunWithLooper
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.dump.logcatLogBuffer
+import com.android.systemui.log.logcatLogBuffer
 import com.android.systemui.statusbar.notification.collection.NotifPipeline
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinatorTest.kt
index 255cf6f..9b4a100 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinatorTest.kt
@@ -31,6 +31,7 @@
 import com.android.systemui.statusbar.notification.collection.render.NotifStats
 import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
 import com.android.systemui.statusbar.notification.domain.interactor.RenderNotificationListInteractor
+import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor
 import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor
 import com.android.systemui.statusbar.notification.stack.BUCKET_ALERTING
 import com.android.systemui.statusbar.notification.stack.BUCKET_SILENT
@@ -80,7 +81,7 @@
     }
 
     @Test
-    @DisableFlags(NotificationIconContainerRefactor.FLAG_NAME)
+    @DisableFlags(NotificationIconContainerRefactor.FLAG_NAME, FooterViewRefactor.FLAG_NAME)
     fun testUpdateNotificationIcons() {
         afterRenderListListener.onAfterRenderList(listOf(entry), stackController)
         verify(notificationIconAreaController).updateNotificationIcons(eq(listOf(entry)))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionInconsistencyTrackerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionInconsistencyTrackerTest.kt
index 0b61a8d..22f6bdc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionInconsistencyTrackerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionInconsistencyTrackerTest.kt
@@ -21,7 +21,7 @@
 import android.testing.TestableLooper.RunWithLooper
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.dump.logcatLogBuffer
+import com.android.systemui.log.logcatLogBuffer
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
 import com.android.systemui.util.mockito.eq
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderTest.kt
index bad56a3..11996fe 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderTest.kt
@@ -18,7 +18,7 @@
 
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.dump.logcatLogBuffer
+import com.android.systemui.log.logcatLogBuffer
 import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager
 import com.android.systemui.statusbar.notification.collection.GroupEntry
 import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder
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
index 9a60272..eeabc74 100644
--- 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
@@ -22,7 +22,7 @@
 import android.widget.FrameLayout
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.dump.logcatLogBuffer
+import com.android.systemui.log.logcatLogBuffer
 import org.junit.Assert
 import org.junit.Before
 import org.junit.Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/data/repository/NotificationsKeyguardViewStateRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/data/repository/NotificationsKeyguardViewStateRepositoryTest.kt
deleted file mode 100644
index 170f651..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/data/repository/NotificationsKeyguardViewStateRepositoryTest.kt
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.notification.data.repository
-
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysUITestComponent
-import com.android.systemui.SysUITestModule
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.collectLastValue
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.runCurrent
-import com.android.systemui.runTest
-import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator
-import com.android.systemui.util.mockito.whenever
-import com.android.systemui.util.mockito.withArgCaptor
-import com.google.common.truth.Truth.assertThat
-import dagger.BindsInstance
-import dagger.Component
-import org.junit.Test
-import org.mockito.Mockito.verify
-
-@SmallTest
-class NotificationsKeyguardViewStateRepositoryTest : SysuiTestCase() {
-
-    @SysUISingleton
-    @Component(modules = [SysUITestModule::class])
-    interface TestComponent : SysUITestComponent<NotificationsKeyguardViewStateRepositoryImpl> {
-
-        val mockWakeUpCoordinator: NotificationWakeUpCoordinator
-
-        @Component.Factory
-        interface Factory {
-            fun create(
-                @BindsInstance test: SysuiTestCase,
-            ): TestComponent
-        }
-    }
-
-    private val testComponent: TestComponent =
-        DaggerNotificationsKeyguardViewStateRepositoryTest_TestComponent.factory()
-            .create(test = this)
-
-    @Test
-    fun areNotifsFullyHidden_reflectsWakeUpCoordinator() =
-        testComponent.runTest {
-            whenever(mockWakeUpCoordinator.notificationsFullyHidden).thenReturn(false)
-            val notifsFullyHidden by collectLastValue(underTest.areNotificationsFullyHidden)
-            runCurrent()
-
-            assertThat(notifsFullyHidden).isFalse()
-
-            withArgCaptor { verify(mockWakeUpCoordinator).addListener(capture()) }
-                .onFullyHiddenChanged(true)
-            runCurrent()
-
-            assertThat(notifsFullyHidden).isTrue()
-        }
-
-    @Test
-    fun isPulseExpanding_reflectsWakeUpCoordinator() =
-        testComponent.runTest {
-            whenever(mockWakeUpCoordinator.isPulseExpanding()).thenReturn(false)
-            val isPulseExpanding by collectLastValue(underTest.isPulseExpanding)
-            runCurrent()
-
-            assertThat(isPulseExpanding).isFalse()
-
-            withArgCaptor { verify(mockWakeUpCoordinator).addListener(capture()) }
-                .onPulseExpandingChanged(true)
-            runCurrent()
-
-            assertThat(isPulseExpanding).isTrue()
-        }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsKeyguardInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsKeyguardInteractorTest.kt
index bb3113a..3593f5b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsKeyguardInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsKeyguardInteractorTest.kt
@@ -21,7 +21,6 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.runCurrent
 import com.android.systemui.runTest
-import com.android.systemui.statusbar.notification.data.repository.FakeNotificationsKeyguardViewStateRepository
 import com.google.common.truth.Truth.assertThat
 import dagger.BindsInstance
 import dagger.Component
@@ -33,9 +32,6 @@
     @SysUISingleton
     @Component(modules = [SysUITestModule::class])
     interface TestComponent : SysUITestComponent<NotificationsKeyguardInteractor> {
-
-        val repository: FakeNotificationsKeyguardViewStateRepository
-
         @Component.Factory
         interface Factory {
             fun create(@BindsInstance test: SysuiTestCase): TestComponent
@@ -48,13 +44,13 @@
     @Test
     fun areNotifsFullyHidden_reflectsRepository() =
         testComponent.runTest {
-            repository.setNotificationsFullyHidden(false)
+            underTest.setNotificationsFullyHidden(false)
             val notifsFullyHidden by collectLastValue(underTest.areNotificationsFullyHidden)
             runCurrent()
 
             assertThat(notifsFullyHidden).isFalse()
 
-            repository.setNotificationsFullyHidden(true)
+            underTest.setNotificationsFullyHidden(true)
             runCurrent()
 
             assertThat(notifsFullyHidden).isTrue()
@@ -63,13 +59,13 @@
     @Test
     fun isPulseExpanding_reflectsRepository() =
         testComponent.runTest {
-            repository.setPulseExpanding(false)
+            underTest.setPulseExpanding(false)
             val isPulseExpanding by collectLastValue(underTest.isPulseExpanding)
             runCurrent()
 
             assertThat(isPulseExpanding).isFalse()
 
-            repository.setPulseExpanding(true)
+            underTest.setPulseExpanding(true)
             runCurrent()
 
             assertThat(isPulseExpanding).isTrue()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorTest.kt
index 47feccf..7faf562 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorTest.kt
@@ -29,8 +29,8 @@
 import com.android.systemui.statusbar.notification.data.model.activeNotificationModel
 import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository
 import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore
-import com.android.systemui.statusbar.notification.data.repository.FakeNotificationsKeyguardViewStateRepository
 import com.android.systemui.statusbar.notification.domain.interactor.HeadsUpNotificationIconInteractor
+import com.android.systemui.statusbar.notification.domain.interactor.NotificationsKeyguardInteractor
 import com.android.systemui.statusbar.notification.shared.byIsAmbient
 import com.android.systemui.statusbar.notification.shared.byIsLastMessageFromReply
 import com.android.systemui.statusbar.notification.shared.byIsPulsing
@@ -61,7 +61,7 @@
     interface TestComponent : SysUITestComponent<NotificationIconsInteractor> {
 
         val activeNotificationListRepository: ActiveNotificationListRepository
-        val keyguardViewStateRepository: FakeNotificationsKeyguardViewStateRepository
+        val notificationsKeyguardInteractor: NotificationsKeyguardInteractor
 
         @Component.Factory
         interface Factory {
@@ -136,7 +136,7 @@
     fun filteredEntrySet_noPulsing_notifsNotFullyHidden() =
         testComponent.runTest {
             val filteredSet by collectLastValue(underTest.filteredNotifSet(showPulsing = false))
-            keyguardViewStateRepository.setNotificationsFullyHidden(false)
+            notificationsKeyguardInteractor.setNotificationsFullyHidden(false)
             assertThat(filteredSet).comparingElementsUsing(byIsPulsing).doesNotContain(true)
         }
 
@@ -144,7 +144,7 @@
     fun filteredEntrySet_noPulsing_notifsFullyHidden() =
         testComponent.runTest {
             val filteredSet by collectLastValue(underTest.filteredNotifSet(showPulsing = false))
-            keyguardViewStateRepository.setNotificationsFullyHidden(true)
+            notificationsKeyguardInteractor.setNotificationsFullyHidden(true)
             assertThat(filteredSet).comparingElementsUsing(byIsPulsing).contains(true)
         }
 }
@@ -161,7 +161,7 @@
 
         val activeNotificationListRepository: ActiveNotificationListRepository
         val deviceEntryRepository: FakeDeviceEntryRepository
-        val keyguardViewStateRepository: FakeNotificationsKeyguardViewStateRepository
+        val notificationsKeyguardInteractor: NotificationsKeyguardInteractor
 
         @Component.Factory
         interface Factory {
@@ -222,7 +222,7 @@
         testComponent.runTest {
             val filteredSet by collectLastValue(underTest.aodNotifs)
             deviceEntryRepository.setBypassEnabled(false)
-            keyguardViewStateRepository.setNotificationsFullyHidden(false)
+            notificationsKeyguardInteractor.setNotificationsFullyHidden(false)
             assertThat(filteredSet).comparingElementsUsing(byIsPulsing).contains(true)
         }
 
@@ -231,7 +231,7 @@
         testComponent.runTest {
             val filteredSet by collectLastValue(underTest.aodNotifs)
             deviceEntryRepository.setBypassEnabled(false)
-            keyguardViewStateRepository.setNotificationsFullyHidden(true)
+            notificationsKeyguardInteractor.setNotificationsFullyHidden(true)
             assertThat(filteredSet).comparingElementsUsing(byIsPulsing).contains(true)
         }
 
@@ -240,7 +240,7 @@
         testComponent.runTest {
             val filteredSet by collectLastValue(underTest.aodNotifs)
             deviceEntryRepository.setBypassEnabled(true)
-            keyguardViewStateRepository.setNotificationsFullyHidden(false)
+            notificationsKeyguardInteractor.setNotificationsFullyHidden(false)
             assertThat(filteredSet).comparingElementsUsing(byIsPulsing).doesNotContain(true)
         }
 
@@ -249,7 +249,7 @@
         testComponent.runTest {
             val filteredSet by collectLastValue(underTest.aodNotifs)
             deviceEntryRepository.setBypassEnabled(true)
-            keyguardViewStateRepository.setNotificationsFullyHidden(true)
+            notificationsKeyguardInteractor.setNotificationsFullyHidden(true)
             assertThat(filteredSet).comparingElementsUsing(byIsPulsing).contains(true)
         }
 }
@@ -266,7 +266,7 @@
 
         val activeNotificationListRepository: ActiveNotificationListRepository
         val headsUpIconsInteractor: HeadsUpNotificationIconInteractor
-        val keyguardViewStateRepository: FakeNotificationsKeyguardViewStateRepository
+        val notificationsKeyguardInteractor: NotificationsKeyguardInteractor
         val notificationListenerSettingsRepository: NotificationListenerSettingsRepository
 
         @Component.Factory
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinderTest.java
index 04ffab3..60eea9b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinderTest.java
@@ -16,7 +16,7 @@
 
 package com.android.systemui.statusbar.notification.interruption;
 
-import static com.android.systemui.dump.LogBufferHelperKt.logcatLogBuffer;
+import static com.android.systemui.log.LogBufferHelperKt.logcatLogBuffer;
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt
index 7c8199e..9547af1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt
@@ -27,8 +27,8 @@
 import com.android.internal.logging.MetricsLogger
 import com.android.internal.statusbar.IStatusBarService
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.dump.logcatLogBuffer
 import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.log.logcatLogBuffer
 import com.android.systemui.plugins.FalsingManager
 import com.android.systemui.plugins.PluginManager
 import com.android.systemui.plugins.statusbar.StatusBarStateController
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineTest.java
index cf5b3cd..6faebf6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineTest.java
@@ -16,7 +16,7 @@
 
 package com.android.systemui.statusbar.notification.row;
 
-import static com.android.systemui.dump.LogBufferHelperKt.logcatLogBuffer;
+import static com.android.systemui.log.LogBufferHelperKt.logcatLogBuffer;
 
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
index 0cd834d..a6bfaa4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
@@ -21,7 +21,7 @@
 import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
 import static android.app.NotificationManager.IMPORTANCE_HIGH;
 
-import static com.android.systemui.dump.LogBufferHelperKt.logcatLogBuffer;
+import static com.android.systemui.log.LogBufferHelperKt.logcatLogBuffer;
 import static com.android.systemui.statusbar.NotificationEntryHelper.modifyRanking;
 
 import static org.junit.Assert.assertEquals;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/RowContentBindStageTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/RowContentBindStageTest.java
index 32f0fe7..76470db 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/RowContentBindStageTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/RowContentBindStageTest.java
@@ -16,7 +16,7 @@
 
 package com.android.systemui.statusbar.notification.row;
 
-import static com.android.systemui.dump.LogBufferHelperKt.logcatLogBuffer;
+import static com.android.systemui.log.LogBufferHelperKt.logcatLogBuffer;
 import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_ALL;
 import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED;
 import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_EXPANDED;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
index 7558974..88662b6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
@@ -16,7 +16,7 @@
 
 package com.android.systemui.statusbar.notification.stack;
 
-import static com.android.systemui.dump.LogBufferHelperKt.logcatLogBuffer;
+import static com.android.systemui.log.LogBufferHelperKt.logcatLogBuffer;
 import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
 import static com.android.systemui.statusbar.StatusBarState.SHADE;
 import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_ALL;
@@ -38,6 +38,7 @@
 import static kotlinx.coroutines.test.TestCoroutineDispatchersKt.StandardTestDispatcher;
 
 import android.metrics.LogMaker;
+import android.platform.test.annotations.DisableFlags;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.view.View;
@@ -84,6 +85,7 @@
 import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository;
 import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor;
 import com.android.systemui.statusbar.notification.domain.interactor.SeenNotificationsInteractor;
+import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor;
 import com.android.systemui.statusbar.notification.init.NotificationsController;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
@@ -218,6 +220,7 @@
     }
 
     @Test
+    @DisableFlags(FooterViewRefactor.FLAG_NAME)
     public void testUpdateEmptyShadeView_notificationsVisible_zenHiding() {
         when(mZenModeController.areNotificationsHiddenInShade()).thenReturn(true);
         initController(/* viewIsAttached= */ true);
@@ -238,6 +241,7 @@
     }
 
     @Test
+    @DisableFlags(FooterViewRefactor.FLAG_NAME)
     public void testUpdateEmptyShadeView_notificationsHidden_zenNotHiding() {
         when(mZenModeController.areNotificationsHiddenInShade()).thenReturn(false);
         initController(/* viewIsAttached= */ true);
@@ -258,6 +262,7 @@
     }
 
     @Test
+    @DisableFlags(FooterViewRefactor.FLAG_NAME)
     public void testUpdateEmptyShadeView_splitShadeMode_alwaysShowEmptyView() {
         when(mZenModeController.areNotificationsHiddenInShade()).thenReturn(false);
         initController(/* viewIsAttached= */ true);
@@ -285,6 +290,7 @@
     }
 
     @Test
+    @DisableFlags(FooterViewRefactor.FLAG_NAME)
     public void testUpdateEmptyShadeView_bouncerShowing_flagOff_hideEmptyView() {
         when(mZenModeController.areNotificationsHiddenInShade()).thenReturn(false);
         initController(/* viewIsAttached= */ true);
@@ -306,6 +312,7 @@
     }
 
     @Test
+    @DisableFlags(FooterViewRefactor.FLAG_NAME)
     public void testUpdateEmptyShadeView_bouncerShowing_flagOn_hideEmptyView() {
         when(mZenModeController.areNotificationsHiddenInShade()).thenReturn(false);
         initController(/* viewIsAttached= */ true);
@@ -327,6 +334,7 @@
     }
 
     @Test
+    @DisableFlags(FooterViewRefactor.FLAG_NAME)
     public void testUpdateEmptyShadeView_bouncerNotShowing_flagOff_showEmptyView() {
         when(mZenModeController.areNotificationsHiddenInShade()).thenReturn(false);
         initController(/* viewIsAttached= */ true);
@@ -348,6 +356,7 @@
     }
 
     @Test
+    @DisableFlags(FooterViewRefactor.FLAG_NAME)
     public void testUpdateEmptyShadeView_bouncerNotShowing_flagOn_showEmptyView() {
         when(mZenModeController.areNotificationsHiddenInShade()).thenReturn(false);
         initController(/* viewIsAttached= */ true);
@@ -504,6 +513,7 @@
     }
 
     @Test
+    @DisableFlags(FooterViewRefactor.FLAG_NAME)
     public void testSetNotifStats_updatesHasFilteredOutSeenNotifications() {
         initController(/* viewIsAttached= */ true);
         mSeenNotificationsInteractor.setHasFilteredOutSeenNotifications(true);
@@ -545,6 +555,7 @@
     }
 
     @Test
+    @DisableFlags(FooterViewRefactor.FLAG_NAME)
     public void updateImportantForAccessibility_noChild_onKeyGuard_notImportantForA11y() {
         // GIVEN: Controller is attached, active notifications is empty,
         // and mNotificationStackScrollLayout.onKeyguard() is true
@@ -561,6 +572,7 @@
     }
 
     @Test
+    @DisableFlags(FooterViewRefactor.FLAG_NAME)
     public void updateImportantForAccessibility_hasChild_onKeyGuard_importantForA11y() {
         // GIVEN: Controller is attached, active notifications is not empty,
         // and mNotificationStackScrollLayout.onKeyguard() is true
@@ -584,6 +596,7 @@
     }
 
     @Test
+    @DisableFlags(FooterViewRefactor.FLAG_NAME)
     public void updateImportantForAccessibility_hasChild_notOnKeyGuard_importantForA11y() {
         // GIVEN: Controller is attached, active notifications is not empty,
         // and mNotificationStackScrollLayout.onKeyguard() is false
@@ -607,6 +620,7 @@
     }
 
     @Test
+    @DisableFlags(FooterViewRefactor.FLAG_NAME)
     public void updateImportantForAccessibility_noChild_notOnKeyGuard_importantForA11y() {
         // GIVEN: Controller is attached, active notifications is empty,
         // and mNotificationStackScrollLayout.onKeyguard() is false
@@ -623,6 +637,7 @@
     }
 
     @Test
+    @DisableFlags(FooterViewRefactor.FLAG_NAME)
     public void updateEmptyShadeView_onKeyguardTransitionToAod_hidesView() {
         initController(/* viewIsAttached= */ true);
         mController.onKeyguardTransitionChanged(
@@ -633,6 +648,7 @@
     }
 
     @Test
+    @DisableFlags(FooterViewRefactor.FLAG_NAME)
     public void updateEmptyShadeView_onKeyguardOccludedTransitionToAod_hidesView() {
         initController(/* viewIsAttached= */ true);
         mController.onKeyguardTransitionChanged(
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 ad7dee3..a172120 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
@@ -51,6 +51,8 @@
 
 import android.graphics.Insets;
 import android.graphics.Rect;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.testing.TestableResources;
@@ -81,6 +83,7 @@
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
 import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
+import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor;
 import com.android.systemui.statusbar.notification.footer.ui.view.FooterView;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.ExpandableView;
@@ -191,7 +194,7 @@
         mStackScrollerInternal.initView(getContext(), mNotificationSwipeHelper,
                 mNotificationStackSizeCalculator);
         mStackScroller = spy(mStackScrollerInternal);
-        mStackScroller.setResetUserExpandedStatesRunnable(()->{});
+        mStackScroller.setResetUserExpandedStatesRunnable(() -> {});
         mStackScroller.setEmptyShadeView(mEmptyShadeView);
         when(mStackScrollLayoutController.isHistoryEnabled()).thenReturn(true);
         when(mStackScrollLayoutController.getNotificationRoundnessManager())
@@ -309,7 +312,9 @@
     public void updateEmptyView_dndSuppressing() {
         when(mEmptyShadeView.willBeGone()).thenReturn(true);
 
-        mStackScroller.updateEmptyShadeView(true, true);
+        mStackScroller.updateEmptyShadeView(/* visible = */ true,
+                /* areNotificationsHiddenInShade = */ true,
+                /* hasFilteredOutSeenNotifications = */ false);
 
         verify(mEmptyShadeView).setText(R.string.dnd_suppressing_shade_text);
     }
@@ -319,7 +324,9 @@
         mStackScroller.setEmptyShadeView(mEmptyShadeView);
         when(mEmptyShadeView.willBeGone()).thenReturn(true);
 
-        mStackScroller.updateEmptyShadeView(true, false);
+        mStackScroller.updateEmptyShadeView(/* visible = */ true,
+                /* areNotificationsHiddenInShade = */ false,
+                /* hasFilteredOutSeenNotifications = */ false);
 
         verify(mEmptyShadeView).setText(R.string.empty_shade_text);
     }
@@ -328,10 +335,14 @@
     public void updateEmptyView_noNotificationsToDndSuppressing() {
         mStackScroller.setEmptyShadeView(mEmptyShadeView);
         when(mEmptyShadeView.willBeGone()).thenReturn(true);
-        mStackScroller.updateEmptyShadeView(true, false);
+        mStackScroller.updateEmptyShadeView(/* visible = */ true,
+                /* areNotificationsHiddenInShade = */ false,
+                /* hasFilteredOutSeenNotifications = */ false);
         verify(mEmptyShadeView).setText(R.string.empty_shade_text);
 
-        mStackScroller.updateEmptyShadeView(true, true);
+        mStackScroller.updateEmptyShadeView(/* visible = */ true,
+                /* areNotificationsHiddenInShade = */ true,
+                /* hasFilteredOutSeenNotifications = */ false);
         verify(mEmptyShadeView).setText(R.string.dnd_suppressing_shade_text);
     }
 
@@ -385,8 +396,8 @@
         mStackScroller.setExpandedHeight(100f);
     }
 
-
     @Test
+    @DisableFlags(FooterViewRefactor.FLAG_NAME)
     public void manageNotifications_visible() {
         FooterView view = mock(FooterView.class);
         mStackScroller.setFooterView(view);
@@ -399,6 +410,7 @@
     }
 
     @Test
+    @DisableFlags(FooterViewRefactor.FLAG_NAME)
     public void clearAll_visible() {
         FooterView view = mock(FooterView.class);
         mStackScroller.setFooterView(view);
@@ -411,6 +423,7 @@
     }
 
     @Test
+    @DisableFlags(FooterViewRefactor.FLAG_NAME)
     public void testInflateFooterView() {
         mStackScroller.inflateFooterView();
         ArgumentCaptor<FooterView> captor = ArgumentCaptor.forClass(FooterView.class);
@@ -444,7 +457,7 @@
         FooterView view = mock(FooterView.class);
         mStackScroller.setFooterView(view);
         mStackScroller.updateFooter();
-        verify(mStackScroller).updateFooterView(false, true, true);
+        verify(mStackScroller, atLeastOnce()).updateFooterView(false, true, true);
     }
 
     @Test
@@ -459,7 +472,7 @@
         FooterView view = mock(FooterView.class);
         mStackScroller.setFooterView(view);
         mStackScroller.updateFooter();
-        verify(mStackScroller).updateFooterView(false, false, true);
+        verify(mStackScroller, atLeastOnce()).updateFooterView(false, false, true);
     }
 
     @Test
@@ -474,7 +487,7 @@
         FooterView view = mock(FooterView.class);
         mStackScroller.setFooterView(view);
         mStackScroller.updateFooter();
-        verify(mStackScroller).updateFooterView(true, true, true);
+        verify(mStackScroller, atLeastOnce()).updateFooterView(true, true, true);
     }
 
     @Test
@@ -490,7 +503,7 @@
         FooterView view = mock(FooterView.class);
         mStackScroller.setFooterView(view);
         mStackScroller.updateFooter();
-        verify(mStackScroller).updateFooterView(true, true, false);
+        verify(mStackScroller, atLeastOnce()).updateFooterView(true, true, false);
     }
 
     @Test
@@ -505,7 +518,7 @@
         FooterView view = mock(FooterView.class);
         mStackScroller.setFooterView(view);
         mStackScroller.updateFooter();
-        verify(mStackScroller).updateFooterView(false, true, true);
+        verify(mStackScroller, atLeastOnce()).updateFooterView(false, true, true);
     }
 
     @Test
@@ -521,7 +534,7 @@
         FooterView view = mock(FooterView.class);
         mStackScroller.setFooterView(view);
         mStackScroller.updateFooter();
-        verify(mStackScroller).updateFooterView(true, false, true);
+        verify(mStackScroller, atLeastOnce()).updateFooterView(true, false, true);
     }
 
     @Test
@@ -529,7 +542,8 @@
         mStackScroller.setCurrentUserSetup(true);
 
         // add footer
-        mStackScroller.inflateFooterView();
+        FooterView view = mock(FooterView.class);
+        mStackScroller.setFooterView(view);
 
         // add notification
         ExpandableNotificationRow row = createClearableRow();
@@ -545,6 +559,7 @@
     }
 
     @Test
+    @DisableFlags(FooterViewRefactor.FLAG_NAME)
     public void testReInflatesFooterViews() {
         when(mEmptyShadeView.getTextResource()).thenReturn(R.string.empty_shade_text);
         clearInvocations(mStackScroller);
@@ -554,6 +569,16 @@
     }
 
     @Test
+    @EnableFlags(FooterViewRefactor.FLAG_NAME)
+    public void testReInflatesEmptyShadeView() {
+        when(mEmptyShadeView.getTextResource()).thenReturn(R.string.empty_shade_text);
+        clearInvocations(mStackScroller);
+        mStackScroller.reinflateViews();
+        verify(mStackScroller, never()).setFooterView(any());
+        verify(mStackScroller).setEmptyShadeView(any());
+    }
+
+    @Test
     public void testSetIsBeingDraggedResetsExposedMenu() {
         mStackScroller.setIsBeingDragged(true);
         verify(mNotificationSwipeHelper).resetExposedMenuView(true, true);
@@ -872,6 +897,7 @@
     }
 
     @Test
+    @DisableFlags(FooterViewRefactor.FLAG_NAME)
     public void hasFilteredOutSeenNotifs_updateFooter() {
         mStackScroller.setCurrentUserSetup(true);
 
@@ -887,6 +913,7 @@
     }
 
     @Test
+    @DisableFlags(FooterViewRefactor.FLAG_NAME)
     public void hasFilteredOutSeenNotifs_updateEmptyShadeView() {
         mStackScroller.setHasFilteredOutSeenNotifications(true);
         mStackScroller.updateEmptyShadeView(true, false);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
index 36a4712..20020f2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
@@ -43,6 +43,7 @@
 import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
@@ -418,13 +419,13 @@
         }
 
     @Test
-    fun shadeCollpaseFadeIn() =
+    fun shadeCollapseFadeIn() =
         testScope.runTest {
+            val fadeIn by collectLastValue(underTest.shadeCollpaseFadeIn)
+
             // Start on lockscreen without the shade
             underTest.setShadeCollapseFadeInComplete(false)
             showLockscreen()
-
-            val fadeIn by collectLastValue(underTest.shadeCollpaseFadeIn)
             assertThat(fadeIn).isEqualTo(false)
 
             // ... then the shade expands
@@ -440,10 +441,12 @@
             assertThat(fadeIn).isEqualTo(false)
         }
 
-    private suspend fun showLockscreen() {
+    private suspend fun TestScope.showLockscreen() {
         shadeRepository.setLockscreenShadeExpansion(0f)
         shadeRepository.setQsExpansion(0f)
+        runCurrent()
         keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
+        runCurrent()
         keyguardTransitionRepository.sendTransitionSteps(
             from = KeyguardState.AOD,
             to = KeyguardState.LOCKSCREEN,
@@ -451,10 +454,12 @@
         )
     }
 
-    private suspend fun showLockscreenWithShadeExpanded() {
+    private suspend fun TestScope.showLockscreenWithShadeExpanded() {
         shadeRepository.setLockscreenShadeExpansion(1f)
         shadeRepository.setQsExpansion(0f)
+        runCurrent()
         keyguardRepository.setStatusBarState(StatusBarState.SHADE_LOCKED)
+        runCurrent()
         keyguardTransitionRepository.sendTransitionSteps(
             from = KeyguardState.AOD,
             to = KeyguardState.LOCKSCREEN,
@@ -462,10 +467,12 @@
         )
     }
 
-    private suspend fun showLockscreenWithQSExpanded() {
+    private suspend fun TestScope.showLockscreenWithQSExpanded() {
         shadeRepository.setLockscreenShadeExpansion(0f)
         shadeRepository.setQsExpansion(1f)
+        runCurrent()
         keyguardRepository.setStatusBarState(StatusBarState.SHADE_LOCKED)
+        runCurrent()
         keyguardTransitionRepository.sendTransitionSteps(
             from = KeyguardState.AOD,
             to = KeyguardState.LOCKSCREEN,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
index e339636..316f2b9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
@@ -334,15 +334,8 @@
     public void setup() throws Exception {
         MockitoAnnotations.initMocks(this);
 
-        // CentralSurfacesImpl's runtime flag check fails if the flag is absent.
-        // This value is unused, because test manifest is opted in.
-        mFeatureFlags.set(Flags.WM_ENABLE_PREDICTIVE_BACK_SYSUI, false);
         // Set default value to avoid IllegalStateException.
         mFeatureFlags.set(Flags.SHORTCUT_LIST_SEARCH_LAYOUT, false);
-        // For the Shade to respond to Back gesture, we must enable the event routing
-        mFeatureFlags.set(Flags.WM_SHADE_ALLOW_BACK_GESTURE, true);
-        // For the Shade to animate during the Back gesture, we must enable the animation flag.
-        mFeatureFlags.set(Flags.WM_SHADE_ANIMATE_BACK_GESTURE, true);
         mSetFlagsRule.enableFlags(FLAG_LIGHT_REVEAL_MIGRATION);
         // Turn AOD on and toggle feature flag for jank fixes
         mFeatureFlags.set(Flags.ZJ_285570694_LOCKSCREEN_TRANSITION_FROM_AOD, true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
index e1bd89f..2b1f5fc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
@@ -16,7 +16,7 @@
 
 package com.android.systemui.statusbar.phone;
 
-import static com.android.systemui.dump.LogBufferHelperKt.logcatLogBuffer;
+import static com.android.systemui.log.LogBufferHelperKt.logcatLogBuffer;
 import static com.android.systemui.util.concurrency.MockExecutorHandlerKt.mockExecutorHandler;
 
 import static junit.framework.Assert.assertFalse;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
index 3556703..4dc4798 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
@@ -84,7 +84,7 @@
         LogBuffer logBuffer = FakeLogBuffer.Factory.Companion.create();
         mClockPositionAlgorithm = new KeyguardClockPositionAlgorithm(logBuffer);
         when(mResources.getDimensionPixelSize(anyInt())).thenReturn(0);
-        mClockPositionAlgorithm.loadDimens(mResources);
+        mClockPositionAlgorithm.loadDimens(mContext, mResources);
 
         mClockPosition = new KeyguardClockPositionAlgorithm.Result();
     }
@@ -297,7 +297,7 @@
                 .thenReturn(100);
         when(mResources.getDimensionPixelSize(R.dimen.large_screen_shade_header_height))
                 .thenReturn(70);
-        mClockPositionAlgorithm.loadDimens(mResources);
+        mClockPositionAlgorithm.loadDimens(mContext, mResources);
         givenLockScreen();
         mIsSplitShade = true;
         // WHEN the position algorithm is run
@@ -589,7 +589,7 @@
     private void setSplitShadeTopMargin(int value) {
         when(mResources.getDimensionPixelSize(R.dimen.keyguard_split_shade_top_margin))
                 .thenReturn(value);
-        mClockPositionAlgorithm.loadDimens(mResources);
+        mClockPositionAlgorithm.loadDimens(mContext, mResources);
     }
 
     private void givenHighestBurnInOffset() {
@@ -603,7 +603,7 @@
     private void givenMaxBurnInOffset(int offset) {
         when(mResources.getDimensionPixelSize(R.dimen.burn_in_prevention_offset_y_clock))
                 .thenReturn(offset);
-        mClockPositionAlgorithm.loadDimens(mResources);
+        mClockPositionAlgorithm.loadDimens(mContext, mResources);
     }
 
     private void givenAOD() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ManagedProfileControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ManagedProfileControllerImplTest.kt
index 7eba3b46..c44c178 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ManagedProfileControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ManagedProfileControllerImplTest.kt
@@ -22,11 +22,15 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.settings.UserTracker
 import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.argumentCaptor
+import com.android.systemui.util.mockito.capture
 import com.android.systemui.util.time.FakeSystemClock
 import junit.framework.Assert
 import org.junit.Before
 import org.junit.Test
 import org.mockito.Mock
+import org.mockito.Mockito.verify
 import org.mockito.Mockito.`when`
 import org.mockito.MockitoAnnotations
 
@@ -72,6 +76,34 @@
         Assert.assertEquals(true, controller.hasActiveProfile())
     }
 
+    @Test
+    fun callbackRemovedWhileDispatching_doesntCrash() {
+        var remove = false
+        val callback =
+            object : ManagedProfileController.Callback {
+                override fun onManagedProfileChanged() {
+                    if (remove) {
+                        controller.removeCallback(this)
+                    }
+                }
+
+                override fun onManagedProfileRemoved() {
+                    if (remove) {
+                        controller.removeCallback(this)
+                    }
+                }
+            }
+        controller.addCallback(callback)
+        controller.addCallback(TestCallback)
+
+        remove = true
+        setupWorkingProfile(1)
+
+        val captor = argumentCaptor<UserTracker.Callback>()
+        verify(userTracker).addCallback(capture(captor), any())
+        captor.value.onProfilesChanged(userManager.getEnabledProfiles(1))
+    }
+
     private fun setupWorkingProfile(userId: Int) {
         `when`(userManager.getEnabledProfiles(userId))
             .thenReturn(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImplTest.kt
index 6f04f36..f6a8243 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImplTest.kt
@@ -23,6 +23,9 @@
 import com.android.systemui.statusbar.CommandQueue
 import com.android.systemui.statusbar.phone.StatusBarIconController.TAG_PRIMARY
 import com.android.systemui.statusbar.phone.StatusBarIconControllerImpl.EXTERNAL_SLOT_SUFFIX
+import com.android.systemui.statusbar.pipeline.icons.shared.BindableIconsRegistry
+import com.android.systemui.statusbar.pipeline.icons.shared.model.BindableIcon
+import com.android.systemui.statusbar.pipeline.icons.shared.model.ModernStatusBarViewCreator
 import com.android.systemui.util.mockito.kotlinArgumentCaptor
 import com.android.systemui.util.mockito.mock
 import com.google.common.truth.Truth.assertThat
@@ -49,14 +52,15 @@
         iconList = StatusBarIconList(arrayOf())
         underTest =
             StatusBarIconControllerImpl(
-                context,
-                commandQueue,
-                mock(),
-                mock(),
-                mock(),
-                mock(),
-                iconList,
-                mock(),
+                /* context = */ context,
+                /* commandQueue = */ commandQueue,
+                /* demoModeController = */ mock(),
+                /* configurationController = */ mock(),
+                /* tunerService = */ mock(),
+                /* dumpManager = */ mock(),
+                /* statusBarIconList = */ iconList,
+                /* statusBarPipelineFlags = */ mock(),
+                /* modernIconsRegistry = */ mock(),
             )
         underTest.addIconGroup(iconGroup)
         val commandQueueCallbacksCaptor = kotlinArgumentCaptor<CommandQueue.Callbacks>()
@@ -366,6 +370,31 @@
         assertThat(iconList.slots[0].name).isEqualTo("myslot$EXTERNAL_SLOT_SUFFIX")
     }
 
+    @Test
+    fun bindableIcons_addedOnInit() {
+        val fakeIcon = FakeBindableIcon("test_slot")
+
+        iconList = StatusBarIconList(arrayOf())
+
+        // WHEN there are registered icons
+        underTest =
+            StatusBarIconControllerImpl(
+                /* context = */ context,
+                /* commandQueue = */ commandQueue,
+                /* demoModeController = */ mock(),
+                /* configurationController = */ mock(),
+                /* tunerService = */ mock(),
+                /* dumpManager = */ mock(),
+                /* statusBarIconList = */ iconList,
+                /* statusBarPipelineFlags = */ mock(),
+                /* modernIconsRegistry = */ FakeBindableIconsRegistry(listOf(fakeIcon)),
+            )
+
+        // THEN they are properly added to the list on init
+        assertThat(iconList.getIconHolder("test_slot", 0))
+            .isInstanceOf(StatusBarIconHolder.BindableIconHolder::class.java)
+    }
+
     private fun createExternalIcon(): StatusBarIcon {
         return StatusBarIcon(
             "external.package",
@@ -377,3 +406,20 @@
         )
     }
 }
+
+class FakeBindableIconsRegistry(
+    override val bindableIcons: List<BindableIcon>,
+) : BindableIconsRegistry
+
+class FakeBindableIcon(
+    override val slot: String,
+    override val shouldBindIcon: Boolean = true,
+) : BindableIcon {
+    // Track initialized so we can know that our icon was properly bound
+    var hasInitialized = false
+
+    override val initializer = ModernStatusBarViewCreator { _ ->
+        hasInitialized = true
+        mock()
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java
index 0ff6f20..ca31623 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java
@@ -43,6 +43,7 @@
 import com.android.systemui.statusbar.phone.StatusBarIconController.DarkIconManager;
 import com.android.systemui.statusbar.phone.StatusBarIconController.IconManager;
 import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags;
+import com.android.systemui.statusbar.pipeline.icons.shared.BindableIconsRegistry;
 import com.android.systemui.statusbar.pipeline.mobile.ui.MobileUiAdapter;
 import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsViewModel;
 import com.android.systemui.statusbar.pipeline.wifi.ui.WifiUiAdapter;
@@ -108,7 +109,8 @@
                 mock(TunerService.class),
                 mock(DumpManager.class),
                 mock(StatusBarIconList.class),
-                flags
+                flags,
+                mock(BindableIconsRegistry.class)
         );
 
         iconController.addIconGroup(manager);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index 225ddb6..8dde935 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -37,6 +37,9 @@
 
 import static kotlinx.coroutines.test.TestCoroutineDispatchersKt.StandardTestDispatcher;
 
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 import android.service.trust.TrustAgentService;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
@@ -98,6 +101,7 @@
 import com.google.common.truth.Truth;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
@@ -165,6 +169,8 @@
     @Captor
     private ArgumentCaptor<KeyguardUpdateMonitorCallback> mKeyguardUpdateMonitorCallback;
 
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
 
     @Before
     public void setUp() {
@@ -175,7 +181,6 @@
         when(mBouncerView.getDelegate()).thenReturn(mBouncerViewDelegate);
         when(mBouncerViewDelegate.getBackCallback()).thenReturn(mBouncerViewDelegateBackCallback);
         mFeatureFlags = new FakeFeatureFlags();
-        mFeatureFlags.set(Flags.WM_ENABLE_PREDICTIVE_BACK_BOUNCER_ANIM, true);
         mFeatureFlags.set(Flags.REFACTOR_KEYGUARD_DISMISS_INTENT, false);
         mFeatureFlags.set(Flags.KEYGUARD_WM_STATE_REFACTOR, false);
         mSetFlagsRule.disableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR);
@@ -584,6 +589,7 @@
     }
 
     @Test
+    @RequiresFlagsEnabled(com.android.systemui.Flags.FLAG_PREDICTIVE_BACK_ANIMATE_BOUNCER)
     public void testPredictiveBackCallback_registration() {
         /* verify that a predictive back callback is registered when the bouncer becomes visible */
         mBouncerExpansionCallback.onVisibilityChanged(true);
@@ -598,6 +604,7 @@
     }
 
     @Test
+    @RequiresFlagsEnabled(com.android.systemui.Flags.FLAG_PREDICTIVE_BACK_ANIMATE_BOUNCER)
     public void testPredictiveBackCallback_invocationHidesBouncer() {
         mBouncerExpansionCallback.onVisibilityChanged(true);
         /* capture the predictive back callback during registration */
@@ -615,6 +622,7 @@
     }
 
     @Test
+    @RequiresFlagsEnabled(com.android.systemui.Flags.FLAG_PREDICTIVE_BACK_ANIMATE_BOUNCER)
     public void testPredictiveBackCallback_noBackAnimationForFullScreenBouncer() {
         when(mKeyguardSecurityModel.getSecurityMode(anyInt()))
                 .thenReturn(KeyguardSecurityModel.SecurityMode.SimPin);
@@ -634,6 +642,7 @@
     }
 
     @Test
+    @RequiresFlagsEnabled(com.android.systemui.Flags.FLAG_PREDICTIVE_BACK_ANIMATE_BOUNCER)
     public void testPredictiveBackCallback_forwardsBackDispatches() {
         mBouncerExpansionCallback.onVisibilityChanged(true);
         /* capture the predictive back callback during registration */
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarMoveFromCenterAnimationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarMoveFromCenterAnimationControllerTest.kt
index 7594c90..feff046 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarMoveFromCenterAnimationControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarMoveFromCenterAnimationControllerTest.kt
@@ -1,6 +1,7 @@
 package com.android.systemui.statusbar.phone
 
 import android.graphics.Point
+import android.testing.TestableLooper
 import android.view.Display
 import android.view.Surface
 import android.view.View
@@ -19,6 +20,7 @@
 import org.mockito.MockitoAnnotations
 
 @SmallTest
+@TestableLooper.RunWithLooper
 class StatusBarMoveFromCenterAnimationControllerTest : SysuiTestCase() {
 
     @Mock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
index 592c78f..597e2e3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
@@ -20,7 +20,7 @@
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
-import static com.android.systemui.dump.LogBufferHelperKt.logcatLogBuffer;
+import static com.android.systemui.log.LogBufferHelperKt.logcatLogBuffer;
 
 import static com.google.common.truth.Truth.assertThat;
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImplTest.kt
new file mode 100644
index 0000000..02e6fd5
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImplTest.kt
@@ -0,0 +1,391 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.satellite.data.prod
+
+import android.os.OutcomeReceiver
+import android.os.Process
+import android.telephony.satellite.NtnSignalStrength
+import android.telephony.satellite.NtnSignalStrengthCallback
+import android.telephony.satellite.SatelliteManager
+import android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED
+import android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_DATAGRAM_RETRYING
+import android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_DATAGRAM_TRANSFERRING
+import android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_IDLE
+import android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_LISTENING
+import android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED
+import android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_OFF
+import android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_UNAVAILABLE
+import android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_UNKNOWN
+import android.telephony.satellite.SatelliteManager.SatelliteException
+import android.telephony.satellite.SatelliteModemStateCallback
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.statusbar.pipeline.satellite.data.prod.DeviceBasedSatelliteRepositoryImpl.Companion.MIN_UPTIME
+import com.android.systemui.statusbar.pipeline.satellite.data.prod.DeviceBasedSatelliteRepositoryImpl.Companion.POLLING_INTERVAL_MS
+import com.android.systemui.statusbar.pipeline.satellite.shared.model.SatelliteConnectionState
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.whenever
+import com.android.systemui.util.mockito.withArgCaptor
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import java.util.Optional
+import kotlin.test.Test
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.advanceTimeBy
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.Mockito.doAnswer
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+class DeviceBasedSatelliteRepositoryImplTest : SysuiTestCase() {
+    private lateinit var underTest: DeviceBasedSatelliteRepositoryImpl
+
+    @Mock private lateinit var satelliteManager: SatelliteManager
+
+    private val systemClock = FakeSystemClock()
+    private val dispatcher = StandardTestDispatcher()
+    private val testScope = TestScope(dispatcher)
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+    }
+
+    @Test
+    fun nullSatelliteManager_usesDefaultValues() =
+        testScope.runTest {
+            setupDefaultRepo()
+            underTest =
+                DeviceBasedSatelliteRepositoryImpl(
+                    Optional.empty(),
+                    dispatcher,
+                    testScope.backgroundScope,
+                    systemClock,
+                )
+
+            val connectionState by collectLastValue(underTest.connectionState)
+            val strength by collectLastValue(underTest.signalStrength)
+            val allowed by collectLastValue(underTest.isSatelliteAllowedForCurrentLocation)
+
+            assertThat(connectionState).isEqualTo(SatelliteConnectionState.Off)
+            assertThat(strength).isEqualTo(0)
+            assertThat(allowed).isFalse()
+        }
+
+    @Test
+    fun connectionState_mapsFromSatelliteModemState() =
+        testScope.runTest {
+            setupDefaultRepo()
+            val latest by collectLastValue(underTest.connectionState)
+            runCurrent()
+            val callback =
+                withArgCaptor<SatelliteModemStateCallback> {
+                    verify(satelliteManager).registerForSatelliteModemStateChanged(any(), capture())
+                }
+
+            // Mapping from modem state to SatelliteConnectionState is rote, just run all of the
+            // possibilities here
+
+            // Off states
+            callback.onSatelliteModemStateChanged(SATELLITE_MODEM_STATE_OFF)
+            assertThat(latest).isEqualTo(SatelliteConnectionState.Off)
+            callback.onSatelliteModemStateChanged(SATELLITE_MODEM_STATE_UNAVAILABLE)
+            assertThat(latest).isEqualTo(SatelliteConnectionState.Off)
+
+            // On states
+            callback.onSatelliteModemStateChanged(SATELLITE_MODEM_STATE_IDLE)
+            assertThat(latest).isEqualTo(SatelliteConnectionState.On)
+            callback.onSatelliteModemStateChanged(SATELLITE_MODEM_STATE_LISTENING)
+            assertThat(latest).isEqualTo(SatelliteConnectionState.On)
+            callback.onSatelliteModemStateChanged(SATELLITE_MODEM_STATE_NOT_CONNECTED)
+            assertThat(latest).isEqualTo(SatelliteConnectionState.On)
+
+            // Connected states
+            callback.onSatelliteModemStateChanged(SATELLITE_MODEM_STATE_CONNECTED)
+            assertThat(latest).isEqualTo(SatelliteConnectionState.Connected)
+            callback.onSatelliteModemStateChanged(SATELLITE_MODEM_STATE_DATAGRAM_TRANSFERRING)
+            assertThat(latest).isEqualTo(SatelliteConnectionState.Connected)
+            callback.onSatelliteModemStateChanged(SATELLITE_MODEM_STATE_DATAGRAM_RETRYING)
+            assertThat(latest).isEqualTo(SatelliteConnectionState.Connected)
+
+            // Unknown states
+            callback.onSatelliteModemStateChanged(SATELLITE_MODEM_STATE_UNKNOWN)
+            assertThat(latest).isEqualTo(SatelliteConnectionState.Unknown)
+            // Garbage value (for completeness' sake)
+            callback.onSatelliteModemStateChanged(123456)
+            assertThat(latest).isEqualTo(SatelliteConnectionState.Unknown)
+        }
+
+    @Test
+    fun signalStrength_readsSatelliteManagerState() =
+        testScope.runTest {
+            setupDefaultRepo()
+            val latest by collectLastValue(underTest.signalStrength)
+            runCurrent()
+            val callback =
+                withArgCaptor<NtnSignalStrengthCallback> {
+                    verify(satelliteManager).registerForNtnSignalStrengthChanged(any(), capture())
+                }
+
+            assertThat(latest).isNull()
+
+            callback.onNtnSignalStrengthChanged(NtnSignalStrength(1))
+            assertThat(latest).isEqualTo(1)
+
+            callback.onNtnSignalStrengthChanged(NtnSignalStrength(2))
+            assertThat(latest).isEqualTo(2)
+
+            callback.onNtnSignalStrengthChanged(NtnSignalStrength(3))
+            assertThat(latest).isEqualTo(3)
+
+            callback.onNtnSignalStrengthChanged(NtnSignalStrength(4))
+            assertThat(latest).isEqualTo(4)
+        }
+
+    @Test
+    fun isSatelliteAllowed_readsSatelliteManagerState_enabled() =
+        testScope.runTest {
+            setupDefaultRepo()
+            // GIVEN satellite is allowed in this location
+            val allowed = true
+
+            doAnswer {
+                    val receiver = it.arguments[1] as OutcomeReceiver<Boolean, SatelliteException>
+                    receiver.onResult(allowed)
+                    null
+                }
+                .`when`(satelliteManager)
+                .requestIsSatelliteCommunicationAllowedForCurrentLocation(
+                    any(),
+                    any<OutcomeReceiver<Boolean, SatelliteException>>()
+                )
+
+            val latest by collectLastValue(underTest.isSatelliteAllowedForCurrentLocation)
+
+            assertThat(latest).isTrue()
+        }
+
+    @Test
+    fun isSatelliteAllowed_readsSatelliteManagerState_disabled() =
+        testScope.runTest {
+            setupDefaultRepo()
+            // GIVEN satellite is not allowed in this location
+            val allowed = false
+
+            doAnswer {
+                    val receiver = it.arguments[1] as OutcomeReceiver<Boolean, SatelliteException>
+                    receiver.onResult(allowed)
+                    null
+                }
+                .`when`(satelliteManager)
+                .requestIsSatelliteCommunicationAllowedForCurrentLocation(
+                    any(),
+                    any<OutcomeReceiver<Boolean, SatelliteException>>()
+                )
+
+            val latest by collectLastValue(underTest.isSatelliteAllowedForCurrentLocation)
+
+            assertThat(latest).isFalse()
+        }
+
+    @Test
+    fun isSatelliteAllowed_pollsOnTimeout() =
+        testScope.runTest {
+            setupDefaultRepo()
+            // GIVEN satellite is not allowed in this location
+            var allowed = false
+
+            doAnswer {
+                    val receiver = it.arguments[1] as OutcomeReceiver<Boolean, SatelliteException>
+                    receiver.onResult(allowed)
+                    null
+                }
+                .`when`(satelliteManager)
+                .requestIsSatelliteCommunicationAllowedForCurrentLocation(
+                    any(),
+                    any<OutcomeReceiver<Boolean, SatelliteException>>()
+                )
+
+            val latest by collectLastValue(underTest.isSatelliteAllowedForCurrentLocation)
+
+            assertThat(latest).isFalse()
+
+            // WHEN satellite becomes enabled
+            allowed = true
+
+            // WHEN the timeout has not yet been reached
+            advanceTimeBy(POLLING_INTERVAL_MS / 2)
+
+            // THEN the value is still false
+            assertThat(latest).isFalse()
+
+            // WHEN time advances beyond the polling interval
+            advanceTimeBy(POLLING_INTERVAL_MS / 2 + 1)
+
+            // THEN then new value is emitted
+            assertThat(latest).isTrue()
+        }
+
+    @Test
+    fun isSatelliteAllowed_pollingRestartsWhenCollectionRestarts() =
+        testScope.runTest {
+            setupDefaultRepo()
+            // Use the old school launch/cancel so we can simulate subscribers arriving and leaving
+
+            var latest: Boolean? = false
+            var job =
+                underTest.isSatelliteAllowedForCurrentLocation.onEach { latest = it }.launchIn(this)
+
+            // GIVEN satellite is not allowed in this location
+            var allowed = false
+
+            doAnswer {
+                    val receiver = it.arguments[1] as OutcomeReceiver<Boolean, SatelliteException>
+                    receiver.onResult(allowed)
+                    null
+                }
+                .`when`(satelliteManager)
+                .requestIsSatelliteCommunicationAllowedForCurrentLocation(
+                    any(),
+                    any<OutcomeReceiver<Boolean, SatelliteException>>()
+                )
+
+            assertThat(latest).isFalse()
+
+            // WHEN satellite becomes enabled
+            allowed = true
+
+            // WHEN the job is restarted
+            advanceTimeBy(POLLING_INTERVAL_MS / 2)
+
+            job.cancel()
+            job =
+                underTest.isSatelliteAllowedForCurrentLocation.onEach { latest = it }.launchIn(this)
+
+            // THEN the value is re-fetched
+            assertThat(latest).isTrue()
+
+            job.cancel()
+        }
+
+    @Test
+    fun isSatelliteAllowed_falseWhenErrorOccurs() =
+        testScope.runTest {
+            setupDefaultRepo()
+            doAnswer {
+                    val receiver = it.arguments[1] as OutcomeReceiver<Boolean, SatelliteException>
+                    receiver.onError(SatelliteException(1 /* unused */))
+                    null
+                }
+                .`when`(satelliteManager)
+                .requestIsSatelliteCommunicationAllowedForCurrentLocation(
+                    any(),
+                    any<OutcomeReceiver<Boolean, SatelliteException>>()
+                )
+
+            val latest by collectLastValue(underTest.isSatelliteAllowedForCurrentLocation)
+
+            assertThat(latest).isFalse()
+        }
+
+    @Test
+    fun satelliteNotSupported_listenersAreNotRegistered() =
+        testScope.runTest {
+            setupDefaultRepo()
+            // GIVEN satellite is not supported
+            setUpRepo(
+                uptime = MIN_UPTIME,
+                satMan = satelliteManager,
+                satelliteSupported = false,
+            )
+
+            // WHEN data is requested from the repo
+            val connectionState by collectLastValue(underTest.connectionState)
+            val signalStrength by collectLastValue(underTest.signalStrength)
+
+            // THEN the manager is not asked for the information, and default values are returned
+            verify(satelliteManager, never()).registerForSatelliteModemStateChanged(any(), any())
+            verify(satelliteManager, never()).registerForNtnSignalStrengthChanged(any(), any())
+        }
+
+    @Test
+    fun repoDoesNotCheckForSupportUntilMinUptime() =
+        testScope.runTest {
+            // GIVEN we init 100ms after sysui starts up
+            setUpRepo(
+                uptime = 100,
+                satMan = satelliteManager,
+                satelliteSupported = true,
+            )
+
+            // WHEN data is requested
+            val connectionState by collectLastValue(underTest.connectionState)
+            val signalStrength by collectLastValue(underTest.signalStrength)
+
+            // THEN we have not yet talked to satellite manager, since we are well before MIN_UPTIME
+            Mockito.verifyZeroInteractions(satelliteManager)
+
+            // WHEN enough time has passed
+            systemClock.advanceTime(MIN_UPTIME)
+            runCurrent()
+
+            // THEN we finally register with the satellite manager
+            verify(satelliteManager).registerForSatelliteModemStateChanged(any(), any())
+        }
+
+    private fun setUpRepo(
+        uptime: Long = MIN_UPTIME,
+        satMan: SatelliteManager? = satelliteManager,
+        satelliteSupported: Boolean = true,
+    ) {
+        doAnswer {
+                val callback: OutcomeReceiver<Boolean, SatelliteException> =
+                    it.getArgument(1) as OutcomeReceiver<Boolean, SatelliteException>
+                callback.onResult(satelliteSupported)
+            }
+            .whenever(satelliteManager)
+            .requestIsSatelliteSupported(any(), any())
+
+        systemClock.setUptimeMillis(Process.getStartUptimeMillis() + uptime)
+
+        underTest =
+            DeviceBasedSatelliteRepositoryImpl(
+                if (satMan != null) Optional.of(satMan) else Optional.empty(),
+                dispatcher,
+                testScope.backgroundScope,
+                systemClock,
+            )
+    }
+
+    // Set system time to MIN_UPTIME and create a repo with satellite supported
+    private fun setupDefaultRepo() {
+        setUpRepo(uptime = MIN_UPTIME, satMan = satelliteManager, satelliteSupported = true)
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/FakeDeviceBasedSatelliteRepository.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/FakeDeviceBasedSatelliteRepository.kt
new file mode 100644
index 0000000..5fa2d33
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/FakeDeviceBasedSatelliteRepository.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.pipeline.satellite.data.prod
+
+import com.android.systemui.statusbar.pipeline.satellite.data.DeviceBasedSatelliteRepository
+import com.android.systemui.statusbar.pipeline.satellite.shared.model.SatelliteConnectionState.Off
+import kotlinx.coroutines.flow.MutableStateFlow
+
+class FakeDeviceBasedSatelliteRepository() : DeviceBasedSatelliteRepository {
+    override val connectionState = MutableStateFlow(Off)
+
+    override val signalStrength = MutableStateFlow(0)
+
+    override val isSatelliteAllowedForCurrentLocation = MutableStateFlow(false)
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt
new file mode 100644
index 0000000..e010b86
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt
@@ -0,0 +1,322 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.satellite.domain.interactor
+
+import androidx.test.filters.SmallTest
+import com.android.internal.telephony.flags.Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.FakeMobileIconsInteractor
+import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
+import com.android.systemui.statusbar.pipeline.satellite.data.prod.FakeDeviceBasedSatelliteRepository
+import com.android.systemui.statusbar.pipeline.satellite.shared.model.SatelliteConnectionState
+import com.android.systemui.util.mockito.mock
+import com.google.common.truth.Truth.assertThat
+import kotlin.test.Test
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+
+@SmallTest
+class DeviceBasedSatelliteInteractorTest : SysuiTestCase() {
+    private lateinit var underTest: DeviceBasedSatelliteInteractor
+
+    private val dispatcher = StandardTestDispatcher()
+    private val testScope = TestScope(dispatcher)
+
+    private val iconsInteractor =
+        FakeMobileIconsInteractor(
+            FakeMobileMappingsProxy(),
+            mock(),
+        )
+
+    private val repo = FakeDeviceBasedSatelliteRepository()
+
+    @Before
+    fun setUp() {
+        mSetFlagsRule.enableFlags(FLAG_OEM_ENABLED_SATELLITE_FLAG)
+
+        underTest =
+            DeviceBasedSatelliteInteractor(
+                repo,
+                iconsInteractor,
+                testScope.backgroundScope,
+            )
+    }
+
+    @Test
+    fun isSatelliteAllowed_falseWhenNotAllowed() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.isSatelliteAllowed)
+
+            // WHEN satellite is allowed
+            repo.isSatelliteAllowedForCurrentLocation.value = false
+
+            // THEN the interactor returns false due to the flag value
+            assertThat(latest).isFalse()
+        }
+
+    @Test
+    fun isSatelliteAllowed_trueWhenAllowed() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.isSatelliteAllowed)
+
+            // WHEN satellite is allowed
+            repo.isSatelliteAllowedForCurrentLocation.value = true
+
+            // THEN the interactor returns false due to the flag value
+            assertThat(latest).isTrue()
+        }
+
+    @Test
+    fun isSatelliteAllowed_offWhenFlagIsOff() =
+        testScope.runTest {
+            // GIVEN feature is disabled
+            mSetFlagsRule.disableFlags(FLAG_OEM_ENABLED_SATELLITE_FLAG)
+
+            // Remake the interactor so the flag is read
+            underTest =
+                DeviceBasedSatelliteInteractor(
+                    repo,
+                    iconsInteractor,
+                    testScope.backgroundScope,
+                )
+
+            val latest by collectLastValue(underTest.isSatelliteAllowed)
+
+            // WHEN satellite is allowed
+            repo.isSatelliteAllowedForCurrentLocation.value = true
+
+            // THEN the interactor returns false due to the flag value
+            assertThat(latest).isFalse()
+        }
+
+    @Test
+    fun connectionState_matchesRepositoryValue() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.connectionState)
+
+            // Off
+            repo.connectionState.value = SatelliteConnectionState.Off
+            assertThat(latest).isEqualTo(SatelliteConnectionState.Off)
+
+            // On
+            repo.connectionState.value = SatelliteConnectionState.On
+            assertThat(latest).isEqualTo(SatelliteConnectionState.On)
+
+            // Connected
+            repo.connectionState.value = SatelliteConnectionState.Connected
+            assertThat(latest).isEqualTo(SatelliteConnectionState.Connected)
+
+            // Unknown
+            repo.connectionState.value = SatelliteConnectionState.Unknown
+            assertThat(latest).isEqualTo(SatelliteConnectionState.Unknown)
+        }
+
+    @Test
+    fun connectionState_offWhenFeatureIsDisabled() =
+        testScope.runTest {
+            // GIVEN the flag is disabled
+            mSetFlagsRule.disableFlags(FLAG_OEM_ENABLED_SATELLITE_FLAG)
+
+            // Remake the interactor so the flag is read
+            underTest =
+                DeviceBasedSatelliteInteractor(
+                    repo,
+                    iconsInteractor,
+                    testScope.backgroundScope,
+                )
+
+            val latest by collectLastValue(underTest.connectionState)
+
+            // THEN the state is always Off, regardless of status in system_server
+
+            // Off
+            repo.connectionState.value = SatelliteConnectionState.Off
+            assertThat(latest).isEqualTo(SatelliteConnectionState.Off)
+
+            // On
+            repo.connectionState.value = SatelliteConnectionState.On
+            assertThat(latest).isEqualTo(SatelliteConnectionState.Off)
+
+            // Connected
+            repo.connectionState.value = SatelliteConnectionState.Connected
+            assertThat(latest).isEqualTo(SatelliteConnectionState.Off)
+
+            // Unknown
+            repo.connectionState.value = SatelliteConnectionState.Unknown
+            assertThat(latest).isEqualTo(SatelliteConnectionState.Off)
+        }
+
+    @Test
+    fun signalStrength_matchesRepo() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.signalStrength)
+
+            repo.signalStrength.value = 1
+            assertThat(latest).isEqualTo(1)
+
+            repo.signalStrength.value = 2
+            assertThat(latest).isEqualTo(2)
+
+            repo.signalStrength.value = 3
+            assertThat(latest).isEqualTo(3)
+
+            repo.signalStrength.value = 4
+            assertThat(latest).isEqualTo(4)
+        }
+
+    @Test
+    fun signalStrength_zeroWhenDisabled() =
+        testScope.runTest {
+            // GIVEN the flag is enabled
+            mSetFlagsRule.disableFlags(FLAG_OEM_ENABLED_SATELLITE_FLAG)
+
+            // Remake the interactor so the flag is read
+            underTest =
+                DeviceBasedSatelliteInteractor(
+                    repo,
+                    iconsInteractor,
+                    testScope.backgroundScope,
+                )
+
+            val latest by collectLastValue(underTest.signalStrength)
+
+            // THEN the value is always 0, regardless of what the system says
+            repo.signalStrength.value = 1
+            assertThat(latest).isEqualTo(0)
+
+            repo.signalStrength.value = 2
+            assertThat(latest).isEqualTo(0)
+
+            repo.signalStrength.value = 3
+            assertThat(latest).isEqualTo(0)
+
+            repo.signalStrength.value = 4
+            assertThat(latest).isEqualTo(0)
+        }
+
+    @Test
+    fun areAllConnectionsOutOfService_twoConnectionsOos_yes() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.areAllConnectionsOutOfService)
+
+            // GIVEN, 2 connections
+            val i1 = iconsInteractor.getMobileConnectionInteractorForSubId(1)
+            val i2 = iconsInteractor.getMobileConnectionInteractorForSubId(2)
+
+            // WHEN all of the connections are OOS
+            i1.isInService.value = false
+            i2.isInService.value = false
+
+            // THEN the value is propagated to this interactor
+            assertThat(latest).isTrue()
+        }
+
+    @Test
+    fun areAllConnectionsOutOfService_oneConnectionOos_yes() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.areAllConnectionsOutOfService)
+
+            // GIVEN, 1 connection
+            val i1 = iconsInteractor.getMobileConnectionInteractorForSubId(1)
+
+            // WHEN all of the connections are OOS
+            i1.isInService.value = false
+
+            // THEN the value is propagated to this interactor
+            assertThat(latest).isTrue()
+        }
+
+    @Test
+    fun areAllConnectionsOutOfService_oneConnectionInService_no() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.areAllConnectionsOutOfService)
+
+            // GIVEN, 1 connection
+            val i1 = iconsInteractor.getMobileConnectionInteractorForSubId(1)
+
+            // WHEN all of the connections are NOT OOS
+            i1.isInService.value = true
+
+            // THEN the value is propagated to this interactor
+            assertThat(latest).isFalse()
+        }
+
+    @Test
+    fun areAllConnectionsOutOfService_twoConnectionsOneInService_no() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.areAllConnectionsOutOfService)
+
+            // GIVEN, 2 connection
+            val i1 = iconsInteractor.getMobileConnectionInteractorForSubId(1)
+            val i2 = iconsInteractor.getMobileConnectionInteractorForSubId(2)
+
+            // WHEN at least 1 connection is NOT OOS.
+            i1.isInService.value = false
+            i2.isInService.value = true
+
+            // THEN the value is propagated to this interactor
+            assertThat(latest).isFalse()
+        }
+
+    @Test
+    fun areAllConnectionsOutOfService_twoConnectionsInService_no() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.areAllConnectionsOutOfService)
+
+            // GIVEN, 2 connection
+            val i1 = iconsInteractor.getMobileConnectionInteractorForSubId(1)
+            val i2 = iconsInteractor.getMobileConnectionInteractorForSubId(1)
+
+            // WHEN all connections are NOT OOS.
+            i1.isInService.value = true
+            i2.isInService.value = true
+
+            // THEN the value is propagated to this interactor
+            assertThat(latest).isFalse()
+        }
+
+    @Test
+    fun areAllConnectionsOutOfService_falseWhenFlagIsOff() =
+        testScope.runTest {
+            // GIVEN the flag is disabled
+            mSetFlagsRule.disableFlags(FLAG_OEM_ENABLED_SATELLITE_FLAG)
+
+            // Remake the interactor so the flag is read
+            underTest =
+                DeviceBasedSatelliteInteractor(
+                    repo,
+                    iconsInteractor,
+                    testScope.backgroundScope,
+                )
+
+            val latest by collectLastValue(underTest.areAllConnectionsOutOfService)
+
+            // GIVEN a condition that should return true (all conections OOS)
+
+            val i1 = iconsInteractor.getMobileConnectionInteractorForSubId(1)
+            val i2 = iconsInteractor.getMobileConnectionInteractorForSubId(1)
+
+            i1.isInService.value = true
+            i2.isInService.value = true
+
+            // THEN the value is still false, because the flag is off
+            assertThat(latest).isFalse()
+        }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java
index 4c893e3..6a3b2c3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java
@@ -18,7 +18,7 @@
 
 import static android.app.Notification.FLAG_FSI_REQUESTED_BUT_DENIED;
 
-import static com.android.systemui.dump.LogBufferHelperKt.logcatLogBuffer;
+import static com.android.systemui.log.LogBufferHelperKt.logcatLogBuffer;
 import static com.android.systemui.util.concurrency.MockExecutorHandlerKt.mockExecutorHandler;
 
 import static com.google.common.truth.Truth.assertThat;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryControllerTest.java
index 2f79955..a5c766d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryControllerTest.java
@@ -17,10 +17,12 @@
 package com.android.systemui.statusbar.policy;
 
 import static android.os.BatteryManager.EXTRA_PRESENT;
+
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.inOrder;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.staticMockMarker;
 import static com.android.settingslib.fuelgauge.BatterySaverLogging.SAVER_ENABLED_QS;
+
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
@@ -59,6 +61,7 @@
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
@@ -286,6 +289,24 @@
         Assert.assertFalse(mBatteryController.isIncompatibleCharging());
     }
 
+    @Test
+    public void callbackRemovedWhileDispatching_doesntCrash() {
+        final AtomicBoolean remove = new AtomicBoolean(false);
+        BatteryStateChangeCallback callback = new BatteryStateChangeCallback() {
+            @Override
+            public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
+                if (remove.get()) {
+                    mBatteryController.removeCallback(this);
+                }
+            }
+        };
+        mBatteryController.addCallback(callback);
+        // Add another callback so the iteration continues
+        mBatteryController.addCallback(new BatteryStateChangeCallback() {});
+        remove.set(true);
+        mBatteryController.fireBatteryLevelChanged();
+    }
+
     private void setupIncompatibleCharging() {
         final List<UsbPort> usbPorts = new ArrayList<>();
         usbPorts.add(mUsbPort);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CastControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CastControllerImplTest.java
index b8e4306..68c1b8d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CastControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CastControllerImplTest.java
@@ -127,6 +127,25 @@
         }
     }
 
+    /** Regression test for b/317700495 */
+    @Test
+    public void removeCallbackWhileIterating_doesntCrash() {
+        final AtomicBoolean remove = new AtomicBoolean(false);
+        Callback callback = new Callback() {
+            @Override
+            public void onCastDevicesChanged() {
+                if (remove.get()) {
+                    mController.removeCallback(this);
+                }
+            }
+        };
+        mController.addCallback(callback);
+        // Add another callback so the iteration continues
+        mController.addCallback(() -> {});
+        remove.set(true);
+        mController.fireOnCastDevicesChanged();
+    }
+
     @Test
     public void hasConnectedCastDevice_connected() {
         CastController.CastDevice castDevice = new CastController.CastDevice();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/FlashlightControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/FlashlightControllerImplTest.kt
index db0029a..777fa28 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/FlashlightControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/FlashlightControllerImplTest.kt
@@ -26,6 +26,8 @@
 import com.android.systemui.broadcast.BroadcastSender
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.mockito.argumentCaptor
+import com.android.systemui.util.mockito.capture
 import com.android.systemui.util.settings.FakeSettings
 import com.android.systemui.util.time.FakeSystemClock
 import java.util.concurrent.Executor
@@ -128,11 +130,42 @@
         verify(cameraManager).setTorchMode(id, enable)
     }
 
+    @Test
+    fun testCallbackRemovedWhileDispatching_doesntCrash() {
+        injectCamera()
+        var remove = false
+        val callback = object : FlashlightController.FlashlightListener {
+            override fun onFlashlightChanged(enabled: Boolean) {
+                if (remove) {
+                    controller.removeCallback(this)
+                }
+            }
+
+            override fun onFlashlightError() {}
+
+            override fun onFlashlightAvailabilityChanged(available: Boolean) {}
+        }
+        controller.addCallback(callback)
+        controller.addCallback(object : FlashlightController.FlashlightListener {
+            override fun onFlashlightChanged(enabled: Boolean) {}
+
+            override fun onFlashlightError() {}
+
+            override fun onFlashlightAvailabilityChanged(available: Boolean) {}
+        })
+        backgroundExecutor.runAllReady()
+
+        val captor = argumentCaptor<CameraManager.TorchCallback>()
+        verify(cameraManager).registerTorchCallback(any(), capture(captor))
+        remove = true
+        captor.value.onTorchModeChanged(ID, true)
+    }
+
     private fun injectCamera(
         flash: Boolean = true,
         facing: Int = CameraCharacteristics.LENS_FACING_BACK
     ): String {
-        val cameraID = "ID"
+        val cameraID = ID
         val camera = CameraCharacteristics(CameraMetadataNative().apply {
             set(CameraCharacteristics.FLASH_INFO_AVAILABLE, flash)
             set(CameraCharacteristics.LENS_FACING, facing)
@@ -141,4 +174,8 @@
         `when`(cameraManager.getCameraCharacteristics(cameraID)).thenReturn(camera)
         return cameraID
     }
+
+    companion object {
+        private const val ID = "ID"
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SafetyControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SafetyControllerTest.kt
index 89989ce..b03edaf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SafetyControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SafetyControllerTest.kt
@@ -27,6 +27,7 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.util.mockito.any
+import com.google.common.truth.Truth.assertThat
 import org.junit.Assert.assertEquals
 import org.junit.Before
 import org.junit.Test
@@ -130,4 +131,61 @@
         controller.mPermControllerChangeReceiver.onReceive(context, testIntent)
         verify(listener, never()).onSafetyCenterEnableChanged(true)
     }
+
+    @Test
+    fun listenerRemovedWhileDispatching_doesNotCrash() {
+        var remove = false
+        val callback = object : SafetyController.Listener {
+            override fun onSafetyCenterEnableChanged(isSafetyCenterEnabled: Boolean) {
+                if (remove) {
+                    controller.removeCallback(this)
+                }
+            }
+        }
+
+        controller.addCallback(callback)
+        controller.addCallback {}
+
+        remove = true
+
+        `when`(scm.isSafetyCenterEnabled).thenReturn(true)
+        val testIntent = Intent(Intent.ACTION_PACKAGE_CHANGED)
+        testIntent.data = Uri.parse("package:$TEST_PC_PKG")
+        controller.mPermControllerChangeReceiver.onReceive(context, testIntent)
+    }
+
+    @Test
+    fun listenerRemovedWhileDispatching_otherCallbacksCalled() {
+        var remove = false
+        var called = false
+
+        val callback1 = object : SafetyController.Listener {
+            override fun onSafetyCenterEnableChanged(isSafetyCenterEnabled: Boolean) {
+                if (remove) {
+                    controller.removeCallback(this)
+                }
+            }
+        }
+
+        val callback2 = object : SafetyController.Listener {
+            override fun onSafetyCenterEnableChanged(isSafetyCenterEnabled: Boolean) {
+                // When the first callback is removed, we track if this is called
+                if (remove) {
+                    called = true
+                }
+            }
+        }
+
+        controller.addCallback(callback1)
+        controller.addCallback(callback2)
+
+        remove = true
+
+        `when`(scm.isSafetyCenterEnabled).thenReturn(true)
+        val testIntent = Intent(Intent.ACTION_PACKAGE_CHANGED)
+        testIntent.data = Uri.parse("package:$TEST_PC_PKG")
+        controller.mPermControllerChangeReceiver.onReceive(context, testIntent)
+
+        assertThat(called).isTrue()
+    }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java
index c35bc69..cb6ce68 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java
@@ -17,6 +17,7 @@
 package com.android.systemui.statusbar.policy;
 
 import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_FINANCED;
+import static android.net.NetworkCapabilities.TRANSPORT_VPN;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -63,6 +64,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
@@ -213,10 +215,33 @@
     public void testNetworkRequest() {
         verify(mConnectivityManager, times(1)).registerNetworkCallback(argThat(
                 (NetworkRequest request) ->
-                        request.equals(new NetworkRequest.Builder().clearCapabilities().build())
+                        request.equals(new NetworkRequest.Builder()
+                                .clearCapabilities().addTransportType(TRANSPORT_VPN).build())
                 ), any(NetworkCallback.class));
     }
 
+    @Test
+    public void testRemoveCallbackWhileDispatch_doesntCrash() {
+        final AtomicBoolean remove = new AtomicBoolean(false);
+        SecurityController.SecurityControllerCallback callback =
+                new SecurityController.SecurityControllerCallback() {
+                    @Override
+                    public void onStateChanged() {
+                        if (remove.get()) {
+                            mSecurityController.removeCallback(this);
+                        }
+                    }
+                };
+        mSecurityController.addCallback(callback);
+        // Add another callback so the iteration continues
+        mSecurityController.addCallback(() -> {});
+        mBgExecutor.runAllReady();
+        remove.set(true);
+
+        mSecurityController.onUserSwitched(10);
+        mBgExecutor.runAllReady();
+    }
+
     /**
      * refresh CA certs by sending a user unlocked broadcast for the desired user
      */
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ZenModeControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ZenModeControllerImplTest.java
index 6825f65..f1a2c28 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ZenModeControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ZenModeControllerImplTest.java
@@ -47,6 +47,7 @@
 import org.mockito.MockitoAnnotations;
 
 import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
 
 @SmallTest
@@ -174,4 +175,26 @@
         }
 
     }
+
+    @Test
+    public void testCallbackRemovedWhileDispatching_doesntCrash() {
+        final AtomicBoolean remove = new AtomicBoolean(false);
+        mGlobalSettings.putInt(Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_OFF);
+        TestableLooper.get(this).processAllMessages();
+        final ZenModeController.Callback callback = new ZenModeController.Callback() {
+            @Override
+            public void onZenChanged(int zen) {
+                if (remove.get()) {
+                    mController.removeCallback(this);
+                }
+            }
+        };
+        mController.addCallback(callback);
+        mController.addCallback(new ZenModeController.Callback() {});
+
+        remove.set(true);
+
+        mGlobalSettings.putInt(Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_NO_INTERRUPTIONS);
+        TestableLooper.get(this).processAllMessages();
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java b/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java
index bfc5bdb..0581e0e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java
@@ -18,7 +18,7 @@
 
 import static android.view.accessibility.AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED;
 
-import static com.android.systemui.dump.LogBufferHelperKt.logcatLogBuffer;
+import static com.android.systemui.log.LogBufferHelperKt.logcatLogBuffer;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -62,10 +62,10 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.util.IntPair;
-import com.android.systemui.res.R;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.plugins.PluginManager;
+import com.android.systemui.res.R;
 import com.android.systemui.statusbar.CommandQueue;
 
 import org.junit.Before;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt
new file mode 100644
index 0000000..ee2e5ad
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt
@@ -0,0 +1,324 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.unfold
+
+import android.content.Context
+import android.content.res.Resources
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.internal.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.display.data.repository.DeviceStateRepository
+import com.android.systemui.display.data.repository.DeviceStateRepository.DeviceState
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.power.shared.model.ScreenPowerState
+import com.android.systemui.power.shared.model.WakeSleepReason
+import com.android.systemui.power.shared.model.WakefulnessModel
+import com.android.systemui.power.shared.model.WakefulnessState
+import com.android.systemui.shared.system.SysUiStatsLog
+import com.android.systemui.unfold.DisplaySwitchLatencyTracker.Companion.FOLDABLE_DEVICE_STATE_CLOSED
+import com.android.systemui.unfold.DisplaySwitchLatencyTracker.Companion.FOLDABLE_DEVICE_STATE_HALF_OPEN
+import com.android.systemui.unfold.DisplaySwitchLatencyTracker.DisplaySwitchLatencyEvent
+import com.android.systemui.unfold.data.repository.UnfoldTransitionRepositoryImpl
+import com.android.systemui.unfold.domain.interactor.UnfoldTransitionInteractorImpl
+import com.android.systemui.util.animation.data.repository.AnimationStatusRepository
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.capture
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import java.util.Optional
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.asExecutor
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.Captor
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class DisplaySwitchLatencyTrackerTest : SysuiTestCase() {
+    private lateinit var displaySwitchLatencyTracker: DisplaySwitchLatencyTracker
+    @Captor private lateinit var loggerArgumentCaptor: ArgumentCaptor<DisplaySwitchLatencyEvent>
+
+    private val mockContext = mock<Context>()
+    private val resources = mock<Resources>()
+    private val foldStateRepository = mock<DeviceStateRepository>()
+    private val powerInteractor = mock<PowerInteractor>()
+    private val animationStatusRepository = mock<AnimationStatusRepository>()
+    private val keyguardInteractor = mock<KeyguardInteractor>()
+    private val displaySwitchLatencyLogger = mock<DisplaySwitchLatencyLogger>()
+
+    private val nonEmptyClosedDeviceStatesArray: IntArray = IntArray(2) { 0 }
+    private val testDispatcher: TestDispatcher = StandardTestDispatcher()
+    private val testScope: TestScope = TestScope(testDispatcher)
+    private val isAsleep = MutableStateFlow(false)
+    private val isAodAvailable = MutableStateFlow(false)
+    private val deviceState = MutableStateFlow(DeviceState.UNFOLDED)
+    private val screenPowerState = MutableStateFlow(ScreenPowerState.SCREEN_ON)
+    private val areAnimationEnabled = MutableStateFlow(true)
+    private val lastWakefulnessEvent = MutableStateFlow(WakefulnessModel())
+    private val systemClock = FakeSystemClock()
+    private val unfoldTransitionProgressProvider = TestUnfoldTransitionProvider()
+    private val unfoldTransitionRepository =
+        UnfoldTransitionRepositoryImpl(Optional.of(unfoldTransitionProgressProvider))
+    private val unfoldTransitionInteractor =
+        UnfoldTransitionInteractorImpl(unfoldTransitionRepository)
+
+    @Before
+    fun setup() {
+        MockitoAnnotations.initMocks(this)
+        whenever(mockContext.resources).thenReturn(resources)
+        whenever(resources.getIntArray(R.array.config_foldedDeviceStates))
+            .thenReturn(nonEmptyClosedDeviceStatesArray)
+        whenever(foldStateRepository.state).thenReturn(deviceState)
+        whenever(powerInteractor.isAsleep).thenReturn(isAsleep)
+        whenever(animationStatusRepository.areAnimationsEnabled()).thenReturn(areAnimationEnabled)
+        whenever(powerInteractor.screenPowerState).thenReturn(screenPowerState)
+        whenever(keyguardInteractor.isAodAvailable).thenReturn(isAodAvailable)
+        whenever(powerInteractor.detailedWakefulness).thenReturn(lastWakefulnessEvent)
+
+        displaySwitchLatencyTracker =
+            DisplaySwitchLatencyTracker(
+                mockContext,
+                foldStateRepository,
+                powerInteractor,
+                unfoldTransitionInteractor,
+                animationStatusRepository,
+                keyguardInteractor,
+                testDispatcher.asExecutor(),
+                testScope.backgroundScope,
+                displaySwitchLatencyLogger,
+                systemClock
+            )
+    }
+
+    @Test
+    fun unfold_logsLatencyTillTransitionStarted() {
+        testScope.runTest {
+            areAnimationEnabled.emit(true)
+
+            displaySwitchLatencyTracker.start()
+            deviceState.emit(DeviceState.FOLDED)
+            screenPowerState.emit(ScreenPowerState.SCREEN_OFF)
+            systemClock.advanceTime(50)
+            runCurrent()
+            deviceState.emit(DeviceState.HALF_FOLDED)
+            runCurrent()
+            systemClock.advanceTime(50)
+            screenPowerState.emit(ScreenPowerState.SCREEN_ON)
+            systemClock.advanceTime(200)
+            unfoldTransitionProgressProvider.onTransitionStarted()
+            runCurrent()
+            deviceState.emit(DeviceState.UNFOLDED)
+
+            verify(displaySwitchLatencyLogger).log(capture(loggerArgumentCaptor))
+            val loggedEvent = loggerArgumentCaptor.value
+            val expectedLoggedEvent =
+                DisplaySwitchLatencyEvent(
+                    latencyMs = 250,
+                    fromFoldableDeviceState = FOLDABLE_DEVICE_STATE_CLOSED,
+                    toFoldableDeviceState = FOLDABLE_DEVICE_STATE_HALF_OPEN
+                )
+            assertThat(loggedEvent).isEqualTo(expectedLoggedEvent)
+        }
+    }
+
+    @Test
+    fun unfold_progressUnavailable_logsLatencyTillScreenTurnedOn() {
+        testScope.runTest {
+            val unfoldTransitionInteractorWithEmptyProgressProvider =
+                UnfoldTransitionInteractorImpl(UnfoldTransitionRepositoryImpl(Optional.empty()))
+            displaySwitchLatencyTracker =
+                DisplaySwitchLatencyTracker(
+                    mockContext,
+                    foldStateRepository,
+                    powerInteractor,
+                    unfoldTransitionInteractorWithEmptyProgressProvider,
+                    animationStatusRepository,
+                    keyguardInteractor,
+                    testDispatcher.asExecutor(),
+                    testScope.backgroundScope,
+                    displaySwitchLatencyLogger,
+                    systemClock
+                )
+            areAnimationEnabled.emit(true)
+
+            displaySwitchLatencyTracker.start()
+            deviceState.emit(DeviceState.FOLDED)
+            screenPowerState.emit(ScreenPowerState.SCREEN_OFF)
+            systemClock.advanceTime(50)
+            runCurrent()
+            deviceState.emit(DeviceState.HALF_FOLDED)
+            systemClock.advanceTime(50)
+            runCurrent()
+            screenPowerState.emit(ScreenPowerState.SCREEN_ON)
+            systemClock.advanceTime(50)
+            runCurrent()
+            systemClock.advanceTime(200)
+            unfoldTransitionProgressProvider.onTransitionStarted()
+            runCurrent()
+            deviceState.emit(DeviceState.UNFOLDED)
+
+            verify(displaySwitchLatencyLogger).log(capture(loggerArgumentCaptor))
+            val loggedEvent = loggerArgumentCaptor.value
+            val expectedLoggedEvent =
+                DisplaySwitchLatencyEvent(
+                    latencyMs = 50,
+                    fromFoldableDeviceState = FOLDABLE_DEVICE_STATE_CLOSED,
+                    toFoldableDeviceState = FOLDABLE_DEVICE_STATE_HALF_OPEN
+                )
+            assertThat(loggedEvent).isEqualTo(expectedLoggedEvent)
+        }
+    }
+
+    @Test
+    fun unfold_animationDisabled_logsLatencyTillScreenTurnedOn() {
+        testScope.runTest {
+            areAnimationEnabled.emit(false)
+
+            displaySwitchLatencyTracker.start()
+            deviceState.emit(DeviceState.FOLDED)
+            screenPowerState.emit(ScreenPowerState.SCREEN_OFF)
+            systemClock.advanceTime(50)
+            runCurrent()
+            deviceState.emit(DeviceState.HALF_FOLDED)
+            systemClock.advanceTime(50)
+            runCurrent()
+            screenPowerState.emit(ScreenPowerState.SCREEN_ON)
+            systemClock.advanceTime(50)
+            runCurrent()
+            unfoldTransitionProgressProvider.onTransitionStarted()
+            systemClock.advanceTime(200)
+            runCurrent()
+            deviceState.emit(DeviceState.UNFOLDED)
+
+            verify(displaySwitchLatencyLogger).log(capture(loggerArgumentCaptor))
+            val loggedEvent = loggerArgumentCaptor.value
+            val expectedLoggedEvent =
+                DisplaySwitchLatencyEvent(
+                    latencyMs = 50,
+                    fromFoldableDeviceState = FOLDABLE_DEVICE_STATE_CLOSED,
+                    toFoldableDeviceState = FOLDABLE_DEVICE_STATE_HALF_OPEN
+                )
+            assertThat(loggedEvent).isEqualTo(expectedLoggedEvent)
+        }
+    }
+
+    @Test
+    fun foldWhileStayingAwake_logsLatency() {
+        testScope.runTest {
+            areAnimationEnabled.emit(true)
+            deviceState.emit(DeviceState.UNFOLDED)
+            screenPowerState.emit(ScreenPowerState.SCREEN_ON)
+
+            displaySwitchLatencyTracker.start()
+            deviceState.emit(DeviceState.HALF_FOLDED)
+            systemClock.advanceTime(50)
+            runCurrent()
+            deviceState.emit(DeviceState.FOLDED)
+            screenPowerState.emit(ScreenPowerState.SCREEN_OFF)
+            runCurrent()
+            systemClock.advanceTime(200)
+            screenPowerState.emit(ScreenPowerState.SCREEN_ON)
+            runCurrent()
+
+            verify(displaySwitchLatencyLogger).log(capture(loggerArgumentCaptor))
+            val loggedEvent = loggerArgumentCaptor.value
+            val expectedLoggedEvent =
+                DisplaySwitchLatencyEvent(
+                    latencyMs = 200,
+                    fromFoldableDeviceState = FOLDABLE_DEVICE_STATE_HALF_OPEN,
+                    toFoldableDeviceState = FOLDABLE_DEVICE_STATE_CLOSED
+                )
+            assertThat(loggedEvent).isEqualTo(expectedLoggedEvent)
+        }
+    }
+
+    @Test
+    fun foldToAod_capturesToStateAsAod() {
+        testScope.runTest {
+            areAnimationEnabled.emit(true)
+            deviceState.emit(DeviceState.UNFOLDED)
+            isAodAvailable.emit(true)
+
+            displaySwitchLatencyTracker.start()
+            deviceState.emit(DeviceState.HALF_FOLDED)
+            systemClock.advanceTime(50)
+            runCurrent()
+            deviceState.emit(DeviceState.FOLDED)
+            lastWakefulnessEvent.emit(
+                WakefulnessModel(
+                    internalWakefulnessState = WakefulnessState.ASLEEP,
+                    lastSleepReason = WakeSleepReason.FOLD
+                )
+            )
+            screenPowerState.emit(ScreenPowerState.SCREEN_OFF)
+            runCurrent()
+            systemClock.advanceTime(200)
+            screenPowerState.emit(ScreenPowerState.SCREEN_ON)
+            runCurrent()
+
+            verify(displaySwitchLatencyLogger).log(capture(loggerArgumentCaptor))
+            val loggedEvent = loggerArgumentCaptor.value
+            val expectedLoggedEvent =
+                DisplaySwitchLatencyEvent(
+                    latencyMs = 200,
+                    fromFoldableDeviceState = FOLDABLE_DEVICE_STATE_HALF_OPEN,
+                    toFoldableDeviceState = FOLDABLE_DEVICE_STATE_CLOSED,
+                    toState = SysUiStatsLog.DISPLAY_SWITCH_LATENCY_TRACKED__TO_STATE__AOD
+                )
+            assertThat(loggedEvent).isEqualTo(expectedLoggedEvent)
+        }
+    }
+
+    @Test
+    fun fold_notAFoldable_shouldNotLogLatency() {
+        testScope.runTest {
+            areAnimationEnabled.emit(true)
+            deviceState.emit(DeviceState.UNFOLDED)
+            whenever(resources.getIntArray(R.array.config_foldedDeviceStates))
+                .thenReturn(IntArray(0))
+
+            displaySwitchLatencyTracker.start()
+            deviceState.emit(DeviceState.HALF_FOLDED)
+            systemClock.advanceTime(50)
+            runCurrent()
+            deviceState.emit(DeviceState.FOLDED)
+            screenPowerState.emit(ScreenPowerState.SCREEN_OFF)
+            runCurrent()
+            systemClock.advanceTime(200)
+            screenPowerState.emit(ScreenPowerState.SCREEN_ON)
+            runCurrent()
+
+            verify(displaySwitchLatencyLogger, never()).log(any())
+        }
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/FoldStateRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldStateRepositoryTest.kt
similarity index 82%
rename from packages/SystemUI/tests/src/com/android/systemui/unfold/updates/FoldStateRepositoryTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/unfold/FoldStateRepositoryTest.kt
index 0651323..ab779a7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/FoldStateRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldStateRepositoryTest.kt
@@ -13,13 +13,20 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.systemui.unfold.updates
+package com.android.systemui.unfold
 
 import android.testing.AndroidTestingRunner
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.unfold.updates.FoldStateRepository.FoldUpdate
+import com.android.systemui.unfold.data.repository.FoldStateRepository.FoldUpdate
+import com.android.systemui.unfold.data.repository.FoldStateRepositoryImpl
+import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_CLOSED
+import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_FULL_OPEN
+import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_HALF_OPEN
+import com.android.systemui.unfold.updates.FOLD_UPDATE_START_CLOSING
+import com.android.systemui.unfold.updates.FOLD_UPDATE_START_OPENING
+import com.android.systemui.unfold.updates.FoldStateProvider
 import com.android.systemui.util.mockito.argumentCaptor
 import com.android.systemui.util.mockito.capture
 import com.android.systemui.util.mockito.mock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/MainThreadUnfoldTransitionProgressProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/MainThreadUnfoldTransitionProgressProviderTest.kt
index 4e61b89..2bc05fc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/MainThreadUnfoldTransitionProgressProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/MainThreadUnfoldTransitionProgressProviderTest.kt
@@ -16,6 +16,8 @@
 
 package com.android.systemui.unfold.progress
 
+import android.os.Handler
+import android.os.HandlerThread
 import android.os.Looper
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper.RunWithLooper
@@ -24,6 +26,7 @@
 import com.android.systemui.unfold.TestUnfoldTransitionProvider
 import com.android.systemui.utils.os.FakeHandler
 import kotlin.test.Test
+import kotlinx.coroutines.test.runTest
 import org.junit.runner.RunWith
 
 @RunWith(AndroidTestingRunner::class)
@@ -93,4 +96,13 @@
 
         listener.assertNotStarted()
     }
+
+    @Test
+    fun addCallback_fromBackgroundThread_succeeds() = runTest {
+        val bgHandler = Handler(HandlerThread("TestBgThread").apply { start() }.looper)
+        bgHandler.runWithScissors({ progressProvider.addCallback(listener) }, 5000L)
+
+        wrappedProgressProvider.onTransitionStarted()
+        listener.assertStarted()
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/TestUnfoldProgressListener.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/TestUnfoldProgressListener.kt
index e461e3f..bbc96f70 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/TestUnfoldProgressListener.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/TestUnfoldProgressListener.kt
@@ -26,8 +26,11 @@
 
     private val recordings: MutableList<UnfoldTransitionRecording> = arrayListOf()
     private var currentRecording: UnfoldTransitionRecording? = null
+    var lastCallbackThread: Thread? = null
+        private set
 
     override fun onTransitionStarted() {
+        lastCallbackThread = Thread.currentThread()
         assertWithMessage("Trying to start a transition when it is already in progress")
             .that(currentRecording)
             .isNull()
@@ -36,6 +39,7 @@
     }
 
     override fun onTransitionProgress(progress: Float) {
+        lastCallbackThread = Thread.currentThread()
         assertWithMessage("Received transition progress event when it's not started")
             .that(currentRecording)
             .isNotNull()
@@ -43,6 +47,7 @@
     }
 
     override fun onTransitionFinishing() {
+        lastCallbackThread = Thread.currentThread()
         assertWithMessage("Received transition finishing event when it's not started")
             .that(currentRecording)
             .isNotNull()
@@ -50,6 +55,7 @@
     }
 
     override fun onTransitionFinished() {
+        lastCallbackThread = Thread.currentThread()
         assertWithMessage("Received transition finish event when it's not started")
             .that(currentRecording)
             .isNotNull()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProviderTest.kt
index a25469b..d864d53 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProviderTest.kt
@@ -16,6 +16,7 @@
 package com.android.systemui.unfold.util
 
 import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
 import android.view.Surface
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
@@ -37,6 +38,7 @@
 
 @RunWith(AndroidTestingRunner::class)
 @SmallTest
+@TestableLooper.RunWithLooper
 class NaturalRotationUnfoldProgressProviderTest : SysuiTestCase() {
 
     @Mock lateinit var rotationChangeProvider: RotationChangeProvider
@@ -48,10 +50,12 @@
     @Captor private lateinit var rotationListenerCaptor: ArgumentCaptor<RotationListener>
 
     lateinit var progressProvider: NaturalRotationUnfoldProgressProvider
+    private lateinit var testableLooper : TestableLooper
 
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
+        testableLooper = TestableLooper.get(this)
 
         progressProvider =
             NaturalRotationUnfoldProgressProvider(context, rotationChangeProvider, sourceProvider)
@@ -123,5 +127,6 @@
 
     private fun onRotationChanged(rotation: Int) {
         rotationListenerCaptor.value.onRotationChanged(rotation)
+        testableLooper.processAllMessages()
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScaleAwareUnfoldProgressProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScaleAwareUnfoldProgressProviderTest.kt
index e1e54a9..2f29b3b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScaleAwareUnfoldProgressProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScaleAwareUnfoldProgressProviderTest.kt
@@ -19,6 +19,7 @@
 import android.database.ContentObserver
 import android.provider.Settings
 import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.unfold.TestUnfoldTransitionProvider
@@ -36,6 +37,7 @@
 
 @RunWith(AndroidTestingRunner::class)
 @SmallTest
+@TestableLooper.RunWithLooper
 class ScaleAwareUnfoldProgressProviderTest : SysuiTestCase() {
 
     @Mock lateinit var sinkProvider: TransitionProgressListener
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProviderTest.kt
new file mode 100644
index 0000000..5b4f4d3
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProviderTest.kt
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.unfold.util
+
+import android.os.Handler
+import android.os.HandlerThread
+import android.os.Process
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.unfold.TestUnfoldTransitionProvider
+import com.android.systemui.unfold.progress.TestUnfoldProgressListener
+import com.google.common.truth.Truth.assertThat
+import kotlin.time.Duration.Companion.seconds
+import kotlinx.coroutines.CancellableContinuation
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.suspendCancellableCoroutine
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runTest
+import kotlinx.coroutines.withTimeout
+import org.junit.Assert.assertThrows
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+@RunWithLooper
+class ScopedUnfoldTransitionProgressProviderTest : SysuiTestCase() {
+
+    private val rootProvider = TestUnfoldTransitionProvider()
+    private val listener = TestUnfoldProgressListener()
+    private val testScope = TestScope(UnconfinedTestDispatcher())
+    private val bgThread =
+        HandlerThread("UnfoldBgTest", Process.THREAD_PRIORITY_FOREGROUND).apply { start() }
+    private val bgHandler = Handler(bgThread.looper)
+    private val scopedProvider =
+        ScopedUnfoldTransitionProgressProvider(rootProvider).apply { addCallback(listener) }
+
+    @Test
+    fun setReadyToHandleTransition_whileTransitionRunning_propagatesCallbacks() =
+        testScope.runTest {
+            runBlockingInBg { rootProvider.onTransitionStarted() }
+
+            scopedProvider.setReadyToHandleTransition(true)
+
+            runBlockingInBg { /* sync barrier */}
+
+            listener.assertStarted()
+
+            runBlockingInBg { rootProvider.onTransitionProgress(1f) }
+
+            listener.assertLastProgress(1f)
+
+            runBlockingInBg { rootProvider.onTransitionFinished() }
+
+            listener.assertNotStarted()
+        }
+
+    @Test
+    fun setReadyToHandleTransition_whileTransitionNotRunning_callbacksInProgressThread() {
+        testScope.runTest {
+            scopedProvider.setReadyToHandleTransition(true)
+
+            val bgThread = runBlockingInBg { Thread.currentThread() }
+
+            runBlockingInBg { rootProvider.onTransitionStarted() }
+
+            listener.assertStarted()
+
+            assertThat(listener.lastCallbackThread).isEqualTo(bgThread)
+        }
+    }
+
+    @Test
+    fun setReadyToHandleTransition_beforeAnyCallback_doesNotCrash() {
+        testScope.runTest { scopedProvider.setReadyToHandleTransition(true) }
+    }
+
+    @Test
+    fun onTransitionStarted_whileNotReadyToHandleTransition_doesNotPropagate() {
+        testScope.runTest {
+            scopedProvider.setReadyToHandleTransition(false)
+
+            rootProvider.onTransitionStarted()
+
+            listener.assertNotStarted()
+        }
+    }
+
+    @Test
+    fun onTransitionStarted_defaultReadiness_doesNotPropagate() {
+        testScope.runTest {
+            rootProvider.onTransitionStarted()
+
+            listener.assertNotStarted()
+        }
+    }
+
+    @Test
+    fun onTransitionStarted_fromDifferentThreads_throws() {
+        testScope.runTest {
+            runBlockingInBg {
+                rootProvider.onTransitionStarted()
+                rootProvider.onTransitionFinished()
+            }
+            assertThrows(IllegalStateException::class.java) { rootProvider.onTransitionStarted() }
+        }
+    }
+
+    @Test
+    fun onTransitionProgress_fromDifferentThreads_throws() {
+        testScope.runTest {
+            runBlockingInBg { rootProvider.onTransitionStarted() }
+            assertThrows(IllegalStateException::class.java) {
+                rootProvider.onTransitionProgress(1f)
+            }
+        }
+    }
+
+    @Test
+    fun onTransitionFinished_fromDifferentThreads_throws() {
+        testScope.runTest {
+            runBlockingInBg { rootProvider.onTransitionStarted() }
+            assertThrows(IllegalStateException::class.java) { rootProvider.onTransitionFinished() }
+        }
+    }
+
+    @Test
+    fun onTransitionFinishing_fromDifferentThreads_throws() {
+        testScope.runTest {
+            runBlockingInBg { rootProvider.onTransitionStarted() }
+            assertThrows(IllegalStateException::class.java) { rootProvider.onTransitionFinishing() }
+        }
+    }
+
+    private fun <T> runBlockingInBg(f: () -> T): T {
+        return runBlocking {
+            withTimeout(5.seconds) {
+                suspendCancellableCoroutine { c: CancellableContinuation<T> ->
+                    bgHandler.post { c.resumeWith(Result.success(f())) }
+                }
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/UnfoldOnlyProgressProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/UnfoldOnlyProgressProviderTest.kt
index 4a38fc0..f484ea0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/UnfoldOnlyProgressProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/UnfoldOnlyProgressProviderTest.kt
@@ -16,6 +16,7 @@
 package com.android.systemui.unfold.util
 
 import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.unfold.TestUnfoldTransitionProvider
@@ -27,6 +28,7 @@
 
 @RunWith(AndroidTestingRunner::class)
 @SmallTest
+@TestableLooper.RunWithLooper
 class UnfoldOnlyProgressProviderTest : SysuiTestCase() {
 
     private val listener = TestUnfoldProgressListener()
@@ -54,9 +56,7 @@
         sourceProvider.onTransitionProgress(0.5f)
         sourceProvider.onTransitionFinished()
 
-        with(listener.ensureTransitionFinished()) {
-            assertLastProgress(0.5f)
-        }
+        with(listener.ensureTransitionFinished()) { assertLastProgress(0.5f) }
     }
 
     @Test
@@ -121,8 +121,6 @@
         sourceProvider.onTransitionProgress(0.1f)
         sourceProvider.onTransitionFinished()
 
-        with(listener.ensureTransitionFinished()) {
-            assertLastProgress(0.1f)
-        }
+        with(listener.ensureTransitionFinished()) { assertLastProgress(0.1f) }
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorTest.kt
index 6714c94..fb5375a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorTest.kt
@@ -32,6 +32,7 @@
 import com.android.internal.logging.UiEventLogger
 import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.keyguard.KeyguardUpdateMonitorCallback
+import com.android.systemui.Flags as AConfigFlags
 import com.android.systemui.GuestResetOrExitSessionReceiver
 import com.android.systemui.GuestResumeSessionReceiver
 import com.android.systemui.SysuiTestCase
@@ -121,6 +122,7 @@
         )
 
         utils.featureFlags.set(Flags.FULL_SCREEN_USER_SWITCHER, false)
+        mSetFlagsRule.enableFlags(AConfigFlags.FLAG_SWITCH_USER_ON_BG)
         spyContext = spy(context)
         keyguardReply = KeyguardInteractorFactory.create(featureFlags = utils.featureFlags)
         keyguardRepository = keyguardReply.repository
@@ -172,6 +174,7 @@
             userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true))
 
             underTest.onRecordSelected(UserRecord(info = userInfos[1]), dialogShower)
+            runCurrent()
 
             verify(uiEventLogger, times(1))
                 .log(MultiUserActionsEvent.SWITCH_TO_USER_FROM_USER_SWITCHER)
@@ -191,6 +194,7 @@
             userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true))
 
             underTest.onRecordSelected(UserRecord(info = userInfos.last()))
+            runCurrent()
 
             verify(uiEventLogger, times(1))
                 .log(MultiUserActionsEvent.SWITCH_TO_GUEST_FROM_USER_SWITCHER)
@@ -218,6 +222,7 @@
             userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true))
 
             underTest.onRecordSelected(UserRecord(info = userInfos.last()))
+            runCurrent()
 
             verify(uiEventLogger, times(1))
                 .log(MultiUserActionsEvent.SWITCH_TO_RESTRICTED_USER_FROM_USER_SWITCHER)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/service/PersistentConnectionManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/service/PersistentConnectionManagerTest.java
index db0139c..55c49ee 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/service/PersistentConnectionManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/service/PersistentConnectionManagerTest.java
@@ -24,6 +24,7 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.dump.DumpManager;
 import com.android.systemui.util.concurrency.FakeExecutor;
 import com.android.systemui.util.time.FakeSystemClock;
 
@@ -41,6 +42,7 @@
     private static final int MAX_RETRIES = 5;
     private static final int RETRY_DELAY_MS = 1000;
     private static final int CONNECTION_MIN_DURATION_MS = 5000;
+    private static final String DUMPSYS_NAME = "dumpsys_name";
 
     private FakeSystemClock mFakeClock = new FakeSystemClock();
     private FakeExecutor mFakeExecutor = new FakeExecutor(mFakeClock);
@@ -49,8 +51,14 @@
     private ObservableServiceConnection<Proxy> mConnection;
 
     @Mock
+    private ObservableServiceConnection.Callback<Proxy> mConnectionCallback;
+
+    @Mock
     private Observer mObserver;
 
+    @Mock
+    private DumpManager mDumpManager;
+
     private static class Proxy {
     }
 
@@ -63,6 +71,8 @@
         mConnectionManager = new PersistentConnectionManager<>(
                 mFakeClock,
                 mFakeExecutor,
+                mDumpManager,
+                DUMPSYS_NAME,
                 mConnection,
                 MAX_RETRIES,
                 RETRY_DELAY_MS,
@@ -154,4 +164,16 @@
         callbackCaptor.getValue().onSourceChanged();
         verify(mConnection).bind();
     }
+
+    @Test
+    public void testAddConnectionCallback() {
+        mConnectionManager.addConnectionCallback(mConnectionCallback);
+        verify(mConnection).addCallback(mConnectionCallback);
+    }
+
+    @Test
+    public void testRemoveConnectionCallback() {
+        mConnectionManager.removeConnectionCallback(mConnectionCallback);
+        verify(mConnection).removeCallback(mConnectionCallback);
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index 814ea19..048120a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -126,6 +126,7 @@
 import com.android.systemui.scene.shared.logger.SceneLogger;
 import com.android.systemui.settings.FakeDisplayTracker;
 import com.android.systemui.settings.UserTracker;
+import com.android.systemui.shade.LargeScreenHeaderHelper;
 import com.android.systemui.shade.NotificationShadeWindowControllerImpl;
 import com.android.systemui.shade.NotificationShadeWindowView;
 import com.android.systemui.shade.ShadeController;
@@ -349,6 +350,8 @@
     private Display mDefaultDisplay;
     @Mock
     private SceneContainerFlags mSceneContainerFlags;
+    @Mock
+    private LargeScreenHeaderHelper mLargeScreenHeaderHelper;
 
     private final SceneTestUtils mUtils = new SceneTestUtils(this);
     private final TestScope mTestScope = mUtils.getTestScope();
@@ -441,6 +444,8 @@
                 keyguardTransitionRepository,
                 keyguardTransitionInteractor,
                 mTestScope.getBackgroundScope(),
+                mUtils.getTestDispatcher(),
+                mUtils.getTestDispatcher(),
                 keyguardInteractor,
                 featureFlags,
                 shadeRepository,
@@ -459,6 +464,8 @@
                 keyguardTransitionRepository,
                 keyguardTransitionInteractor,
                 mTestScope.getBackgroundScope(),
+                mUtils.getTestDispatcher(),
+                mUtils.getTestDispatcher(),
                 keyguardInteractor,
                 featureFlags,
                 mock(KeyguardSecurityModel.class),
@@ -490,7 +497,8 @@
                                         mContext,
                                         splitShadeStateController,
                                         keyguardInteractor,
-                                        deviceEntryUdfpsInteractor),
+                                        deviceEntryUdfpsInteractor,
+                                        () -> mLargeScreenHeaderHelper),
                                 shadeRepository
                         )
                 );
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileUser.kt b/packages/SystemUI/tests/utils/src/com/android/keyguard/TrustManagerKosmos.kt
similarity index 64%
copy from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileUser.kt
copy to packages/SystemUI/tests/utils/src/com/android/keyguard/TrustManagerKosmos.kt
index efc7431..187935b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileUser.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/keyguard/TrustManagerKosmos.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,12 +14,10 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.custom.di.bound
+package com.android.keyguard
 
-import javax.inject.Qualifier
+import android.app.trust.TrustManager
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.util.mockito.mock
 
-/** User associated with current custom tile binding. */
-@Qualifier
-@MustBeDocumented
-@Retention(AnnotationRetention.RUNTIME)
-annotation class CustomTileUser
+val Kosmos.trustManager by Kosmos.Fixture { mock<TrustManager>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/CoroutineTestScopeModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/CoroutineTestScopeModule.kt
index de310b4..c2dc673 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/CoroutineTestScopeModule.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/CoroutineTestScopeModule.kt
@@ -48,9 +48,14 @@
     @get:[Provides Application]
     val appScope: CoroutineScope = scope.backgroundScope
 
+    @get:[Provides Background]
+    val bgScope: CoroutineScope = scope.backgroundScope
+
     @Module
     interface Bindings {
+        @Binds @Main fun bindMainContext(dispatcher: TestDispatcher): CoroutineContext
         @Binds @Main fun bindMainDispatcher(dispatcher: TestDispatcher): CoroutineDispatcher
+        @Binds @Background fun bindBgContext(dispatcher: TestDispatcher): CoroutineContext
         @Binds @Background fun bindBgDispatcher(dispatcher: TestDispatcher): CoroutineDispatcher
     }
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
index 29e737e..d23dae9 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
@@ -33,6 +33,7 @@
 import android.testing.TestableLooper;
 import android.util.Log;
 
+import androidx.annotation.NonNull;
 import androidx.core.animation.AndroidXAnimatorIsolationRule;
 import androidx.test.InstrumentationRegistry;
 import androidx.test.uiautomator.UiDevice;
@@ -68,8 +69,20 @@
     public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT);
 
     @Rule
-    public SysuiTestableContext mContext = new SysuiTestableContext(
-            InstrumentationRegistry.getContext(), getLeakCheck());
+    public SysuiTestableContext mContext = createTestableContext();
+
+    @NonNull
+    private SysuiTestableContext createTestableContext() {
+        SysuiTestableContext context = new SysuiTestableContext(
+                InstrumentationRegistry.getContext(), getLeakCheck());
+        if (isRobolectricTest()) {
+            // Manually associate a Display to context for Robolectric test. Similar to b/214297409
+            return context.createDefaultDisplayContext();
+        } else {
+            return context;
+        }
+    }
+
     @Rule
     public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule =
             new DexmakerShareClassLoaderRule();
@@ -84,10 +97,6 @@
 
     @Before
     public void SysuiSetup() throws Exception {
-        // Manually associate a Display to context for Robolectric test. Similar to b/214297409
-        if (isRobolectricTest()) {
-            mContext = mContext.createDefaultDisplayContext();
-        }
         mSysuiDependency = new SysuiTestDependency(mContext, shouldFailOnLeakedReceiver());
         mDependency = mSysuiDependency.install();
         mRealInstrumentation = InstrumentationRegistry.getInstrumentation();
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt
index 8486508..a6dd3cd 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.authentication.data.repository
 
+import android.os.UserHandle
 import com.android.internal.widget.LockPatternUtils
 import com.android.internal.widget.LockPatternView
 import com.android.internal.widget.LockscreenCredential
@@ -105,6 +106,13 @@
         lockoutStartedReportCount++
     }
 
+    override suspend fun getMaxFailedUnlockAttemptsForWipe(): Int =
+        MAX_FAILED_AUTH_TRIES_BEFORE_WIPE
+
+    var profileWithMinFailedUnlockAttemptsForWipe: Int = UserHandle.USER_SYSTEM
+    override suspend fun getProfileWithMinFailedUnlockAttemptsForWipe(): Int =
+        profileWithMinFailedUnlockAttemptsForWipe
+
     override suspend fun getPinLength(): Int {
         return (credentialOverride ?: DEFAULT_PIN).size
     }
@@ -169,6 +177,9 @@
                 AuthenticationPatternCoordinate(0, 2),
             )
         const val MAX_FAILED_AUTH_TRIES_BEFORE_LOCKOUT = 5
+        const val MAX_FAILED_AUTH_TRIES_BEFORE_WIPE =
+            MAX_FAILED_AUTH_TRIES_BEFORE_LOCKOUT +
+                LockPatternUtils.FAILED_ATTEMPTS_BEFORE_WIPE_GRACE
         const val LOCKOUT_DURATION_SECONDS = 30
         const val LOCKOUT_DURATION_MS = LOCKOUT_DURATION_SECONDS * 1000
         const val HINTING_PIN_LENGTH = 6
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorKosmos.kt
index 05cb059..7c8a7c8 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorKosmos.kt
@@ -19,11 +19,13 @@
 import com.android.systemui.authentication.data.repository.authenticationRepository
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.user.domain.interactor.selectedUserInteractor
 
 val Kosmos.authenticationInteractor by
     Kosmos.Fixture {
         AuthenticationInteractor(
             applicationScope = applicationCoroutineScope,
             repository = authenticationRepository,
+            selectedUserInteractor = selectedUserInteractor,
         )
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileUser.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FacePropertyRepositoryKosmos.kt
similarity index 64%
copy from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileUser.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FacePropertyRepositoryKosmos.kt
index efc7431..6ef7419 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileUser.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FacePropertyRepositoryKosmos.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,12 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.custom.di.bound
+package com.android.systemui.biometrics.data.repository
 
-import javax.inject.Qualifier
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
 
-/** User associated with current custom tile binding. */
-@Qualifier
-@MustBeDocumented
-@Retention(AnnotationRetention.RUNTIME)
-annotation class CustomTileUser
+val Kosmos.facePropertyRepository by Fixture { FakeFacePropertyRepository() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeDisplayStateRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeDisplayStateRepository.kt
index 3fdeb30..3b5ff38 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeDisplayStateRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeDisplayStateRepository.kt
@@ -17,6 +17,7 @@
 
 package com.android.systemui.biometrics.data.repository
 
+import android.util.Size
 import com.android.systemui.biometrics.shared.model.DisplayRotation
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.StateFlow
@@ -29,6 +30,9 @@
     private val _currentRotation = MutableStateFlow<DisplayRotation>(DisplayRotation.ROTATION_0)
     override val currentRotation: StateFlow<DisplayRotation> = _currentRotation.asStateFlow()
 
+    private val _currentDisplaySize = MutableStateFlow<Size>(Size(0, 0))
+    override val currentDisplaySize: StateFlow<Size> = _currentDisplaySize.asStateFlow()
+
     override val isReverseDefaultRotation = false
 
     fun setIsInRearDisplayMode(isInRearDisplayMode: Boolean) {
@@ -38,4 +42,8 @@
     fun setCurrentRotation(currentRotation: DisplayRotation) {
         _currentRotation.value = currentRotation
     }
+
+    fun setCurrentDisplaySize(size: Size) {
+        _currentDisplaySize.value = size
+    }
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeFacePropertyRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeFacePropertyRepository.kt
index 51ce9f0..77f501f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeFacePropertyRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeFacePropertyRepository.kt
@@ -17,6 +17,7 @@
 
 package com.android.systemui.biometrics.data.repository
 
+import android.graphics.Point
 import com.android.systemui.biometrics.shared.model.LockoutMode
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.StateFlow
@@ -28,6 +29,10 @@
 
     private val lockoutModesForUser = mutableMapOf<Int, LockoutMode>()
 
+    private val faceSensorLocation = MutableStateFlow<Point?>(null)
+    override val sensorLocation: StateFlow<Point?>
+        get() = faceSensorLocation
+
     fun setLockoutMode(userId: Int, mode: LockoutMode) {
         lockoutModesForUser[userId] = mode
     }
@@ -38,4 +43,8 @@
     fun setSensorInfo(value: FaceSensorInfo?) {
         faceSensorInfo.value = value
     }
+
+    fun setSensorLocation(value: Point?) {
+        faceSensorLocation.value = value
+    }
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/common/data/repository/FakePackageChangeRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/common/data/repository/FakePackageChangeRepository.kt
new file mode 100644
index 0000000..60f0448
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/common/data/repository/FakePackageChangeRepository.kt
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.common.data.repository
+
+import android.os.UserHandle
+import com.android.systemui.common.data.shared.model.PackageChangeModel
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.filter
+
+class FakePackageChangeRepository : PackageChangeRepository {
+
+    private var _packageChanged = MutableSharedFlow<PackageChangeModel>()
+
+    override fun packageChanged(user: UserHandle) =
+        _packageChanged.filter {
+            user == UserHandle.ALL || user == UserHandle.getUserHandleForUid(it.packageUid)
+        }
+
+    suspend fun notifyChange(model: PackageChangeModel) {
+        _packageChanged.emit(model)
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileUser.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/common/data/repository/PackageChangeRepositoryKosmos.kt
similarity index 66%
copy from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileUser.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/common/data/repository/PackageChangeRepositoryKosmos.kt
index efc7431..adc05e0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileUser.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/common/data/repository/PackageChangeRepositoryKosmos.kt
@@ -14,12 +14,10 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.custom.di.bound
+package com.android.systemui.common.data.repository
 
-import javax.inject.Qualifier
+import com.android.systemui.kosmos.Kosmos
 
-/** User associated with current custom tile binding. */
-@Qualifier
-@MustBeDocumented
-@Retention(AnnotationRetention.RUNTIME)
-annotation class CustomTileUser
+var Kosmos.packageChangeRepository: PackageChangeRepository by
+    Kosmos.Fixture { fakePackageChangeRepository }
+val Kosmos.fakePackageChangeRepository by Kosmos.Fixture { FakePackageChangeRepository() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalMediaRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalMediaRepository.kt
index 3ab1b6c..3ea3ccf 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalMediaRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalMediaRepository.kt
@@ -16,8 +16,24 @@
 
 package com.android.systemui.communal.data.repository
 
+import com.android.systemui.communal.data.model.CommunalMediaModel
+import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
 
-class FakeCommunalMediaRepository(
-    override val mediaPlaying: MutableStateFlow<Boolean> = MutableStateFlow(false)
-) : CommunalMediaRepository
+class FakeCommunalMediaRepository : CommunalMediaRepository {
+    private val _mediaModel = MutableStateFlow(CommunalMediaModel.INACTIVE)
+
+    override val mediaModel: Flow<CommunalMediaModel> = _mediaModel
+
+    fun mediaActive(timestamp: Long = 0L) {
+        _mediaModel.value =
+            CommunalMediaModel(
+                hasAnyMediaOrRecommendation = true,
+                createdTimestampMillis = timestamp,
+            )
+    }
+
+    fun mediaInactive() {
+        _mediaModel.value = CommunalMediaModel.INACTIVE
+    }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalRepository.kt
index c85c27e..e82cae4 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalRepository.kt
@@ -9,6 +9,7 @@
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
 import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.stateIn
@@ -52,4 +53,12 @@
     fun setIsCommunalHubShowing(isCommunalHubShowing: Boolean) {
         _isCommunalHubShowing.value = isCommunalHubShowing
     }
+
+    private val _isCtaTileInViewModeVisible: MutableStateFlow<Boolean> = MutableStateFlow(true)
+    override val isCtaTileInViewModeVisible: Flow<Boolean> =
+        _isCtaTileInViewModeVisible.asStateFlow()
+
+    override fun setCtaTileInViewModeVisibility(isVisible: Boolean) {
+        _isCtaTileInViewModeVisible.value = isVisible
+    }
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt
index c6f12e2..397dc1a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt
@@ -1,15 +1,38 @@
 package com.android.systemui.communal.data.repository
 
+import android.content.ComponentName
 import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.coroutineScope
 import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableSharedFlow
 import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.launch
 
 /** Fake implementation of [CommunalWidgetRepository] */
-class FakeCommunalWidgetRepository : CommunalWidgetRepository {
+class FakeCommunalWidgetRepository(private val coroutineScope: CoroutineScope) :
+    CommunalWidgetRepository {
     private val _communalWidgets = MutableStateFlow<List<CommunalWidgetContentModel>>(emptyList())
     override val communalWidgets: Flow<List<CommunalWidgetContentModel>> = _communalWidgets
+    private val _widgetAdded = MutableSharedFlow<Int>()
+    val widgetAdded: Flow<Int> = _widgetAdded
+
+    private var nextWidgetId = 1
 
     fun setCommunalWidgets(inventory: List<CommunalWidgetContentModel>) {
         _communalWidgets.value = inventory
     }
+
+    override fun addWidget(
+        provider: ComponentName,
+        priority: Int,
+        configureWidget: suspend (id: Int) -> Boolean
+    ) {
+        coroutineScope.launch {
+            val id = nextWidgetId++
+            if (configureWidget.invoke(id)) {
+                _widgetAdded.emit(id)
+            }
+        }
+    }
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorFactory.kt
index faacce6..95ff889 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorFactory.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorFactory.kt
@@ -35,8 +35,10 @@
     @JvmStatic
     fun create(
         testScope: TestScope = TestScope(),
-        communalRepository: FakeCommunalRepository = FakeCommunalRepository(),
-        widgetRepository: FakeCommunalWidgetRepository = FakeCommunalWidgetRepository(),
+        communalRepository: FakeCommunalRepository =
+            FakeCommunalRepository(testScope.backgroundScope),
+        widgetRepository: FakeCommunalWidgetRepository =
+            FakeCommunalWidgetRepository(testScope.backgroundScope),
         mediaRepository: FakeCommunalMediaRepository = FakeCommunalMediaRepository(),
         smartspaceRepository: FakeSmartspaceRepository = FakeSmartspaceRepository(),
         tutorialRepository: FakeCommunalTutorialRepository = FakeCommunalTutorialRepository(),
@@ -50,6 +52,7 @@
                 communalRepository = communalRepository,
             )
         return WithDependencies(
+            testScope,
             communalRepository,
             widgetRepository,
             mediaRepository,
@@ -73,6 +76,7 @@
     }
 
     data class WithDependencies(
+        val testScope: TestScope,
         val communalRepository: FakeCommunalRepository,
         val widgetRepository: FakeCommunalWidgetRepository,
         val mediaRepository: FakeCommunalMediaRepository,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileUser.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FaceWakeUpTriggersConfigKosmos.kt
similarity index 64%
copy from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileUser.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FaceWakeUpTriggersConfigKosmos.kt
index efc7431..21cff0d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileUser.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FaceWakeUpTriggersConfigKosmos.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,12 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.custom.di.bound
+package com.android.systemui.deviceentry.data.repository
 
-import javax.inject.Qualifier
+import com.android.systemui.kosmos.Kosmos
 
-/** User associated with current custom tile binding. */
-@Qualifier
-@MustBeDocumented
-@Retention(AnnotationRetention.RUNTIME)
-annotation class CustomTileUser
+var Kosmos.faceWakeUpTriggersConfig: FaceWakeUpTriggersConfig by
+    Kosmos.Fixture { FakeFaceWakeUpTriggersConfig() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeFaceWakeUpTriggersConfig.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeFaceWakeUpTriggersConfig.kt
new file mode 100644
index 0000000..af617b7
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeFaceWakeUpTriggersConfig.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.deviceentry.data.repository
+
+import com.android.systemui.power.shared.model.WakeSleepReason
+
+class FakeFaceWakeUpTriggersConfig : FaceWakeUpTriggersConfig {
+    private val triggerFaceAuthOnWakeUpFrom: MutableSet<Int> = mutableSetOf()
+    private val wakeSleepReasonsToTriggerFaceAuth: MutableSet<WakeSleepReason> = mutableSetOf()
+    override fun shouldTriggerFaceAuthOnWakeUpFrom(pmWakeReason: Int): Boolean {
+        return triggerFaceAuthOnWakeUpFrom.contains(pmWakeReason)
+    }
+
+    override fun shouldTriggerFaceAuthOnWakeUpFrom(wakeReason: WakeSleepReason): Boolean {
+        return wakeSleepReasonsToTriggerFaceAuth.contains(wakeReason)
+    }
+
+    fun setTriggerFaceAuthOnWakeUpFrom(pmWakeReasons: Set<Int>) {
+        triggerFaceAuthOnWakeUpFrom.clear()
+        triggerFaceAuthOnWakeUpFrom.addAll(pmWakeReasons)
+
+        wakeSleepReasonsToTriggerFaceAuth.clear()
+        wakeSleepReasonsToTriggerFaceAuth.addAll(
+            pmWakeReasons.map { WakeSleepReason.fromPowerManagerWakeReason(it) }
+        )
+    }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/ui/viewmodel/UdfpsAccessibilityOverlayViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/ui/viewmodel/UdfpsAccessibilityOverlayViewModelKosmos.kt
index 9175f09..cdeade1 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/ui/viewmodel/UdfpsAccessibilityOverlayViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/ui/viewmodel/UdfpsAccessibilityOverlayViewModelKosmos.kt
@@ -18,14 +18,16 @@
 
 import com.android.systemui.accessibility.domain.interactor.accessibilityInteractor
 import com.android.systemui.biometrics.domain.interactor.udfpsOverlayInteractor
-import com.android.systemui.deviceentry.ui.viewmodel.UdfpsAccessibilityOverlayViewModel
+import com.android.systemui.deviceentry.ui.viewmodel.DeviceEntryUdfpsAccessibilityOverlayViewModel
 import com.android.systemui.keyguard.ui.viewmodel.deviceEntryForegroundIconViewModel
 import com.android.systemui.keyguard.ui.viewmodel.deviceEntryIconViewModel
 import com.android.systemui.kosmos.Kosmos
+import kotlinx.coroutines.ExperimentalCoroutinesApi
 
-val Kosmos.udfpsAccessibilityOverlayViewModel by
+@ExperimentalCoroutinesApi
+val Kosmos.deviceEntryUdfpsAccessibilityOverlayViewModel by
     Kosmos.Fixture {
-        UdfpsAccessibilityOverlayViewModel(
+        DeviceEntryUdfpsAccessibilityOverlayViewModel(
             udfpsOverlayInteractor = udfpsOverlayInteractor,
             accessibilityInteractor = accessibilityInteractor,
             deviceEntryIconViewModel = deviceEntryIconViewModel,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorKosmos.kt
index d2dff78..0b1fb40 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorKosmos.kt
@@ -18,13 +18,45 @@
 
 package com.android.systemui.deviceentry.domain.interactor
 
+import android.content.applicationContext
+import com.android.keyguard.keyguardUpdateMonitor
+import com.android.keyguard.trustManager
+import com.android.systemui.biometrics.data.repository.facePropertyRepository
+import com.android.systemui.bouncer.domain.interactor.alternateBouncerInteractor
+import com.android.systemui.bouncer.domain.interactor.primaryBouncerInteractor
+import com.android.systemui.deviceentry.data.repository.faceWakeUpTriggersConfig
+import com.android.systemui.keyguard.data.repository.biometricSettingsRepository
 import com.android.systemui.keyguard.data.repository.deviceEntryFaceAuthRepository
+import com.android.systemui.keyguard.data.repository.deviceEntryFingerprintAuthRepository
+import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
 import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.log.FaceAuthenticationLogger
+import com.android.systemui.power.domain.interactor.powerInteractor
+import com.android.systemui.user.data.repository.userRepository
+import com.android.systemui.util.mockito.mock
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 
+val Kosmos.faceAuthLogger by Kosmos.Fixture { mock<FaceAuthenticationLogger>() }
 val Kosmos.deviceEntryFaceAuthInteractor by
     Kosmos.Fixture {
-        DeviceEntryFaceAuthInteractor(
+        SystemUIDeviceEntryFaceAuthInteractor(
+            context = applicationContext,
+            applicationScope = applicationCoroutineScope,
+            mainDispatcher = testDispatcher,
             repository = deviceEntryFaceAuthRepository,
+            primaryBouncerInteractor = { primaryBouncerInteractor },
+            alternateBouncerInteractor = alternateBouncerInteractor,
+            keyguardTransitionInteractor = keyguardTransitionInteractor,
+            faceAuthenticationLogger = faceAuthLogger,
+            keyguardUpdateMonitor = keyguardUpdateMonitor,
+            deviceEntryFingerprintAuthRepository = deviceEntryFingerprintAuthRepository,
+            userRepository = userRepository,
+            facePropertyRepository = facePropertyRepository,
+            faceWakeUpTriggersConfig = faceWakeUpTriggersConfig,
+            powerInteractor = powerInteractor,
+            biometricSettingsRepository = biometricSettingsRepository,
+            trustManager = trustManager,
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryKosmos.kt
index 3d72967..599b5142 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryKosmos.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.keyguard.data.repository
 
+import com.android.systemui.deviceentry.data.repository.DeviceEntryFaceAuthRepository
 import com.android.systemui.kosmos.Kosmos
 
 var Kosmos.deviceEntryFaceAuthRepository: DeviceEntryFaceAuthRepository by
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFaceAuthRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFaceAuthRepository.kt
index a1b6587..e96aeada 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFaceAuthRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFaceAuthRepository.kt
@@ -16,10 +16,11 @@
 
 package com.android.systemui.keyguard.data.repository
 
-import com.android.keyguard.FaceAuthUiEvent
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.shared.model.FaceAuthenticationStatus
-import com.android.systemui.keyguard.shared.model.FaceDetectionStatus
+import com.android.systemui.deviceentry.data.repository.DeviceEntryFaceAuthRepository
+import com.android.systemui.deviceentry.shared.FaceAuthUiEvent
+import com.android.systemui.deviceentry.shared.model.FaceAuthenticationStatus
+import com.android.systemui.deviceentry.shared.model.FaceDetectionStatus
 import dagger.Binds
 import dagger.Module
 import javax.inject.Inject
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 4200f05..975db3b 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
@@ -80,7 +80,7 @@
     override val lastDozeTapToWakePosition = _lastDozeTapToWakePosition.asStateFlow()
 
     private val _isAodAvailable = MutableStateFlow(false)
-    override val isAodAvailable: Flow<Boolean> = _isAodAvailable
+    override val isAodAvailable: StateFlow<Boolean> = _isAodAvailable
 
     private val _isDreaming = MutableStateFlow(false)
     override val isDreaming: Flow<Boolean> = _isDreaming
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt
index a94ca29..0c1dbfe 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt
@@ -147,7 +147,6 @@
                 )
             }
         }
-
         _transitions.emit(step)
     }
 
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractorKosmos.kt
index b0d941d..a9d89a3 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractorKosmos.kt
@@ -26,7 +26,7 @@
 import com.android.systemui.kosmos.applicationCoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 
-val Kosmos.burnInInteractor by Fixture {
+var Kosmos.burnInInteractor by Fixture {
     BurnInInteractor(
         context = applicationContext,
         burnInHelperWrapper = burnInHelperWrapper,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorKosmos.kt
index b03d0b8..b1a0b67 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorKosmos.kt
@@ -20,6 +20,7 @@
 import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.power.domain.interactor.powerInteractor
 import com.android.systemui.shade.data.repository.shadeRepository
 import dagger.Lazy
@@ -30,6 +31,8 @@
             transitionRepository = keyguardTransitionRepository,
             transitionInteractor = keyguardTransitionInteractor,
             scope = applicationCoroutineScope,
+            bgDispatcher = testDispatcher,
+            mainDispatcher = testDispatcher,
             keyguardInteractor = keyguardInteractor,
             flags = featureFlagsClassic,
             shadeRepository = shadeRepository,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorKosmos.kt
index ade3e1a..97536e2 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorKosmos.kt
@@ -21,6 +21,7 @@
 import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.power.domain.interactor.powerInteractor
 import com.android.systemui.user.domain.interactor.selectedUserInteractor
 
@@ -30,6 +31,8 @@
             transitionRepository = keyguardTransitionRepository,
             transitionInteractor = keyguardTransitionInteractor,
             scope = applicationCoroutineScope,
+            bgDispatcher = testDispatcher,
+            mainDispatcher = testDispatcher,
             keyguardInteractor = keyguardInteractor,
             flags = featureFlagsClassic,
             keyguardSecurityModel = keyguardSecurityModel,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileUser.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardBottomAreaInteractorKosmos.kt
similarity index 63%
copy from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileUser.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardBottomAreaInteractorKosmos.kt
index efc7431..a3955f7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileUser.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardBottomAreaInteractorKosmos.kt
@@ -14,12 +14,14 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.custom.di.bound
+package com.android.systemui.keyguard.domain.interactor
 
-import javax.inject.Qualifier
+import com.android.systemui.keyguard.data.repository.keyguardRepository
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
 
-/** User associated with current custom tile binding. */
-@Qualifier
-@MustBeDocumented
-@Retention(AnnotationRetention.RUNTIME)
-annotation class CustomTileUser
+val Kosmos.keyguardBottomAreaInteractor by Fixture {
+    KeyguardBottomAreaInteractor(
+        repository = keyguardRepository,
+    )
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorFactory.kt
index 3cabf0c..5f5d428 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorFactory.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorFactory.kt
@@ -27,6 +27,7 @@
 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
 import com.android.systemui.bouncer.ui.BouncerView
 import com.android.systemui.classifier.FalsingCollector
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
 import com.android.systemui.keyguard.DismissCallbackRegistry
 import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
@@ -73,7 +74,7 @@
                 trustRepository,
                 testScope.backgroundScope,
                 mock(SelectedUserInteractor::class.java),
-                mock(KeyguardFaceAuthInteractor::class.java),
+                mock(DeviceEntryFaceAuthInteractor::class.java),
             )
         val alternateBouncerInteractor =
             AlternateBouncerInteractor(
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlowKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlowKosmos.kt
index 8d6529a..dad1887 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlowKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlowKosmos.kt
@@ -19,6 +19,7 @@
 package com.android.systemui.keyguard.ui
 
 import com.android.keyguard.logging.keyguardTransitionAnimationLogger
+import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
 import com.android.systemui.kosmos.applicationCoroutineScope
@@ -27,6 +28,7 @@
 val Kosmos.keyguardTransitionAnimationFlow by Fixture {
     KeyguardTransitionAnimationFlow(
         scope = applicationCoroutineScope,
+        transitionInteractor = keyguardTransitionInteractor,
         logger = keyguardTransitionAnimationLogger,
     )
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToAodTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToAodTransitionViewModelKosmos.kt
index d9c6e4f..3ed9392 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToAodTransitionViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToAodTransitionViewModelKosmos.kt
@@ -19,7 +19,6 @@
 package com.android.systemui.keyguard.ui.viewmodel
 
 import com.android.systemui.deviceentry.domain.interactor.deviceEntryUdfpsInteractor
-import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
 import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
@@ -27,7 +26,6 @@
 
 val Kosmos.alternateBouncerToAodTransitionViewModel by Fixture {
     AlternateBouncerToAodTransitionViewModel(
-        interactor = keyguardTransitionInteractor,
         deviceEntryUdfpsInteractor = deviceEntryUdfpsInteractor,
         animationFlow = keyguardTransitionAnimationFlow,
     )
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToGoneTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToGoneTransitionViewModelKosmos.kt
index e4821b0..c909dd6 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToGoneTransitionViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToGoneTransitionViewModelKosmos.kt
@@ -18,7 +18,6 @@
 
 package com.android.systemui.keyguard.ui.viewmodel
 
-import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
 import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
@@ -26,7 +25,6 @@
 
 val Kosmos.alternateBouncerToGoneTransitionViewModel by Fixture {
     AlternateBouncerToGoneTransitionViewModel(
-        interactor = keyguardTransitionInteractor,
         bouncerToGoneFlows = bouncerToGoneFlows,
         animationFlow = keyguardTransitionAnimationFlow,
     )
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelKosmos.kt
index 9f0466d..b4f1218 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelKosmos.kt
@@ -18,7 +18,6 @@
 
 package com.android.systemui.keyguard.ui.viewmodel
 
-import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
 import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
@@ -28,7 +27,6 @@
 val Kosmos.alternateBouncerViewModel by Fixture {
     AlternateBouncerViewModel(
         statusBarKeyguardViewManager = statusBarKeyguardViewManager,
-        transitionInteractor = keyguardTransitionInteractor,
         animationFlow = keyguardTransitionAnimationFlow,
     )
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodAlphaViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodAlphaViewModelKosmos.kt
new file mode 100644
index 0000000..6b89e0f
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodAlphaViewModelKosmos.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
+import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+
+val Kosmos.aodAlphaViewModel by Fixture {
+    AodAlphaViewModel(
+        keyguardInteractor = keyguardInteractor,
+        keyguardTransitionInteractor = keyguardTransitionInteractor,
+        occludedToLockscreenTransitionViewModel = occludedToLockscreenTransitionViewModel,
+    )
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelKosmos.kt
new file mode 100644
index 0000000..35cfa89
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelKosmos.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import com.android.systemui.common.ui.domain.interactor.configurationInteractor
+import com.android.systemui.keyguard.domain.interactor.burnInInteractor
+import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
+import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+
+val Kosmos.aodBurnInViewModel by Fixture {
+    AodBurnInViewModel(
+        burnInInteractor = burnInInteractor,
+        configurationInteractor = configurationInteractor,
+        keyguardInteractor = keyguardInteractor,
+        keyguardTransitionInteractor = keyguardTransitionInteractor,
+        goneToAodTransitionViewModel = goneToAodTransitionViewModel,
+        occludedToLockscreenTransitionViewModel = occludedToLockscreenTransitionViewModel,
+        keyguardClockViewModel = keyguardClockViewModel,
+    )
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModelKosmos.kt
index 44e5426..b6f278c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModelKosmos.kt
@@ -18,7 +18,6 @@
 
 package com.android.systemui.keyguard.ui.viewmodel
 
-import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
 import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
@@ -26,7 +25,6 @@
 
 val Kosmos.aodToGoneTransitionViewModel by Fixture {
     AodToGoneTransitionViewModel(
-        interactor = keyguardTransitionInteractor,
         animationFlow = keyguardTransitionAnimationFlow,
     )
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelKosmos.kt
index b5a5f03..733340c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelKosmos.kt
@@ -19,7 +19,6 @@
 package com.android.systemui.keyguard.ui.viewmodel
 
 import com.android.systemui.deviceentry.domain.interactor.deviceEntryUdfpsInteractor
-import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
 import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
@@ -27,7 +26,6 @@
 
 val Kosmos.aodToLockscreenTransitionViewModel by Fixture {
     AodToLockscreenTransitionViewModel(
-        interactor = keyguardTransitionInteractor,
         deviceEntryUdfpsInteractor = deviceEntryUdfpsInteractor,
         animationFlow = keyguardTransitionAnimationFlow,
     )
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModelKosmos.kt
index 27ad0f0..8d066fc 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModelKosmos.kt
@@ -18,7 +18,6 @@
 
 package com.android.systemui.keyguard.ui.viewmodel
 
-import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
 import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
@@ -26,7 +25,6 @@
 
 val Kosmos.aodToOccludedTransitionViewModel by Fixture {
     AodToOccludedTransitionViewModel(
-        interactor = keyguardTransitionInteractor,
         animationFlow = keyguardTransitionAnimationFlow,
     )
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsKosmos.kt
index 6ffcc9a..c71c1c3 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsKosmos.kt
@@ -20,7 +20,6 @@
 
 import com.android.systemui.bouncer.domain.interactor.primaryBouncerInteractor
 import com.android.systemui.flags.featureFlagsClassic
-import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
 import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
@@ -31,7 +30,6 @@
 
 val Kosmos.bouncerToGoneFlows by Fixture {
     BouncerToGoneFlows(
-        interactor = keyguardTransitionInteractor,
         statusBarStateController = sysuiStatusBarStateController,
         primaryBouncerInteractor = primaryBouncerInteractor,
         keyguardDismissActionInteractor = mock(),
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModelKosmos.kt
new file mode 100644
index 0000000..400a0d8
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModelKosmos.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+
+val Kosmos.dozingToLockscreenTransitionViewModel by Fixture {
+    DozingToLockscreenTransitionViewModel(
+        animationFlow = keyguardTransitionAnimationFlow,
+    )
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModelKosmos.kt
index 14e2cff..19e4241 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModelKosmos.kt
@@ -19,15 +19,13 @@
 package com.android.systemui.keyguard.ui.viewmodel
 
 import com.android.systemui.deviceentry.domain.interactor.deviceEntryUdfpsInteractor
-import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
 import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 
-val Kosmos.goneToAodTransitionViewModel by Fixture {
+var Kosmos.goneToAodTransitionViewModel by Fixture {
     GoneToAodTransitionViewModel(
-        interactor = keyguardTransitionInteractor,
         deviceEntryUdfpsInteractor = deviceEntryUdfpsInteractor,
         animationFlow = keyguardTransitionAnimationFlow,
     )
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelKosmos.kt
index 073b34b..b267a96 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelKosmos.kt
@@ -18,7 +18,6 @@
 
 package com.android.systemui.keyguard.ui.viewmodel
 
-import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
 import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
@@ -26,7 +25,6 @@
 
 val Kosmos.goneToDreamingTransitionViewModel by Fixture {
     GoneToDreamingTransitionViewModel(
-        interactor = keyguardTransitionInteractor,
         animationFlow = keyguardTransitionAnimationFlow,
     )
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelKosmos.kt
index d878683..5ca0439 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelKosmos.kt
@@ -20,6 +20,7 @@
 import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.statusbar.policy.splitShadeStateController
 
 val Kosmos.keyguardClockViewModel by
     Kosmos.Fixture {
@@ -27,5 +28,6 @@
             keyguardInteractor = keyguardInteractor,
             keyguardClockInteractor = keyguardClockInteractor,
             applicationScope = applicationCoroutineScope,
+            splitShadeStateController = splitShadeStateController,
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt
index 13ee747..933f50c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt
@@ -18,10 +18,7 @@
 
 package com.android.systemui.keyguard.ui.viewmodel
 
-import com.android.systemui.common.ui.domain.interactor.configurationInteractor
 import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
-import com.android.systemui.flags.FakeFeatureFlagsClassic
-import com.android.systemui.keyguard.domain.interactor.burnInInteractor
 import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
 import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
 import com.android.systemui.kosmos.Kosmos
@@ -33,18 +30,14 @@
 
 val Kosmos.keyguardRootViewModel by Fixture {
     KeyguardRootViewModel(
-        configurationInteractor = configurationInteractor,
         deviceEntryInteractor = deviceEntryInteractor,
         dozeParameters = dozeParameters,
         keyguardInteractor = keyguardInteractor,
         keyguardTransitionInteractor = keyguardTransitionInteractor,
         notificationsKeyguardInteractor = notificationsKeyguardInteractor,
-        burnInInteractor = burnInInteractor,
-        goneToAodTransitionViewModel = goneToAodTransitionViewModel,
         aodToLockscreenTransitionViewModel = aodToLockscreenTransitionViewModel,
-        occludedToLockscreenTransitionViewModel = occludedToLockscreenTransitionViewModel,
         screenOffAnimationController = screenOffAnimationController,
-        keyguardClockViewModel = keyguardClockViewModel,
-        featureFlags = FakeFeatureFlagsClassic(),
+        aodBurnInViewModel = aodBurnInViewModel,
+        aodAlphaViewModel = aodAlphaViewModel,
     )
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModelKosmos.kt
index 7865f71..07b4cd4 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModelKosmos.kt
@@ -19,7 +19,6 @@
 package com.android.systemui.keyguard.ui.viewmodel
 
 import com.android.systemui.deviceentry.domain.interactor.deviceEntryUdfpsInteractor
-import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
 import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
@@ -27,7 +26,6 @@
 
 val Kosmos.lockscreenToAodTransitionViewModel by Fixture {
     LockscreenToAodTransitionViewModel(
-        interactor = keyguardTransitionInteractor,
         deviceEntryUdfpsInteractor = deviceEntryUdfpsInteractor,
         shadeDependentFlows = shadeDependentFlows,
         animationFlow = keyguardTransitionAnimationFlow,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelKosmos.kt
index b9f4b71..56d5ff6 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelKosmos.kt
@@ -18,7 +18,6 @@
 
 package com.android.systemui.keyguard.ui.viewmodel
 
-import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
 import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
@@ -26,7 +25,6 @@
 
 val Kosmos.lockscreenToDreamingTransitionViewModel by Fixture {
     LockscreenToDreamingTransitionViewModel(
-        interactor = keyguardTransitionInteractor,
         shadeDependentFlows = shadeDependentFlows,
         animationFlow = keyguardTransitionAnimationFlow,
     )
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModelKosmos.kt
index 475aa2d..1b2337f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModelKosmos.kt
@@ -18,7 +18,6 @@
 
 package com.android.systemui.keyguard.ui.viewmodel
 
-import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
 import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
@@ -26,7 +25,6 @@
 
 val Kosmos.lockscreenToGoneTransitionViewModel by Fixture {
     LockscreenToGoneTransitionViewModel(
-        interactor = keyguardTransitionInteractor,
         animationFlow = keyguardTransitionAnimationFlow,
     )
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelKosmos.kt
index 8541a4f..9953d39 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelKosmos.kt
@@ -19,7 +19,6 @@
 package com.android.systemui.keyguard.ui.viewmodel
 
 import com.android.systemui.common.ui.domain.interactor.configurationInteractor
-import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
 import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
@@ -27,7 +26,6 @@
 
 val Kosmos.lockscreenToOccludedTransitionViewModel by Fixture {
     LockscreenToOccludedTransitionViewModel(
-        interactor = keyguardTransitionInteractor,
         shadeDependentFlows = shadeDependentFlows,
         configurationInteractor = configurationInteractor,
         animationFlow = keyguardTransitionAnimationFlow,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelKosmos.kt
index 65c47fc..f094f22 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelKosmos.kt
@@ -18,7 +18,6 @@
 
 package com.android.systemui.keyguard.ui.viewmodel
 
-import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
 import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
@@ -26,7 +25,6 @@
 
 val Kosmos.lockscreenToPrimaryBouncerTransitionViewModel by Fixture {
     LockscreenToPrimaryBouncerTransitionViewModel(
-        interactor = keyguardTransitionInteractor,
         shadeDependentFlows = shadeDependentFlows,
         animationFlow = keyguardTransitionAnimationFlow,
     )
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModelKosmos.kt
index ddde549..b7867b6 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModelKosmos.kt
@@ -19,7 +19,6 @@
 package com.android.systemui.keyguard.ui.viewmodel
 
 import com.android.systemui.deviceentry.domain.interactor.deviceEntryUdfpsInteractor
-import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
 import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
@@ -27,7 +26,6 @@
 
 val Kosmos.occludedToAodTransitionViewModel by Fixture {
     OccludedToAodTransitionViewModel(
-        interactor = keyguardTransitionInteractor,
         deviceEntryUdfpsInteractor = deviceEntryUdfpsInteractor,
         animationFlow = keyguardTransitionAnimationFlow,
     )
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelKosmos.kt
index 5bbde2b..e6651a4 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelKosmos.kt
@@ -20,15 +20,13 @@
 
 import com.android.systemui.common.ui.domain.interactor.configurationInteractor
 import com.android.systemui.deviceentry.domain.interactor.deviceEntryUdfpsInteractor
-import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
 import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 
-val Kosmos.occludedToLockscreenTransitionViewModel by Fixture {
+var Kosmos.occludedToLockscreenTransitionViewModel by Fixture {
     OccludedToLockscreenTransitionViewModel(
-        interactor = keyguardTransitionInteractor,
         deviceEntryUdfpsInteractor = deviceEntryUdfpsInteractor,
         configurationInteractor = configurationInteractor,
         animationFlow = keyguardTransitionAnimationFlow,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModelKosmos.kt
index a7f29d6..8d88730 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModelKosmos.kt
@@ -19,7 +19,6 @@
 package com.android.systemui.keyguard.ui.viewmodel
 
 import com.android.systemui.deviceentry.domain.interactor.deviceEntryUdfpsInteractor
-import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
 import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
@@ -27,7 +26,6 @@
 
 val Kosmos.primaryBouncerToAodTransitionViewModel by Fixture {
     PrimaryBouncerToAodTransitionViewModel(
-        interactor = keyguardTransitionInteractor,
         deviceEntryUdfpsInteractor = deviceEntryUdfpsInteractor,
         animationFlow = keyguardTransitionAnimationFlow,
     )
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelKosmos.kt
index ace6ae3..ab28d0d6 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelKosmos.kt
@@ -20,7 +20,6 @@
 
 import com.android.systemui.bouncer.domain.interactor.primaryBouncerInteractor
 import com.android.systemui.flags.featureFlagsClassic
-import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
 import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
@@ -30,7 +29,6 @@
 
 val Kosmos.primaryBouncerToGoneTransitionViewModel by Fixture {
     PrimaryBouncerToGoneTransitionViewModel(
-        interactor = keyguardTransitionInteractor,
         statusBarStateController = sysuiStatusBarStateController,
         primaryBouncerInteractor = primaryBouncerInteractor,
         keyguardDismissActionInteractor = mock(),
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModelKosmos.kt
index 3bbabf7..8566251 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModelKosmos.kt
@@ -19,7 +19,6 @@
 package com.android.systemui.keyguard.ui.viewmodel
 
 import com.android.systemui.deviceentry.domain.interactor.deviceEntryUdfpsInteractor
-import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
 import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
@@ -27,7 +26,6 @@
 
 val Kosmos.primaryBouncerToLockscreenTransitionViewModel by Fixture {
     PrimaryBouncerToLockscreenTransitionViewModel(
-        interactor = keyguardTransitionInteractor,
         deviceEntryUdfpsInteractor = deviceEntryUdfpsInteractor,
         animationFlow = keyguardTransitionAnimationFlow,
     )
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dump/LogBufferHelper.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/log/LogBufferHelper.kt
similarity index 72%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/dump/LogBufferHelper.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/log/LogBufferHelper.kt
index 0538227..45ecb4c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dump/LogBufferHelper.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/log/LogBufferHelper.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,23 +14,18 @@
  * limitations under the License.
  */
 
-package com.android.systemui.dump
+package com.android.systemui.log
 
-import com.android.systemui.log.LogBuffer
 import com.android.systemui.log.core.LogLevel
-import com.android.systemui.log.LogcatEchoTracker
 
-/**
- * Creates a LogBuffer that will echo everything to logcat, which is useful for debugging tests.
- */
+/** Creates a LogBuffer that will echo everything to logcat, which is useful for debugging tests. */
 @JvmOverloads
 fun logcatLogBuffer(name: String = "EchoToLogcatLogBuffer") =
     LogBuffer(name, 50, LogcatEchoTrackerAlways())
 
-/**
- * A [LogcatEchoTracker] that always allows echoing to the logcat.
- */
+/** A [LogcatEchoTracker] that always allows echoing to the logcat. */
 class LogcatEchoTrackerAlways : LogcatEchoTracker {
     override fun isBufferLoggable(bufferName: String, level: LogLevel): Boolean = true
+
     override fun isTagLoggable(tagName: String, level: LogLevel): Boolean = true
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileUser.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/process/ProcessKosmos.kt
similarity index 71%
copy from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileUser.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/process/ProcessKosmos.kt
index efc7431..79167f8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileUser.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/process/ProcessKosmos.kt
@@ -14,12 +14,8 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.custom.di.bound
+package com.android.systemui.process
 
-import javax.inject.Qualifier
+import com.android.systemui.kosmos.Kosmos
 
-/** User associated with current custom tile binding. */
-@Qualifier
-@MustBeDocumented
-@Retention(AnnotationRetention.RUNTIME)
-annotation class CustomTileUser
+val Kosmos.processWrapper: ProcessWrapperFake by Kosmos.Fixture { ProcessWrapperFake() }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileUser.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/process/ProcessWrapperFake.kt
similarity index 71%
copy from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileUser.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/process/ProcessWrapperFake.kt
index efc7431..9841778 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileUser.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/process/ProcessWrapperFake.kt
@@ -14,12 +14,13 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.custom.di.bound
+package com.android.systemui.process
 
-import javax.inject.Qualifier
+class ProcessWrapperFake : ProcessWrapper() {
 
-/** User associated with current custom tile binding. */
-@Qualifier
-@MustBeDocumented
-@Retention(AnnotationRetention.RUNTIME)
-annotation class CustomTileUser
+    var systemUser: Boolean = false
+
+    override fun isSystemUser(): Boolean {
+        return systemUser
+    }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/FakeQSTileIntentUserInputHandler.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/FakeQSTileIntentUserInputHandler.kt
index 1185f2e..0307c41 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/FakeQSTileIntentUserInputHandler.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/FakeQSTileIntentUserInputHandler.kt
@@ -35,14 +35,21 @@
         mutableInputs.add(Input.Intent(view, intent))
     }
 
-    override fun handle(view: View?, pendingIntent: PendingIntent) {
-        mutableInputs.add(Input.PendingIntent(view, pendingIntent))
+    override fun handle(
+        view: View?,
+        pendingIntent: PendingIntent,
+        requestLaunchingDefaultActivity: Boolean
+    ) {
+        mutableInputs.add(Input.PendingIntent(view, pendingIntent, requestLaunchingDefaultActivity))
     }
 
     sealed interface Input {
         data class Intent(val view: View?, val intent: android.content.Intent) : Input
-        data class PendingIntent(val view: View?, val pendingIntent: android.app.PendingIntent) :
-            Input
+        data class PendingIntent(
+            val view: View?,
+            val pendingIntent: android.app.PendingIntent,
+            val requestLaunchingDefaultActivity: Boolean
+        ) : Input
     }
 }
 
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/CustomTileKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/CustomTileKosmos.kt
index d705248..14f28fe 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/CustomTileKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/CustomTileKosmos.kt
@@ -33,6 +33,7 @@
 val Kosmos.customTileRepository: FakeCustomTileRepository by
     Kosmos.Fixture {
         FakeCustomTileRepository(
+            tileSpec,
             customTileStatePersister,
             packageManagerAdapterFacade,
             testScope.testScheduler,
@@ -46,4 +47,4 @@
     Kosmos.Fixture { FakeCustomTilePackageUpdatesRepository() }
 
 val Kosmos.packageManagerAdapterFacade: FakePackageManagerAdapterFacade by
-    Kosmos.Fixture { FakePackageManagerAdapterFacade(tileSpec) }
+    Kosmos.Fixture { FakePackageManagerAdapterFacade(tileSpec.componentName) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/data/repository/FakeCustomTileRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/data/repository/FakeCustomTileRepository.kt
index ba803d8..c110da0 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/data/repository/FakeCustomTileRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/data/repository/FakeCustomTileRepository.kt
@@ -19,11 +19,13 @@
 import android.os.UserHandle
 import android.service.quicksettings.Tile
 import com.android.systemui.qs.external.FakeCustomTileStatePersister
+import com.android.systemui.qs.pipeline.shared.TileSpec
 import com.android.systemui.qs.tiles.impl.custom.data.entity.CustomTileDefaults
 import kotlin.coroutines.CoroutineContext
 import kotlinx.coroutines.flow.Flow
 
 class FakeCustomTileRepository(
+    tileSpec: TileSpec.CustomTileSpec,
     customTileStatePersister: FakeCustomTileStatePersister,
     private val packageManagerAdapterFacade: FakePackageManagerAdapterFacade,
     testBackgroundContext: CoroutineContext,
@@ -31,12 +33,16 @@
 
     private val realDelegate: CustomTileRepository =
         CustomTileRepositoryImpl(
-            packageManagerAdapterFacade.tileSpec,
+            tileSpec,
             customTileStatePersister,
             packageManagerAdapterFacade.packageManagerAdapter,
             testBackgroundContext,
         )
 
+    init {
+        require(tileSpec.componentName == packageManagerAdapterFacade.componentName)
+    }
+
     override suspend fun restoreForTheUserIfNeeded(user: UserHandle, isPersistable: Boolean) =
         realDelegate.restoreForTheUserIfNeeded(user, isPersistable)
 
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/data/repository/FakePackageManagerAdapterFacade.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/data/repository/FakePackageManagerAdapterFacade.kt
index c9a7655..634d121 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/data/repository/FakePackageManagerAdapterFacade.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/data/repository/FakePackageManagerAdapterFacade.kt
@@ -16,17 +16,27 @@
 
 package com.android.systemui.qs.tiles.impl.custom.data.repository
 
+import android.content.ComponentName
 import android.content.pm.ServiceInfo
 import android.os.Bundle
 import com.android.systemui.qs.external.PackageManagerAdapter
-import com.android.systemui.qs.pipeline.shared.TileSpec
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.eq
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
 
+/**
+ * Facade for [PackageManagerAdapter] to provide a fake-like behaviour. You can create this class
+ * and then get [packageManagerAdapter] to use in your test code.
+ *
+ * This allows to mock [PackageManagerAdapter] to provide a custom behaviour for
+ * [CustomTileRepository.isTileActive], [CustomTileRepository.isTileToggleable],
+ * [com.android.systemui.qs.external.TileServiceManager.isToggleableTile] or
+ * [com.android.systemui.qs.external.TileServiceManager.isActiveTile] when the real objects are
+ * used.
+ */
 class FakePackageManagerAdapterFacade(
-    val tileSpec: TileSpec.CustomTileSpec,
+    val componentName: ComponentName,
     val packageManagerAdapter: PackageManagerAdapter = mock {},
 ) {
 
@@ -34,22 +44,21 @@
     private var isActive: Boolean = false
 
     init {
-        whenever(packageManagerAdapter.getServiceInfo(eq(tileSpec.componentName), any()))
-            .thenAnswer {
-                ServiceInfo().apply {
-                    metaData =
-                        Bundle().apply {
-                            putBoolean(
-                                android.service.quicksettings.TileService.META_DATA_TOGGLEABLE_TILE,
-                                isToggleable
-                            )
-                            putBoolean(
-                                android.service.quicksettings.TileService.META_DATA_ACTIVE_TILE,
-                                isActive
-                            )
-                        }
-                }
+        whenever(packageManagerAdapter.getServiceInfo(eq(componentName), any())).thenAnswer {
+            ServiceInfo().apply {
+                metaData =
+                    Bundle().apply {
+                        putBoolean(
+                            android.service.quicksettings.TileService.META_DATA_TOGGLEABLE_TILE,
+                            isToggleable
+                        )
+                        putBoolean(
+                            android.service.quicksettings.TileService.META_DATA_ACTIVE_TILE,
+                            isActive
+                        )
+                    }
             }
+        }
     }
 
     fun setIsActive(isActive: Boolean) {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt
index 25b97b3..09ab655 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.scene
 
 import android.app.ActivityTaskManager
+import android.app.admin.DevicePolicyManager
 import android.content.Context
 import android.content.Intent
 import android.content.pm.UserInfo
@@ -51,20 +52,20 @@
 import com.android.systemui.communal.data.repository.FakeCommunalRepository
 import com.android.systemui.communal.domain.interactor.CommunalInteractor
 import com.android.systemui.communal.domain.interactor.CommunalInteractorFactory
+import com.android.systemui.deviceentry.data.repository.DeviceEntryFaceAuthRepository
 import com.android.systemui.deviceentry.data.repository.DeviceEntryRepository
 import com.android.systemui.deviceentry.data.repository.FakeDeviceEntryRepository
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
 import com.android.systemui.doze.DozeLogger
 import com.android.systemui.flags.FakeFeatureFlagsClassic
 import com.android.systemui.flags.Flags
-import com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepository
 import com.android.systemui.keyguard.data.repository.FakeCommandQueue
 import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFaceAuthRepository
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
 import com.android.systemui.keyguard.data.repository.FakeTrustRepository
 import com.android.systemui.keyguard.data.repository.KeyguardRepository
 import com.android.systemui.keyguard.data.repository.TrustRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
 import com.android.systemui.kosmos.Kosmos
@@ -96,6 +97,7 @@
 import com.android.systemui.user.ui.viewmodel.UserViewModel
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
+import com.android.systemui.util.time.SystemClock
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.flowOf
@@ -103,7 +105,6 @@
 import kotlinx.coroutines.test.currentTime
 import org.mockito.ArgumentMatchers.anyInt
 import org.mockito.ArgumentMatchers.anyString
-import org.mockito.Mockito
 
 /**
  * Utilities for creating scene container framework related repositories, interactors, and
@@ -144,18 +145,20 @@
         )
     }
     val telephonyRepository: FakeTelephonyRepository by lazy { FakeTelephonyRepository() }
-
     val bouncerRepository = BouncerRepository(featureFlags)
     val communalRepository: FakeCommunalRepository by lazy { FakeCommunalRepository() }
     val keyguardRepository: FakeKeyguardRepository by lazy { FakeKeyguardRepository() }
     val powerRepository: FakePowerRepository by lazy { FakePowerRepository() }
     val simBouncerRepository: FakeSimBouncerRepository by lazy { FakeSimBouncerRepository() }
-    val telephonyManager: TelephonyManager =
-        Mockito.mock(TelephonyManager::class.java).apply {
-            whenever(createForSubscriptionId(anyInt())).thenReturn(this)
-            whenever(supplyIccLockPin(anyString()))
-                .thenReturn(PinResult(PIN_RESULT_TYPE_SUCCESS, 3))
-        }
+
+    val clock: SystemClock = mock {
+        whenever(elapsedRealtime()).thenAnswer { testScope.currentTime }
+    }
+    val telephonyManager: TelephonyManager = mock {
+        whenever(createForSubscriptionId(anyInt())).thenReturn(this)
+        whenever(supplyIccLockPin(anyString())).thenReturn(PinResult(PIN_RESULT_TYPE_SUCCESS, 3))
+    }
+    val devicePolicyManager: DevicePolicyManager = mock {}
     val mobileConnectionsRepository: FakeMobileConnectionsRepository by lazy {
         FakeMobileConnectionsRepository(mock(), mock())
     }
@@ -235,6 +238,7 @@
         return AuthenticationInteractor(
             applicationScope = applicationScope(),
             repository = repository,
+            selectedUserInteractor = selectedUserInteractor(),
         )
     }
 
@@ -262,14 +266,14 @@
 
     fun bouncerInteractor(
         authenticationInteractor: AuthenticationInteractor,
-        keyguardFaceAuthInteractor: KeyguardFaceAuthInteractor = mock(),
+        deviceEntryFaceAuthInteractor: DeviceEntryFaceAuthInteractor = mock(),
     ): BouncerInteractor {
         return BouncerInteractor(
             applicationScope = applicationScope(),
             applicationContext = context,
             repository = bouncerRepository,
             authenticationInteractor = authenticationInteractor,
-            keyguardFaceAuthInteractor = keyguardFaceAuthInteractor,
+            deviceEntryFaceAuthInteractor = deviceEntryFaceAuthInteractor,
             falsingInteractor = falsingInteractor(),
             powerInteractor = powerInteractor(),
             simBouncerInteractor = simBouncerInteractor,
@@ -292,7 +296,7 @@
     fun bouncerViewModel(
         bouncerInteractor: BouncerInteractor,
         authenticationInteractor: AuthenticationInteractor,
-        actionButtonInteractor: BouncerActionButtonInteractor,
+        actionButtonInteractor: BouncerActionButtonInteractor = bouncerActionButtonInteractor(),
         users: List<UserViewModel> = createUsers(),
     ): BouncerViewModel {
         return BouncerViewModel(
@@ -300,14 +304,15 @@
             applicationScope = applicationScope(),
             mainDispatcher = testDispatcher,
             bouncerInteractor = bouncerInteractor,
+            simBouncerInteractor = simBouncerInteractor,
             authenticationInteractor = authenticationInteractor,
             flags = sceneContainerFlags,
             selectedUser = flowOf(users.first { it.isSelectionMarkerVisible }),
             users = flowOf(users),
             userSwitcherMenu = flowOf(createMenuActions()),
-            actionButtonInteractor = actionButtonInteractor,
-            simBouncerInteractor = simBouncerInteractor,
-            clock = mock { whenever(elapsedRealtime()).thenAnswer { testScope.currentTime } },
+            actionButton = actionButtonInteractor.actionButton,
+            clock = clock,
+            devicePolicyManager = devicePolicyManager,
         )
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileUser.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/LargeScreenHeaderHelperKosmos.kt
similarity index 64%
copy from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileUser.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/shade/LargeScreenHeaderHelperKosmos.kt
index efc7431..d3e7574 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileUser.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/LargeScreenHeaderHelperKosmos.kt
@@ -14,12 +14,11 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.custom.di.bound
+package com.android.systemui.shade
 
-import javax.inject.Qualifier
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.util.mockito.mock
 
-/** User associated with current custom tile binding. */
-@Qualifier
-@MustBeDocumented
-@Retention(AnnotationRetention.RUNTIME)
-annotation class CustomTileUser
+var Kosmos.largeScreenHeaderHelper by Fixture { mockLargeScreenHeaderHelper }
+val Kosmos.mockLargeScreenHeaderHelper by Fixture { mock<LargeScreenHeaderHelper>() }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/FakeKeyguardStatusBarRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeKeyguardStatusBarRepository.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/FakeKeyguardStatusBarRepository.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeKeyguardStatusBarRepository.kt
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileUser.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeRemoteInputRepository.kt
similarity index 70%
copy from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileUser.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeRemoteInputRepository.kt
index efc7431..c416ea1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileUser.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeRemoteInputRepository.kt
@@ -14,12 +14,10 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.custom.di.bound
+package com.android.systemui.statusbar.data.repository
 
-import javax.inject.Qualifier
+import kotlinx.coroutines.flow.MutableStateFlow
 
-/** User associated with current custom tile binding. */
-@Qualifier
-@MustBeDocumented
-@Retention(AnnotationRetention.RUNTIME)
-annotation class CustomTileUser
+class FakeRemoteInputRepository : RemoteInputRepository {
+    override val isRemoteInputActive = MutableStateFlow(false)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileUser.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/RemoteInputRepositoryKosmos.kt
similarity index 67%
copy from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileUser.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/RemoteInputRepositoryKosmos.kt
index efc7431..1684efb 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileUser.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/RemoteInputRepositoryKosmos.kt
@@ -14,12 +14,10 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.custom.di.bound
+package com.android.systemui.statusbar.data.repository
 
-import javax.inject.Qualifier
+import com.android.systemui.kosmos.Kosmos
 
-/** User associated with current custom tile binding. */
-@Qualifier
-@MustBeDocumented
-@Retention(AnnotationRetention.RUNTIME)
-annotation class CustomTileUser
+var Kosmos.remoteInputRepository: RemoteInputRepository by
+    Kosmos.Fixture { fakeRemoteInputRepository }
+val Kosmos.fakeRemoteInputRepository by Kosmos.Fixture { FakeRemoteInputRepository() }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileUser.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/domain/interactor/RemoteInputInteractorKosmos.kt
similarity index 68%
copy from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileUser.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/domain/interactor/RemoteInputInteractorKosmos.kt
index efc7431..07b39dc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileUser.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/domain/interactor/RemoteInputInteractorKosmos.kt
@@ -14,12 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.custom.di.bound
+package com.android.systemui.statusbar.domain.interactor
 
-import javax.inject.Qualifier
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.statusbar.data.repository.remoteInputRepository
 
-/** User associated with current custom tile binding. */
-@Qualifier
-@MustBeDocumented
-@Retention(AnnotationRetention.RUNTIME)
-annotation class CustomTileUser
+val Kosmos.remoteInputInteractor by Kosmos.Fixture { RemoteInputInteractor(remoteInputRepository) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/FakeStatusBarNotificationsDataLayerModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/FakeStatusBarNotificationsDataLayerModule.kt
index 788e3aa..1ffc9f4 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/FakeStatusBarNotificationsDataLayerModule.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/FakeStatusBarNotificationsDataLayerModule.kt
@@ -15,8 +15,6 @@
  */
 package com.android.systemui.statusbar.notification.data
 
-import com.android.systemui.statusbar.notification.data.repository.FakeNotificationsKeyguardStateRepositoryModule
 import dagger.Module
 
-@Module(includes = [FakeNotificationsKeyguardStateRepositoryModule::class])
-object FakeStatusBarNotificationsDataLayerModule
+@Module(includes = []) object FakeStatusBarNotificationsDataLayerModule
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/repository/FakeNotificationsKeyguardViewStateRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/repository/FakeNotificationsKeyguardViewStateRepository.kt
deleted file mode 100644
index 5d3cb4d..0000000
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/repository/FakeNotificationsKeyguardViewStateRepository.kt
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.systemui.statusbar.notification.data.repository
-
-import com.android.systemui.dagger.SysUISingleton
-import dagger.Binds
-import dagger.Module
-import javax.inject.Inject
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.MutableStateFlow
-
-@SysUISingleton
-class FakeNotificationsKeyguardViewStateRepository @Inject constructor() :
-    NotificationsKeyguardViewStateRepository {
-    private val _notificationsFullyHidden = MutableStateFlow(false)
-    override val areNotificationsFullyHidden: Flow<Boolean> = _notificationsFullyHidden
-
-    private val _isPulseExpanding = MutableStateFlow(false)
-    override val isPulseExpanding: Flow<Boolean> = _isPulseExpanding
-
-    fun setNotificationsFullyHidden(fullyHidden: Boolean) {
-        _notificationsFullyHidden.value = fullyHidden
-    }
-
-    fun setPulseExpanding(expanding: Boolean) {
-        _isPulseExpanding.value = expanding
-    }
-}
-
-@Module
-interface FakeNotificationsKeyguardStateRepositoryModule {
-    @Binds
-    fun bindFake(
-        fake: FakeNotificationsKeyguardViewStateRepository
-    ): NotificationsKeyguardViewStateRepository
-}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/repository/NotificationsKeyguardViewStateRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/repository/NotificationsKeyguardViewStateRepositoryKosmos.kt
index f2b9da4..df7fd94 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/repository/NotificationsKeyguardViewStateRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/repository/NotificationsKeyguardViewStateRepositoryKosmos.kt
@@ -18,7 +18,5 @@
 
 import com.android.systemui.kosmos.Kosmos
 
-var Kosmos.notificationsKeyguardViewStateRepository: NotificationsKeyguardViewStateRepository by
-    Kosmos.Fixture { fakeNotificationsKeyguardViewStateRepository }
-val Kosmos.fakeNotificationsKeyguardViewStateRepository by
-    Kosmos.Fixture { FakeNotificationsKeyguardViewStateRepository() }
+val Kosmos.notificationsKeyguardViewStateRepository: NotificationsKeyguardViewStateRepository by
+    Kosmos.Fixture { NotificationsKeyguardViewStateRepository() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorKosmos.kt
index 2d2f546..774782c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorKosmos.kt
@@ -21,6 +21,7 @@
 import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.statusbar.data.repository.notificationListenerSettingsRepository
 import com.android.systemui.statusbar.notification.data.repository.notificationsKeyguardViewStateRepository
 import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor
@@ -30,16 +31,20 @@
 
 val Kosmos.alwaysOnDisplayNotificationIconsInteractor by Fixture {
     AlwaysOnDisplayNotificationIconsInteractor(
+        bgContext = testDispatcher,
         deviceEntryInteractor = deviceEntryInteractor,
         iconsInteractor = notificationIconsInteractor,
     )
 }
+
 val Kosmos.statusBarNotificationIconsInteractor by Fixture {
     StatusBarNotificationIconsInteractor(
+        bgContext = testDispatcher,
         iconsInteractor = notificationIconsInteractor,
         settingsRepository = notificationListenerSettingsRepository,
     )
 }
+
 val Kosmos.notificationIconsInteractor by Fixture {
     NotificationIconsInteractor(
         activeNotificationsInteractor = activeNotificationsInteractor,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelKosmos.kt
index 6295b83..18c063f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelKosmos.kt
@@ -20,12 +20,14 @@
 import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
 import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
 import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.shade.domain.interactor.shadeInteractor
 import com.android.systemui.statusbar.notification.icon.domain.interactor.alwaysOnDisplayNotificationIconsInteractor
 
 val Kosmos.notificationIconContainerAlwaysOnDisplayViewModel by
     Kosmos.Fixture {
         NotificationIconContainerAlwaysOnDisplayViewModel(
+            bgContext = testDispatcher,
             iconsInteractor = alwaysOnDisplayNotificationIconsInteractor,
             keyguardInteractor = keyguardInteractor,
             keyguardTransitionInteractor = keyguardTransitionInteractor,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerShelfViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerShelfViewModelKosmos.kt
index d679bb6..4492af5 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerShelfViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerShelfViewModelKosmos.kt
@@ -17,11 +17,13 @@
 package com.android.systemui.statusbar.notification.icon.ui.viewmodel
 
 import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.statusbar.notification.icon.domain.interactor.notificationIconsInteractor
 
 val Kosmos.notificationIconContainerShelfViewModel by
     Kosmos.Fixture {
         NotificationIconContainerShelfViewModel(
+            bgContext = testDispatcher,
             interactor = notificationIconsInteractor,
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelKosmos.kt
index 04bb52d..3632a3d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelKosmos.kt
@@ -19,8 +19,8 @@
 import android.content.res.mainResources
 import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
 import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.shade.domain.interactor.shadeInteractor
-import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor
 import com.android.systemui.statusbar.notification.domain.interactor.headsUpNotificationIconInteractor
 import com.android.systemui.statusbar.notification.icon.domain.interactor.statusBarNotificationIconsInteractor
 import com.android.systemui.statusbar.phone.domain.interactor.darkIconInteractor
@@ -28,11 +28,11 @@
 val Kosmos.notificationIconContainerStatusBarViewModel by
     Kosmos.Fixture {
         NotificationIconContainerStatusBarViewModel(
+            bgContext = testDispatcher,
             darkIconInteractor = darkIconInteractor,
             iconsInteractor = statusBarNotificationIconsInteractor,
             headsUpIconInteractor = headsUpNotificationIconInteractor,
             keyguardInteractor = keyguardInteractor,
-            notificationsInteractor = activeNotificationsInteractor,
             resources = mainResources,
             shadeInteractor = shadeInteractor,
         )
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationsKeyguardInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationsKeyguardInteractorKosmos.kt
index 432464e..61a38b8 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationsKeyguardInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationsKeyguardInteractorKosmos.kt
@@ -18,13 +18,11 @@
 
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
-import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.statusbar.notification.data.repository.notificationsKeyguardViewStateRepository
 import com.android.systemui.statusbar.notification.domain.interactor.NotificationsKeyguardInteractor
 
 val Kosmos.notificationsKeyguardInteractor by Fixture {
     NotificationsKeyguardInteractor(
         repository = notificationsKeyguardViewStateRepository,
-        backgroundDispatcher = testDispatcher,
     )
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractorKosmos.kt
index 13d577b..8909d75 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractorKosmos.kt
@@ -21,6 +21,7 @@
 import com.android.systemui.deviceentry.domain.interactor.deviceEntryUdfpsInteractor
 import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
 import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.shade.largeScreenHeaderHelper
 import com.android.systemui.statusbar.policy.splitShadeStateController
 
 val Kosmos.sharedNotificationContainerInteractor by
@@ -31,5 +32,6 @@
             splitShadeStateController = splitShadeStateController,
             keyguardInteractor = keyguardInteractor,
             deviceEntryUdfpsInteractor = deviceEntryUdfpsInteractor,
+            largeScreenHeaderHelperLazy = { largeScreenHeaderHelper }
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeSecurityController.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeSecurityController.kt
index 021e7df..ac90a45 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeSecurityController.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeSecurityController.kt
@@ -77,6 +77,8 @@
 
     override fun isVpnBranded(): Boolean = fakeState.isVpnBranded
 
+    override fun isVpnValidated(): Boolean = fakeState.isVpnValidated
+
     override fun getPrimaryVpnName(): String? = fakeState.primaryVpnName
 
     override fun getWorkProfileVpnName(): String? = fakeState.workProfileVpnName
@@ -110,6 +112,7 @@
         var isVpnEnabled: Boolean = false,
         var isVpnRestricted: Boolean = false,
         var isVpnBranded: Boolean = false,
+        var isVpnValidated: Boolean = false,
         var primaryVpnName: String? = null,
         var workProfileVpnName: String? = null,
         var hasCACertInCurrentUser: Boolean = false,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/FakeSystemUIDialogController.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/util/FakeSystemUIDialogController.kt
index 0c9ce0f..697b508 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/util/FakeSystemUIDialogController.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/FakeSystemUIDialogController.kt
@@ -17,6 +17,7 @@
 
 package com.android.systemui.util
 
+import android.content.Context
 import android.content.DialogInterface
 import com.android.systemui.statusbar.phone.SystemUIDialog
 import com.android.systemui.util.mockito.any
@@ -27,13 +28,15 @@
 import org.mockito.Mockito.verify
 import org.mockito.stubbing.Stubber
 
-class FakeSystemUIDialogController {
+class FakeSystemUIDialogController(context: Context) {
 
     val dialog: SystemUIDialog = mock()
 
+
     private val clickListeners: MutableMap<Int, DialogInterface.OnClickListener> = mutableMapOf()
 
     init {
+        whenever(dialog.context).thenReturn(context)
         saveListener(DialogInterface.BUTTON_POSITIVE)
             .whenever(dialog)
             .setPositiveButton(any(), any())
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeSecurityController.java b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeSecurityController.java
index 76199e3..791165d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeSecurityController.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeSecurityController.java
@@ -109,6 +109,11 @@
     }
 
     @Override
+    public boolean isVpnValidated() {
+        return false;
+    }
+
+    @Override
     public String getPrimaryVpnName() {
         return null;
     }
diff --git a/packages/SystemUI/unfold/lint-baseline.xml b/packages/SystemUI/unfold/lint-baseline.xml
deleted file mode 100644
index 449ed2e..0000000
--- a/packages/SystemUI/unfold/lint-baseline.xml
+++ /dev/null
@@ -1,3 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 7.1.0-dev" type="baseline" client="" name="" variant="all" version="7.1.0-dev">
-</issues>
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedModule.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedModule.kt
index f7fb014..1b7e71a 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedModule.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedModule.kt
@@ -27,8 +27,6 @@
 import com.android.systemui.unfold.progress.UnfoldTransitionProgressForwarder
 import com.android.systemui.unfold.updates.DeviceFoldStateProvider
 import com.android.systemui.unfold.updates.FoldStateProvider
-import com.android.systemui.unfold.updates.FoldStateRepository
-import com.android.systemui.unfold.updates.FoldStateRepositoryImpl
 import com.android.systemui.unfold.updates.RotationChangeProvider
 import com.android.systemui.unfold.updates.hinge.EmptyHingeAngleProvider
 import com.android.systemui.unfold.updates.hinge.HingeAngleProvider
@@ -68,10 +66,6 @@
     fun unfoldKeyguardVisibilityManager(
         impl: UnfoldKeyguardVisibilityManagerImpl
     ): UnfoldKeyguardVisibilityManager = impl
-
-    @Provides
-    @Singleton
-    fun foldStateRepository(impl: FoldStateRepositoryImpl): FoldStateRepository = impl
 }
 
 @Module
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/MainThreadUnfoldTransitionProgressProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/MainThreadUnfoldTransitionProgressProvider.kt
index 9bdf3d5..fdce147 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/MainThreadUnfoldTransitionProgressProvider.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/MainThreadUnfoldTransitionProgressProvider.kt
@@ -24,12 +24,16 @@
 import dagger.assisted.Assisted
 import dagger.assisted.AssistedFactory
 import dagger.assisted.AssistedInject
+import java.util.Collections.synchronizedMap
 
 /**
  * [UnfoldTransitionProgressProvider] that forwards all progress to the main thread handler.
  *
  * This is needed when progress are calculated in the background, but some listeners need the
  * callbacks in the main thread.
+ *
+ * Note that this class assumes that the root provider has thread safe callback registration, as
+ * they might be called from any thread.
  */
 class MainThreadUnfoldTransitionProgressProvider
 @AssistedInject
@@ -38,27 +42,20 @@
     @Assisted private val rootProvider: UnfoldTransitionProgressProvider
 ) : UnfoldTransitionProgressProvider {
 
-    private val listenerMap = mutableMapOf<TransitionProgressListener, TransitionProgressListener>()
+    private val listenerMap: MutableMap<TransitionProgressListener, TransitionProgressListener> =
+        synchronizedMap(mutableMapOf())
 
     override fun addCallback(listener: TransitionProgressListener) {
-        assertMainThread()
         val proxy = TransitionProgressListerProxy(listener)
         rootProvider.addCallback(proxy)
         listenerMap[listener] = proxy
     }
 
     override fun removeCallback(listener: TransitionProgressListener) {
-        assertMainThread()
         val proxy = listenerMap.remove(listener) ?: return
         rootProvider.removeCallback(proxy)
     }
 
-    private fun assertMainThread() {
-        check(mainHandler.looper.isCurrentThread) {
-            "Should be called from the main thread, but this is ${Thread.currentThread()}"
-        }
-    }
-
     override fun destroy() {
         rootProvider.destroy()
     }
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/UnfoldRemoteFilter.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/UnfoldRemoteFilter.kt
index 843cc3b..54d8054 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/UnfoldRemoteFilter.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/UnfoldRemoteFilter.kt
@@ -41,8 +41,6 @@
             if (inProgress) {
                 logCounter({ "$TAG#filtered_progress" }, newProgress)
                 listener.onTransitionProgress(newProgress)
-            } else {
-                Log.e(TAG, "Filtered progress received received while animation not in progress.")
             }
             field = newProgress
         }
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProvider.kt
index f9751d9..2bca272 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProvider.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProvider.kt
@@ -15,8 +15,11 @@
  */
 package com.android.systemui.unfold.util
 
+import android.os.Handler
+import android.os.Looper
 import com.android.systemui.unfold.UnfoldTransitionProgressProvider
 import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
+import java.util.concurrent.CopyOnWriteArrayList
 
 /**
  * Manages progress listeners that can have smaller lifespan than the unfold animation.
@@ -33,12 +36,13 @@
 constructor(source: UnfoldTransitionProgressProvider? = null) :
     UnfoldTransitionProgressProvider, TransitionProgressListener {
 
+    private var progressHandler: Handler? = null
     private var source: UnfoldTransitionProgressProvider? = null
 
-    private val listeners: MutableList<TransitionProgressListener> = mutableListOf()
+    private val listeners = CopyOnWriteArrayList<TransitionProgressListener>()
 
-    private var isReadyToHandleTransition = false
-    private var isTransitionRunning = false
+    @Volatile private var isReadyToHandleTransition = false
+    @Volatile private var isTransitionRunning = false
     private var lastTransitionProgress = PROGRESS_UNSET
 
     init {
@@ -70,15 +74,18 @@
      * Call it with readyToHandleTransition = false when listeners can't process the events.
      */
     fun setReadyToHandleTransition(isReadyToHandleTransition: Boolean) {
-        if (isTransitionRunning) {
-            if (isReadyToHandleTransition) {
-                listeners.forEach { it.onTransitionStarted() }
-                if (lastTransitionProgress != PROGRESS_UNSET) {
-                    listeners.forEach { it.onTransitionProgress(lastTransitionProgress) }
+        val progressHandler = this.progressHandler
+        if (isTransitionRunning && progressHandler != null) {
+            progressHandler.post {
+                if (isReadyToHandleTransition) {
+                    listeners.forEach { it.onTransitionStarted() }
+                    if (lastTransitionProgress != PROGRESS_UNSET) {
+                        listeners.forEach { it.onTransitionProgress(lastTransitionProgress) }
+                    }
+                } else {
+                    isTransitionRunning = false
+                    listeners.forEach { it.onTransitionFinished() }
                 }
-            } else {
-                isTransitionRunning = false
-                listeners.forEach { it.onTransitionFinished() }
             }
         }
         this.isReadyToHandleTransition = isReadyToHandleTransition
@@ -98,6 +105,7 @@
     }
 
     override fun onTransitionStarted() {
+        assertInProgressThread()
         isTransitionRunning = true
         if (isReadyToHandleTransition) {
             listeners.forEach { it.onTransitionStarted() }
@@ -105,6 +113,7 @@
     }
 
     override fun onTransitionProgress(progress: Float) {
+        assertInProgressThread()
         if (isReadyToHandleTransition) {
             listeners.forEach { it.onTransitionProgress(progress) }
         }
@@ -112,12 +121,14 @@
     }
 
     override fun onTransitionFinishing() {
+        assertInProgressThread()
         if (isReadyToHandleTransition) {
             listeners.forEach { it.onTransitionFinishing() }
         }
     }
 
     override fun onTransitionFinished() {
+        assertInProgressThread()
         if (isReadyToHandleTransition) {
             listeners.forEach { it.onTransitionFinished() }
         }
@@ -125,6 +136,21 @@
         lastTransitionProgress = PROGRESS_UNSET
     }
 
+    private fun assertInProgressThread() {
+        val cachedProgressHandler = progressHandler
+        if (cachedProgressHandler == null) {
+            val thisLooper = Looper.myLooper() ?: error("This thread is expected to have a looper.")
+            progressHandler = Handler(thisLooper)
+        } else {
+            check(cachedProgressHandler.looper.isCurrentThread) {
+                """Receiving unfold transition callback from different threads.
+                    |Current: ${Thread.currentThread()}
+                    |expected: ${cachedProgressHandler.looper.thread}"""
+                    .trimMargin()
+            }
+        }
+    }
+
     companion object {
         private const val PROGRESS_UNSET = -1f
     }
diff --git a/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java b/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java
index 1fc74f7..67a7d12 100644
--- a/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java
+++ b/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java
@@ -16,6 +16,8 @@
 
 package com.android.vpndialogs;
 
+import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
+
 import android.content.DialogInterface;
 import android.net.VpnManager;
 import android.os.Bundle;
@@ -87,6 +89,7 @@
             mAlertParams.mNegativeButtonListener = this;
             mAlertParams.mView = view;
             setupAlert();
+            getWindow().addPrivateFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
 
             if (mHandler == null) {
                 mHandler = new Handler(this);
diff --git a/packages/overlays/Android.bp b/packages/overlays/Android.bp
new file mode 100644
index 0000000..5e001fb
--- /dev/null
+++ b/packages/overlays/Android.bp
@@ -0,0 +1,44 @@
+// 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 {
+    // See: http://go/android-license-faq
+    default_applicable_licenses: [
+        "frameworks_base_license",
+    ],
+}
+
+phony {
+    name: "frameworks-base-overlays",
+    required: [
+        "DisplayCutoutEmulationCornerOverlay",
+        "DisplayCutoutEmulationDoubleOverlay",
+        "DisplayCutoutEmulationHoleOverlay",
+        "DisplayCutoutEmulationTallOverlay",
+        "DisplayCutoutEmulationWaterfallOverlay",
+        "FontNotoSerifSourceOverlay",
+        "NavigationBarMode3ButtonOverlay",
+        "NavigationBarModeGesturalOverlay",
+        "NavigationBarModeGesturalOverlayNarrowBack",
+        "NavigationBarModeGesturalOverlayWideBack",
+        "NavigationBarModeGesturalOverlayExtraWideBack",
+        "TransparentNavigationBarOverlay",
+        "NotesRoleEnabledOverlay",
+        "preinstalled-packages-platform-overlays.xml",
+    ],
+}
+
+phony {
+    name: "frameworks-base-overlays-debug",
+}
diff --git a/packages/overlays/Android.mk b/packages/overlays/Android.mk
deleted file mode 100644
index a41d0e5..0000000
--- a/packages/overlays/Android.mk
+++ /dev/null
@@ -1,47 +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.
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := frameworks-base-overlays
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../NOTICE
-LOCAL_REQUIRED_MODULES := \
-	DisplayCutoutEmulationCornerOverlay \
-	DisplayCutoutEmulationDoubleOverlay \
-    DisplayCutoutEmulationHoleOverlay \
-	DisplayCutoutEmulationTallOverlay \
-	DisplayCutoutEmulationWaterfallOverlay \
-	FontNotoSerifSourceOverlay \
-	NavigationBarMode3ButtonOverlay \
-	NavigationBarModeGesturalOverlay \
-	NavigationBarModeGesturalOverlayNarrowBack \
-	NavigationBarModeGesturalOverlayWideBack \
-	NavigationBarModeGesturalOverlayExtraWideBack \
-	TransparentNavigationBarOverlay \
-	NotesRoleEnabledOverlay \
-	preinstalled-packages-platform-overlays.xml
-
-include $(BUILD_PHONY_PACKAGE)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := frameworks-base-overlays-debug
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../NOTICE
-
-include $(BUILD_PHONY_PACKAGE)
-include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/packages/overlays/NoCutoutOverlay/res/values/config.xml b/packages/overlays/NoCutoutOverlay/res/values/config.xml
index ed0340b..b44a153a 100644
--- a/packages/overlays/NoCutoutOverlay/res/values/config.xml
+++ b/packages/overlays/NoCutoutOverlay/res/values/config.xml
@@ -20,10 +20,17 @@
          black in software (to avoid aliasing or emulate a cutout that is not physically existent).
      -->
     <bool name="config_fillMainBuiltInDisplayCutout">false</bool>
+    <!-- Whether the display cutout region of the secondary built-in display should be forced to
+         black in software (to avoid aliasing or emulate a cutout that is not physically existent).
+     -->
+    <bool name="config_fillSecondaryBuiltInDisplayCutout">false</bool>
 
     <!-- If true, and there is a cutout on the main built in display, the cutout will be masked
          by shrinking the display such that it does not overlap the cutout area. -->
     <bool name="config_maskMainBuiltInDisplayCutout">true</bool>
+    <!-- If true, and there is a cutout on the secondary built in display, the cutout will be masked
+         by shrinking the display such that it does not overlap the cutout area. -->
+    <bool name="config_maskSecondaryBuiltInDisplayCutout">true</bool>
 
     <!-- Height of the status bar -->
     <dimen name="status_bar_height_portrait">28dp</dimen>
diff --git a/proto/src/criticalevents/critical_event_log.proto b/proto/src/criticalevents/critical_event_log.proto
index 9cda267..cffcd09 100644
--- a/proto/src/criticalevents/critical_event_log.proto
+++ b/proto/src/criticalevents/critical_event_log.proto
@@ -60,8 +60,11 @@
     JavaCrash java_crash = 5;
     NativeCrash native_crash = 6;
     SystemServerStarted system_server_started = 7;
+    InstallPackages install_packages = 8;
   }
 
+  message InstallPackages {}
+
   message SystemServerStarted {}
 
   message Watchdog {
diff --git a/proto/src/system_messages.proto b/proto/src/system_messages.proto
index b403a7f..7f542d1 100644
--- a/proto/src/system_messages.proto
+++ b/proto/src/system_messages.proto
@@ -408,5 +408,9 @@
     // Notify the user about external display events related to screenshot.
     // Package: com.android.systemui
     NOTE_GLOBAL_SCREENSHOT_EXTERNAL_DISPLAY = 1008;
+
+    // Notify the user that accessibility floating menu is hidden.
+    // Package: com.android.systemui
+    NOTE_A11Y_FLOATING_MENU_HIDDEN = 1009;
   }
 }
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
index 513c095..d967874 100644
--- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
+++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
@@ -142,6 +142,11 @@
                     Assume.assumeFalse(IS_UNDER_RAVENWOOD);
                 }
 
+                // Stopgap for http://g/ravenwood/EPAD-N5ntxM
+                if (description.getMethodName().endsWith("$noRavenwood")) {
+                    Assume.assumeFalse(IS_UNDER_RAVENWOOD);
+                }
+
                 RavenwoodRuleImpl.init(RavenwoodRule.this);
                 try {
                     base.evaluate();
diff --git a/ravenwood/minimum-test/test/com/android/ravenwood/RavenwoodMinimumTest.java b/ravenwood/minimum-test/test/com/android/ravenwood/RavenwoodMinimumTest.java
index 085c186..7abfecf 100644
--- a/ravenwood/minimum-test/test/com/android/ravenwood/RavenwoodMinimumTest.java
+++ b/ravenwood/minimum-test/test/com/android/ravenwood/RavenwoodMinimumTest.java
@@ -42,4 +42,9 @@
     public void testIgnored() {
         throw new RuntimeException("Shouldn't be executed under ravenwood");
     }
+
+    @Test
+    public void testIgnored$noRavenwood() {
+        throw new RuntimeException("Shouldn't be executed under ravenwood");
+    }
 }
diff --git a/ravenwood/ravenwood-annotation-allowed-classes.txt b/ravenwood/ravenwood-annotation-allowed-classes.txt
index 7744fca..ab2546b 100644
--- a/ravenwood/ravenwood-annotation-allowed-classes.txt
+++ b/ravenwood/ravenwood-annotation-allowed-classes.txt
@@ -9,8 +9,10 @@
 com.android.internal.os.LongArrayMultiStateCounter
 com.android.internal.os.LongArrayMultiStateCounter$LongArrayContainer
 com.android.internal.os.MonotonicClock
+com.android.internal.os.PowerProfile
 com.android.internal.os.PowerStats
 com.android.internal.os.PowerStats$Descriptor
+com.android.internal.power.ModemPowerProfile
 
 android.util.AtomicFile
 android.util.DataUnit
@@ -32,11 +34,16 @@
 android.util.TimeUtils
 android.util.Xml
 
+android.os.AggregateBatteryConsumer
 android.os.BatteryConsumer
+android.os.BatteryStats
 android.os.BatteryStats$HistoryItem
 android.os.BatteryStats$HistoryStepDetails
 android.os.BatteryStats$HistoryTag
+android.os.BatteryStats$LongCounter
 android.os.BatteryStats$ProcessStateChange
+android.os.BatteryUsageStats
+android.os.BatteryUsageStatsQuery
 android.os.Binder
 android.os.Binder$IdentitySupplier
 android.os.Broadcaster
@@ -54,10 +61,14 @@
 android.os.PackageTagsList
 android.os.Parcel
 android.os.Parcelable
+android.os.PowerComponents
 android.os.Process
+android.os.ServiceSpecificException
 android.os.SystemClock
 android.os.ThreadLocalWorkSource
 android.os.TimestampedValue
+android.os.UidBatteryConsumer
+android.os.UidBatteryConsumer$Builder
 android.os.UserHandle
 android.os.WorkSource
 
@@ -116,6 +127,7 @@
 android.content.ContentProvider
 
 com.android.server.LocalServices
+com.android.server.power.stats.BatteryStatsImpl
 
 com.android.internal.util.BitUtils
 com.android.internal.util.BitwiseInputStream
diff --git a/services/Android.bp b/services/Android.bp
index 5cb8ec6..7e8333c 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -220,6 +220,9 @@
     required: [
         "libukey2_jni_shared",
     ],
+    lint: {
+        baseline_filename: "lint-baseline.xml",
+    },
 
     // Uncomment to enable output of certain warnings (deprecated, unchecked)
     //javacflags: ["-Xlint"],
diff --git a/services/accessibility/Android.bp b/services/accessibility/Android.bp
index e2488a5..69cc68a 100644
--- a/services/accessibility/Android.bp
+++ b/services/accessibility/Android.bp
@@ -21,6 +21,8 @@
     ],
     lint: {
         error_checks: ["MissingPermissionAnnotation"],
+        baseline_filename: "lint-baseline.xml",
+
     },
     srcs: [
         ":services.accessibility-sources",
diff --git a/services/accessibility/accessibility.aconfig b/services/accessibility/accessibility.aconfig
index a19920f..993b254 100644
--- a/services/accessibility/accessibility.aconfig
+++ b/services/accessibility/accessibility.aconfig
@@ -59,13 +59,6 @@
 }
 
 flag {
-    name: "reduce_touch_exploration_sensitivity"
-    namespace: "accessibility"
-    description: "Reduces touch exploration sensitivity by only sending a hover event when the ifnger has moved the amount of pixels defined by the system's touch slop."
-    bug: "303677860"
-}
-
-flag {
     name: "scan_packages_without_lock"
     namespace: "accessibility"
     description: "Scans packages for accessibility service/activity info without holding the A11yMS lock"
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
index fc8d4f8..c418485 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
@@ -882,22 +882,10 @@
         final int pointerIndex = event.findPointerIndex(pointerId);
         switch (event.getPointerCount()) {
             case 1:
-                // Touch exploration.
+            // Touch exploration.
                 sendTouchExplorationGestureStartAndHoverEnterIfNeeded(policyFlags);
-                if (Flags.reduceTouchExplorationSensitivity()
-                        && mState.getLastInjectedHoverEvent() != null) {
-                    final MotionEvent lastEvent = mState.getLastInjectedHoverEvent();
-                    final float deltaX = lastEvent.getX() - rawEvent.getX();
-                    final float deltaY = lastEvent.getY() - rawEvent.getY();
-                    final double moveDelta = Math.hypot(deltaX, deltaY);
-                    if (moveDelta > mTouchSlop) {
-                        mDispatcher.sendMotionEvent(
-                                event, ACTION_HOVER_MOVE, rawEvent, pointerIdBits, policyFlags);
-                    }
-                } else {
-                    mDispatcher.sendMotionEvent(
-                            event, ACTION_HOVER_MOVE, rawEvent, pointerIdBits, policyFlags);
-                }
+                mDispatcher.sendMotionEvent(
+                        event, ACTION_HOVER_MOVE, rawEvent, pointerIdBits, policyFlags);
                 break;
             case 2:
                 if (mGestureDetector.isMultiFingerGesturesEnabled()
diff --git a/services/accessibility/lint-baseline.xml b/services/accessibility/lint-baseline.xml
index 6bec8cf..b808219 100644
--- a/services/accessibility/lint-baseline.xml
+++ b/services/accessibility/lint-baseline.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.1.0-dev" type="baseline" client="" dependencies="true" name="" variant="all" version="8.1.0-dev">
+<issues format="6" by="lint 8.4.0-alpha01" type="baseline" client="" dependencies="true" name="" variant="all" version="8.4.0-alpha01">
 
     <issue
         id="SimpleManualPermissionEnforcement"
@@ -23,4 +23,4 @@
             column="9"/>
     </issue>
 
-</issues>
+</issues>
\ No newline at end of file
diff --git a/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java b/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java
index 6b0fdb5..cf414d1 100644
--- a/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java
+++ b/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java
@@ -240,10 +240,20 @@
         mEventInternal = Optional.of(new PresentationStatsEventInternal());
     }
 
+    /**
+     * Set request_id
+     */
     public void maybeSetRequestId(int requestId) {
         mEventInternal.ifPresent(event -> event.mRequestId = requestId);
     }
 
+    /**
+     * Set is_credential_request
+     */
+    public void maybeSetIsCredentialRequest(boolean isCredentialRequest) {
+        mEventInternal.ifPresent(event -> event.mIsCredentialRequest = isCredentialRequest);
+    }
+
     public void maybeSetNoPresentationEventReason(@NotShownReason int reason) {
         mEventInternal.ifPresent(event -> {
             if (event.mCountShown == 0) {
@@ -567,7 +577,8 @@
                     + " mSelectedDatasetPickedReason=" + event.mSelectedDatasetPickedReason
                     + " mDetectionPreference=" + event.mDetectionPreference
                     + " mFieldClassificationRequestId=" + event.mFieldClassificationRequestId
-                    + " mAppPackageUid=" + mCallingAppUid);
+                    + " mAppPackageUid=" + mCallingAppUid
+                    + " mIsCredentialRequest=" + event.mIsCredentialRequest);
         }
 
         // TODO(b/234185326): Distinguish empty responses from other no presentation reasons.
@@ -606,7 +617,8 @@
                 event.mSelectedDatasetPickedReason,
                 event.mDetectionPreference,
                 event.mFieldClassificationRequestId,
-                mCallingAppUid);
+                mCallingAppUid,
+                event.mIsCredentialRequest);
         mEventInternal = Optional.empty();
     }
 
@@ -640,6 +652,7 @@
         @DatasetPickedReason int mSelectedDatasetPickedReason = PICK_REASON_UNKNOWN;
         @DetectionPreference int mDetectionPreference = DETECTION_PREFER_UNKNOWN;
         int mFieldClassificationRequestId = -1;
+        boolean mIsCredentialRequest = false;
 
         PresentationStatsEventInternal() {}
     }
diff --git a/services/autofill/java/com/android/server/autofill/RemoteFillService.java b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
index 4688658..6d0915b 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteFillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
@@ -38,6 +38,7 @@
 import android.service.autofill.SaveRequest;
 import android.text.format.DateUtils;
 import android.util.Slog;
+import android.view.autofill.IAutoFillManagerClient;
 
 import com.android.internal.infra.AbstractRemoteService;
 import com.android.internal.infra.ServiceConnector;
@@ -56,12 +57,22 @@
     private static final long TIMEOUT_IDLE_BIND_MILLIS = 5 * DateUtils.SECOND_IN_MILLIS;
     private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 5 * DateUtils.SECOND_IN_MILLIS;
 
+    private static final ComponentName CREDMAN_SERVICE_COMPONENT_NAME =
+            new ComponentName("com.android.credentialmanager",
+                    "com.android.credentialmanager.autofill.CredentialAutofillService");
+
     private final FillServiceCallbacks mCallbacks;
     private final Object mLock = new Object();
     private CompletableFuture<FillResponse> mPendingFillRequest;
     private int mPendingFillRequestId = INVALID_REQUEST_ID;
     private final ComponentName mComponentName;
 
+    private final boolean mIsCredentialAutofillService;
+
+    public boolean isCredentialAutofillService() {
+        return mIsCredentialAutofillService;
+    }
+
     public interface FillServiceCallbacks
             extends AbstractRemoteService.VultureCallback<RemoteFillService> {
         void onFillRequestSuccess(int requestId, @Nullable FillResponse response,
@@ -83,6 +94,7 @@
                 userId, IAutoFillService.Stub::asInterface);
         mCallbacks = callbacks;
         mComponentName = componentName;
+        mIsCredentialAutofillService = mComponentName.equals(CREDMAN_SERVICE_COMPONENT_NAME);
     }
 
     @Override // from ServiceConnector.Impl
@@ -117,6 +129,10 @@
         super.addLast(iAutoFillServiceJob);
     }
 
+    public ComponentName getComponentName() {
+        return mComponentName;
+    }
+
     /**
      * Cancel the currently pending request.
      *
@@ -134,6 +150,78 @@
         }
     }
 
+    public void onFillCredentialRequest(@NonNull FillRequest request,
+            IAutoFillManagerClient autofillCallback) {
+        if (sVerbose) {
+            Slog.v(TAG, "onFillRequest:" + request);
+        }
+        AtomicReference<ICancellationSignal> cancellationSink = new AtomicReference<>();
+        AtomicReference<CompletableFuture<FillResponse>> futureRef = new AtomicReference<>();
+
+        CompletableFuture<FillResponse> connectThenFillRequest = postAsync(remoteService -> {
+            if (sVerbose) {
+                Slog.v(TAG, "calling onFillRequest() for id=" + request.getId());
+            }
+
+            CompletableFuture<FillResponse> fillRequest = new CompletableFuture<>();
+            remoteService.onFillCredentialRequest(request, new IFillCallback.Stub() {
+                @Override
+                public void onCancellable(ICancellationSignal cancellation) {
+                    CompletableFuture<FillResponse> future = futureRef.get();
+                    if (future != null && future.isCancelled()) {
+                        dispatchCancellationSignal(cancellation);
+                    } else {
+                        cancellationSink.set(cancellation);
+                    }
+                }
+
+                @Override
+                public void onSuccess(FillResponse response) {
+                    fillRequest.complete(response);
+                }
+
+                @Override
+                public void onFailure(int requestId, CharSequence message) {
+                    String errorMessage = message == null ? "" : String.valueOf(message);
+                    fillRequest.completeExceptionally(
+                            new RuntimeException(errorMessage));
+                }
+            }, autofillCallback);
+            return fillRequest;
+        }).orTimeout(TIMEOUT_REMOTE_REQUEST_MILLIS, TimeUnit.MILLISECONDS);
+        futureRef.set(connectThenFillRequest);
+
+        synchronized (mLock) {
+            mPendingFillRequest = connectThenFillRequest;
+            mPendingFillRequestId = request.getId();
+        }
+
+        connectThenFillRequest.whenComplete((res, err) -> Handler.getMain().post(() -> {
+            synchronized (mLock) {
+                mPendingFillRequest = null;
+                mPendingFillRequestId = INVALID_REQUEST_ID;
+            }
+            if (mCallbacks == null) {
+                Slog.w(TAG, "Error calling RemoteFillService - service already unbound");
+                return;
+            }
+            if (err == null) {
+                mCallbacks.onFillRequestSuccess(request.getId(), res,
+                        mComponentName.getPackageName(), request.getFlags());
+            } else {
+                Slog.e(TAG, "Error calling on fill request", err);
+                if (err instanceof TimeoutException) {
+                    dispatchCancellationSignal(cancellationSink.get());
+                    mCallbacks.onFillRequestTimeout(request.getId());
+                } else if (err instanceof CancellationException) {
+                    dispatchCancellationSignal(cancellationSink.get());
+                } else {
+                    mCallbacks.onFillRequestFailure(request.getId(), err.getMessage());
+                }
+            }
+        }));
+    }
+
     public void onFillRequest(@NonNull FillRequest request) {
         if (sVerbose) {
             Slog.v(TAG, "onFillRequest:" + request);
diff --git a/services/autofill/java/com/android/server/autofill/SecondaryProviderHandler.java b/services/autofill/java/com/android/server/autofill/SecondaryProviderHandler.java
index 4a6d5c9b..7fc1738 100644
--- a/services/autofill/java/com/android/server/autofill/SecondaryProviderHandler.java
+++ b/services/autofill/java/com/android/server/autofill/SecondaryProviderHandler.java
@@ -16,14 +16,20 @@
 
 package com.android.server.autofill;
 
+import static com.android.server.autofill.Session.REQUEST_ID_KEY;
+import static com.android.server.autofill.Session.SESSION_ID_KEY;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.IntentSender;
+import android.os.Bundle;
 import android.service.autofill.FillRequest;
 import android.service.autofill.FillResponse;
 import android.util.Slog;
+import android.view.autofill.IAutoFillManagerClient;
+import android.view.inputmethod.InlineSuggestionsRequest;
 
 /**
  * Requests autofill response from a Remote Autofill Service. This autofill service can be
@@ -95,10 +101,35 @@
     /**
      * Requests a new fill response.
      */
-    public void onFillRequest(FillRequest pendingFillRequest, int flag) {
+    public void onFillRequest(FillRequest pendingFillRequest,
+            InlineSuggestionsRequest pendingInlineSuggestionsRequest, int flag, int id,
+            IAutoFillManagerClient client) {
         Slog.v(TAG, "Requesting fill response to secondary provider.");
         mLastFlag = flag;
-        mRemoteFillService.onFillRequest(pendingFillRequest);
+        if (mRemoteFillService != null && mRemoteFillService.isCredentialAutofillService()) {
+            Slog.v(TAG, "About to call CredAutofill service as secondary provider");
+            addSessionIdAndRequestIdToClientState(pendingFillRequest,
+                    pendingInlineSuggestionsRequest, id);
+            mRemoteFillService.onFillCredentialRequest(pendingFillRequest, client);
+        } else {
+            mRemoteFillService.onFillRequest(pendingFillRequest);
+        }
+    }
+
+    private FillRequest addSessionIdAndRequestIdToClientState(FillRequest pendingFillRequest,
+            InlineSuggestionsRequest pendingInlineSuggestionsRequest, int sessionId) {
+        if (pendingFillRequest.getClientState() == null) {
+            pendingFillRequest = new FillRequest(pendingFillRequest.getId(),
+                    pendingFillRequest.getFillContexts(),
+                    pendingFillRequest.getHints(),
+                    new Bundle(),
+                    pendingFillRequest.getFlags(),
+                    pendingInlineSuggestionsRequest,
+                    pendingFillRequest.getDelayedFillIntentSender());
+        }
+        pendingFillRequest.getClientState().putInt(SESSION_ID_KEY, sessionId);
+        pendingFillRequest.getClientState().putInt(REQUEST_ID_KEY, pendingFillRequest.getId());
+        return pendingFillRequest;
     }
 
     public void destroy() {
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index c4e8f12..6a81425 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -141,6 +141,7 @@
 import android.service.autofill.FillEventHistory.Event.NoSaveReason;
 import android.service.autofill.FillRequest;
 import android.service.autofill.FillResponse;
+import android.service.autofill.Flags;
 import android.service.autofill.InlinePresentation;
 import android.service.autofill.InternalSanitizer;
 import android.service.autofill.InternalValidator;
@@ -233,6 +234,9 @@
             new ComponentName("com.android.credentialmanager",
                     "com.android.credentialmanager.autofill.CredentialAutofillService");
 
+    static final String SESSION_ID_KEY = "autofill_session_id";
+    static final String REQUEST_ID_KEY = "autofill_request_id";
+
     final Object mLock;
 
     private final AutofillManagerServiceImpl mService;
@@ -382,7 +386,7 @@
      */
     private boolean mHasCallback;
 
-    /** Whether the session has credential provider as the primary provider. */
+    /** Whether the session has credential manager provider as the primary provider. */
     private boolean mIsPrimaryCredential;
 
     @GuardedBy("mLock")
@@ -579,6 +583,8 @@
     @GuardedBy("mLock")
     private AutofillId[] mLastFillDialogTriggerIds;
 
+    private boolean mIgnoreViewStateResetToEmpty;
+
     void onSwitchInputMethodLocked() {
         // One caveat is that for the case where the focus is on a field for which regular autofill
         // returns null, and augmented autofill is triggered,  and then the user switches the input
@@ -720,9 +726,16 @@
                     && mSecondaryProviderHandler != null) {
                 Slog.v(TAG, "Requesting fill response to secondary provider.");
                 mSecondaryProviderHandler.onFillRequest(mPendingFillRequest,
-                        mPendingFillRequest.getFlags());
+                        mPendingInlineSuggestionsRequest,
+                        mPendingFillRequest.getFlags(), id, mClient);
             } else if (mRemoteFillService != null) {
-                mRemoteFillService.onFillRequest(mPendingFillRequest);
+                if (mIsPrimaryCredential) {
+                    mPendingFillRequest = addSessionIdAndRequestIdToClientState(mPendingFillRequest,
+                            mPendingInlineSuggestionsRequest, id);
+                    mRemoteFillService.onFillCredentialRequest(mPendingFillRequest, mClient);
+                } else {
+                    mRemoteFillService.onFillRequest(mPendingFillRequest);
+                }
             }
             mPendingInlineSuggestionsRequest = null;
             mWaitForInlineRequest = false;
@@ -865,6 +878,22 @@
         }
     }
 
+    private FillRequest addSessionIdAndRequestIdToClientState(FillRequest pendingFillRequest,
+            InlineSuggestionsRequest pendingInlineSuggestionsRequest, int sessionId) {
+        if (pendingFillRequest.getClientState() == null) {
+            pendingFillRequest = new FillRequest(pendingFillRequest.getId(),
+                    pendingFillRequest.getFillContexts(),
+                    pendingFillRequest.getHints(),
+                    new Bundle(),
+                    pendingFillRequest.getFlags(),
+                    pendingInlineSuggestionsRequest,
+                    pendingFillRequest.getDelayedFillIntentSender());
+        }
+        pendingFillRequest.getClientState().putInt(SESSION_ID_KEY, sessionId);
+        pendingFillRequest.getClientState().putInt(REQUEST_ID_KEY, pendingFillRequest.getId());
+        return pendingFillRequest;
+    }
+
     /**
      * Get the list of valid autofill hint types from Device flags
      * Returns empty list if PCC is off or no types available
@@ -1263,7 +1292,9 @@
             Slog.v(TAG, "Requesting structure for request #" + ordinal + " ,requestId=" + requestId
                     + ", flags=" + flags);
         }
+        boolean isCredmanRequested = (flags & FLAG_VIEW_REQUESTS_CREDMAN_SERVICE) != 0;
         mPresentationStatsEventLogger.maybeSetRequestId(requestId);
+        mPresentationStatsEventLogger.maybeSetIsCredentialRequest(isCredmanRequested);
         mPresentationStatsEventLogger.maybeSetFieldClassificationRequestId(
                 mFieldClassificationIdSnapshot);
         mFillRequestEventLogger.maybeSetRequestId(requestId);
@@ -1441,6 +1472,7 @@
         mSessionCommittedEventLogger.maybeSetComponentPackageUid(uid);
         mSaveEventLogger = SaveEventLogger.forSessionId(sessionId);
         mIsPrimaryCredential = isPrimaryCredential;
+        mIgnoreViewStateResetToEmpty = Flags.ignoreViewStateResetToEmpty();
 
         synchronized (mLock) {
             mSessionFlags = new SessionFlags();
@@ -4332,8 +4364,10 @@
                 }
 
                 if (viewState.getResponse() != null) {
+                    boolean isCredmanRequested = (flags & FLAG_VIEW_REQUESTS_CREDMAN_SERVICE) != 0;
                     FillResponse response = viewState.getResponse();
                     mPresentationStatsEventLogger.maybeSetRequestId(response.getRequestId());
+                    mPresentationStatsEventLogger.maybeSetIsCredentialRequest(isCredmanRequested);
                     mPresentationStatsEventLogger.maybeSetFieldClassificationRequestId(
                             mFieldClassificationIdSnapshot);
                     mPresentationStatsEventLogger.maybeSetAvailableCount(
@@ -4419,6 +4453,15 @@
     @GuardedBy("mLock")
     private void updateViewStateAndUiOnValueChangedLocked(AutofillId id, AutofillValue value,
             ViewState viewState, int flags) {
+        if (mIgnoreViewStateResetToEmpty && (value == null || value.isEmpty())
+                && viewState.getCurrentValue() != null && viewState.getCurrentValue().isText()
+                && viewState.getCurrentValue().getTextValue() != null
+                && viewState.getCurrentValue().getTextValue().length() > 1) {
+            if (sVerbose) {
+                Slog.v(TAG, "Ignoring view state reset to empty on id " + id);
+            }
+            return;
+        }
         final String textValue;
         if (value == null || !value.isText()) {
             textValue = null;
diff --git a/services/backup/Android.bp b/services/backup/Android.bp
index acb5911..d08a97e 100644
--- a/services/backup/Android.bp
+++ b/services/backup/Android.bp
@@ -19,7 +19,13 @@
     defaults: ["platform_service_defaults"],
     srcs: [":services.backup-sources"],
     libs: ["services.core"],
-    static_libs: ["app-compat-annotations", "backup_flags_lib"],
+    static_libs: [
+        "app-compat-annotations",
+        "backup_flags_lib",
+    ],
+    lint: {
+        baseline_filename: "lint-baseline.xml",
+    },
 }
 
 aconfig_declarations {
diff --git a/services/backup/flags.aconfig b/services/backup/flags.aconfig
index d695d36..549fa36 100644
--- a/services/backup/flags.aconfig
+++ b/services/backup/flags.aconfig
@@ -7,4 +7,12 @@
             "restore for apps that have been launched."
     bug: "308401499"
     is_fixed_read_only: true
+}
+
+flag {
+    name: "enable_max_size_writes_to_pipes"
+    namespace: "onboarding"
+    description: "Enables the write buffer to pipes to be of maximum size."
+    bug: "265976737"
+    is_fixed_read_only: true
 }
\ No newline at end of file
diff --git a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
index 6aed9aa..cca166b 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
@@ -40,8 +40,8 @@
 
 import com.android.server.EventLogTags;
 import com.android.server.backup.BackupAgentTimeoutParameters;
-import com.android.server.backup.BackupAndRestoreFeatureFlags;
 import com.android.server.backup.BackupRestoreTask;
+import com.android.server.backup.Flags;
 import com.android.server.backup.FullBackupJob;
 import com.android.server.backup.OperationStorage;
 import com.android.server.backup.OperationStorage.OpState;
@@ -390,8 +390,11 @@
 
             // Set up to send data to the transport
             final int N = mPackages.size();
-            final int chunkSizeInBytes =
-                    BackupAndRestoreFeatureFlags.getFullBackupWriteToTransportBufferSizeBytes();
+            int chunkSizeInBytes = 8 * 1024; // 8KB
+            if (Flags.enableMaxSizeWritesToPipes()) {
+                // Linux pipe capacity (buffer size) is 16 pages where each page is 4KB
+                chunkSizeInBytes = 64 * 1024; // 64KB
+            }
             final byte[] buffer = new byte[chunkSizeInBytes];
             for (int i = 0; i < N; i++) {
                 mBackupRunner = null;
diff --git a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
index ff72476..2c9eb51 100644
--- a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
+++ b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
@@ -29,7 +29,6 @@
 import android.app.IBackupAgent;
 import android.app.backup.BackupAgent;
 import android.app.backup.BackupAnnotations;
-import android.app.backup.BackupManager;
 import android.app.backup.FullBackup;
 import android.app.backup.IBackupManagerMonitor;
 import android.app.backup.IFullBackupRestoreObserver;
@@ -51,6 +50,7 @@
 import com.android.server.backup.BackupAgentTimeoutParameters;
 import com.android.server.backup.BackupRestoreTask;
 import com.android.server.backup.FileMetadata;
+import com.android.server.backup.Flags;
 import com.android.server.backup.KeyValueAdbRestoreEngine;
 import com.android.server.backup.OperationStorage;
 import com.android.server.backup.OperationStorage.OpType;
@@ -157,13 +157,19 @@
         mMonitor = monitor;
         mOnlyPackage = onlyPackage;
         mAllowApks = allowApks;
-        mBuffer = new byte[32 * 1024];
         mAgentTimeoutParameters = Objects.requireNonNull(
                 backupManagerService.getAgentTimeoutParameters(),
                 "Timeout parameters cannot be null");
         mIsAdbRestore = isAdbRestore;
         mUserId = backupManagerService.getUserId();
         mBackupEligibilityRules = backupEligibilityRules;
+
+        if (Flags.enableMaxSizeWritesToPipes()) {
+            // Linux pipe capacity (buffer size) is 16 pages where each page is 4KB
+            mBuffer = new byte[64 * 1024]; // 64KB
+        } else {
+            mBuffer = new byte[32 * 1024];
+        }
     }
 
     @VisibleForTesting
diff --git a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
index 316a16d..2fbc3cd 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
@@ -968,7 +968,12 @@
             throws Exception {
         Set<String> excludedKeysForPackage = getExcludedKeysForPackage(packageName);
 
-        byte[] buffer = new byte[8192]; // will grow when needed
+        int bufferSize = 8192; // 8KB
+        if (Flags.enableMaxSizeWritesToPipes()) {
+            // Linux pipe capacity (buffer size) is 16 pages where each page is 4KB
+            bufferSize = 64 * 1024; // 64KB
+        }
+        byte[] buffer = new byte[bufferSize]; // will grow when needed
         while (in.readNextHeader()) {
             final String key = in.getKey();
             final int size = in.getDataSize();
@@ -1116,7 +1121,11 @@
             ParcelFileDescriptor tReadEnd = mTransportPipes[0];
             ParcelFileDescriptor tWriteEnd = mTransportPipes[1];
 
-            int bufferSize = 32 * 1024;
+            int bufferSize = 32 * 1024; // 32KB
+            if (Flags.enableMaxSizeWritesToPipes()) {
+                // Linux pipe capacity (buffer size) is 16 pages where each page is 4KB
+                bufferSize = 64 * 1024; // 64KB
+            }
             byte[] buffer = new byte[bufferSize];
             FileOutputStream engineOut = new FileOutputStream(eWriteEnd.getFileDescriptor());
             FileInputStream transportIn = new FileInputStream(tReadEnd.getFileDescriptor());
diff --git a/services/backup/java/com/android/server/backup/utils/FullBackupUtils.java b/services/backup/java/com/android/server/backup/utils/FullBackupUtils.java
index 1c0cd87..843354e 100644
--- a/services/backup/java/com/android/server/backup/utils/FullBackupUtils.java
+++ b/services/backup/java/com/android/server/backup/utils/FullBackupUtils.java
@@ -21,7 +21,7 @@
 import android.os.ParcelFileDescriptor;
 import android.util.Slog;
 
-import com.android.server.backup.BackupAndRestoreFeatureFlags;
+import com.android.server.backup.Flags;
 
 import java.io.DataInputStream;
 import java.io.EOFException;
@@ -46,8 +46,11 @@
         // We do not take close() responsibility for the pipe FD
         FileInputStream raw = new FileInputStream(inPipe.getFileDescriptor());
         DataInputStream in = new DataInputStream(raw);
-        final int chunkSizeInBytes =
-                BackupAndRestoreFeatureFlags.getFullBackupUtilsRouteBufferSizeBytes();
+        int chunkSizeInBytes = 32 * 1024; // 32KB
+        if (Flags.enableMaxSizeWritesToPipes()) {
+            // Linux pipe capacity (buffer size) is 16 pages where each page is 4KB
+            chunkSizeInBytes = 64 * 1024; // 64KB
+        }
         byte[] buffer = new byte[chunkSizeInBytes];
         int chunkTotal;
         while ((chunkTotal = in.readInt()) > 0) {
diff --git a/services/backup/java/com/android/server/backup/utils/RestoreUtils.java b/services/backup/java/com/android/server/backup/utils/RestoreUtils.java
index 0accb9f..5a8533a 100644
--- a/services/backup/java/com/android/server/backup/utils/RestoreUtils.java
+++ b/services/backup/java/com/android/server/backup/utils/RestoreUtils.java
@@ -40,6 +40,7 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.server.LocalServices;
 import com.android.server.backup.FileMetadata;
+import com.android.server.backup.Flags;
 import com.android.server.backup.restore.RestoreDeleteObserver;
 import com.android.server.backup.restore.RestorePolicy;
 
@@ -93,7 +94,12 @@
                 try (Session session = installer.openSession(sessionId)) {
                     try (OutputStream apkStream = session.openWrite(info.packageName, 0,
                             info.size)) {
-                        byte[] buffer = new byte[32 * 1024];
+                        int bufferSize = 32 * 1024; // 32KB
+                        if (Flags.enableMaxSizeWritesToPipes()) {
+                            // Linux pipe capacity (buffer size) is 16 pages where each page is 4KB
+                            bufferSize = 64 * 1024; // 64KB
+                        }
+                        byte[] buffer = new byte[bufferSize];
                         long size = info.size;
                         while (size > 0) {
                             long toRead = (buffer.length < size) ? buffer.length : size;
diff --git a/services/backup/lint-baseline.xml b/services/backup/lint-baseline.xml
index 93c9390..46de2cdd 100644
--- a/services/backup/lint-baseline.xml
+++ b/services/backup/lint-baseline.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 7.1.0-dev" type="baseline" client="" dependencies="true" name="" variant="all" version="7.1.0-dev">
+<issues format="6" by="lint 8.4.0-alpha01" type="baseline" client="" dependencies="true" name="" variant="all" version="8.4.0-alpha01">
 
     <issue
         id="NonUserGetterCalled"
@@ -36,4 +36,4 @@
             line="207"/>
     </issue>
 
-</issues>
+</issues>
\ No newline at end of file
diff --git a/services/companion/Android.bp b/services/companion/Android.bp
index 550e17b..2bfdd0a 100644
--- a/services/companion/Android.bp
+++ b/services/companion/Android.bp
@@ -31,4 +31,7 @@
         "virtualdevice_flags_lib",
         "virtual_camera_service_aidl-java",
     ],
+    lint: {
+        baseline_filename: "lint-baseline.xml",
+    },
 }
diff --git a/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java b/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java
index 6964763..4b3772a 100644
--- a/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java
+++ b/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java
@@ -37,6 +37,7 @@
 import android.annotation.Nullable;
 import android.annotation.SuppressLint;
 import android.annotation.UserIdInt;
+import android.app.ActivityOptions;
 import android.app.PendingIntent;
 import android.companion.AssociatedDevice;
 import android.companion.AssociationInfo;
@@ -283,34 +284,35 @@
         final AssociationInfo association = new AssociationInfo(id, userId, packageName,
                 /* tag */ null, macAddress, displayName, deviceProfile, associatedDevice,
                 selfManaged, /* notifyOnDeviceNearby */ false, /* revoked */ false,
-                timestamp, Long.MAX_VALUE, /* systemDataSyncFlags */ 0);
+                /* pending */ false, timestamp, Long.MAX_VALUE, /* systemDataSyncFlags */ 0);
 
-        if (deviceProfile != null) {
-            // If the "Device Profile" is specified, make the companion application a holder of the
-            // corresponding role.
-            addRoleHolderForAssociation(mService.getContext(), association, success -> {
-                if (success) {
-                    addAssociationToStore(association, deviceProfile);
-
-                    sendCallbackAndFinish(association, callback, resultReceiver);
-                } else {
-                    Slog.e(TAG, "Failed to add u" + userId + "\\" + packageName
-                            + " to the list of " + deviceProfile + " holders.");
-
-                    sendCallbackAndFinish(null, callback, resultReceiver);
-                }
-            });
-        } else {
-            addAssociationToStore(association, null);
-
-            sendCallbackAndFinish(association, callback, resultReceiver);
-        }
+        // Add role holder for association (if specified) and add new association to store.
+        maybeGrantRoleAndStoreAssociation(association, callback, resultReceiver);
 
         // Don't need to update the mRevokedAssociationsPendingRoleHolderRemoval since
         // maybeRemoveRoleHolderForAssociation in PackageInactivityListener will handle the case
         // that there are other devices with the same profile, so the role holder won't be removed.
     }
 
+    public void maybeGrantRoleAndStoreAssociation(@NonNull AssociationInfo association,
+            @Nullable IAssociationRequestCallback callback,
+            @Nullable ResultReceiver resultReceiver) {
+        // If the "Device Profile" is specified, make the companion application a holder of the
+        // corresponding role.
+        // If it is null, then the operation will succeed without granting any role.
+        addRoleHolderForAssociation(mService.getContext(), association, success -> {
+            if (success) {
+                addAssociationToStore(association);
+                sendCallbackAndFinish(association, callback, resultReceiver);
+            } else {
+                Slog.e(TAG, "Failed to add u" + association.getUserId()
+                        + "\\" + association.getPackageName()
+                        + " to the list of " + association.getDeviceProfile() + " holders.");
+                sendCallbackAndFinish(null, callback, resultReceiver);
+            }
+        });
+    }
+
     public void enableSystemDataSync(int associationId, int flags) {
         AssociationInfo association = mAssociationStore.getAssociationById(associationId);
         AssociationInfo updated = (new AssociationInfo.Builder(association))
@@ -325,15 +327,14 @@
         mAssociationStore.updateAssociation(updated);
     }
 
-    private void addAssociationToStore(@NonNull AssociationInfo association,
-            @Nullable String deviceProfile) {
+    private void addAssociationToStore(@NonNull AssociationInfo association) {
         Slog.i(TAG, "New CDM association created=" + association);
 
         mAssociationStore.addAssociation(association);
 
         mService.updateSpecialAccessPermissionForAssociatedPackage(association);
 
-        logCreateAssociation(deviceProfile);
+        logCreateAssociation(association.getDeviceProfile());
     }
 
     private void sendCallbackAndFinish(@Nullable AssociationInfo association,
@@ -398,7 +399,11 @@
             pendingIntent = PendingIntent.getActivityAsUser(
                     mContext, /*requestCode */ packageUid, intent,
                     FLAG_ONE_SHOT | FLAG_CANCEL_CURRENT | FLAG_IMMUTABLE,
-                    /* options= */ null, UserHandle.CURRENT);
+                    ActivityOptions.makeBasic()
+                            .setPendingIntentCreatorBackgroundActivityStartMode(
+                                    ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED)
+                            .toBundle(),
+                    UserHandle.CURRENT);
         } finally {
             Binder.restoreCallingIdentity(token);
         }
diff --git a/services/companion/java/com/android/server/companion/BackupRestoreProcessor.java b/services/companion/java/com/android/server/companion/BackupRestoreProcessor.java
new file mode 100644
index 0000000..e4cc1f8
--- /dev/null
+++ b/services/companion/java/com/android/server/companion/BackupRestoreProcessor.java
@@ -0,0 +1,277 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.companion;
+
+import static android.os.UserHandle.getCallingUserId;
+
+import static com.android.server.companion.CompanionDeviceManagerService.PerUserAssociationSet;
+
+import android.annotation.NonNull;
+import android.annotation.SuppressLint;
+import android.annotation.UserIdInt;
+import android.companion.AssociationInfo;
+import android.companion.Flags;
+import android.companion.datatransfer.SystemDataTransferRequest;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManagerInternal;
+import android.util.ArraySet;
+import android.util.Log;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.CollectionUtils;
+import com.android.server.companion.datatransfer.SystemDataTransferRequestStore;
+
+import java.nio.ByteBuffer;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.function.Predicate;
+
+@SuppressLint("LongLogTag")
+class BackupRestoreProcessor {
+    static final String TAG = "CDM_BackupRestoreProcessor";
+    private static final int BACKUP_AND_RESTORE_VERSION = 0;
+
+    @NonNull
+    private final CompanionDeviceManagerService mService;
+    @NonNull
+    private final PackageManagerInternal mPackageManager;
+    @NonNull
+    private final AssociationStoreImpl mAssociationStore;
+    @NonNull
+    private final PersistentDataStore mPersistentStore;
+    @NonNull
+    private final SystemDataTransferRequestStore mSystemDataTransferRequestStore;
+    @NonNull
+    private final AssociationRequestsProcessor mAssociationRequestsProcessor;
+
+    /**
+     * A structure that consists of a set of restored associations that are pending corresponding
+     * companion app to be installed.
+     */
+    @GuardedBy("mAssociationsPendingAppInstall")
+    private final PerUserAssociationSet mAssociationsPendingAppInstall =
+            new PerUserAssociationSet();
+
+    BackupRestoreProcessor(@NonNull CompanionDeviceManagerService service,
+                           @NonNull AssociationStoreImpl associationStore,
+                           @NonNull PersistentDataStore persistentStore,
+                           @NonNull SystemDataTransferRequestStore systemDataTransferRequestStore,
+                           @NonNull AssociationRequestsProcessor associationRequestsProcessor) {
+        mService = service;
+        mPackageManager = service.mPackageManagerInternal;
+        mAssociationStore = associationStore;
+        mPersistentStore = persistentStore;
+        mSystemDataTransferRequestStore = systemDataTransferRequestStore;
+        mAssociationRequestsProcessor = associationRequestsProcessor;
+    }
+
+    /**
+     * Generate CDM state payload to be backed up.
+     * Backup payload is formatted as following:
+     * | (4) payload version | (4) AssociationInfo length | AssociationInfo XML
+     * | (4) SystemDataTransferRequest length | SystemDataTransferRequest XML (without userId)|
+     */
+    byte[] getBackupPayload(int userId) {
+        // Persist state first to generate an up-to-date XML file
+        mService.persistStateForUser(userId);
+        byte[] associationsPayload = mPersistentStore.getBackupPayload(userId);
+        int associationsPayloadLength = associationsPayload.length;
+
+        // System data transfer requests are persisted up-to-date already
+        byte[] requestsPayload = mSystemDataTransferRequestStore.getBackupPayload(userId);
+        int requestsPayloadLength = requestsPayload.length;
+
+        int payloadSize = /* 3 integers */ 12
+                + associationsPayloadLength
+                + requestsPayloadLength;
+
+        return ByteBuffer.allocate(payloadSize)
+                .putInt(BACKUP_AND_RESTORE_VERSION)
+                .putInt(associationsPayloadLength)
+                .put(associationsPayload)
+                .putInt(requestsPayloadLength)
+                .put(requestsPayload)
+                .array();
+    }
+
+    /**
+     * Create new associations and system data transfer request consents using backed up payload.
+     */
+    void applyRestoredPayload(byte[] payload, int userId) {
+        ByteBuffer buffer = ByteBuffer.wrap(payload);
+
+        // Make sure that payload version matches current version to ensure proper deserialization
+        int version = buffer.getInt();
+        if (version != BACKUP_AND_RESTORE_VERSION) {
+            Slog.e(TAG, "Unsupported backup payload version");
+            return;
+        }
+
+        // Read the bytes containing backed-up associations
+        byte[] associationsPayload = new byte[buffer.getInt()];
+        buffer.get(associationsPayload);
+        final Set<AssociationInfo> restoredAssociations = new HashSet<>();
+        mPersistentStore.readStateFromPayload(associationsPayload, userId,
+                restoredAssociations, new HashMap<>());
+
+        // Read the bytes containing backed-up system data transfer requests user consent
+        byte[] requestsPayload = new byte[buffer.getInt()];
+        buffer.get(requestsPayload);
+        List<SystemDataTransferRequest> restoredRequestsForUser =
+                mSystemDataTransferRequestStore.readRequestsFromPayload(requestsPayload, userId);
+
+        // Get a list of installed packages ahead of time.
+        List<ApplicationInfo> installedApps = mPackageManager.getInstalledApplications(
+                0, userId, getCallingUserId());
+
+        // Restored device may have a different user ID than the backed-up user's user-ID. Since
+        // association ID is dependent on the user ID, restored associations must account for
+        // this potential difference on their association IDs.
+        for (AssociationInfo restored : restoredAssociations) {
+            // Don't restore a revoked association. Since they weren't added to the device being
+            // restored in the first place, there is no need to worry about revoking a role that
+            // was never granted either.
+            if (restored.isRevoked()) {
+                continue;
+            }
+
+            // Filter restored requests for those that belong to the restored association.
+            List<SystemDataTransferRequest> restoredRequests = CollectionUtils.filter(
+                    restoredRequestsForUser, it -> it.getAssociationId() == restored.getId());
+
+            // Handle collision: If a local association belonging to the same package already exists
+            // and their tags match, then keep the local one in favor of creating a new association.
+            if (handleCollision(userId, restored, restoredRequests)) {
+                continue;
+            }
+
+            // Create a new association reassigned to this user and a valid association ID
+            final String packageName = restored.getPackageName();
+            final int newId = mService.getNewAssociationIdForPackage(userId, packageName);
+            AssociationInfo newAssociation =
+                    new AssociationInfo.Builder(newId, userId, packageName, restored)
+                            .build();
+
+            // Check if the companion app for this association is already installed, then do one
+            // of the following:
+            // (1) If the app is already installed, then go ahead and add this association and grant
+            // the role attached to this association to the app.
+            // (2) If the app isn't yet installed, then add this association to the list of pending
+            // associations to be added when the package is installed in the future.
+            boolean isPackageInstalled = installedApps.stream()
+                    .anyMatch(app -> packageName.equals(app.packageName));
+            if (isPackageInstalled) {
+                mAssociationRequestsProcessor.maybeGrantRoleAndStoreAssociation(newAssociation,
+                        null, null);
+            } else {
+                addToPendingAppInstall(newAssociation);
+            }
+
+            // Re-map restored system data transfer requests to newly created associations
+            for (SystemDataTransferRequest restoredRequest : restoredRequests) {
+                SystemDataTransferRequest newRequest = restoredRequest.copyWithNewId(newId);
+                newRequest.setUserId(userId);
+                mSystemDataTransferRequestStore.writeRequest(userId, newRequest);
+            }
+        }
+
+        // Persist restored state.
+        mService.persistStateForUser(userId);
+    }
+
+    void addToPendingAppInstall(@NonNull AssociationInfo association) {
+        association = (new AssociationInfo.Builder(association))
+                .setPending(true)
+                .build();
+
+        synchronized (mAssociationsPendingAppInstall) {
+            mAssociationsPendingAppInstall.forUser(association.getUserId()).add(association);
+        }
+    }
+
+    void removeFromPendingAppInstall(@NonNull AssociationInfo association) {
+        synchronized (mAssociationsPendingAppInstall) {
+            mAssociationsPendingAppInstall.forUser(association.getUserId()).remove(association);
+        }
+    }
+
+    @NonNull
+    Set<AssociationInfo> getAssociationsPendingAppInstallForUser(@UserIdInt int userId) {
+        synchronized (mAssociationsPendingAppInstall) {
+            // Return a copy.
+            return new ArraySet<>(mAssociationsPendingAppInstall.forUser(userId));
+        }
+    }
+
+    /**
+     * Detects and handles collision between restored association and local association. Returns
+     * true if there has been a collision and false otherwise.
+     */
+    private boolean handleCollision(@UserIdInt int userId,
+            AssociationInfo restored,
+            List<SystemDataTransferRequest> restoredRequests) {
+        List<AssociationInfo> localAssociations = mAssociationStore.getAssociationsForPackage(
+                restored.getUserId(), restored.getPackageName());
+        Predicate<AssociationInfo> isSameDevice = associationInfo -> {
+            boolean matchesMacAddress = Objects.equals(
+                    associationInfo.getDeviceMacAddress(),
+                    restored.getDeviceMacAddress());
+            boolean matchesTag = !Flags.associationTag()
+                    || Objects.equals(associationInfo.getTag(), restored.getTag());
+            return matchesMacAddress && matchesTag;
+        };
+        AssociationInfo local = CollectionUtils.find(localAssociations, isSameDevice);
+
+        // No collision detected
+        if (local == null) {
+            return false;
+        }
+
+        Log.d(TAG, "Conflict detected with association id=" + local.getId()
+                + " while restoring CDM backup. Keeping local association.");
+
+        List<SystemDataTransferRequest> localRequests = mSystemDataTransferRequestStore
+                .readRequestsByAssociationId(userId, local.getId());
+
+        // If local association doesn't have any existing system data transfer request of same type
+        // attached, then restore corresponding request onto the local association. Otherwise, keep
+        // the locally stored request.
+        for (SystemDataTransferRequest restoredRequest : restoredRequests) {
+            boolean requestTypeExists = CollectionUtils.any(localRequests, request ->
+                    request.getDataType() == restoredRequest.getDataType());
+
+            // This type of request consent already exists for the association.
+            if (requestTypeExists) {
+                continue;
+            }
+
+            Log.d(TAG, "Restoring " + restoredRequest.getClass().getSimpleName()
+                    + " to an existing association id=" + local.getId() + ".");
+
+            SystemDataTransferRequest newRequest =
+                    restoredRequest.copyWithNewId(local.getId());
+            newRequest.setUserId(userId);
+            mSystemDataTransferRequestStore.writeRequest(userId, newRequest);
+        }
+
+        return true;
+    }
+}
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 487b66c..056ec89 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -164,6 +164,7 @@
     private final SystemDataTransferRequestStore mSystemDataTransferRequestStore;
     private AssociationRequestsProcessor mAssociationRequestsProcessor;
     private SystemDataTransferProcessor mSystemDataTransferProcessor;
+    private BackupRestoreProcessor mBackupRestoreProcessor;
     private CompanionDevicePresenceMonitor mDevicePresenceMonitor;
     private CompanionApplicationController mCompanionAppController;
     private CompanionTransportManager mTransportManager;
@@ -256,6 +257,9 @@
         mSystemDataTransferProcessor = new SystemDataTransferProcessor(this,
                 mPackageManagerInternal, mAssociationStore,
                 mSystemDataTransferRequestStore, mTransportManager);
+        mBackupRestoreProcessor = new BackupRestoreProcessor(
+                /* cdmService */ this, mAssociationStore, mPersistentStore,
+                mSystemDataTransferRequestStore, mAssociationRequestsProcessor);
         // TODO(b/279663946): move context sync to a dedicated system service
         mCrossDeviceSyncController = new CrossDeviceSyncController(getContext(), mTransportManager);
 
@@ -283,7 +287,9 @@
         final Set<Integer> usersToPersistStateFor = new ArraySet<>();
 
         for (AssociationInfo association : allAssociations) {
-            if (!association.isRevoked()) {
+            if (association.isPending()) {
+                mBackupRestoreProcessor.addToPendingAppInstall(association);
+            } else if (!association.isRevoked()) {
                 activeAssociations.add(association);
             } else if (maybeRemoveRoleHolderForAssociation(association)) {
                 // Nothing more to do here, but we'll need to persist all the associations to the
@@ -501,7 +507,7 @@
         updateAtm(userId, updatedAssociations);
     }
 
-    private void persistStateForUser(@UserIdInt int userId) {
+    void persistStateForUser(@UserIdInt int userId) {
         // We want to store both active associations and the revoked (removed) association that we
         // are keeping around for the final clean-up (delayed role holder removal).
         final List<AssociationInfo> allAssociations;
@@ -510,6 +516,9 @@
                 mAssociationStore.getAssociationsForUser(userId));
         // ... and add the revoked (removed) association, that are yet to be permanently removed.
         allAssociations.addAll(getPendingRoleHolderRemovalAssociationsForUser(userId));
+        // ... and add the restored associations that are pending missing package installation.
+        allAssociations.addAll(mBackupRestoreProcessor
+                .getAssociationsPendingAppInstallForUser(userId));
 
         final Map<String, Set<Integer>> usedIdsForUser = getPreviouslyUsedIdsForUser(userId);
 
@@ -577,6 +586,23 @@
         mCompanionAppController.onPackagesChanged(userId);
     }
 
+    private void onPackageAddedInternal(@UserIdInt int userId, @NonNull String packageName) {
+        if (DEBUG) Log.i(TAG, "onPackageAddedInternal() u" + userId + "/" + packageName);
+
+        Set<AssociationInfo> associationsPendingAppInstall = mBackupRestoreProcessor
+                .getAssociationsPendingAppInstallForUser(userId);
+        for (AssociationInfo association : associationsPendingAppInstall) {
+            if (!packageName.equals(association.getPackageName())) continue;
+
+            AssociationInfo newAssociation = new AssociationInfo.Builder(association)
+                    .setPending(false)
+                    .build();
+            mAssociationRequestsProcessor.maybeGrantRoleAndStoreAssociation(newAssociation,
+                    null, null);
+            mBackupRestoreProcessor.removeFromPendingAppInstall(association);
+        }
+    }
+
     // Revoke associations if the selfManaged companion device does not connect for 3 months.
     void removeInactiveSelfManagedAssociations() {
         final long currentTime = System.currentTimeMillis();
@@ -1052,13 +1078,14 @@
 
         @Override
         public byte[] getBackupPayload(int userId) {
-            // TODO(b/286124853): back up CDM data
-            return new byte[0];
+            Log.i(TAG, "getBackupPayload() userId=" + userId);
+            return mBackupRestoreProcessor.getBackupPayload(userId);
         }
 
         @Override
         public void applyRestoredPayload(byte[] payload, int userId) {
-            // TODO(b/286124853): restore CDM data
+            Log.i(TAG, "applyRestoredPayload() userId=" + userId);
+            mBackupRestoreProcessor.applyRestoredPayload(payload, userId);
         }
 
         @Override
@@ -1067,7 +1094,8 @@
                 @NonNull String[] args) {
             return new CompanionDeviceShellCommand(CompanionDeviceManagerService.this,
                     mAssociationStore, mDevicePresenceMonitor, mTransportManager,
-                    mSystemDataTransferProcessor, mAssociationRequestsProcessor)
+                    mSystemDataTransferProcessor, mAssociationRequestsProcessor,
+                    mBackupRestoreProcessor)
                     .exec(this, in.getFileDescriptor(), out.getFileDescriptor(),
                             err.getFileDescriptor(), args);
         }
@@ -1141,6 +1169,15 @@
                 usedIds.put(it.getId(), true);
             }
 
+            // Some IDs may be reserved by associations that aren't stored yet due to missing
+            // package after a backup restoration. We don't want the ID to have been taken by
+            // another association by the time when it is activated from the package installation.
+            final Set<AssociationInfo> pendingAssociations = mBackupRestoreProcessor
+                    .getAssociationsPendingAppInstallForUser(userId);
+            for (AssociationInfo it: pendingAssociations) {
+                usedIds.put(it.getId(), true);
+            }
+
             // Second: collect all IDs that have been previously used for this package (and user).
             final Set<Integer> previouslyUsedIds =
                     getPreviouslyUsedIdsForPackageLocked(userId, packageName);
@@ -1499,6 +1536,11 @@
         public void onPackageModified(String packageName) {
             onPackageModifiedInternal(getChangingUserId(), packageName);
         }
+
+        @Override
+        public void onPackageAdded(String packageName, int uid) {
+            onPackageAddedInternal(getChangingUserId(), packageName);
+        }
     };
 
     static int getFirstAssociationIdForUser(@UserIdInt int userId) {
@@ -1702,7 +1744,7 @@
         }
     }
 
-    private static class PerUserAssociationSet extends PerUser<Set<AssociationInfo>> {
+    static class PerUserAssociationSet extends PerUser<Set<AssociationInfo>> {
         @Override
         protected @NonNull Set<AssociationInfo> create(int userId) {
             return new ArraySet<>();
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
index 9fdf5c2..53c0184c 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
@@ -18,6 +18,8 @@
 
 import static android.companion.CompanionDeviceManager.MESSAGE_REQUEST_CONTEXT_SYNC;
 
+import static com.android.server.companion.PermissionsUtils.sanitizeWithCallerChecks;
+
 import android.companion.AssociationInfo;
 import android.companion.ContextSyncMessage;
 import android.companion.Flags;
@@ -26,6 +28,7 @@
 import android.net.MacAddress;
 import android.os.Binder;
 import android.os.ShellCommand;
+import android.util.Base64;
 import android.util.proto.ProtoOutputStream;
 
 import com.android.server.companion.datatransfer.SystemDataTransferProcessor;
@@ -47,19 +50,22 @@
 
     private final SystemDataTransferProcessor mSystemDataTransferProcessor;
     private final AssociationRequestsProcessor mAssociationRequestsProcessor;
+    private final BackupRestoreProcessor mBackupRestoreProcessor;
 
     CompanionDeviceShellCommand(CompanionDeviceManagerService service,
             AssociationStoreImpl associationStore,
             CompanionDevicePresenceMonitor devicePresenceMonitor,
             CompanionTransportManager transportManager,
             SystemDataTransferProcessor systemDataTransferProcessor,
-            AssociationRequestsProcessor associationRequestsProcessor) {
+            AssociationRequestsProcessor associationRequestsProcessor,
+            BackupRestoreProcessor backupRestoreProcessor) {
         mService = service;
         mAssociationStore = associationStore;
         mDevicePresenceMonitor = devicePresenceMonitor;
         mTransportManager = transportManager;
         mSystemDataTransferProcessor = systemDataTransferProcessor;
         mAssociationRequestsProcessor = associationRequestsProcessor;
+        mBackupRestoreProcessor = backupRestoreProcessor;
     }
 
     @Override
@@ -111,6 +117,19 @@
                 }
                 break;
 
+                case "disassociate-all": {
+                    final int userId = getNextIntArgRequired();
+                    final String packageName = getNextArgRequired();
+                    final List<AssociationInfo> userAssociations =
+                            mAssociationStore.getAssociationsForPackage(userId, packageName);
+                    for (AssociationInfo association : userAssociations) {
+                        if (sanitizeWithCallerChecks(mService.getContext(), association) != null) {
+                            mService.disassociateInternal(association.getId());
+                        }
+                    }
+                }
+                break;
+
                 case "clear-association-memory-cache":
                     mService.persistState();
                     mService.loadAssociationsFromDisk();
@@ -126,6 +145,20 @@
                     mDevicePresenceMonitor.simulateDeviceEvent(associationId, /* event */ 1);
                     break;
 
+                case "get-backup-payload": {
+                    final int userId = getNextIntArgRequired();
+                    byte[] payload = mBackupRestoreProcessor.getBackupPayload(userId);
+                    out.println(Base64.encodeToString(payload, Base64.NO_WRAP));
+                }
+                break;
+
+                case "apply-restored-payload": {
+                    final int userId = getNextIntArgRequired();
+                    byte[] payload = Base64.decode(getNextArgRequired(), Base64.NO_WRAP);
+                    mBackupRestoreProcessor.applyRestoredPayload(payload, userId);
+                }
+                break;
+
                 case "remove-inactive-associations": {
                     // This command should trigger the same "clean-up" job as performed by the
                     // InactiveAssociationsRemovalService JobService. However, since the
@@ -355,6 +388,8 @@
         pw.println("      Create a new Association.");
         pw.println("  disassociate USER_ID PACKAGE MAC_ADDRESS");
         pw.println("      Remove an existing Association.");
+        pw.println("  disassociate-all USER_ID");
+        pw.println("      Remove all Associations for a user.");
         pw.println("  clear-association-memory-cache");
         pw.println("      Clear the in-memory association cache and reload all association ");
         pw.println("      information from persistent storage. USE FOR DEBUGGING PURPOSES ONLY.");
@@ -378,6 +413,14 @@
         pw.println("      invoked for the same device (same ASSOCIATION_ID) no longer than");
         pw.println("      60 seconds ago.");
 
+        pw.println("  get-backup-payload USER_ID");
+        pw.println("      Generate backup payload for the given user and print its content");
+        pw.println("      encoded to a Base64 string.");
+        pw.println("      USE FOR DEBUGGING AND/OR TESTING PURPOSES ONLY.");
+        pw.println("  apply-restored-payload USER_ID PAYLOAD");
+        pw.println("      Apply restored backup payload for the given user.");
+        pw.println("      USE FOR DEBUGGING AND/OR TESTING PURPOSES ONLY.");
+
         if (Flags.devicePresence()) {
             pw.println("  simulate-device-event ASSOCIATION_ID EVENT");
             pw.println("  Simulate the companion device event changes:");
diff --git a/services/companion/java/com/android/server/companion/DataStoreUtils.java b/services/companion/java/com/android/server/companion/DataStoreUtils.java
index c182529..04ce1f6 100644
--- a/services/companion/java/com/android/server/companion/DataStoreUtils.java
+++ b/services/companion/java/com/android/server/companion/DataStoreUtils.java
@@ -30,8 +30,11 @@
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
+import java.io.ByteArrayOutputStream;
 import java.io.File;
+import java.io.FileInputStream;
 import java.io.FileOutputStream;
+import java.io.IOException;
 
 /**
  * Util class for CDM data stores
@@ -88,6 +91,29 @@
         }
     }
 
+    /**
+     * Read a file and return the byte array containing the bytes of the file.
+     */
+    @NonNull
+    public static byte[] fileToByteArray(@NonNull AtomicFile file) {
+        if (!file.getBaseFile().exists()) {
+            Slog.d(TAG, "File does not exist");
+            return new byte[0];
+        }
+        try (FileInputStream in = file.openRead()) {
+            ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+            byte[] buffer = new byte[1024];
+            int read;
+            while ((read = in.read(buffer)) != -1) {
+                bytes.write(buffer, 0, read);
+            }
+            return bytes.toByteArray();
+        } catch (IOException e) {
+            Slog.e(TAG, "Error while reading requests file", e);
+        }
+        return new byte[0];
+    }
+
     private DataStoreUtils() {
     }
 }
diff --git a/services/companion/java/com/android/server/companion/PersistentDataStore.java b/services/companion/java/com/android/server/companion/PersistentDataStore.java
index b4b9379..1ebe65c 100644
--- a/services/companion/java/com/android/server/companion/PersistentDataStore.java
+++ b/services/companion/java/com/android/server/companion/PersistentDataStore.java
@@ -28,6 +28,7 @@
 import static com.android.server.companion.CompanionDeviceManagerService.getFirstAssociationIdForUser;
 import static com.android.server.companion.CompanionDeviceManagerService.getLastAssociationIdForUser;
 import static com.android.server.companion.DataStoreUtils.createStorageFileForUser;
+import static com.android.server.companion.DataStoreUtils.fileToByteArray;
 import static com.android.server.companion.DataStoreUtils.isEndOfTag;
 import static com.android.server.companion.DataStoreUtils.isStartOfTag;
 import static com.android.server.companion.DataStoreUtils.writeToFileSafely;
@@ -55,9 +56,11 @@
 import org.xmlpull.v1.XmlPullParserException;
 import org.xmlpull.v1.XmlSerializer;
 
+import java.io.ByteArrayInputStream;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.IOException;
+import java.io.InputStream;
 import java.util.Collection;
 import java.util.HashSet;
 import java.util.List;
@@ -186,6 +189,7 @@
     private static final String XML_ATTR_SELF_MANAGED = "self_managed";
     private static final String XML_ATTR_NOTIFY_DEVICE_NEARBY = "notify_device_nearby";
     private static final String XML_ATTR_REVOKED = "revoked";
+    private static final String XML_ATTR_PENDING = "pending";
     private static final String XML_ATTR_TIME_APPROVED = "time_approved";
     private static final String XML_ATTR_LAST_TIME_CONNECTED = "last_time_connected";
     private static final String XML_ATTR_SYSTEM_DATA_SYNC_FLAGS = "system_data_sync_flags";
@@ -328,34 +332,42 @@
             @NonNull String rootTag, @Nullable Collection<AssociationInfo> associationsOut,
             @NonNull Map<String, Set<Integer>> previouslyUsedIdsPerPackageOut) {
         try (FileInputStream in = file.openRead()) {
-            final TypedXmlPullParser parser = Xml.resolvePullParser(in);
-
-            XmlUtils.beginDocument(parser, rootTag);
-            final int version = readIntAttribute(parser, XML_ATTR_PERSISTENCE_VERSION, 0);
-            switch (version) {
-                case 0:
-                    readAssociationsV0(parser, userId, associationsOut);
-                    break;
-                case 1:
-                    while (true) {
-                        parser.nextTag();
-                        if (isStartOfTag(parser, XML_TAG_ASSOCIATIONS)) {
-                            readAssociationsV1(parser, userId, associationsOut);
-                        } else if (isStartOfTag(parser, XML_TAG_PREVIOUSLY_USED_IDS)) {
-                            readPreviouslyUsedIdsV1(parser, previouslyUsedIdsPerPackageOut);
-                        } else if (isEndOfTag(parser, rootTag)) {
-                            break;
-                        }
-                    }
-                    break;
-            }
-            return version;
+            return readStateFromInputStream(userId, in, rootTag, associationsOut,
+                    previouslyUsedIdsPerPackageOut);
         } catch (XmlPullParserException | IOException e) {
             Slog.e(TAG, "Error while reading associations file", e);
             return -1;
         }
     }
 
+    private int readStateFromInputStream(@UserIdInt int userId, @NonNull InputStream in,
+            @NonNull String rootTag, @Nullable Collection<AssociationInfo> associationsOut,
+            @NonNull Map<String, Set<Integer>> previouslyUsedIdsPerPackageOut)
+            throws XmlPullParserException, IOException {
+        final TypedXmlPullParser parser = Xml.resolvePullParser(in);
+
+        XmlUtils.beginDocument(parser, rootTag);
+        final int version = readIntAttribute(parser, XML_ATTR_PERSISTENCE_VERSION, 0);
+        switch (version) {
+            case 0:
+                readAssociationsV0(parser, userId, associationsOut);
+                break;
+            case 1:
+                while (true) {
+                    parser.nextTag();
+                    if (isStartOfTag(parser, XML_TAG_ASSOCIATIONS)) {
+                        readAssociationsV1(parser, userId, associationsOut);
+                    } else if (isStartOfTag(parser, XML_TAG_PREVIOUSLY_USED_IDS)) {
+                        readPreviouslyUsedIdsV1(parser, previouslyUsedIdsPerPackageOut);
+                    } else if (isEndOfTag(parser, rootTag)) {
+                        break;
+                    }
+                }
+                break;
+        }
+        return version;
+    }
+
     private void persistStateToFileLocked(@NonNull AtomicFile file,
             @Nullable Collection<AssociationInfo> associations,
             @NonNull Map<String, Set<Integer>> previouslyUsedIdsPerPackage) {
@@ -391,6 +403,26 @@
                 u -> createStorageFileForUser(userId, FILE_NAME));
     }
 
+    byte[] getBackupPayload(@UserIdInt int userId) {
+        Slog.i(TAG, "Fetching stored state data for user " + userId + " from disk");
+        final AtomicFile file = getStorageFileForUser(userId);
+
+        synchronized (file) {
+            return fileToByteArray(file);
+        }
+    }
+
+    void readStateFromPayload(byte[] payload, @UserIdInt int userId,
+                              @NonNull Set<AssociationInfo> associationsOut,
+                              @NonNull Map<String, Set<Integer>> previouslyUsedIdsPerPackageOut) {
+        try (ByteArrayInputStream in = new ByteArrayInputStream(payload)) {
+            readStateFromInputStream(userId, in, XML_TAG_STATE, associationsOut,
+                    previouslyUsedIdsPerPackageOut);
+        } catch (XmlPullParserException | IOException e) {
+            Slog.e(TAG, "Error while reading associations file", e);
+        }
+    }
+
     private static @NonNull File getBaseLegacyStorageFileForUser(@UserIdInt int userId) {
         return new File(Environment.getUserSystemDirectory(userId), FILE_NAME_LEGACY);
     }
@@ -433,8 +465,8 @@
 
         out.add(new AssociationInfo(associationId, userId, appPackage, tag,
                 MacAddress.fromString(deviceAddress), null, profile, null,
-                /* managedByCompanionApp */ false, notify, /* revoked */ false, timeApproved,
-                Long.MAX_VALUE, /* systemDataSyncFlags */ 0));
+                /* managedByCompanionApp */ false, notify, /* revoked */ false, /* pending */ false,
+                timeApproved, Long.MAX_VALUE, /* systemDataSyncFlags */ 0));
     }
 
     private static void readAssociationsV1(@NonNull TypedXmlPullParser parser,
@@ -465,6 +497,7 @@
         final boolean selfManaged = readBooleanAttribute(parser, XML_ATTR_SELF_MANAGED);
         final boolean notify = readBooleanAttribute(parser, XML_ATTR_NOTIFY_DEVICE_NEARBY);
         final boolean revoked = readBooleanAttribute(parser, XML_ATTR_REVOKED, false);
+        final boolean pending = readBooleanAttribute(parser, XML_ATTR_PENDING, false);
         final long timeApproved = readLongAttribute(parser, XML_ATTR_TIME_APPROVED, 0L);
         final long lastTimeConnected = readLongAttribute(
                 parser, XML_ATTR_LAST_TIME_CONNECTED, Long.MAX_VALUE);
@@ -473,7 +506,7 @@
 
         final AssociationInfo associationInfo = createAssociationInfoNoThrow(associationId, userId,
                 appPackage, tag, macAddress, displayName, profile, selfManaged, notify, revoked,
-                timeApproved, lastTimeConnected, systemDataSyncFlags);
+                pending, timeApproved, lastTimeConnected, systemDataSyncFlags);
         if (associationInfo != null) {
             out.add(associationInfo);
         }
@@ -527,8 +560,8 @@
         writeBooleanAttribute(serializer, XML_ATTR_SELF_MANAGED, a.isSelfManaged());
         writeBooleanAttribute(
                 serializer, XML_ATTR_NOTIFY_DEVICE_NEARBY, a.isNotifyOnDeviceNearby());
-        writeBooleanAttribute(
-                serializer, XML_ATTR_REVOKED, a.isRevoked());
+        writeBooleanAttribute(serializer, XML_ATTR_REVOKED, a.isRevoked());
+        writeBooleanAttribute(serializer, XML_ATTR_PENDING, a.isPending());
         writeLongAttribute(serializer, XML_ATTR_TIME_APPROVED, a.getTimeApprovedMs());
         writeLongAttribute(
                 serializer, XML_ATTR_LAST_TIME_CONNECTED, a.getLastTimeConnectedMs());
@@ -572,14 +605,14 @@
             @UserIdInt int userId, @NonNull String appPackage, @Nullable String tag,
             @Nullable MacAddress macAddress, @Nullable CharSequence displayName,
             @Nullable String profile, boolean selfManaged, boolean notify, boolean revoked,
-            long timeApproved, long lastTimeConnected, int systemDataSyncFlags) {
+            boolean pending, long timeApproved, long lastTimeConnected, int systemDataSyncFlags) {
         AssociationInfo associationInfo = null;
         try {
             // We do not persist AssociatedDevice, which means that AssociationInfo retrieved from
             // datastore is not guaranteed to be identical to the one from initial association.
             associationInfo = new AssociationInfo(associationId, userId, appPackage, tag,
                     macAddress, displayName, profile, null, selfManaged, notify,
-                    revoked, timeApproved, lastTimeConnected, systemDataSyncFlags);
+                    revoked, pending, timeApproved, lastTimeConnected, systemDataSyncFlags);
         } catch (Exception e) {
             if (DEBUG) Log.w(TAG, "Could not create AssociationInfo", e);
         }
diff --git a/services/companion/java/com/android/server/companion/RolesUtils.java b/services/companion/java/com/android/server/companion/RolesUtils.java
index 163f614..af9d2d7 100644
--- a/services/companion/java/com/android/server/companion/RolesUtils.java
+++ b/services/companion/java/com/android/server/companion/RolesUtils.java
@@ -47,6 +47,17 @@
         return roleHolders.contains(packageName);
     }
 
+    /**
+     * Attempt to add the association's companion app as the role holder for the device profile
+     * specified in the association. If the association does not have any device profile specified,
+     * then the operation will always be successful as a no-op.
+     *
+     * @param context
+     * @param associationInfo the association for which the role should be granted to the app
+     * @param roleGrantResult the result callback for adding role holder. True if successful, and
+     *                        false if failed. If the association does not have any device profile
+     *                        specified, then the operation will always be successful as a no-op.
+     */
     static void addRoleHolderForAssociation(
             @NonNull Context context, @NonNull AssociationInfo associationInfo,
             @NonNull Consumer<Boolean> roleGrantResult) {
@@ -55,7 +66,11 @@
         }
 
         final String deviceProfile = associationInfo.getDeviceProfile();
-        if (deviceProfile == null) return;
+        if (deviceProfile == null) {
+            // If no device profile is specified, then no-op and resolve callback with success.
+            roleGrantResult.accept(true);
+            return;
+        }
 
         final RoleManager roleManager = context.getSystemService(RoleManager.class);
 
diff --git a/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java b/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java
index bd646fa..4e471f5 100644
--- a/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java
+++ b/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java
@@ -27,6 +27,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
+import android.app.ActivityOptions;
 import android.app.PendingIntent;
 import android.companion.AssociationInfo;
 import android.companion.DeviceNotAssociatedException;
@@ -186,7 +187,11 @@
         final long token = Binder.clearCallingIdentity();
         try {
             return PendingIntent.getActivityAsUser(mContext, /*requestCode */ associationId, intent,
-                    FLAG_ONE_SHOT | FLAG_CANCEL_CURRENT | FLAG_IMMUTABLE, /* options= */ null,
+                    FLAG_ONE_SHOT | FLAG_CANCEL_CURRENT | FLAG_IMMUTABLE,
+                    ActivityOptions.makeBasic()
+                            .setPendingIntentCreatorBackgroundActivityStartMode(
+                                    ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED)
+                            .toBundle(),
                     UserHandle.CURRENT);
         } finally {
             Binder.restoreCallingIdentity(token);
diff --git a/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferRequestStore.java b/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferRequestStore.java
index 9f489e8..51c5fd6 100644
--- a/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferRequestStore.java
+++ b/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferRequestStore.java
@@ -23,6 +23,7 @@
 import static com.android.internal.util.XmlUtils.writeBooleanAttribute;
 import static com.android.internal.util.XmlUtils.writeIntAttribute;
 import static com.android.server.companion.DataStoreUtils.createStorageFileForUser;
+import static com.android.server.companion.DataStoreUtils.fileToByteArray;
 import static com.android.server.companion.DataStoreUtils.isEndOfTag;
 import static com.android.server.companion.DataStoreUtils.isStartOfTag;
 import static com.android.server.companion.DataStoreUtils.writeToFileSafely;
@@ -44,6 +45,7 @@
 
 import org.xmlpull.v1.XmlPullParserException;
 
+import java.io.ByteArrayInputStream;
 import java.io.FileInputStream;
 import java.io.IOException;
 import java.util.ArrayList;
@@ -67,7 +69,6 @@
  *   <request
  *     association_id="1"
  *     data_type="1"
- *     user_id="12"
  *     is_user_consented="true"
  *   </request>
  * </requests>
@@ -84,7 +85,6 @@
 
     private static final String XML_ATTR_ASSOCIATION_ID = "association_id";
     private static final String XML_ATTR_DATA_TYPE = "data_type";
-    private static final String XML_ATTR_USER_ID = "user_id";
     private static final String XML_ATTR_IS_USER_CONSENTED = "is_user_consented";
 
     private static final int READ_FROM_DISK_TIMEOUT = 5; // in seconds
@@ -152,6 +152,33 @@
         mExecutor.execute(() -> writeRequestsToStore(userId, cachedRequests));
     }
 
+    /**
+     * Return the byte contents of the XML file storing current system data transfer requests.
+     */
+    public byte[] getBackupPayload(@UserIdInt int userId) {
+        final AtomicFile file = getStorageFileForUser(userId);
+
+        synchronized (file) {
+            return fileToByteArray(file);
+        }
+    }
+
+    /**
+     * Parse the byte array containing XML information of system data transfer requests into
+     * an array list of requests.
+     */
+    public List<SystemDataTransferRequest> readRequestsFromPayload(byte[] payload, int userId) {
+        try (ByteArrayInputStream in = new ByteArrayInputStream(payload)) {
+            final TypedXmlPullParser parser = Xml.resolvePullParser(in);
+            XmlUtils.beginDocument(parser, XML_TAG_REQUESTS);
+
+            return readRequestsFromXml(parser, userId);
+        } catch (XmlPullParserException | IOException e) {
+            Slog.e(LOG_TAG, "Error while reading requests file", e);
+            return new ArrayList<>();
+        }
+    }
+
     @GuardedBy("mLock")
     private ArrayList<SystemDataTransferRequest> readRequestsFromCache(@UserIdInt int userId) {
         ArrayList<SystemDataTransferRequest> cachedRequests = mCachedPerUser.get(userId);
@@ -197,7 +224,7 @@
                 final TypedXmlPullParser parser = Xml.resolvePullParser(in);
                 XmlUtils.beginDocument(parser, XML_TAG_REQUESTS);
 
-                return readRequestsFromXml(parser);
+                return readRequestsFromXml(parser, userId);
             } catch (XmlPullParserException | IOException e) {
                 Slog.e(LOG_TAG, "Error while reading requests file", e);
                 return new ArrayList<>();
@@ -207,7 +234,8 @@
 
     @NonNull
     private ArrayList<SystemDataTransferRequest> readRequestsFromXml(
-            @NonNull TypedXmlPullParser parser) throws XmlPullParserException, IOException {
+            @NonNull TypedXmlPullParser parser, int userId)
+            throws XmlPullParserException, IOException {
         if (!isStartOfTag(parser, XML_TAG_REQUESTS)) {
             throw new XmlPullParserException("The XML doesn't have start tag: " + XML_TAG_REQUESTS);
         }
@@ -220,14 +248,15 @@
                 break;
             }
             if (isStartOfTag(parser, XML_TAG_REQUEST)) {
-                requests.add(readRequestFromXml(parser));
+                requests.add(readRequestFromXml(parser, userId));
             }
         }
 
         return requests;
     }
 
-    private SystemDataTransferRequest readRequestFromXml(@NonNull TypedXmlPullParser parser)
+    private SystemDataTransferRequest readRequestFromXml(@NonNull TypedXmlPullParser parser,
+            int userId)
             throws XmlPullParserException, IOException {
         if (!isStartOfTag(parser, XML_TAG_REQUEST)) {
             throw new XmlPullParserException("XML doesn't have start tag: " + XML_TAG_REQUEST);
@@ -235,7 +264,6 @@
 
         final int associationId = readIntAttribute(parser, XML_ATTR_ASSOCIATION_ID);
         final int dataType = readIntAttribute(parser, XML_ATTR_DATA_TYPE);
-        final int userId = readIntAttribute(parser, XML_ATTR_USER_ID);
         final boolean isUserConsented = readBooleanAttribute(parser, XML_ATTR_IS_USER_CONSENTED);
 
         switch (dataType) {
@@ -292,7 +320,6 @@
 
         writeIntAttribute(serializer, XML_ATTR_ASSOCIATION_ID, request.getAssociationId());
         writeIntAttribute(serializer, XML_ATTR_DATA_TYPE, request.getDataType());
-        writeIntAttribute(serializer, XML_ATTR_USER_ID, request.getUserId());
         writeBooleanAttribute(serializer, XML_ATTR_IS_USER_CONSENTED, request.isUserConsented());
 
         serializer.endTag(null, XML_TAG_REQUEST);
diff --git a/services/companion/java/com/android/server/companion/securechannel/SecureChannel.java b/services/companion/java/com/android/server/companion/securechannel/SecureChannel.java
index 720687e..0e66fbc 100644
--- a/services/companion/java/com/android/server/companion/securechannel/SecureChannel.java
+++ b/services/companion/java/com/android/server/companion/securechannel/SecureChannel.java
@@ -23,12 +23,12 @@
 import android.os.Build;
 import android.util.Slog;
 
-import com.google.security.cryptauth.lib.securegcm.BadHandleException;
-import com.google.security.cryptauth.lib.securegcm.CryptoException;
-import com.google.security.cryptauth.lib.securegcm.D2DConnectionContextV1;
-import com.google.security.cryptauth.lib.securegcm.D2DHandshakeContext;
-import com.google.security.cryptauth.lib.securegcm.D2DHandshakeContext.Role;
-import com.google.security.cryptauth.lib.securegcm.HandshakeException;
+import com.google.security.cryptauth.lib.securegcm.ukey2.BadHandleException;
+import com.google.security.cryptauth.lib.securegcm.ukey2.CryptoException;
+import com.google.security.cryptauth.lib.securegcm.ukey2.D2DConnectionContextV1;
+import com.google.security.cryptauth.lib.securegcm.ukey2.D2DHandshakeContext;
+import com.google.security.cryptauth.lib.securegcm.ukey2.D2DHandshakeContext.Role;
+import com.google.security.cryptauth.lib.securegcm.ukey2.HandshakeException;
 
 import libcore.io.IoUtils;
 import libcore.io.Streams;
diff --git a/services/companion/java/com/android/server/companion/transport/CompanionTransportManager.java b/services/companion/java/com/android/server/companion/transport/CompanionTransportManager.java
index 62c6703..3e45626 100644
--- a/services/companion/java/com/android/server/companion/transport/CompanionTransportManager.java
+++ b/services/companion/java/com/android/server/companion/transport/CompanionTransportManager.java
@@ -125,7 +125,7 @@
      * Send a message to remote devices through the transports
      */
     public void sendMessage(int message, byte[] data, int[] associationIds) {
-        Slog.i(TAG, "Sending message 0x" + Integer.toHexString(message)
+        Slog.d(TAG, "Sending message 0x" + Integer.toHexString(message)
                 + " data length " + data.length);
         synchronized (mTransports) {
             for (int i = 0; i < associationIds.length; i++) {
diff --git a/services/companion/java/com/android/server/companion/transport/Transport.java b/services/companion/java/com/android/server/companion/transport/Transport.java
index 22b18ac..8a5774e 100644
--- a/services/companion/java/com/android/server/companion/transport/Transport.java
+++ b/services/companion/java/com/android/server/companion/transport/Transport.java
@@ -284,7 +284,7 @@
         if (mListeners.containsKey(message)) {
             try {
                 mListeners.get(message).onMessageReceived(getAssociationId(), data);
-                Slog.i(TAG, "Message 0x" + Integer.toHexString(message)
+                Slog.d(TAG, "Message 0x" + Integer.toHexString(message)
                         + " is received from associationId " + mAssociationId
                         + ", sending data length " + data.length + " to the listener.");
             } catch (RemoteException ignored) {
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 8c728f1..31766f2 100644
--- a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
+++ b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
@@ -288,16 +288,15 @@
         }
         final UserHandle activityUser =
                 UserHandle.getUserHandleForUid(activityInfo.applicationInfo.uid);
+        final ComponentName activityComponent = activityInfo.getComponentName();
+        if (BLOCKED_APP_STREAMING_COMPONENT.equals(activityComponent) && activityUser.isSystem()) {
+            // The error dialog alerting users that streaming is blocked is always allowed.
+            return true;
+        }
         if (!mAllowedUsers.contains(activityUser)) {
             Slog.d(TAG, "Virtual device launch disallowed from user " + activityUser);
             return false;
         }
-
-        final ComponentName activityComponent = activityInfo.getComponentName();
-        if (BLOCKED_APP_STREAMING_COMPONENT.equals(activityComponent)) {
-            // The error dialog alerting users that streaming is blocked is always allowed.
-            return true;
-        }
         if (!activityMatchesDisplayCategory(activityInfo)) {
             Slog.d(TAG, "The activity's required display category '"
                     + activityInfo.requiredDisplayCategory
diff --git a/services/companion/java/com/android/server/companion/virtual/InputController.java b/services/companion/java/com/android/server/companion/virtual/InputController.java
index 1f89e57b..d1274d4 100644
--- a/services/companion/java/com/android/server/companion/virtual/InputController.java
+++ b/services/companion/java/com/android/server/companion/virtual/InputController.java
@@ -245,8 +245,8 @@
         mInputManagerInternal.setPointerIconVisible(visible, displayId);
     }
 
-    void setPointerAcceleration(float pointerAcceleration, int displayId) {
-        mInputManagerInternal.setPointerAcceleration(pointerAcceleration, displayId);
+    void setMousePointerAccelerationEnabled(boolean enabled, int displayId) {
+        mInputManagerInternal.setMousePointerAccelerationEnabled(enabled, displayId);
     }
 
     void setDisplayEligibilityForPointerCapture(boolean isEligible, int displayId) {
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
index 58aa2c3..44c3a8d 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -1110,7 +1110,7 @@
         final long token = Binder.clearCallingIdentity();
         try {
             mInputController.setShowPointerIcon(showPointer, displayId);
-            mInputController.setPointerAcceleration(1f, displayId);
+            mInputController.setMousePointerAccelerationEnabled(false, displayId);
             mInputController.setDisplayEligibilityForPointerCapture(/* isEligible= */ false,
                     displayId);
             // WM throws a SecurityException if the display is untrusted.
diff --git a/services/companion/java/com/android/server/companion/virtual/camera/VirtualCameraController.java b/services/companion/java/com/android/server/companion/virtual/camera/VirtualCameraController.java
index d089b05..2f9b6a5 100644
--- a/services/companion/java/com/android/server/companion/virtual/camera/VirtualCameraController.java
+++ b/services/companion/java/com/android/server/companion/virtual/camera/VirtualCameraController.java
@@ -55,9 +55,7 @@
     @GuardedBy("mCameras")
     private final Map<IBinder, CameraDescriptor> mCameras = new ArrayMap<>();
 
-    public VirtualCameraController() {
-        connectVirtualCameraService();
-    }
+    public VirtualCameraController() {}
 
     @VisibleForTesting
     VirtualCameraController(IVirtualCameraService virtualCameraService) {
diff --git a/services/companion/lint-baseline.xml b/services/companion/lint-baseline.xml
index 03eae39..020126f 100644
--- a/services/companion/lint-baseline.xml
+++ b/services/companion/lint-baseline.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 7.1.0-dev" type="baseline" client="" dependencies="true" name="" variant="all" version="7.1.0-dev">
+<issues format="6" by="lint 8.4.0-alpha01" type="baseline" client="" dependencies="true" name="" variant="all" version="8.4.0-alpha01">
 
     <issue
         id="NonUserGetterCalled"
@@ -12,4 +12,4 @@
             column="14"/>
     </issue>
 
-</issues>
+</issues>
\ No newline at end of file
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java
index 3a90a95..73ed97f 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java
@@ -35,8 +35,8 @@
 import android.util.LocalLog;
 import android.util.Slog;
 import android.view.contentcapture.ContentCaptureContext;
+import android.view.contentcapture.ContentCaptureSession;
 import android.view.contentcapture.ContentCaptureSessionId;
-import android.view.contentcapture.MainContentCaptureSession;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.os.IResultReceiver;
@@ -123,7 +123,7 @@
     public void setContentCaptureEnabledLocked(boolean enabled) {
         try {
             final Bundle extras = new Bundle();
-            extras.putBoolean(MainContentCaptureSession.EXTRA_ENABLED_STATE, true);
+            extras.putBoolean(ContentCaptureSession.EXTRA_ENABLED_STATE, true);
             mSessionStateReceiver.send(enabled ? RESULT_CODE_TRUE : RESULT_CODE_FALSE, extras);
         } catch (RemoteException e) {
             Slog.w(TAG, "Error async reporting result to client: " + e);
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 5111b08..a6ed498 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -203,6 +203,7 @@
         "com_android_wm_shell_flags_lib",
         "com.android.server.utils_aconfig-java",
         "service-jobscheduler-deviceidle.flags-aconfig-java",
+        "policy_flags_lib",
     ],
     javac_shard_size: 50,
     javacflags: [
@@ -212,6 +213,9 @@
         "-J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED",
         "-J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED",
     ],
+    lint: {
+        baseline_filename: "lint-baseline.xml",
+    },
 }
 
 java_genrule {
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index cac2efb..08093c0 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -1463,4 +1463,9 @@
      */
     @NonNull
     public abstract PackageArchiver getPackageArchiver();
+
+    /**
+     * Returns true if the device is upgrading from an SDK version lower than the one specified.
+     */
+    public abstract boolean isUpgradingFromLowerThan(int sdkVersion);
 }
diff --git a/services/core/java/com/android/server/BootReceiver.java b/services/core/java/com/android/server/BootReceiver.java
index 9f279b1..329aac6 100644
--- a/services/core/java/com/android/server/BootReceiver.java
+++ b/services/core/java/com/android/server/BootReceiver.java
@@ -48,6 +48,8 @@
 import com.android.modules.utils.TypedXmlPullParser;
 import com.android.modules.utils.TypedXmlSerializer;
 import com.android.server.am.DropboxRateLimiter;
+import com.android.server.os.TombstoneProtos;
+import com.android.server.os.TombstoneProtos.Tombstone;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -60,11 +62,14 @@
 import java.io.IOException;
 import java.nio.file.Files;
 import java.nio.file.attribute.PosixFilePermissions;
+import java.util.AbstractMap;
 import java.util.HashMap;
 import java.util.Iterator;
+import java.util.Map;
 import java.util.concurrent.locks.ReentrantLock;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
+import java.util.stream.Collectors;
 
 /**
  * Performs a number of miscellaneous, non-system-critical actions
@@ -332,12 +337,12 @@
      *
      * @param ctx Context
      * @param tombstone path to the tombstone
-     * @param proto whether the tombstone is stored as proto
+     * @param tombstoneProto the parsed proto tombstone
      * @param processName the name of the process corresponding to the tombstone
      * @param tmpFileLock the lock for reading/writing tmp files
      */
     public static void addTombstoneToDropBox(
-                Context ctx, File tombstone, boolean proto, String processName,
+                Context ctx, File tombstone, Tombstone tombstoneProto, String processName,
                 ReentrantLock tmpFileLock) {
         final DropBoxManager db = ctx.getSystemService(DropBoxManager.class);
         if (db == null) {
@@ -347,31 +352,33 @@
 
         // Check if we should rate limit and abort early if needed.
         DropboxRateLimiter.RateLimitResult rateLimitResult =
-                sDropboxRateLimiter.shouldRateLimit(
-                        proto ? TAG_TOMBSTONE_PROTO_WITH_HEADERS : TAG_TOMBSTONE, processName);
+                sDropboxRateLimiter.shouldRateLimit(TAG_TOMBSTONE_PROTO_WITH_HEADERS, processName);
         if (rateLimitResult.shouldRateLimit()) return;
 
         HashMap<String, Long> timestamps = readTimestamps();
         try {
-            if (proto) {
-                if (recordFileTimestamp(tombstone, timestamps)) {
-                    // We need to attach the count indicating the number of dropped dropbox entries
-                    // due to rate limiting. Do this by enclosing the proto tombsstone in a
-                    // container proto that has the dropped entry count and the proto tombstone as
-                    // bytes (to avoid the complexity of reading and writing nested protos).
-                    tmpFileLock.lock();
-                    try {
-                        addAugmentedProtoToDropbox(tombstone, db, rateLimitResult);
-                    } finally {
-                        tmpFileLock.unlock();
-                    }
+            // Remove the memory data from the proto.
+            Tombstone tombstoneProtoWithoutMemory = removeMemoryFromTombstone(tombstoneProto);
+
+            final byte[] tombstoneBytes = tombstoneProtoWithoutMemory.toByteArray();
+
+            // Use JNI to call the c++ proto to text converter and add the headers to the tombstone.
+            String tombstoneWithoutMemory = new StringBuilder(getBootHeadersToLogAndUpdate())
+                    .append(rateLimitResult.createHeader())
+                    .append(getTombstoneText(tombstoneBytes))
+                    .toString();
+
+            // Add the tombstone without memory data to dropbox.
+            db.addText(TAG_TOMBSTONE, tombstoneWithoutMemory);
+
+            // Add the tombstone proto to dropbox.
+            if (recordFileTimestamp(tombstone, timestamps)) {
+                tmpFileLock.lock();
+                try {
+                    addAugmentedProtoToDropbox(tombstone, tombstoneBytes, db, rateLimitResult);
+                } finally {
+                    tmpFileLock.unlock();
                 }
-            } else {
-                // Add the header indicating how many events have been dropped due to rate limiting.
-                final String headers = getBootHeadersToLogAndUpdate()
-                        + rateLimitResult.createHeader();
-                addFileToDropBox(db, timestamps, headers, tombstone.getPath(), LOG_SIZE,
-                                 TAG_TOMBSTONE);
             }
         } catch (IOException e) {
             Slog.e(TAG, "Can't log tombstone", e);
@@ -380,11 +387,8 @@
     }
 
     private static void addAugmentedProtoToDropbox(
-                File tombstone, DropBoxManager db,
+                File tombstone, byte[] tombstoneBytes, DropBoxManager db,
                 DropboxRateLimiter.RateLimitResult rateLimitResult) throws IOException {
-        // Read the proto tombstone file as bytes.
-        final byte[] tombstoneBytes = Files.readAllBytes(tombstone.toPath());
-
         final File tombstoneProtoWithHeaders = File.createTempFile(
                 tombstone.getName(), ".tmp", TOMBSTONE_TMP_DIR);
         Files.setPosixFilePermissions(
@@ -417,6 +421,8 @@
         }
     }
 
+    private static native String getTombstoneText(byte[] tombstoneBytes);
+
     private static void addLastkToDropBox(
             DropBoxManager db, HashMap<String, Long> timestamps,
             String headers, String footers, String filename, int maxSize,
@@ -434,6 +440,31 @@
         addFileWithFootersToDropBox(db, timestamps, headers, footers, filename, maxSize, tag);
     }
 
+    /** Removes memory information from the Tombstone proto. */
+    @VisibleForTesting
+    public static Tombstone removeMemoryFromTombstone(Tombstone tombstoneProto) {
+        Tombstone.Builder tombstoneBuilder = tombstoneProto.toBuilder()
+                .clearMemoryMappings()
+                .clearThreads()
+                .putAllThreads(tombstoneProto.getThreadsMap().entrySet()
+                        .stream()
+                        .map(BootReceiver::clearMemoryDump)
+                        .collect(Collectors.toMap(e->e.getKey(), e->e.getValue())));
+
+        if (tombstoneProto.hasSignalInfo()) {
+            tombstoneBuilder.setSignalInfo(
+                    tombstoneProto.getSignalInfo().toBuilder().clearFaultAdjacentMetadata());
+        }
+
+        return tombstoneBuilder.build();
+    }
+
+    private static AbstractMap.SimpleEntry<Integer, TombstoneProtos.Thread> clearMemoryDump(
+            Map.Entry<Integer, TombstoneProtos.Thread> e) {
+        return new AbstractMap.SimpleEntry<Integer, TombstoneProtos.Thread>(
+            e.getKey(), e.getValue().toBuilder().clearMemoryDump().build());
+    }
+
     private static void addFileToDropBox(
             DropBoxManager db, HashMap<String, Long> timestamps,
             String headers, String filename, int maxSize, String tag) throws IOException {
diff --git a/services/core/java/com/android/server/OWNERS b/services/core/java/com/android/server/OWNERS
index e289a56..e923e30a 100644
--- a/services/core/java/com/android/server/OWNERS
+++ b/services/core/java/com/android/server/OWNERS
@@ -43,3 +43,6 @@
 per-file TelephonyRegistry.java = file:/telephony/OWNERS
 per-file UiModeManagerService.java = file:/packages/SystemUI/OWNERS
 per-file VcnManagementService.java = file:/services/core/java/com/android/server/vcn/OWNERS
+
+# SystemConfig
+per-file SystemConfig.java = file:/PACKAGE_MANAGER_OWNERS
diff --git a/services/core/java/com/android/server/SensitiveContentProtectionManagerService.java b/services/core/java/com/android/server/SensitiveContentProtectionManagerService.java
new file mode 100644
index 0000000..70bd4b3
--- /dev/null
+++ b/services/core/java/com/android/server/SensitiveContentProtectionManagerService.java
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import static com.android.internal.util.Preconditions.checkNotNull;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Context;
+import android.media.projection.MediaProjectionInfo;
+import android.media.projection.MediaProjectionManager;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.service.notification.NotificationListenerService;
+import android.service.notification.NotificationListenerService.RankingMap;
+import android.service.notification.StatusBarNotification;
+import android.util.ArraySet;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.wm.SensitiveContentPackages.PackageInfo;
+import com.android.server.wm.WindowManagerInternal;
+
+import java.util.Collections;
+import java.util.Set;
+
+/**
+ * Service that monitors for notifications with sensitive content and protects content from screen
+ * sharing
+ */
+public final class SensitiveContentProtectionManagerService extends SystemService {
+    private static final String TAG = "SensitiveContentProtect";
+    private static final boolean DEBUG = false;
+
+    @VisibleForTesting
+    NotificationListener mNotificationListener;
+    private @Nullable MediaProjectionManager mProjectionManager;
+    private @Nullable WindowManagerInternal mWindowManager;
+
+    private final MediaProjectionManager.Callback mProjectionCallback =
+            new MediaProjectionManager.Callback() {
+                @Override
+                public void onStart(MediaProjectionInfo info) {
+                    if (DEBUG) Log.d(TAG, "onStart projection: " + info);
+                    onProjectionStart();
+                }
+
+                @Override
+                public void onStop(MediaProjectionInfo info) {
+                    if (DEBUG) Log.d(TAG, "onStop projection: " + info);
+                    onProjectionEnd();
+                }
+            };
+
+    public SensitiveContentProtectionManagerService(@NonNull Context context) {
+        super(context);
+        mNotificationListener = new NotificationListener();
+    }
+
+    @Override
+    public void onStart() {}
+
+    @Override
+    public void onBootPhase(int phase) {
+        if (phase != SystemService.PHASE_BOOT_COMPLETED) {
+            return;
+        }
+
+        if (DEBUG) Log.d(TAG, "onBootPhase - PHASE_BOOT_COMPLETED");
+
+        init(getContext().getSystemService(MediaProjectionManager.class),
+                LocalServices.getService(WindowManagerInternal.class));
+    }
+
+    @VisibleForTesting
+    void init(MediaProjectionManager projectionManager,
+            WindowManagerInternal windowManager) {
+        if (DEBUG) Log.d(TAG, "init");
+
+        checkNotNull(projectionManager, "Failed to get valid MediaProjectionManager");
+        checkNotNull(windowManager, "Failed to get valid WindowManagerInternal");
+
+        mProjectionManager = projectionManager;
+        mWindowManager = windowManager;
+
+        // TODO(b/317250444): use MediaProjectionManagerService directly, reduces unnecessary
+        //  handler, delegate, and binder death recipient
+        mProjectionManager.addCallback(mProjectionCallback, new Handler(Looper.getMainLooper()));
+
+        try {
+            mNotificationListener.registerAsSystemService(getContext(),
+                    new ComponentName(getContext(), NotificationListener.class),
+                    UserHandle.USER_ALL);
+        } catch (RemoteException e) {
+            // Intra-process call, should never happen.
+        }
+    }
+
+    /** Cleanup any callbacks and listeners */
+    @VisibleForTesting
+    void onDestroy() {
+        if (mProjectionManager != null) {
+            mProjectionManager.removeCallback(mProjectionCallback);
+        }
+
+        try {
+            mNotificationListener.unregisterAsSystemService();
+        } catch (RemoteException e) {
+            // Intra-process call, should never happen.
+        }
+
+        if (mWindowManager != null) {
+            onProjectionEnd();
+        }
+    }
+
+    private void onProjectionStart() {
+        StatusBarNotification[] notifications;
+        try {
+            notifications = mNotificationListener.getActiveNotifications();
+        } catch (SecurityException e) {
+            Log.e(TAG, "SensitiveContentProtectionManagerService doesn't have access.", e);
+            notifications = new StatusBarNotification[0];
+        }
+
+        RankingMap rankingMap;
+        try {
+            rankingMap = mNotificationListener.getCurrentRanking();
+        } catch (SecurityException e) {
+            Log.e(TAG, "SensitiveContentProtectionManagerService doesn't have access.", e);
+            rankingMap = null;
+        }
+
+        // notify windowmanager of any currently posted sensitive content notifications
+        Set<PackageInfo> packageInfos = getSensitivePackagesFromNotifications(
+                notifications,
+                rankingMap);
+
+        mWindowManager.setShouldBlockScreenCaptureForApp(packageInfos);
+    }
+
+    private void onProjectionEnd() {
+        // notify windowmanager to clear any sensitive notifications observed during projection
+        // session
+        mWindowManager.setShouldBlockScreenCaptureForApp(Collections.emptySet());
+    }
+
+    private Set<PackageInfo> getSensitivePackagesFromNotifications(
+            StatusBarNotification[] notifications, RankingMap rankingMap) {
+        if (rankingMap == null) {
+            Log.w(TAG, "Ranking map not initialized.");
+            return Collections.emptySet();
+        }
+
+        Set<PackageInfo> sensitivePackages = new ArraySet<>();
+        for (StatusBarNotification sbn : notifications) {
+            NotificationListenerService.Ranking ranking =
+                    rankingMap.getRawRankingObject(sbn.getKey());
+            if (ranking != null && ranking.hasSensitiveContent()) {
+                PackageInfo info = new PackageInfo(sbn.getPackageName(), sbn.getUid());
+                sensitivePackages.add(info);
+            }
+        }
+        return sensitivePackages;
+    }
+
+    // TODO(b/317251408): add trigger that updates on onNotificationPosted,
+    //  onNotificationRankingUpdate and onListenerConnected
+    @VisibleForTesting
+    static class NotificationListener extends NotificationListenerService {}
+}
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 39b8643..7a4ac6a 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -1356,8 +1356,8 @@
 
         final int flags = StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE;
         for (UserInfo user : users) {
-            prepareUserStorageInternal(fromVolumeUuid, user.id, user.serialNumber, flags);
-            prepareUserStorageInternal(toVolumeUuid, user.id, user.serialNumber, flags);
+            prepareUserStorageInternal(fromVolumeUuid, user.id, flags);
+            prepareUserStorageInternal(toVolumeUuid, user.id, flags);
         }
     }
 
@@ -3231,12 +3231,12 @@
 
     @android.annotation.EnforcePermission(android.Manifest.permission.STORAGE_INTERNAL)
     @Override
-    public void createUserStorageKeys(int userId, int serialNumber, boolean ephemeral) {
+    public void createUserStorageKeys(int userId, boolean ephemeral) {
 
         super.createUserStorageKeys_enforcePermission();
 
         try {
-            mVold.createUserStorageKeys(userId, serialNumber, ephemeral);
+            mVold.createUserStorageKeys(userId, ephemeral);
             // Since the user's CE key was just created, the user's CE storage is now unlocked.
             synchronized (mLock) {
                 mCeUnlockedUsers.append(userId);
@@ -3276,12 +3276,11 @@
     /* Only for use by LockSettingsService */
     @android.annotation.EnforcePermission(android.Manifest.permission.STORAGE_INTERNAL)
     @Override
-    public void unlockCeStorage(@UserIdInt int userId, int serialNumber, byte[] secret)
-            throws RemoteException {
+    public void unlockCeStorage(@UserIdInt int userId, byte[] secret) throws RemoteException {
         super.unlockCeStorage_enforcePermission();
 
         if (StorageManager.isFileEncrypted()) {
-            mVold.unlockCeStorage(userId, serialNumber, HexDump.toHexString(secret));
+            mVold.unlockCeStorage(userId, HexDump.toHexString(secret));
         }
         synchronized (mLock) {
             mCeUnlockedUsers.append(userId);
@@ -3348,27 +3347,27 @@
                 continue;
             }
 
-            prepareUserStorageInternal(vol.fsUuid, user.id, user.serialNumber, flags);
+            prepareUserStorageInternal(vol.fsUuid, user.id, flags);
         }
     }
 
     @android.annotation.EnforcePermission(android.Manifest.permission.STORAGE_INTERNAL)
     @Override
-    public void prepareUserStorage(String volumeUuid, int userId, int serialNumber, int flags) {
+    public void prepareUserStorage(String volumeUuid, int userId, int flags) {
 
         super.prepareUserStorage_enforcePermission();
 
         try {
-            prepareUserStorageInternal(volumeUuid, userId, serialNumber, flags);
+            prepareUserStorageInternal(volumeUuid, userId, flags);
         } catch (Exception e) {
             throw new RuntimeException(e);
         }
     }
 
-    private void prepareUserStorageInternal(String volumeUuid, int userId, int serialNumber,
-            int flags) throws Exception {
+    private void prepareUserStorageInternal(String volumeUuid, int userId, int flags)
+            throws Exception {
         try {
-            mVold.prepareUserStorage(volumeUuid, userId, serialNumber, flags);
+            mVold.prepareUserStorage(volumeUuid, userId, flags);
             // After preparing user storage, we should check if we should mount data mirror again,
             // and we do it for user 0 only as we only need to do once for all users.
             if (volumeUuid != null) {
diff --git a/services/core/java/com/android/server/SystemConfig.java b/services/core/java/com/android/server/SystemConfig.java
index 40b29d7..3483c1a 100644
--- a/services/core/java/com/android/server/SystemConfig.java
+++ b/services/core/java/com/android/server/SystemConfig.java
@@ -315,6 +315,11 @@
     private final ArraySet<String> mBugreportWhitelistedPackages = new ArraySet<>();
     private final ArraySet<String> mAppDataIsolationWhitelistedApps = new ArraySet<>();
 
+    // These packages will be set as 'prevent disable', where they are no longer possible
+    // for the end user to disable via settings. This flag should only be used for packages
+    // which meet the 'force or keep enabled apps' policy.
+    private final ArrayList<String> mPreventUserDisablePackages = new ArrayList<>();
+
     // Map of packagesNames to userTypes. Stored temporarily until cleared by UserManagerService().
     private ArrayMap<String, Set<String>> mPackageToUserTypeWhitelist = new ArrayMap<>();
     private ArrayMap<String, Set<String>> mPackageToUserTypeBlacklist = new ArrayMap<>();
@@ -504,6 +509,10 @@
         return mAppDataIsolationWhitelistedApps;
     }
 
+    public @NonNull ArrayList<String> getPreventUserDisablePackages() {
+        return mPreventUserDisablePackages;
+    }
+
     /**
      * Gets map of packagesNames to userTypes, dictating on which user types each package should be
      * initially installed, and then removes this map from SystemConfig.
@@ -1309,6 +1318,16 @@
                         }
                         XmlUtils.skipCurrentTag(parser);
                     } break;
+                    case "prevent-disable": {
+                        String pkgname = parser.getAttributeValue(null, "package");
+                        if (pkgname == null) {
+                            Slog.w(TAG, "<" + name + "> without package in " + permFile
+                                    + " at " + parser.getPositionDescription());
+                        } else {
+                            mPreventUserDisablePackages.add(pkgname);
+                        }
+                        XmlUtils.skipCurrentTag(parser);
+                    } break;
                     case "install-in-user-type": {
                         // NB: We allow any directory permission to declare install-in-user-type.
                         readInstallInUserType(parser,
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 9eb35fd..eb6fdd7 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -101,6 +101,7 @@
 import com.android.internal.telephony.IPhoneStateListener;
 import com.android.internal.telephony.ITelephonyRegistry;
 import com.android.internal.telephony.TelephonyPermissions;
+import com.android.internal.telephony.flags.Flags;
 import com.android.internal.telephony.util.TelephonyUtils;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.DumpUtils;
@@ -2679,6 +2680,14 @@
         if (!checkNotifyPermission("notifyEmergencyNumberList()")) {
             return;
         }
+        if (Flags.enforceTelephonyFeatureMappingForPublicApis()) {
+            if (!mContext.getPackageManager().hasSystemFeature(
+                    PackageManager.FEATURE_TELEPHONY_CALLING)) {
+                // TelephonyManager.getEmergencyNumberList() throws an exception if
+                // FEATURE_TELEPHONY_CALLING is not defined.
+                return;
+            }
+        }
 
         synchronized (mRecords) {
             if (validatePhoneId(phoneId)) {
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 85abf87..130a733 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -3152,6 +3152,7 @@
                                     new AccountAuthenticatorResponse(this),
                                     authTokenType,
                                     true);
+                            mCanStartAccountManagerActivity = true;
                             Bundle bundle = new Bundle();
                             bundle.putParcelable(AccountManager.KEY_INTENT, intent);
                             onResult(bundle);
@@ -4933,6 +4934,7 @@
         IAccountAuthenticator mAuthenticator = null;
 
         private final boolean mStripAuthTokenFromResult;
+        protected boolean mCanStartAccountManagerActivity = false;
         protected final UserAccounts mAccounts;
 
         public Session(UserAccounts accounts, IAccountManagerResponse response, String accountType,
@@ -5068,9 +5070,13 @@
 
         private boolean isExportedSystemActivity(ActivityInfo activityInfo) {
             String className = activityInfo.name;
-            return "android".equals(activityInfo.packageName) &&
-                    (GrantCredentialsPermissionActivity.class.getName().equals(className)
-                    || CantAddAccountActivity.class.getName().equals(className));
+            if (!"android".equals(activityInfo.packageName)) {
+                return false;
+
+            }
+            return (mCanStartAccountManagerActivity
+                    && GrantCredentialsPermissionActivity.class.getName().equals(className))
+                    || CantAddAccountActivity.class.getName().equals(className);
         }
 
         private void close() {
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index d461643..0cff8b7 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -303,6 +303,23 @@
     @Retention(RetentionPolicy.SOURCE)
     @interface FgsStopReason {}
 
+    /**
+     * Disables foreground service background starts from BOOT_COMPLETED broadcasts for all types
+     * except:
+     * <ul>
+     *     <li>{@link android.content.pm.ServiceInfo#FOREGROUND_SERVICE_TYPE_LOCATION}</li>
+     *     <li>{@link android.content.pm.ServiceInfo#FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE}</li>
+     *     <li>{@link android.content.pm.ServiceInfo#FOREGROUND_SERVICE_TYPE_REMOTE_MESSAGING}</li>
+     *     <li>{@link android.content.pm.ServiceInfo#FOREGROUND_SERVICE_TYPE_HEALTH}</li>
+     *     <li>{@link android.content.pm.ServiceInfo#FOREGROUND_SERVICE_TYPE_SYSTEM_EXEMPTED}</li>
+     *     <li>{@link android.content.pm.ServiceInfo#FOREGROUND_SERVICE_TYPE_SPECIAL_USE}</li>
+     * </ul>
+     */
+    @ChangeId
+    @EnabledSince(targetSdkVersion = VERSION_CODES.VANILLA_ICE_CREAM)
+    @Overridable
+    public static final long FGS_BOOT_COMPLETED_RESTRICTIONS = 296558535L;
+
     final ActivityManagerService mAm;
 
     // Maximum number of services that we allow to start in the background
@@ -1053,6 +1070,20 @@
         }
     }
 
+    private boolean shouldAllowBootCompletedStart(ServiceRecord r, int foregroundServiceType) {
+        @PowerExemptionManager.ReasonCode final int fgsStartReasonCode =
+                r.mInfoTempFgsAllowListReason != null ? r.mInfoTempFgsAllowListReason.mReasonCode
+                                                      : REASON_DENIED;
+        if (Flags.fgsBootCompleted()
+                && CompatChanges.isChangeEnabled(FGS_BOOT_COMPLETED_RESTRICTIONS, r.appInfo.uid)
+                && fgsStartReasonCode == PowerExemptionManager.REASON_BOOT_COMPLETED) {
+            // Filter through types
+            return ((foregroundServiceType & mAm.mConstants.FGS_BOOT_COMPLETED_ALLOWLIST) != 0);
+        }
+        // Not BOOT_COMPLETED
+        return true;
+    }
+
     private ComponentName startServiceInnerLocked(ServiceRecord r, Intent service,
             int callingUid, int callingPid, String callingProcessName,
             int callingProcessState, boolean fgRequired, boolean callerFg,
@@ -2101,6 +2132,12 @@
                 mServiceFGAnrTimer.cancel(r);
             }
 
+            if (!shouldAllowBootCompletedStart(r, foregroundServiceType)) {
+                throw new ForegroundServiceStartNotAllowedException("FGS type "
+                        + ServiceInfo.foregroundServiceTypeToLabel(foregroundServiceType)
+                        + " not allowed to start from BOOT_COMPLETED!");
+            }
+
             final ProcessServiceRecord psr = r.app.mServices;
             try {
                 boolean ignoreForeground = false;
@@ -4427,6 +4464,12 @@
                     }
                 }
                 if (userId > 0) {
+                    if (mAm.isSystemUserOnly(sInfo.flags)) {
+                        Slog.w(TAG_SERVICE, service + " is only available for the SYSTEM user,"
+                                + " calling userId is: " + userId);
+                        return null;
+                    }
+
                     if (mAm.isSingleton(sInfo.processName, sInfo.applicationInfo,
                             sInfo.name, sInfo.flags)
                             && mAm.isValidSingletonCall(callingUid, sInfo.applicationInfo.uid)) {
@@ -5416,7 +5459,7 @@
         // Force an immediate oomAdjUpdate, so the client app could be in the correct process state
         // before doing any service related transactions
         mAm.enqueueOomAdjTargetLocked(app);
-        mAm.updateOomAdjLocked(app, OOM_ADJ_REASON_START_SERVICE);
+        mAm.updateOomAdjPendingTargetsLocked(OOM_ADJ_REASON_START_SERVICE);
 
         boolean created = false;
         try {
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index 1d69905..72e62c3 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -16,6 +16,12 @@
 
 package com.android.server.am;
 
+import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE;
+import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_HEALTH;
+import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION;
+import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_REMOTE_MESSAGING;
+import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE;
+import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_SYSTEM_EXEMPTED;
 import static android.os.PowerExemptionManager.TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED;
 import static android.os.PowerExemptionManager.TEMPORARY_ALLOW_LIST_TYPE_NONE;
 
@@ -73,6 +79,9 @@
             = "fgservice_screen_on_before_time";
     private static final String KEY_FGSERVICE_SCREEN_ON_AFTER_TIME
             = "fgservice_screen_on_after_time";
+
+    private static final String KEY_FGS_BOOT_COMPLETED_ALLOWLIST = "fgs_boot_completed_allowlist";
+
     private static final String KEY_CONTENT_PROVIDER_RETAIN_TIME = "content_provider_retain_time";
     private static final String KEY_GC_TIMEOUT = "gc_timeout";
     private static final String KEY_GC_MIN_INTERVAL = "gc_min_interval";
@@ -166,6 +175,15 @@
     private static final long DEFAULT_FGSERVICE_MIN_REPORT_TIME = 3*1000;
     private static final long DEFAULT_FGSERVICE_SCREEN_ON_BEFORE_TIME = 1*1000;
     private static final long DEFAULT_FGSERVICE_SCREEN_ON_AFTER_TIME = 5*1000;
+
+    private static final int DEFAULT_FGS_BOOT_COMPLETED_ALLOWLIST =
+            FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE
+                | FOREGROUND_SERVICE_TYPE_HEALTH
+                | FOREGROUND_SERVICE_TYPE_REMOTE_MESSAGING
+                | FOREGROUND_SERVICE_TYPE_SYSTEM_EXEMPTED
+                | FOREGROUND_SERVICE_TYPE_SPECIAL_USE
+                | FOREGROUND_SERVICE_TYPE_LOCATION;
+
     private static final long DEFAULT_CONTENT_PROVIDER_RETAIN_TIME = 20*1000;
     private static final long DEFAULT_GC_TIMEOUT = 5*1000;
     private static final long DEFAULT_GC_MIN_INTERVAL = 60*1000;
@@ -446,6 +464,9 @@
     // on until we will stop reporting it.
     public long FGSERVICE_SCREEN_ON_AFTER_TIME = DEFAULT_FGSERVICE_SCREEN_ON_AFTER_TIME;
 
+    // Allow-list for FGS types that are allowed to start from BOOT_COMPLETED.
+    public int FGS_BOOT_COMPLETED_ALLOWLIST = DEFAULT_FGS_BOOT_COMPLETED_ALLOWLIST;
+
     // How long we will retain processes hosting content providers in the "last activity"
     // state before allowing them to drop down to the regular cached LRU list.  This is
     // to avoid thrashing of provider processes under low memory situations.
@@ -629,6 +650,10 @@
     // foreground service background start restriction.
     volatile boolean mFgsStartRestrictionNotificationEnabled = false;
 
+    // Indicates whether PSS profiling in AppProfiler is force-enabled, even if RSS is used by
+    // default. Controlled by Settings.Global.FORCE_ENABLE_PSS_PROFILING
+    volatile boolean mForceEnablePssProfiling = false;
+
     /**
      * Indicates whether the foreground service background start restriction is enabled for
      * caller app that is targeting S+.
@@ -958,6 +983,9 @@
     private static final Uri ENABLE_AUTOMATIC_SYSTEM_SERVER_HEAP_DUMPS_URI =
             Settings.Global.getUriFor(Settings.Global.ENABLE_AUTOMATIC_SYSTEM_SERVER_HEAP_DUMPS);
 
+    private static final Uri FORCE_ENABLE_PSS_PROFILING_URI =
+            Settings.Global.getUriFor(Settings.Global.FORCE_ENABLE_PSS_PROFILING);
+
     /**
      * The threshold to decide if a given association should be dumped into metrics.
      */
@@ -1368,6 +1396,7 @@
             mResolver.registerContentObserver(ENABLE_AUTOMATIC_SYSTEM_SERVER_HEAP_DUMPS_URI,
                     false, this);
         }
+        mResolver.registerContentObserver(FORCE_ENABLE_PSS_PROFILING_URI, false, this);
         updateConstants();
         if (mSystemServerAutomaticHeapDumpEnabled) {
             updateEnableAutomaticSystemServerHeapDumps();
@@ -1383,6 +1412,7 @@
         // The following read from Settings.
         updateActivityStartsLoggingEnabled();
         updateForegroundServiceStartsLoggingEnabled();
+        updateForceEnablePssProfiling();
         // Read DropboxRateLimiter params from flags.
         mService.initDropboxRateLimiter();
     }
@@ -1424,6 +1454,8 @@
             updateForegroundServiceStartsLoggingEnabled();
         } else if (ENABLE_AUTOMATIC_SYSTEM_SERVER_HEAP_DUMPS_URI.equals(uri)) {
             updateEnableAutomaticSystemServerHeapDumps();
+        } else if (FORCE_ENABLE_PSS_PROFILING_URI.equals(uri)) {
+            updateForceEnablePssProfiling();
         }
     }
 
@@ -1450,6 +1482,8 @@
                     DEFAULT_FGSERVICE_SCREEN_ON_BEFORE_TIME);
             FGSERVICE_SCREEN_ON_AFTER_TIME = mParser.getLong(KEY_FGSERVICE_SCREEN_ON_AFTER_TIME,
                     DEFAULT_FGSERVICE_SCREEN_ON_AFTER_TIME);
+            FGS_BOOT_COMPLETED_ALLOWLIST = mParser.getInt(KEY_FGS_BOOT_COMPLETED_ALLOWLIST,
+                    DEFAULT_FGS_BOOT_COMPLETED_ALLOWLIST);
             CONTENT_PROVIDER_RETAIN_TIME = mParser.getLong(KEY_CONTENT_PROVIDER_RETAIN_TIME,
                     DEFAULT_CONTENT_PROVIDER_RETAIN_TIME);
             GC_TIMEOUT = mParser.getLong(KEY_GC_TIMEOUT,
@@ -1536,6 +1570,11 @@
                 Settings.Global.ACTIVITY_STARTS_LOGGING_ENABLED, 1) == 1;
     }
 
+    private void updateForceEnablePssProfiling() {
+        mForceEnablePssProfiling = Settings.Global.getInt(mResolver,
+                Settings.Global.FORCE_ENABLE_PSS_PROFILING, 0) == 1;
+    }
+
     private void updateBackgroundActivityStarts() {
         mFlagBackgroundActivityStartsEnabled = DeviceConfig.getBoolean(
                 DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
@@ -2091,6 +2130,8 @@
         pw.println(FGSERVICE_SCREEN_ON_BEFORE_TIME);
         pw.print("  "); pw.print(KEY_FGSERVICE_SCREEN_ON_AFTER_TIME); pw.print("=");
         pw.println(FGSERVICE_SCREEN_ON_AFTER_TIME);
+        pw.print("  "); pw.print(KEY_FGS_BOOT_COMPLETED_ALLOWLIST); pw.print("=");
+        pw.println(FGS_BOOT_COMPLETED_ALLOWLIST);
         pw.print("  "); pw.print(KEY_CONTENT_PROVIDER_RETAIN_TIME); pw.print("=");
         pw.println(CONTENT_PROVIDER_RETAIN_TIME);
         pw.print("  "); pw.print(KEY_GC_TIMEOUT); pw.print("=");
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index f8451fd..0c70bfe 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -327,7 +327,6 @@
 import android.os.DropBoxManager;
 import android.os.FactoryTest;
 import android.os.FileUtils;
-import android.os.Flags;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.IDeviceIdentifiersPolicyService;
@@ -1222,7 +1221,7 @@
      * by the user ID the sticky is for, and can include UserHandle.USER_ALL
      * for stickies that are sent to all users.
      */
-    @GuardedBy("this")
+    @GuardedBy("mStickyBroadcasts")
     final SparseArray<ArrayMap<String, ArrayList<StickyBroadcast>>> mStickyBroadcasts =
             new SparseArray<>();
 
@@ -4399,7 +4398,9 @@
 
         if (packageName == null) {
             // Remove all sticky broadcasts from this user.
-            mStickyBroadcasts.remove(userId);
+            synchronized (mStickyBroadcasts) {
+                mStickyBroadcasts.remove(userId);
+            }
         }
 
         ArrayList<ContentProviderRecord> providers = new ArrayList<>();
@@ -8608,7 +8609,7 @@
                         final long initialIdlePssOrRss, lastPssOrRss, lastSwapPss;
                         synchronized (mAppProfiler.mProfilerLock) {
                             initialIdlePssOrRss = pr.getInitialIdlePssOrRss();
-                            lastPssOrRss = !Flags.removeAppProfilerPssCollection()
+                            lastPssOrRss = mAppProfiler.isProfilingPss()
                                     ? pr.getLastPss() : pr.getLastRss();
                             lastSwapPss = pr.getLastSwapPss();
                         }
@@ -8618,14 +8619,14 @@
                             final StringBuilder sb2 = new StringBuilder(128);
                             sb2.append("Kill");
                             sb2.append(proc.processName);
-                            if (!Flags.removeAppProfilerPssCollection()) {
+                            if (mAppProfiler.isProfilingPss()) {
                                 sb2.append(" in idle maint: pss=");
                             } else {
                                 sb2.append(" in idle maint: rss=");
                             }
                             sb2.append(lastPssOrRss);
 
-                            if (!Flags.removeAppProfilerPssCollection()) {
+                            if (mAppProfiler.isProfilingPss()) {
                                 sb2.append(", swapPss=");
                                 sb2.append(lastSwapPss);
                                 sb2.append(", initialPss=");
@@ -8640,7 +8641,7 @@
                             Slog.wtfQuiet(TAG, sb2.toString());
                             mHandler.post(() -> {
                                 synchronized (ActivityManagerService.this) {
-                                    proc.killLocked(!Flags.removeAppProfilerPssCollection()
+                                    proc.killLocked(mAppProfiler.isProfilingPss()
                                             ? "idle maint (pss " : "idle maint (rss " + lastPssOrRss
                                             + " from " + initialIdlePssOrRss + ")",
                                             ApplicationExitInfo.REASON_OTHER,
@@ -11336,20 +11337,23 @@
         for (BroadcastQueue q : mBroadcastQueues) {
             q.dumpDebug(proto, ActivityManagerServiceDumpBroadcastsProto.BROADCAST_QUEUE);
         }
-        for (int user=0; user<mStickyBroadcasts.size(); user++) {
-            long token = proto.start(ActivityManagerServiceDumpBroadcastsProto.STICKY_BROADCASTS);
-            proto.write(StickyBroadcastProto.USER, mStickyBroadcasts.keyAt(user));
-            for (Map.Entry<String, ArrayList<StickyBroadcast>> ent
-                    : mStickyBroadcasts.valueAt(user).entrySet()) {
-                long actionToken = proto.start(StickyBroadcastProto.ACTIONS);
-                proto.write(StickyBroadcastProto.StickyAction.NAME, ent.getKey());
-                for (StickyBroadcast broadcast : ent.getValue()) {
-                    broadcast.intent.dumpDebug(proto, StickyBroadcastProto.StickyAction.INTENTS,
-                            false, true, true, false);
+        synchronized (mStickyBroadcasts) {
+            for (int user = 0; user < mStickyBroadcasts.size(); user++) {
+                long token = proto.start(
+                        ActivityManagerServiceDumpBroadcastsProto.STICKY_BROADCASTS);
+                proto.write(StickyBroadcastProto.USER, mStickyBroadcasts.keyAt(user));
+                for (Map.Entry<String, ArrayList<StickyBroadcast>> ent
+                        : mStickyBroadcasts.valueAt(user).entrySet()) {
+                    long actionToken = proto.start(StickyBroadcastProto.ACTIONS);
+                    proto.write(StickyBroadcastProto.StickyAction.NAME, ent.getKey());
+                    for (StickyBroadcast broadcast : ent.getValue()) {
+                        broadcast.intent.dumpDebug(proto, StickyBroadcastProto.StickyAction.INTENTS,
+                                false, true, true, false);
+                    }
+                    proto.end(actionToken);
                 }
-                proto.end(actionToken);
+                proto.end(token);
             }
-            proto.end(token);
         }
 
         long handlerToken = proto.start(ActivityManagerServiceDumpBroadcastsProto.HANDLER);
@@ -11489,45 +11493,50 @@
 
         needSep = true;
 
-        if (!onlyHistory && !onlyReceivers && mStickyBroadcasts != null && dumpPackage == null) {
-            for (int user=0; user<mStickyBroadcasts.size(); user++) {
-                if (needSep) {
-                    pw.println();
-                }
-                needSep = true;
-                printedAnything = true;
-                pw.print("  Sticky broadcasts for user ");
-                        pw.print(mStickyBroadcasts.keyAt(user)); pw.println(":");
-                StringBuilder sb = new StringBuilder(128);
-                for (Map.Entry<String, ArrayList<StickyBroadcast>> ent
-                        : mStickyBroadcasts.valueAt(user).entrySet()) {
-                    pw.print("  * Sticky action "); pw.print(ent.getKey());
-                    if (dumpAll) {
-                        pw.println(":");
-                        ArrayList<StickyBroadcast> broadcasts = ent.getValue();
-                        final int N = broadcasts.size();
-                        for (int i=0; i<N; i++) {
-                            final Intent intent = broadcasts.get(i).intent;
-                            final boolean deferUntilActive = broadcasts.get(i).deferUntilActive;
-                            sb.setLength(0);
-                            sb.append("    Intent: ");
-                            intent.toShortString(sb, false, true, false, false);
-                            pw.print(sb);
-                            if (deferUntilActive) {
-                                pw.print(" [D]");
+        synchronized (mStickyBroadcasts) {
+            if (!onlyHistory && !onlyReceivers && mStickyBroadcasts != null
+                    && dumpPackage == null) {
+                for (int user = 0; user < mStickyBroadcasts.size(); user++) {
+                    if (needSep) {
+                        pw.println();
+                    }
+                    needSep = true;
+                    printedAnything = true;
+                    pw.print("  Sticky broadcasts for user ");
+                    pw.print(mStickyBroadcasts.keyAt(user));
+                    pw.println(":");
+                    StringBuilder sb = new StringBuilder(128);
+                    for (Map.Entry<String, ArrayList<StickyBroadcast>> ent
+                            : mStickyBroadcasts.valueAt(user).entrySet()) {
+                        pw.print("  * Sticky action ");
+                        pw.print(ent.getKey());
+                        if (dumpAll) {
+                            pw.println(":");
+                            ArrayList<StickyBroadcast> broadcasts = ent.getValue();
+                            final int N = broadcasts.size();
+                            for (int i = 0; i < N; i++) {
+                                final Intent intent = broadcasts.get(i).intent;
+                                final boolean deferUntilActive = broadcasts.get(i).deferUntilActive;
+                                sb.setLength(0);
+                                sb.append("    Intent: ");
+                                intent.toShortString(sb, false, true, false, false);
+                                pw.print(sb);
+                                if (deferUntilActive) {
+                                    pw.print(" [D]");
+                                }
+                                pw.println();
+                                pw.print("      originalCallingUid: ");
+                                pw.println(broadcasts.get(i).originalCallingUid);
+                                pw.println();
+                                Bundle bundle = intent.getExtras();
+                                if (bundle != null) {
+                                    pw.print("      extras: ");
+                                    pw.println(bundle);
+                                }
                             }
-                            pw.println();
-                            pw.print("      originalCallingUid: ");
-                            pw.println(broadcasts.get(i).originalCallingUid);
-                            pw.println();
-                            Bundle bundle = intent.getExtras();
-                            if (bundle != null) {
-                                pw.print("      extras: ");
-                                pw.println(bundle);
-                            }
+                        } else {
+                            pw.println("");
                         }
-                    } else {
-                        pw.println("");
                     }
                 }
             }
@@ -13738,6 +13747,11 @@
         return result;
     }
 
+    boolean isSystemUserOnly(int flags) {
+        return android.multiuser.Flags.enableSystemUserOnlyForServicesAndProviders()
+                && (flags & ServiceInfo.FLAG_SYSTEM_USER_ONLY) != 0;
+    }
+
     /**
      * Checks to see if the caller is in the same app as the singleton
      * component, or the component is in a special app. It allows special apps
@@ -14192,7 +14206,7 @@
         int callingUid;
         int callingPid;
         boolean instantApp;
-        synchronized(this) {
+        synchronized (mProcLock) {
             callerApp = getRecordForAppLOSP(caller);
             if (callerApp == null) {
                 Slog.w(TAG, "registerReceiverWithFeature: no app for " + caller);
@@ -14208,57 +14222,59 @@
             callingPid = callerApp.getPid();
 
             instantApp = isInstantApp(callerApp, callerPackage, callingUid);
-            userId = mUserController.handleIncomingUser(callingPid, callingUid, userId, true,
-                    ALLOW_FULL_ONLY, "registerReceiver", callerPackage);
+        }
+        userId = mUserController.handleIncomingUser(callingPid, callingUid, userId, true,
+                ALLOW_FULL_ONLY, "registerReceiver", callerPackage);
 
-            // Warn if system internals are registering for important broadcasts
-            // without also using a priority to ensure they process the event
-            // before normal apps hear about it
-            if (UserHandle.isCore(callingUid)) {
-                final int priority = filter.getPriority();
-                final boolean systemPriority = (priority >= IntentFilter.SYSTEM_HIGH_PRIORITY)
-                        || (priority <= IntentFilter.SYSTEM_LOW_PRIORITY);
-                if (!systemPriority) {
-                    final int N = filter.countActions();
-                    for (int i = 0; i < N; i++) {
-                        // TODO: expand to additional important broadcasts over time
-                        final String action = filter.getAction(i);
-                        if (action.startsWith("android.intent.action.USER_")
-                                || action.startsWith("android.intent.action.PACKAGE_")
-                                || action.startsWith("android.intent.action.UID_")
-                                || action.startsWith("android.intent.action.EXTERNAL_")
-                                || action.startsWith("android.bluetooth.")
-                                || action.equals(Intent.ACTION_SHUTDOWN)) {
-                            if (DEBUG_BROADCAST) {
-                                Slog.wtf(TAG,
-                                        "System internals registering for " + filter.toLongString()
-                                                + " with app priority; this will race with apps!",
-                                        new Throwable());
-                            }
-
-                            // When undefined, assume that system internals need
-                            // to hear about the event first; they can use
-                            // SYSTEM_LOW_PRIORITY if they need to hear last
-                            if (priority == 0) {
-                                filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
-                            }
-                            break;
+        // Warn if system internals are registering for important broadcasts
+        // without also using a priority to ensure they process the event
+        // before normal apps hear about it
+        if (UserHandle.isCore(callingUid)) {
+            final int priority = filter.getPriority();
+            final boolean systemPriority = (priority >= IntentFilter.SYSTEM_HIGH_PRIORITY)
+                    || (priority <= IntentFilter.SYSTEM_LOW_PRIORITY);
+            if (!systemPriority) {
+                final int N = filter.countActions();
+                for (int i = 0; i < N; i++) {
+                    // TODO: expand to additional important broadcasts over time
+                    final String action = filter.getAction(i);
+                    if (action.startsWith("android.intent.action.USER_")
+                            || action.startsWith("android.intent.action.PACKAGE_")
+                            || action.startsWith("android.intent.action.UID_")
+                            || action.startsWith("android.intent.action.EXTERNAL_")
+                            || action.startsWith("android.bluetooth.")
+                            || action.equals(Intent.ACTION_SHUTDOWN)) {
+                        if (DEBUG_BROADCAST) {
+                            Slog.wtf(TAG,
+                                    "System internals registering for " + filter.toLongString()
+                                            + " with app priority; this will race with apps!",
+                                    new Throwable());
                         }
+
+                        // When undefined, assume that system internals need
+                        // to hear about the event first; they can use
+                        // SYSTEM_LOW_PRIORITY if they need to hear last
+                        if (priority == 0) {
+                            filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
+                        }
+                        break;
                     }
                 }
             }
+        }
 
-            Iterator<String> actions = filter.actionsIterator();
-            if (actions == null) {
-                ArrayList<String> noAction = new ArrayList<String>(1);
-                noAction.add(null);
-                actions = noAction.iterator();
-            }
-            boolean onlyProtectedBroadcasts = true;
+        Iterator<String> actions = filter.actionsIterator();
+        if (actions == null) {
+            ArrayList<String> noAction = new ArrayList<String>(1);
+            noAction.add(null);
+            actions = noAction.iterator();
+        }
+        boolean onlyProtectedBroadcasts = true;
 
-            // Collect stickies of users and check if broadcast is only registered for protected
-            // broadcasts
-            int[] userIds = { UserHandle.USER_ALL, UserHandle.getUserId(callingUid) };
+        // Collect stickies of users and check if broadcast is only registered for protected
+        // broadcasts
+        int[] userIds = { UserHandle.USER_ALL, UserHandle.getUserId(callingUid) };
+        synchronized (mStickyBroadcasts) {
             while (actions.hasNext()) {
                 String action = actions.next();
                 for (int id : userIds) {
@@ -14284,68 +14300,68 @@
                     }
                 }
             }
+        }
 
-            if (Process.isSdkSandboxUid(Binder.getCallingUid())) {
-                SdkSandboxManagerLocal sdkSandboxManagerLocal =
-                        LocalManagerRegistry.getManager(SdkSandboxManagerLocal.class);
-                if (sdkSandboxManagerLocal == null) {
-                    throw new IllegalStateException("SdkSandboxManagerLocal not found when checking"
-                            + " whether SDK sandbox uid can register to broadcast receivers.");
-                }
-                if (!sdkSandboxManagerLocal.canRegisterBroadcastReceiver(
-                        /*IntentFilter=*/ filter, flags, onlyProtectedBroadcasts)) {
-                    throw new SecurityException("SDK sandbox not allowed to register receiver"
-                            + " with the given IntentFilter: " + filter.toLongString());
-                }
+        if (Process.isSdkSandboxUid(Binder.getCallingUid())) {
+            SdkSandboxManagerLocal sdkSandboxManagerLocal =
+                    LocalManagerRegistry.getManager(SdkSandboxManagerLocal.class);
+            if (sdkSandboxManagerLocal == null) {
+                throw new IllegalStateException("SdkSandboxManagerLocal not found when checking"
+                        + " whether SDK sandbox uid can register to broadcast receivers.");
             }
-
-            // If the change is enabled, but neither exported or not exported is set, we need to log
-            // an error so the consumer can know to explicitly set the value for their flag.
-            // If the caller is registering for a sticky broadcast with a null receiver, we won't
-            // require a flag
-            final boolean explicitExportStateDefined =
-                    (flags & (Context.RECEIVER_EXPORTED | Context.RECEIVER_NOT_EXPORTED)) != 0;
-            if (((flags & Context.RECEIVER_EXPORTED) != 0) && (
-                    (flags & Context.RECEIVER_NOT_EXPORTED) != 0)) {
-                throw new IllegalArgumentException(
-                        "Receiver can't specify both RECEIVER_EXPORTED and RECEIVER_NOT_EXPORTED"
-                                + "flag");
+            if (!sdkSandboxManagerLocal.canRegisterBroadcastReceiver(
+                    /*IntentFilter=*/ filter, flags, onlyProtectedBroadcasts)) {
+                throw new SecurityException("SDK sandbox not allowed to register receiver"
+                        + " with the given IntentFilter: " + filter.toLongString());
             }
+        }
 
-            // Don't enforce the flag check if we're EITHER registering for only protected
-            // broadcasts, or the receiver is null (a sticky broadcast). Sticky broadcasts should
-            // not be used generally, so we will be marking them as exported by default
-            boolean requireExplicitFlagForDynamicReceivers = CompatChanges.isChangeEnabled(
-                    DYNAMIC_RECEIVER_EXPLICIT_EXPORT_REQUIRED, callingUid);
+        // If the change is enabled, but neither exported or not exported is set, we need to log
+        // an error so the consumer can know to explicitly set the value for their flag.
+        // If the caller is registering for a sticky broadcast with a null receiver, we won't
+        // require a flag
+        final boolean explicitExportStateDefined =
+                (flags & (Context.RECEIVER_EXPORTED | Context.RECEIVER_NOT_EXPORTED)) != 0;
+        if (((flags & Context.RECEIVER_EXPORTED) != 0) && (
+                (flags & Context.RECEIVER_NOT_EXPORTED) != 0)) {
+            throw new IllegalArgumentException(
+                    "Receiver can't specify both RECEIVER_EXPORTED and RECEIVER_NOT_EXPORTED"
+                            + "flag");
+        }
 
-            // A receiver that is visible to instant apps must also be exported.
-            final boolean unexportedReceiverVisibleToInstantApps =
-                    ((flags & Context.RECEIVER_VISIBLE_TO_INSTANT_APPS) != 0) && (
-                            (flags & Context.RECEIVER_NOT_EXPORTED) != 0);
-            if (unexportedReceiverVisibleToInstantApps && requireExplicitFlagForDynamicReceivers) {
-                throw new IllegalArgumentException(
-                        "Receiver can't specify both RECEIVER_VISIBLE_TO_INSTANT_APPS and "
-                                + "RECEIVER_NOT_EXPORTED flag");
-            }
+        // Don't enforce the flag check if we're EITHER registering for only protected
+        // broadcasts, or the receiver is null (a sticky broadcast). Sticky broadcasts should
+        // not be used generally, so we will be marking them as exported by default
+        boolean requireExplicitFlagForDynamicReceivers = CompatChanges.isChangeEnabled(
+                DYNAMIC_RECEIVER_EXPLICIT_EXPORT_REQUIRED, callingUid);
 
-            if (!onlyProtectedBroadcasts) {
-                if (receiver == null && !explicitExportStateDefined) {
-                    // sticky broadcast, no flag specified (flag isn't required)
-                    flags |= Context.RECEIVER_EXPORTED;
-                } else if (requireExplicitFlagForDynamicReceivers && !explicitExportStateDefined) {
-                    throw new SecurityException(
-                            callerPackage + ": One of RECEIVER_EXPORTED or "
-                                    + "RECEIVER_NOT_EXPORTED should be specified when a receiver "
-                                    + "isn't being registered exclusively for system broadcasts");
-                    // Assume default behavior-- flag check is not enforced
-                } else if (!requireExplicitFlagForDynamicReceivers && (
-                        (flags & Context.RECEIVER_NOT_EXPORTED) == 0)) {
-                    // Change is not enabled, assume exported unless otherwise specified.
-                    flags |= Context.RECEIVER_EXPORTED;
-                }
-            } else if ((flags & Context.RECEIVER_NOT_EXPORTED) == 0) {
+        // A receiver that is visible to instant apps must also be exported.
+        final boolean unexportedReceiverVisibleToInstantApps =
+                ((flags & Context.RECEIVER_VISIBLE_TO_INSTANT_APPS) != 0) && (
+                        (flags & Context.RECEIVER_NOT_EXPORTED) != 0);
+        if (unexportedReceiverVisibleToInstantApps && requireExplicitFlagForDynamicReceivers) {
+            throw new IllegalArgumentException(
+                    "Receiver can't specify both RECEIVER_VISIBLE_TO_INSTANT_APPS and "
+                            + "RECEIVER_NOT_EXPORTED flag");
+        }
+
+        if (!onlyProtectedBroadcasts) {
+            if (receiver == null && !explicitExportStateDefined) {
+                // sticky broadcast, no flag specified (flag isn't required)
+                flags |= Context.RECEIVER_EXPORTED;
+            } else if (requireExplicitFlagForDynamicReceivers && !explicitExportStateDefined) {
+                throw new SecurityException(
+                        callerPackage + ": One of RECEIVER_EXPORTED or "
+                                + "RECEIVER_NOT_EXPORTED should be specified when a receiver "
+                                + "isn't being registered exclusively for system broadcasts");
+                // Assume default behavior-- flag check is not enforced
+            } else if (!requireExplicitFlagForDynamicReceivers && (
+                    (flags & Context.RECEIVER_NOT_EXPORTED) == 0)) {
+                // Change is not enabled, assume exported unless otherwise specified.
                 flags |= Context.RECEIVER_EXPORTED;
             }
+        } else if ((flags & Context.RECEIVER_NOT_EXPORTED) == 0) {
+            flags |= Context.RECEIVER_EXPORTED;
         }
 
         // Dynamic receivers are exported by default for versions prior to T
@@ -15335,55 +15351,58 @@
                 throw new SecurityException(
                         "Sticky broadcasts can't target a specific component");
             }
-            // We use userId directly here, since the "all" target is maintained
-            // as a separate set of sticky broadcasts.
-            if (userId != UserHandle.USER_ALL) {
-                // But first, if this is not a broadcast to all users, then
-                // make sure it doesn't conflict with an existing broadcast to
-                // all users.
-                ArrayMap<String, ArrayList<StickyBroadcast>> stickies = mStickyBroadcasts.get(
-                        UserHandle.USER_ALL);
-                if (stickies != null) {
-                    ArrayList<StickyBroadcast> list = stickies.get(intent.getAction());
-                    if (list != null) {
-                        int N = list.size();
-                        int i;
-                        for (i=0; i<N; i++) {
-                            if (intent.filterEquals(list.get(i).intent)) {
-                                throw new IllegalArgumentException(
-                                        "Sticky broadcast " + intent + " for user "
-                                        + userId + " conflicts with existing global broadcast");
+            synchronized (mStickyBroadcasts) {
+                // We use userId directly here, since the "all" target is maintained
+                // as a separate set of sticky broadcasts.
+                if (userId != UserHandle.USER_ALL) {
+                    // But first, if this is not a broadcast to all users, then
+                    // make sure it doesn't conflict with an existing broadcast to
+                    // all users.
+                    ArrayMap<String, ArrayList<StickyBroadcast>> stickies = mStickyBroadcasts.get(
+                            UserHandle.USER_ALL);
+                    if (stickies != null) {
+                        ArrayList<StickyBroadcast> list = stickies.get(intent.getAction());
+                        if (list != null) {
+                            int N = list.size();
+                            int i;
+                            for (i = 0; i < N; i++) {
+                                if (intent.filterEquals(list.get(i).intent)) {
+                                    throw new IllegalArgumentException("Sticky broadcast " + intent
+                                            + " for user " + userId
+                                            + " conflicts with existing global broadcast");
+                                }
                             }
                         }
                     }
                 }
-            }
-            ArrayMap<String, ArrayList<StickyBroadcast>> stickies = mStickyBroadcasts.get(userId);
-            if (stickies == null) {
-                stickies = new ArrayMap<>();
-                mStickyBroadcasts.put(userId, stickies);
-            }
-            ArrayList<StickyBroadcast> list = stickies.get(intent.getAction());
-            if (list == null) {
-                list = new ArrayList<>();
-                stickies.put(intent.getAction(), list);
-            }
-            final boolean deferUntilActive = BroadcastRecord.calculateDeferUntilActive(
-                    callingUid, brOptions, resultTo, ordered,
-                    BroadcastRecord.calculateUrgent(intent, brOptions));
-            final int stickiesCount = list.size();
-            int i;
-            for (i = 0; i < stickiesCount; i++) {
-                if (intent.filterEquals(list.get(i).intent)) {
-                    // This sticky already exists, replace it.
-                    list.set(i, StickyBroadcast.create(new Intent(intent), deferUntilActive,
-                            callingUid, callerAppProcessState));
-                    break;
+                ArrayMap<String, ArrayList<StickyBroadcast>> stickies =
+                        mStickyBroadcasts.get(userId);
+                if (stickies == null) {
+                    stickies = new ArrayMap<>();
+                    mStickyBroadcasts.put(userId, stickies);
                 }
-            }
-            if (i >= stickiesCount) {
-                list.add(StickyBroadcast.create(new Intent(intent), deferUntilActive, callingUid,
-                        callerAppProcessState));
+                ArrayList<StickyBroadcast> list = stickies.get(intent.getAction());
+                if (list == null) {
+                    list = new ArrayList<>();
+                    stickies.put(intent.getAction(), list);
+                }
+                final boolean deferUntilActive = BroadcastRecord.calculateDeferUntilActive(
+                        callingUid, brOptions, resultTo, ordered,
+                        BroadcastRecord.calculateUrgent(intent, brOptions));
+                final int stickiesCount = list.size();
+                int i;
+                for (i = 0; i < stickiesCount; i++) {
+                    if (intent.filterEquals(list.get(i).intent)) {
+                        // This sticky already exists, replace it.
+                        list.set(i, StickyBroadcast.create(new Intent(intent), deferUntilActive,
+                                callingUid, callerAppProcessState));
+                        break;
+                    }
+                }
+                if (i >= stickiesCount) {
+                    list.add(StickyBroadcast.create(new Intent(intent), deferUntilActive,
+                            callingUid, callerAppProcessState));
+                }
             }
         }
 
@@ -15620,13 +15639,15 @@
     }
 
     @VisibleForTesting
-    ArrayList<StickyBroadcast> getStickyBroadcasts(String action, int userId) {
-        final ArrayMap<String, ArrayList<StickyBroadcast>> stickyBroadcasts =
-                mStickyBroadcasts.get(userId);
-        if (stickyBroadcasts == null) {
-            return null;
+    ArrayList<StickyBroadcast> getStickyBroadcastsForTest(String action, int userId) {
+        synchronized (mStickyBroadcasts) {
+            final ArrayMap<String, ArrayList<StickyBroadcast>> stickyBroadcasts =
+                    mStickyBroadcasts.get(userId);
+            if (stickyBroadcasts == null) {
+                return null;
+            }
+            return stickyBroadcasts.get(action);
         }
-        return stickyBroadcasts.get(action);
     }
 
     /**
@@ -15791,6 +15812,7 @@
         }
     }
 
+    @Override
     public final void unbroadcastIntent(IApplicationThread caller, Intent intent, int userId) {
         // Refuse possible leaked file descriptors
         if (intent != null && intent.hasFileDescriptors() == true) {
@@ -15800,23 +15822,23 @@
         userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
                 userId, true, ALLOW_NON_FULL, "removeStickyBroadcast", null);
 
-        synchronized(this) {
-            if (checkCallingPermission(android.Manifest.permission.BROADCAST_STICKY)
-                    != PackageManager.PERMISSION_GRANTED) {
-                String msg = "Permission Denial: unbroadcastIntent() from pid="
-                        + Binder.getCallingPid()
-                        + ", uid=" + Binder.getCallingUid()
-                        + " requires " + android.Manifest.permission.BROADCAST_STICKY;
-                Slog.w(TAG, msg);
-                throw new SecurityException(msg);
-            }
+        if (checkCallingPermission(android.Manifest.permission.BROADCAST_STICKY)
+                != PackageManager.PERMISSION_GRANTED) {
+            String msg = "Permission Denial: unbroadcastIntent() from pid="
+                    + Binder.getCallingPid()
+                    + ", uid=" + Binder.getCallingUid()
+                    + " requires " + android.Manifest.permission.BROADCAST_STICKY;
+            Slog.w(TAG, msg);
+            throw new SecurityException(msg);
+        }
+        synchronized (mStickyBroadcasts) {
             ArrayMap<String, ArrayList<StickyBroadcast>> stickies = mStickyBroadcasts.get(userId);
             if (stickies != null) {
                 ArrayList<StickyBroadcast> list = stickies.get(intent.getAction());
                 if (list != null) {
                     int N = list.size();
                     int i;
-                    for (i=0; i<N; i++) {
+                    for (i = 0; i < N; i++) {
                         if (intent.filterEquals(list.get(i).intent)) {
                             list.remove(i);
                             break;
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 00dd169..57c52c2 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -30,6 +30,7 @@
 import static android.app.usage.UsageStatsManager.REASON_MAIN_FORCED_BY_USER;
 import static android.app.usage.UsageStatsManager.REASON_SUB_FORCED_SYSTEM_FLAG_UNDEFINED;
 import static android.content.pm.PackageManager.MATCH_ANY_USER;
+import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
 import static android.os.Process.INVALID_UID;
 import static android.view.Display.INVALID_DISPLAY;
 import static android.window.DisplayAreaOrganizer.FEATURE_UNDEFINED;
@@ -401,7 +402,7 @@
                 case "get-bg-restriction-level":
                     return runGetBgRestrictionLevel(pw);
                 case "observe-foreground-process":
-                    return runGetCurrentForegroundProcess(pw, mInternal, mTaskInterface);
+                    return runGetCurrentForegroundProcess(pw, mInternal);
                 case "reset-dropbox-rate-limiter":
                     return runResetDropboxRateLimiter();
                 case "list-displays-for-starting-users":
@@ -555,6 +556,13 @@
                 } else if (opt.equals("--dismiss-keyguard-if-insecure")
                       || opt.equals("--dismiss-keyguard")) {
                     mDismissKeyguardIfInsecure = true;
+                } else if (opt.equals("--allow-fgs-start-reason")) {
+                    final int reasonCode = Integer.parseInt(getNextArgRequired());
+                    mBroadcastOptions = BroadcastOptions.makeBasic();
+                    mBroadcastOptions.setTemporaryAppAllowlist(10_000,
+                            TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
+                            reasonCode,
+                            "");
                 } else {
                     return false;
                 }
@@ -3682,11 +3690,10 @@
         return -1;
     }
 
-    private int runGetCurrentForegroundProcess(PrintWriter pw,
-            IActivityManager iam, IActivityTaskManager iatm)
+    private int runGetCurrentForegroundProcess(PrintWriter pw, IActivityManager iam)
             throws RemoteException {
 
-        ProcessObserver observer = new ProcessObserver(pw, iam, iatm, mInternal);
+        ProcessObserver observer = new ProcessObserver(pw, iam);
         iam.registerProcessObserver(observer);
 
         final InputStream mInput = getRawInputStream();
@@ -3721,15 +3728,10 @@
 
         private PrintWriter mPw;
         private IActivityManager mIam;
-        private IActivityTaskManager mIatm;
-        private ActivityManagerService mInternal;
 
-        ProcessObserver(PrintWriter mPw, IActivityManager mIam,
-                IActivityTaskManager mIatm, ActivityManagerService ams) {
+        ProcessObserver(PrintWriter mPw, IActivityManager mIam) {
             this.mPw = mPw;
             this.mIam = mIam;
-            this.mIatm = mIatm;
-            this.mInternal = ams;
         }
 
         @Override
diff --git a/services/core/java/com/android/server/am/AnrHelper.java b/services/core/java/com/android/server/am/AnrHelper.java
index e0a2246..9fc0bf9 100644
--- a/services/core/java/com/android/server/am/AnrHelper.java
+++ b/services/core/java/com/android/server/am/AnrHelper.java
@@ -63,6 +63,11 @@
     private static final long CONSECUTIVE_ANR_TIME_MS = TimeUnit.MINUTES.toMillis(2);
 
     /**
+     * Time to wait before taking dumps for other processes to reduce load at boot time.
+     */
+    private static final long SELF_ONLY_AFTER_BOOT_MS = TimeUnit.MINUTES.toMillis(10);
+
+    /**
      * The keep alive time for the threads in the helper threadpool executor
     */
     private static final int DEFAULT_THREAD_KEEP_ALIVE_SECOND = 10;
@@ -231,7 +236,8 @@
                 // If there are many ANR at the same time, the latency may be larger.
                 // If the latency is too large, the stack trace might not be meaningful.
                 final long reportLatency = startTime - r.mTimestamp;
-                final boolean onlyDumpSelf = reportLatency > EXPIRED_REPORT_TIME_MS;
+                final boolean onlyDumpSelf = reportLatency > EXPIRED_REPORT_TIME_MS
+                        || startTime < SELF_ONLY_AFTER_BOOT_MS;
                 r.appNotResponding(onlyDumpSelf);
                 final long endTime = SystemClock.uptimeMillis();
                 Slog.d(TAG, "Completed ANR of " + r.mApp.processName + " in "
diff --git a/services/core/java/com/android/server/am/AppProfiler.java b/services/core/java/com/android/server/am/AppProfiler.java
index 2e0aec9..e4956b3 100644
--- a/services/core/java/com/android/server/am/AppProfiler.java
+++ b/services/core/java/com/android/server/am/AppProfiler.java
@@ -602,7 +602,7 @@
         public void handleMessage(Message msg) {
             switch (msg.what) {
                 case COLLECT_PSS_BG_MSG:
-                    if (!Flags.removeAppProfilerPssCollection()) {
+                    if (isProfilingPss()) {
                         collectPssInBackground();
                     } else {
                         collectRssInBackground();
@@ -748,6 +748,11 @@
         } while (true);
     }
 
+    boolean isProfilingPss() {
+        return !Flags.removeAppProfilerPssCollection()
+                || mService.mConstants.mForceEnablePssProfiling;
+    }
+
     // This method is analogous to collectPssInBackground() and is intended to be used as a
     // replacement if Flags.removeAppProfilerPssCollection() is enabled. References to PSS in
     // methods outside of AppProfiler have generally been kept where a new RSS equivalent is not
diff --git a/services/core/java/com/android/server/am/BroadcastProcessQueue.java b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
index 0b56146..c37e619 100644
--- a/services/core/java/com/android/server/am/BroadcastProcessQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
@@ -353,8 +353,7 @@
             // If we come across the record that's being enqueued in the queue, then that means
             // we already enqueued it for a receiver in this process and trying to insert a new
             // one past this could create priority inversion in the queue, so bail out.
-            if (record == testRecord && record.blockedUntilBeyondCount[recordIndex]
-                    > testRecord.blockedUntilBeyondCount[testRecordIndex]) {
+            if (record == testRecord) {
                 break;
             }
             if ((record.callingUid == testRecord.callingUid)
diff --git a/services/core/java/com/android/server/am/ContentProviderHelper.java b/services/core/java/com/android/server/am/ContentProviderHelper.java
index 095d907..30f21a6 100644
--- a/services/core/java/com/android/server/am/ContentProviderHelper.java
+++ b/services/core/java/com/android/server/am/ContentProviderHelper.java
@@ -1249,9 +1249,9 @@
             ProviderInfo cpi = providers.get(i);
             boolean singleton = mService.isSingleton(cpi.processName, cpi.applicationInfo,
                     cpi.name, cpi.flags);
-            if (singleton && app.userId != UserHandle.USER_SYSTEM) {
-                // This is a singleton provider, but a user besides the
-                // default user is asking to initialize a process it runs
+            if (isSingletonOrSystemUserOnly(cpi) && app.userId != UserHandle.USER_SYSTEM) {
+                // This is a singleton or a SYSTEM user only provider, but a user besides the
+                // SYSTEM user is asking to initialize a process it runs
                 // in...  well, no, it doesn't actually run in this process,
                 // it runs in the process of the default user.  Get rid of it.
                 providers.remove(i);
@@ -1398,8 +1398,7 @@
                                     final boolean processMatch =
                                             Objects.equals(pi.processName, app.processName)
                                             || pi.multiprocess;
-                                    final boolean userMatch = !mService.isSingleton(
-                                            pi.processName, pi.applicationInfo, pi.name, pi.flags)
+                                    final boolean userMatch = !isSingletonOrSystemUserOnly(pi)
                                             || app.userId == UserHandle.USER_SYSTEM;
                                     final boolean isInstantApp = pi.applicationInfo.isInstantApp();
                                     final boolean splitInstalled = pi.splitName == null
@@ -1985,4 +1984,13 @@
             return isAuthRedirected;
         }
     }
+
+    /**
+     * Returns true if Provider is either singleUser or systemUserOnly provider.
+     */
+    private boolean isSingletonOrSystemUserOnly(ProviderInfo pi) {
+        return (android.multiuser.Flags.enableSystemUserOnlyForServicesAndProviders()
+                && mService.isSystemUserOnly(pi.flags))
+                || mService.isSingleton(pi.processName, pi.applicationInfo, pi.name, pi.flags);
+    }
 }
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index b507a60..ef7a0e0 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -143,7 +143,6 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.ServiceInfo;
 import android.net.NetworkPolicyManager;
-import android.os.Flags;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.PowerManagerInternal;
@@ -1386,6 +1385,8 @@
                         break;
                 }
 
+                // TODO: b/319163103 - limit isolated/sandbox trimming to just the processes
+                //  evaluated in the current update.
                 if (app.isolated && psr.numberOfRunningServices() <= 0
                         && app.getIsolatedEntryPoint() == null) {
                     // If this is an isolated process, there are no services
@@ -2418,7 +2419,7 @@
                     // normally be a B service, but if we are low on RAM and it
                     // is large we want to force it down since we would prefer to
                     // keep launcher over it.
-                    long lastPssOrRss = !Flags.removeAppProfilerPssCollection()
+                    long lastPssOrRss = mService.mAppProfiler.isProfilingPss()
                             ? app.mProfile.getLastPss() : app.mProfile.getLastRss();
 
                     // RSS is larger than PSS, but the RSS/PSS ratio varies per-process based on how
@@ -2427,9 +2428,8 @@
                     //
                     // TODO(b/296454553): Tune the second value so that the relative number of
                     // service B is similar before/after this flag is enabled.
-                    double thresholdModifier = !Flags.removeAppProfilerPssCollection()
-                            ? 1
-                            : mConstants.PSS_TO_RSS_THRESHOLD_MODIFIER;
+                    double thresholdModifier = mService.mAppProfiler.isProfilingPss()
+                            ? 1 : mConstants.PSS_TO_RSS_THRESHOLD_MODIFIER;
                     double cachedRestoreThreshold =
                             mProcessList.getCachedRestoreThresholdKb() * thresholdModifier;
 
diff --git a/services/core/java/com/android/server/am/OomAdjusterModernImpl.java b/services/core/java/com/android/server/am/OomAdjusterModernImpl.java
index 7cc7c51..5a3fbe9 100644
--- a/services/core/java/com/android/server/am/OomAdjusterModernImpl.java
+++ b/services/core/java/com/android/server/am/OomAdjusterModernImpl.java
@@ -724,24 +724,13 @@
 
         if (fullUpdate) {
             assignCachedAdjIfNecessary(mProcessList.getLruProcessesLOSP());
-            postUpdateOomAdjInnerLSP(oomAdjReason, activeUids, now, nowElapsed, oldTime);
         } else {
             activeProcesses.clear();
             activeProcesses.addAll(targetProcesses);
             assignCachedAdjIfNecessary(activeProcesses);
-
-            for (int  i = activeUids.size() - 1; i >= 0; i--) {
-                final UidRecord uidRec = activeUids.valueAt(i);
-                uidRec.forEachProcess(this::updateAppUidRecIfNecessaryLSP);
-            }
-            updateUidsLSP(activeUids, nowElapsed);
-
-            for (int i = 0, size = targetProcesses.size(); i < size; i++) {
-                applyOomAdjLSP(targetProcesses.valueAt(i), false, now, nowElapsed, oomAdjReason);
-            }
-
             activeProcesses.clear();
         }
+        postUpdateOomAdjInnerLSP(oomAdjReason, activeUids, now, nowElapsed, oldTime);
         targetProcesses.clear();
 
         if (startProfiling) {
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index e57206e..b03183c 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -94,7 +94,6 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.os.DropBoxManager;
-import android.os.Flags;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
@@ -2482,7 +2481,6 @@
                         app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
                         app.info.dataDir, null, app.info.packageName,
                         app.getDisabledCompatChanges(),
-                        bindOverrideSysprops,
                         new String[]{PROC_START_SEQ_IDENT + app.getStartSeq()});
             } else if (hostingRecord.usesAppZygote()) {
                 final AppZygote appZygote = createAppZygoteForProcessIfNeeded(app);
@@ -4733,7 +4731,7 @@
                 pw.print("state: cur="); pw.print(makeProcStateString(state.getCurProcState()));
                 pw.print(" set="); pw.print(makeProcStateString(state.getSetProcState()));
                 // These values won't be collected if the flag is enabled.
-                if (!Flags.removeAppProfilerPssCollection()) {
+                if (service.mAppProfiler.isProfilingPss()) {
                     pw.print(" lastPss=");
                     DebugUtils.printSizeValue(pw, r.mProfile.getLastPss() * 1024);
                     pw.print(" lastSwapPss=");
diff --git a/services/core/java/com/android/server/am/ProcessProfileRecord.java b/services/core/java/com/android/server/am/ProcessProfileRecord.java
index 8ca64f8..d8f797c 100644
--- a/services/core/java/com/android/server/am/ProcessProfileRecord.java
+++ b/services/core/java/com/android/server/am/ProcessProfileRecord.java
@@ -23,7 +23,6 @@
 import android.app.ProcessMemoryState.HostingComponentType;
 import android.content.pm.ApplicationInfo;
 import android.os.Debug;
-import android.os.Flags;
 import android.os.Process;
 import android.os.SystemClock;
 import android.util.DebugUtils;
@@ -677,7 +676,7 @@
     void dumpPss(PrintWriter pw, String prefix, long nowUptime) {
         synchronized (mProfilerLock) {
             // TODO(b/297542292): Remove this case once PSS profiling is replaced
-            if (!Flags.removeAppProfilerPssCollection()) {
+            if (mService.mAppProfiler.isProfilingPss()) {
                 pw.print(prefix);
                 pw.print("lastPssTime=");
                 TimeUtils.formatDuration(mLastPssTime, nowUptime, pw);
diff --git a/services/core/java/com/android/server/am/ProcessStateRecord.java b/services/core/java/com/android/server/am/ProcessStateRecord.java
index 5ad921f..3391ec7 100644
--- a/services/core/java/com/android/server/am/ProcessStateRecord.java
+++ b/services/core/java/com/android/server/am/ProcessStateRecord.java
@@ -29,7 +29,6 @@
 import android.annotation.ElapsedRealtimeLong;
 import android.app.ActivityManager;
 import android.content.ComponentName;
-import android.os.Flags;
 import android.os.SystemClock;
 import android.os.Trace;
 import android.util.Slog;
@@ -1351,7 +1350,7 @@
         }
         if (mNotCachedSinceIdle) {
             pw.print(prefix); pw.print("notCachedSinceIdle="); pw.print(mNotCachedSinceIdle);
-            if (!Flags.removeAppProfilerPssCollection()) {
+            if (mService.mAppProfiler.isProfilingPss()) {
                 pw.print(" initialIdlePss=");
             } else {
                 pw.print(" initialIdleRss=");
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index 08b129e..2771572 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -39,7 +39,7 @@
 import android.app.RemoteServiceException.CannotPostForegroundServiceNotificationException;
 import android.app.compat.CompatChanges;
 import android.compat.annotation.ChangeId;
-import android.compat.annotation.Disabled;
+import android.compat.annotation.EnabledAfter;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -49,6 +49,7 @@
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Build;
+import android.os.Build.VERSION_CODES;
 import android.os.IBinder;
 import android.os.PowerExemptionManager;
 import android.os.SystemClock;
@@ -94,16 +95,14 @@
      * (See also android.app.ForegroundServiceTypePolicy)
      */
     @ChangeId
-    // @EnabledAfter(targetSdkVersion = VERSION_CODES.UPSIDE_DOWN_CAKE)
-    @Disabled
+    @EnabledAfter(targetSdkVersion = VERSION_CODES.UPSIDE_DOWN_CAKE)
     static final long USE_NEW_WIU_LOGIC_FOR_START = 311208629L;
 
     /**
      * Compat ID to enable the new FGS start logic, for capability calculation.
      */
     @ChangeId
-    // Always enabled
-    @Disabled
+    @EnabledAfter(targetSdkVersion = VERSION_CODES.UPSIDE_DOWN_CAKE)
     static final long USE_NEW_WIU_LOGIC_FOR_CAPABILITIES = 313677553L;
 
     /**
@@ -111,8 +110,7 @@
      * the background.
      */
     @ChangeId
-    // @EnabledAfter(targetSdkVersion = VERSION_CODES.UPSIDE_DOWN_CAKE)
-    @Disabled
+    @EnabledAfter(targetSdkVersion = VERSION_CODES.UPSIDE_DOWN_CAKE)
     static final long USE_NEW_BFSL_LOGIC = 311208749L;
 
     final ActivityManagerService ams;
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index cc56110..9db5d0a 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -162,7 +162,7 @@
         "pdf_viewer",
         "pixel_audio_android",
         "pixel_bluetooth",
-        "pixel_system_sw_touch",
+        "pixel_system_sw_video",
         "pixel_watch",
         "platform_security",
         "power",
@@ -174,12 +174,16 @@
         "safety_center",
         "sensors",
         "system_performance",
+        "system_sw_touch",
         "system_sw_usb",
+        "statsd",
         "test_suites",
         "text",
         "threadnetwork",
         "tv_system_ui",
+        "usb",
         "vibrator",
+        "virtualization",
         "virtual_devices",
         "wallet_integration",
         "wear_calling_messaging",
diff --git a/services/core/java/com/android/server/am/TEST_MAPPING b/services/core/java/com/android/server/am/TEST_MAPPING
index 575db01..e90910a 100644
--- a/services/core/java/com/android/server/am/TEST_MAPPING
+++ b/services/core/java/com/android/server/am/TEST_MAPPING
@@ -146,6 +146,15 @@
         { "include-filter": "android.app.cts.ServiceTest" },
         { "include-filter": "android.app.cts.ActivityManagerFgsBgStartTest" }
       ]
+    },
+    {
+      "name": "CtsStatsdAtomHostTestCases",
+      "options": [
+        { "include-filter": "android.cts.statsdatom.appexit.AppExitHostTest" },
+        { "exclude-annotation": "androidx.test.filters.LargeTest" },
+        { "exclude-annotation": "androidx.test.filters.FlakyTest" },
+        { "exclude-annotation": "org.junit.Ignore" }
+      ]
     }
   ]
 }
diff --git a/services/core/java/com/android/server/ambientcontext/AmbientContextManagerService.java b/services/core/java/com/android/server/ambientcontext/AmbientContextManagerService.java
index 9f31f37..5f12ce1 100644
--- a/services/core/java/com/android/server/ambientcontext/AmbientContextManagerService.java
+++ b/services/core/java/com/android/server/ambientcontext/AmbientContextManagerService.java
@@ -73,7 +73,8 @@
     private static final Set<Integer> DEFAULT_EVENT_SET = Sets.newHashSet(
             AmbientContextEvent.EVENT_COUGH,
             AmbientContextEvent.EVENT_SNORE,
-            AmbientContextEvent.EVENT_BACK_DOUBLE_TAP);
+            AmbientContextEvent.EVENT_BACK_DOUBLE_TAP,
+            AmbientContextEvent.EVENT_HEART_RATE);
 
     /** Default value in absence of {@link DeviceConfig} override. */
     private static final boolean DEFAULT_SERVICE_ENABLED = true;
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index d80638a..df8d9e1 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -33,6 +33,7 @@
 import static android.app.AppOpsManager.MODE_ERRORED;
 import static android.app.AppOpsManager.MODE_FOREGROUND;
 import static android.app.AppOpsManager.MODE_IGNORED;
+import static android.app.AppOpsManager.OP_BLUETOOTH_CONNECT;
 import static android.app.AppOpsManager.OP_CAMERA;
 import static android.app.AppOpsManager.OP_CAMERA_SANDBOXED;
 import static android.app.AppOpsManager.OP_FLAGS_ALL;
@@ -2849,6 +2850,11 @@
         verifyIncomingUid(uid);
         verifyIncomingOp(code);
         if (!isIncomingPackageValid(packageName, UserHandle.getUserId(uid))) {
+            // TODO(b/302609140): Remove extra logging after this issue is diagnosed.
+            if (code == OP_BLUETOOTH_CONNECT) {
+                Slog.e(TAG, "noting OP_BLUETOOTH_CONNECT returned MODE_ERRORED as incoming "
+                        + "package: " + packageName + " and uid: " + uid + " is invalid");
+            }
             return new SyncNotedAppOp(AppOpsManager.MODE_ERRORED, code, attributionTag,
                     packageName);
         }
@@ -2877,6 +2883,13 @@
             }
         } catch (SecurityException e) {
             logVerifyAndGetBypassFailure(uid, e, "noteOperation");
+            // TODO(b/302609140): Remove extra logging after this issue is diagnosed.
+            if (code == OP_BLUETOOTH_CONNECT) {
+                Slog.e(TAG, "noting OP_BLUETOOTH_CONNECT returned MODE_ERRORED as"
+                        + " verifyAndGetBypass returned a SecurityException for package: "
+                        + packageName + " and uid: " + uid + " and attributionTag: "
+                        + attributionTag, e);
+            }
             return new SyncNotedAppOp(AppOpsManager.MODE_ERRORED, code, attributionTag,
                     packageName);
         }
@@ -2890,6 +2903,17 @@
                 if (DEBUG) Slog.d(TAG, "noteOperation: no op for code " + code + " uid " + uid
                         + " package " + packageName + "flags: " +
                         AppOpsManager.flagsToString(flags));
+                // TODO(b/302609140): Remove extra logging after this issue is diagnosed.
+                if (code == OP_BLUETOOTH_CONNECT) {
+                    Slog.e(TAG, "noting OP_BLUETOOTH_CONNECT returned MODE_ERRORED as"
+                            + " #getOpsLocked returned null for"
+                            + " uid: " + uid
+                            + " packageName: " + packageName
+                            + " attributionTag: " + attributionTag
+                            + " pvr.isAttributionTagValid: " + pvr.isAttributionTagValid
+                            + " pvr.bypass: " + pvr.bypass);
+                    Slog.e(TAG, "mUidStates.get(" + uid + "): " + mUidStates.get(uid));
+                }
                 return new SyncNotedAppOp(AppOpsManager.MODE_ERRORED, code, attributionTag,
                         packageName);
             }
@@ -2930,6 +2954,11 @@
                     attributedOp.rejected(uidState.getState(), flags);
                     scheduleOpNotedIfNeededLocked(code, uid, packageName, attributionTag, flags,
                             uidMode);
+                    // TODO(b/302609140): Remove extra logging after this issue is diagnosed.
+                    if (code == OP_BLUETOOTH_CONNECT && uidMode == MODE_ERRORED) {
+                        Slog.e(TAG, "noting OP_BLUETOOTH_CONNECT returned MODE_ERRORED as"
+                                + " uid mode is MODE_ERRORED");
+                    }
                     return new SyncNotedAppOp(uidMode, code, attributionTag, packageName);
                 }
             } else {
@@ -2949,6 +2978,11 @@
                     attributedOp.rejected(uidState.getState(), flags);
                     scheduleOpNotedIfNeededLocked(code, uid, packageName, attributionTag, flags,
                             mode);
+                    // TODO(b/302609140): Remove extra logging after this issue is diagnosed.
+                    if (code == OP_BLUETOOTH_CONNECT && mode == MODE_ERRORED) {
+                        Slog.e(TAG, "noting OP_BLUETOOTH_CONNECT returned MODE_ERRORED as"
+                                + " package mode is MODE_ERRORED");
+                    }
                     return new SyncNotedAppOp(mode, code, attributionTag, packageName);
                 }
             }
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index dada72e..f80228a 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -1812,6 +1812,7 @@
                                         "msg: MSG_L_SET_BT_ACTIVE_DEVICE "
                                             + "received with null profile proxy: "
                                             + btInfo)).printLog(TAG));
+                                sendMsg(MSG_CHECK_MUTE_MUSIC, SENDMSG_REPLACE, 0 /*delay*/);
                                 return;
                             }
                             @AudioSystem.AudioFormatNativeEnumForBtCodec final int codec =
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index e05824a..bf20ae3 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -1992,7 +1992,7 @@
             // TODO: return;
         } else {
             AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
-                    "A2DP source device addr=" + Utils.anonymizeBluetoothAddress(address)
+                    "A2DP sink device addr=" + Utils.anonymizeBluetoothAddress(address)
                             + " now available").printLog(TAG));
         }
 
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 37fe389..4cbee2b 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -7879,7 +7879,6 @@
         DEVICE_MEDIA_UNMUTED_ON_PLUG_SET.addAll(AudioSystem.DEVICE_OUT_ALL_A2DP_SET);
         DEVICE_MEDIA_UNMUTED_ON_PLUG_SET.addAll(AudioSystem.DEVICE_OUT_ALL_BLE_SET);
         DEVICE_MEDIA_UNMUTED_ON_PLUG_SET.addAll(AudioSystem.DEVICE_OUT_ALL_USB_SET);
-        DEVICE_MEDIA_UNMUTED_ON_PLUG_SET.add(AudioSystem.DEVICE_OUT_HDMI);
     }
 
     /** only public for mocking/spying, do not call outside of AudioService */
@@ -8836,6 +8835,8 @@
                 synchronized (VolumeStreamState.class) {
                     oldIndex = getIndex(device);
                     index = getValidIndex(index, hasModifyAudioSettings);
+                    // for STREAM_SYSTEM_ENFORCED, do not sync aliased streams on the enforced index
+                    int aliasIndex = index;
                     if ((mStreamType == AudioSystem.STREAM_SYSTEM_ENFORCED) && mCameraSoundForced) {
                         index = mIndexMax;
                     }
@@ -8854,7 +8855,8 @@
                         if (streamType != mStreamType &&
                                 mStreamVolumeAlias[streamType] == mStreamType &&
                                 (changed || !aliasStreamState.hasIndexForDevice(device))) {
-                            final int scaledIndex = rescaleIndex(index, mStreamType, streamType);
+                            final int scaledIndex =
+                                    rescaleIndex(aliasIndex, mStreamType, streamType);
                             aliasStreamState.setIndex(scaledIndex, device, caller,
                                     hasModifyAudioSettings);
                             if (isCurrentDevice) {
@@ -9376,6 +9378,14 @@
             if (mIsSingleVolume && (streamState.mStreamType != AudioSystem.STREAM_MUSIC)) {
                 return;
             }
+
+            // Persisting STREAM_SYSTEM_ENFORCED index is not needed as its alias (STREAM_RING)
+            // is persisted. This can also be problematic when the enforcement is active as it will
+            // override current SYSTEM_RING persisted value given they share the same settings name
+            // (due to aliasing).
+            if (streamState.mStreamType == AudioSystem.STREAM_SYSTEM_ENFORCED) {
+                return;
+            }
             if (streamState.hasValidSettingsName()) {
                 mSettings.putSystemIntForUser(mContentResolver,
                         streamState.getSettingNameForDevice(device),
@@ -12684,12 +12694,16 @@
     public @Nullable AudioHalVersionInfo getHalVersion() {
         for (AudioHalVersionInfo version : AudioHalVersionInfo.VERSIONS) {
             try {
-                // TODO: check AIDL service.
                 String versionStr = version.getMajorVersion() + "." + version.getMinorVersion();
-                HwBinder.getService(
-                        String.format("android.hardware.audio@%s::IDevicesFactory", versionStr),
-                        "default");
-                return version;
+                final String aidlStr = "android.hardware.audio.core.IModule/default";
+                final String hidlStr = String.format("android.hardware.audio@%s::IDevicesFactory",
+                        versionStr);
+                if (null != ServiceManager.checkService(aidlStr)) {
+                    return version;
+                } else {
+                    HwBinder.getService(hidlStr, "default");
+                    return version;
+                }
             } catch (NoSuchElementException e) {
                 // Ignore, the specified HAL interface is not found.
             } catch (RemoteException re) {
@@ -13588,6 +13602,46 @@
         }
     }
 
+
+    /**
+     * @see AudioManager#shouldNotificationSoundPlay(AudioAttributes)
+     */
+    @android.annotation.EnforcePermission(
+            android.Manifest.permission.QUERY_AUDIO_STATE)
+    public boolean shouldNotificationSoundPlay(@NonNull final AudioAttributes aa) {
+        super.shouldNotificationSoundPlay_enforcePermission();
+        Objects.requireNonNull(aa);
+
+        // don't play notifications if the stream volume associated with the
+        // AudioAttributes of the notification record is 0 (non-zero volume implies
+        // not silenced by SILENT or VIBRATE ringer mode)
+        final int stream = AudioAttributes.toLegacyStreamType(aa);
+        final boolean mutingFromVolume = getStreamVolume(stream) == 0;
+        if (mutingFromVolume) {
+            if (DEBUG_VOL) {
+                Slog.d(TAG, "notification should not play due to muted stream " + stream);
+            }
+            return false;
+        }
+
+        // don't play notifications if there is a user of GAIN_TRANSIENT_EXCLUSIVE audio focus
+        // and the focus owner is recording
+        final int uid = mMediaFocusControl.getExclusiveFocusOwnerUid();
+        if (uid == -1) { // return value is -1 if focus isn't GAIN_TRANSIENT_EXCLUSIVE
+            return true;
+        }
+        // is the owner of GAIN_TRANSIENT_EXCLUSIVE focus also recording?
+        final boolean mutingFromFocusAndRecording = mRecordMonitor.isRecordingActiveForUid(uid);
+        if (mutingFromFocusAndRecording) {
+            if (DEBUG_VOL) {
+                Slog.d(TAG, "notification should not play due to exclusive focus owner recording "
+                        + " uid:" + uid);
+            }
+            return false;
+        }
+        return true;
+    }
+
     //======================
     // Audioserver state dispatch
     //======================
diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java
index a818c30..f51043d 100644
--- a/services/core/java/com/android/server/audio/BtHelper.java
+++ b/services/core/java/com/android/server/audio/BtHelper.java
@@ -634,16 +634,17 @@
             return;
         }
         List<BluetoothDevice> activeDevices = adapter.getActiveDevices(profile);
-        if (activeDevices.isEmpty() || activeDevices.get(0) == null) {
-            return;
+        BluetoothProfileConnectionInfo bpci = new BluetoothProfileConnectionInfo(profile);
+        for (BluetoothDevice device : activeDevices) {
+            if (device == null) {
+                continue;
+            }
+            AudioDeviceBroker.BtDeviceChangedData data = new AudioDeviceBroker.BtDeviceChangedData(
+                    device, null, bpci, "mBluetoothProfileServiceListener");
+            AudioDeviceBroker.BtDeviceInfo info = mDeviceBroker.createBtDeviceInfo(
+                    data, device, BluetoothProfile.STATE_CONNECTED);
+            mDeviceBroker.postBluetoothActiveDevice(info, 0 /* delay */);
         }
-        AudioDeviceBroker.BtDeviceChangedData data = new AudioDeviceBroker.BtDeviceChangedData(
-                activeDevices.get(0), null, new BluetoothProfileConnectionInfo(profile),
-                "mBluetoothProfileServiceListener");
-        AudioDeviceBroker.BtDeviceInfo info =
-                mDeviceBroker.createBtDeviceInfo(data, activeDevices.get(0),
-                        BluetoothProfile.STATE_CONNECTED);
-        mDeviceBroker.postBluetoothActiveDevice(info, 0 /* delay */);
     }
 
     // @GuardedBy("mDeviceBroker.mSetModeLock")
@@ -678,8 +679,11 @@
         if (adapter != null) {
             List<BluetoothDevice> activeDevices =
                     adapter.getActiveDevices(BluetoothProfile.HEADSET);
-            if (activeDevices.size() > 0 && activeDevices.get(0) != null) {
-                onSetBtScoActiveDevice(activeDevices.get(0));
+            for (BluetoothDevice device : activeDevices) {
+                if (device == null) {
+                    continue;
+                }
+                onSetBtScoActiveDevice(device);
             }
         } else {
             Log.e(TAG, "onHeadsetProfileConnected: Null BluetoothAdapter");
diff --git a/services/core/java/com/android/server/audio/FadeOutManager.java b/services/core/java/com/android/server/audio/FadeOutManager.java
index 2cceb5a..4d5bce5 100644
--- a/services/core/java/com/android/server/audio/FadeOutManager.java
+++ b/services/core/java/com/android/server/audio/FadeOutManager.java
@@ -29,6 +29,7 @@
 import android.util.SparseArray;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.server.utils.EventLogger;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -345,7 +346,9 @@
             }
             if (apc.getPlayerProxy() != null) {
                 applyVolumeShaperInternal(apc, piid, volShaper,
-                        skipRamp ? PLAY_SKIP_RAMP : PLAY_CREATE_IF_NEEDED);
+                        skipRamp ? PLAY_SKIP_RAMP : PLAY_CREATE_IF_NEEDED, skipRamp,
+                        PlaybackActivityMonitor.EVENT_TYPE_FADE_OUT);
+                mFadedPlayers.put(piid, volShaper);
             } else {
                 if (DEBUG) {
                     Slog.v(TAG, "Error fading out player piid:" + piid
@@ -360,7 +363,8 @@
                 final AudioPlaybackConfiguration apc = players.get(piid);
                 if ((apc != null) && (apc.getPlayerProxy() != null)) {
                     applyVolumeShaperInternal(apc, piid, /* volShaperConfig= */ null,
-                            VolumeShaper.Operation.REVERSE);
+                            VolumeShaper.Operation.REVERSE, /* skipRamp= */ false,
+                            PlaybackActivityMonitor.EVENT_TYPE_FADE_IN);
                 } else {
                     // this piid was in the list of faded players, but wasn't found
                     if (DEBUG) {
@@ -372,6 +376,7 @@
             mFadedPlayers.clear();
         }
 
+        @GuardedBy("mLock")
         void fadeInPlayer(@NonNull AudioPlaybackConfiguration apc,
                 @Nullable VolumeShaper.Configuration config) {
             int piid = Integer.valueOf(apc.getPlayerInterfaceId());
@@ -384,10 +389,17 @@
                 return;
             }
 
+            VolumeShaper.Operation operation = VolumeShaper.Operation.REVERSE;
+            if (config != null) {
+                // replace and join the volumeshapers with (possibly) in progress fade out operation
+                // for a smoother fade in
+                operation = new VolumeShaper.Operation.Builder()
+                        .replace(mFadedPlayers.get(piid).getId(), /* join= */ true).build();
+            }
             mFadedPlayers.remove(piid);
             if (apc.getPlayerProxy() != null) {
-                applyVolumeShaperInternal(apc, piid, config,
-                        config != null ? PLAY_CREATE_IF_NEEDED : VolumeShaper.Operation.REVERSE);
+                applyVolumeShaperInternal(apc, piid, config, operation, /* skipRamp= */ false,
+                        PlaybackActivityMonitor.EVENT_TYPE_FADE_IN);
             } else {
                 if (DEBUG) {
                     Slog.v(TAG, "Error fading in player piid:" + piid
@@ -396,6 +408,7 @@
             }
         }
 
+        @GuardedBy("mLock")
         void clear() {
             if (mFadedPlayers.size() > 0) {
                 if (DEBUG) {
@@ -412,21 +425,40 @@
         }
 
         private void applyVolumeShaperInternal(AudioPlaybackConfiguration apc, int piid,
-                VolumeShaper.Configuration volShaperConfig, VolumeShaper.Operation operation) {
+                VolumeShaper.Configuration volShaperConfig, VolumeShaper.Operation operation,
+                boolean skipRamp, String eventType) {
             VolumeShaper.Configuration config = volShaperConfig;
             // when operation is reverse, use the fade out volume shaper config instead
             if (operation.equals(VolumeShaper.Operation.REVERSE)) {
                 config = mFadedPlayers.get(piid);
             }
             try {
-                PlaybackActivityMonitor.sEventLogger.enqueue(
-                        (new PlaybackActivityMonitor.FadeEvent(apc, config, operation))
-                                .printLog(TAG));
+                logFadeEvent(apc, piid, volShaperConfig, operation, skipRamp, eventType);
                 apc.getPlayerProxy().applyVolumeShaper(config, operation);
             } catch (Exception e) {
-                Slog.e(TAG, "Error fading player piid:" + piid + " uid:" + mUid
-                        + " operation:" + operation, e);
+                Slog.e(TAG, "Error " + eventType + " piid:" + piid + " uid:" + mUid, e);
             }
         }
+
+        private void logFadeEvent(AudioPlaybackConfiguration apc, int piid,
+                VolumeShaper.Configuration config, VolumeShaper.Operation operation,
+                boolean skipRamp, String eventType) {
+            if (eventType.equals(PlaybackActivityMonitor.EVENT_TYPE_FADE_OUT)) {
+                PlaybackActivityMonitor.sEventLogger.enqueue(
+                        (new PlaybackActivityMonitor.FadeOutEvent(apc, skipRamp, config, operation))
+                                .printLog(TAG));
+                return;
+            }
+
+            if (eventType.equals(PlaybackActivityMonitor.EVENT_TYPE_FADE_IN)) {
+                PlaybackActivityMonitor.sEventLogger.enqueue(
+                        (new PlaybackActivityMonitor.FadeInEvent(apc, skipRamp, config, operation))
+                                .printLog(TAG));
+                return;
+            }
+
+            PlaybackActivityMonitor.sEventLogger.enqueue(
+                    (new EventLogger.StringEvent(eventType + " piid:" + piid)).printLog(TAG));
+        }
     }
 }
diff --git a/services/core/java/com/android/server/audio/MediaFocusControl.java b/services/core/java/com/android/server/audio/MediaFocusControl.java
index 0df0006..1376bde 100644
--- a/services/core/java/com/android/server/audio/MediaFocusControl.java
+++ b/services/core/java/com/android/server/audio/MediaFocusControl.java
@@ -297,6 +297,23 @@
     }
 
     /**
+     * Return the UID of the focus owner that has focus with exclusive focus gain
+     * @return -1 if nobody has exclusive focus, the UID of the owner otherwise
+     */
+    protected int getExclusiveFocusOwnerUid() {
+        synchronized (mAudioFocusLock) {
+            if (mFocusStack.empty()) {
+                return -1;
+            }
+            final FocusRequester owner = mFocusStack.peek();
+            if (owner.getGainRequest() != AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE) {
+                return -1;
+            }
+            return owner.getClientUid();
+        }
+    }
+
+    /**
      * Send AUDIOFOCUS_LOSS to a specific stack entry.
      * Note this method is supporting an external API, and is restricted to LOSS in order to
      * prevent allowing the stack to be in an invalid state (e.g. entry inside stack has focus)
diff --git a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
index e69fbbd..08da32e 100644
--- a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
+++ b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
@@ -84,6 +84,8 @@
     /*package*/ static final int VOLUME_SHAPER_SYSTEM_FADEOUT_ID = 2;
     /*package*/ static final int VOLUME_SHAPER_SYSTEM_MUTE_AWAIT_CONNECTION_ID = 3;
     /*package*/ static final int VOLUME_SHAPER_SYSTEM_STRONG_DUCK_ID = 4;
+    /*package*/ static final String EVENT_TYPE_FADE_OUT = "fading out";
+    /*package*/ static final String EVENT_TYPE_FADE_IN = "fading in";
 
     // ducking settings for a "normal duck" at -14dB
     private static final VolumeShaper.Configuration DUCK_VSHAPE =
@@ -1204,11 +1206,13 @@
                     return;
                 }
                 try {
-                    sEventLogger.enqueue((new DuckEvent(apc, skipRamp, mUseStrongDuck))
-                            .printLog(TAG));
-                    apc.getPlayerProxy().applyVolumeShaper(
-                            mUseStrongDuck ? STRONG_DUCK_VSHAPE : DUCK_VSHAPE,
-                            skipRamp ? PLAY_SKIP_RAMP : PLAY_CREATE_IF_NEEDED);
+                    VolumeShaper.Configuration config =
+                            mUseStrongDuck ? STRONG_DUCK_VSHAPE : DUCK_VSHAPE;
+                    VolumeShaper.Operation operation =
+                            skipRamp ? PLAY_SKIP_RAMP : PLAY_CREATE_IF_NEEDED;
+                    sEventLogger.enqueue((new DuckEvent(apc, skipRamp, mUseStrongDuck, config,
+                            operation)).printLog(TAG));
+                    apc.getPlayerProxy().applyVolumeShaper(config, operation);
                     mDuckedPlayers.add(piid);
                 } catch (Exception e) {
                     Log.e(TAG, "Error ducking player piid:" + piid + " uid:" + mUid, e);
@@ -1363,58 +1367,41 @@
         }
     }
 
-    static final class FadeEvent extends EventLogger.Event {
-        private final int mPlayerIId;
-        private final int mPlayerType;
-        private final int mClientUid;
-        private final int mClientPid;
-        private final AudioAttributes mPlayerAttr;
-        private final VolumeShaper.Configuration mVShaper;
-        private final VolumeShaper.Operation mVOperation;
-
-        FadeEvent(AudioPlaybackConfiguration apc, VolumeShaper.Configuration vShaper,
-                VolumeShaper.Operation vOperation) {
-            mPlayerIId = apc.getPlayerInterfaceId();
-            mClientUid = apc.getClientUid();
-            mClientPid = apc.getClientPid();
-            mPlayerAttr = apc.getAudioAttributes();
-            mPlayerType = apc.getPlayerType();
-            mVShaper = vShaper;
-            mVOperation = vOperation;
-        }
-
-        @Override
-        public String eventToString() {
-            return "Fade Event:" + " player piid:" + mPlayerIId
-                    + " uid/pid:" + mClientUid + "/" + mClientPid
-                    + " player type:"
-                    + AudioPlaybackConfiguration.toLogFriendlyPlayerType(mPlayerType)
-                    + " attr:" + mPlayerAttr
-                    + " volume shaper:" + mVShaper
-                    + " volume operation:" + mVOperation;
-        }
-    }
-
     private abstract static class VolumeShaperEvent extends EventLogger.Event {
         private final int mPlayerIId;
         private final boolean mSkipRamp;
         private final int mClientUid;
         private final int mClientPid;
+        private final int mPlayerType;
+        private final AudioAttributes mPlayerAttr;
+        private final VolumeShaper.Configuration mConfig;
+        private final VolumeShaper.Operation mOperation;
 
         abstract String getVSAction();
 
-        VolumeShaperEvent(@NonNull AudioPlaybackConfiguration apc, boolean skipRamp) {
+        VolumeShaperEvent(@NonNull AudioPlaybackConfiguration apc, boolean skipRamp,
+                VolumeShaper.Configuration config, VolumeShaper.Operation operation) {
             mPlayerIId = apc.getPlayerInterfaceId();
             mSkipRamp = skipRamp;
             mClientUid = apc.getClientUid();
             mClientPid = apc.getClientPid();
+            mPlayerAttr = apc.getAudioAttributes();
+            mPlayerType = apc.getPlayerType();
+            mConfig = config;
+            mOperation = operation;
         }
 
         @Override
         public String eventToString() {
-            return new StringBuilder(getVSAction()).append(" player piid:").append(mPlayerIId)
-                    .append(" uid/pid:").append(mClientUid).append("/").append(mClientPid)
-                    .append(" skip ramp:").append(mSkipRamp).toString();
+            return getVSAction()
+                    + " player piid:" + mPlayerIId
+                    + " uid/pid:" + mClientUid + "/" + mClientPid
+                    + " skip ramp:" + mSkipRamp
+                    + " player type:"
+                    + AudioPlaybackConfiguration.toLogFriendlyPlayerType(mPlayerType)
+                    + " attr:" + mPlayerAttr
+                    + " config:" + mConfig
+                    + " operation:" + mOperation;
         }
     }
 
@@ -1426,9 +1413,10 @@
             return mUseStrongDuck ? "ducking (strong)" : "ducking";
         }
 
-        DuckEvent(@NonNull AudioPlaybackConfiguration apc, boolean skipRamp, boolean useStrongDuck)
+        DuckEvent(@NonNull AudioPlaybackConfiguration apc, boolean skipRamp, boolean useStrongDuck,
+                VolumeShaper.Configuration config, VolumeShaper.Operation operation)
         {
-            super(apc, skipRamp);
+            super(apc, skipRamp, config, operation);
             mUseStrongDuck = useStrongDuck;
         }
     }
@@ -1436,11 +1424,24 @@
     static final class FadeOutEvent extends VolumeShaperEvent {
         @Override
         String getVSAction() {
-            return "fading out";
+            return EVENT_TYPE_FADE_OUT;
         }
 
-        FadeOutEvent(@NonNull AudioPlaybackConfiguration apc, boolean skipRamp) {
-            super(apc, skipRamp);
+        FadeOutEvent(@NonNull AudioPlaybackConfiguration apc, boolean skipRamp,
+                VolumeShaper.Configuration config, VolumeShaper.Operation operation) {
+            super(apc, skipRamp, config, operation);
+        }
+    }
+
+    static final class FadeInEvent extends VolumeShaperEvent {
+        @Override
+        String getVSAction() {
+            return EVENT_TYPE_FADE_IN;
+        }
+
+        FadeInEvent(@NonNull AudioPlaybackConfiguration apc, boolean skipRamp,
+                VolumeShaper.Configuration config, VolumeShaper.Operation operation) {
+            super(apc, skipRamp, config, operation);
         }
     }
 
diff --git a/services/core/java/com/android/server/audio/SoundDoseHelper.java b/services/core/java/com/android/server/audio/SoundDoseHelper.java
index c72632f..a30cdc4 100644
--- a/services/core/java/com/android/server/audio/SoundDoseHelper.java
+++ b/services/core/java/com/android/server/audio/SoundDoseHelper.java
@@ -62,6 +62,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Objects;
 import java.util.concurrent.atomic.AtomicBoolean;
@@ -147,6 +148,15 @@
 
     private static final int SAFE_MEDIA_VOLUME_UNINITIALIZED = -1;
 
+    // see {@link #recordToPersistedString(SoundDoseRecord)}
+    // this is computed conservatively to accommodate the legacy persisting of SoundDoseRecords in
+    // which we materialized more decimal values.
+    // TODO: adjust value after soaking in
+    private static final int MAX_RECORDS_STRING_LENGTH = 50;
+    private static final int MAX_SETTINGS_LENGTH = 32768;
+    private static final int MAX_NUMBER_OF_CACHED_RECORDS =
+            MAX_SETTINGS_LENGTH / MAX_RECORDS_STRING_LENGTH;
+
     private final EventLogger mLogger = new EventLogger(AudioService.LOG_NB_EVENTS_SOUND_DOSE,
             "CSD updates");
 
@@ -893,7 +903,7 @@
             if (AudioService.mStreamVolumeAlias[streamType] == AudioSystem.STREAM_MUSIC
                     && safeDevicesContains(device)) {
                 soundDose.updateAttenuation(
-                        AudioSystem.getStreamVolumeDB(AudioSystem.STREAM_MUSIC,
+                        -AudioSystem.getStreamVolumeDB(AudioSystem.STREAM_MUSIC,
                                 (newIndex + 5) / 10,
                                 device), device);
             }
@@ -923,7 +933,7 @@
         Log.v(TAG, "Initializing sound dose");
 
         try {
-            if (mCachedAudioDeviceCategories.size() > 0) {
+            if (!mCachedAudioDeviceCategories.isEmpty()) {
                 soundDose.initCachedAudioDeviceCategories(mCachedAudioDeviceCategories.toArray(
                         new ISoundDose.AudioDeviceCategory[0]));
                 mCachedAudioDeviceCategories.clear();
@@ -957,6 +967,7 @@
                         mGlobalTimeOffsetInSecs);
                 if (records != null) {
                     mDoseRecords.addAll(records);
+                    sanitizeDoseRecords_l();
                 }
             }
         }
@@ -1176,17 +1187,35 @@
                                 && r.duration == record.duration)) {
                     Log.w(TAG, "Could not find cached record to remove: " + record);
                 }
-            } else {
+            } else if (record.value > 0) {
                 mDoseRecords.add(record);
             }
         }
 
+        sanitizeDoseRecords_l();
+
         mAudioHandler.sendMessageAtTime(mAudioHandler.obtainMessage(MSG_PERSIST_CSD_VALUES,
                 /* arg1= */0, /* arg2= */0, /* obj= */null), /* delay= */0);
 
         mLogger.enqueue(SoundDoseEvent.getDoseUpdateEvent(currentCsd, totalDuration));
     }
 
+    @GuardedBy("mCsdStateLock")
+    private void sanitizeDoseRecords_l() {
+        if (mDoseRecords.size() > MAX_NUMBER_OF_CACHED_RECORDS) {
+            int nrToRemove = MAX_NUMBER_OF_CACHED_RECORDS - mDoseRecords.size();
+            Log.w(TAG,
+                    "Removing " + nrToRemove + " records from the total of " + mDoseRecords.size());
+            // Remove older elements to fit into persisted settings max length
+            Iterator<SoundDoseRecord> recordIterator = mDoseRecords.iterator();
+            while (recordIterator.hasNext() && nrToRemove > 0) {
+                recordIterator.next();
+                recordIterator.remove();
+                --nrToRemove;
+            }
+        }
+    }
+
     @SuppressWarnings("GuardedBy")  // avoid limitation with intra-procedural analysis of lambdas
     private void onPersistSoundDoseRecords() {
         synchronized (mCsdStateLock) {
@@ -1213,8 +1242,8 @@
             long globalTimeOffsetInSecs) {
         return convertToGlobalTime(record.timestamp, globalTimeOffsetInSecs)
                 + PERSIST_CSD_RECORD_FIELD_SEPARATOR + record.duration
-                + PERSIST_CSD_RECORD_FIELD_SEPARATOR + record.value
-                + PERSIST_CSD_RECORD_FIELD_SEPARATOR + record.averageMel;
+                + PERSIST_CSD_RECORD_FIELD_SEPARATOR + String.format("%.3f", record.value)
+                + PERSIST_CSD_RECORD_FIELD_SEPARATOR + String.format("%.3f", record.averageMel);
     }
 
     private static long convertToGlobalTime(long bootTimeInSecs, long globalTimeOffsetInSecs) {
diff --git a/services/core/java/com/android/server/biometrics/AuthenticationStatsCollector.java b/services/core/java/com/android/server/biometrics/AuthenticationStatsCollector.java
index 4df2581..5d609bc 100644
--- a/services/core/java/com/android/server/biometrics/AuthenticationStatsCollector.java
+++ b/services/core/java/com/android/server/biometrics/AuthenticationStatsCollector.java
@@ -54,8 +54,8 @@
 
     @NonNull private final Context mContext;
     @NonNull private final PackageManager mPackageManager;
-    @NonNull private final FaceManager mFaceManager;
-    @NonNull private final FingerprintManager mFingerprintManager;
+    @Nullable private final FaceManager mFaceManager;
+    @Nullable private final FingerprintManager mFingerprintManager;
 
     private final boolean mEnabled;
     private final float mThreshold;
@@ -197,11 +197,11 @@
     }
 
     private boolean hasEnrolledFace(int userId) {
-        return mFaceManager.hasEnrolledTemplates(userId);
+        return mFaceManager != null && mFaceManager.hasEnrolledTemplates(userId);
     }
 
     private boolean hasEnrolledFingerprint(int userId) {
-        return mFingerprintManager.hasEnrolledTemplates(userId);
+        return mFingerprintManager != null && mFingerprintManager.hasEnrolledTemplates(userId);
     }
 
     /**
diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
index 89b638b..89e08c1 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
@@ -16,6 +16,8 @@
 
 package com.android.server.biometrics.sensors;
 
+import static com.android.server.biometrics.sensors.BiometricSchedulerOperation.STATE_STARTED;
+
 import android.annotation.IntDef;
 import android.annotation.MainThread;
 import android.annotation.NonNull;
@@ -28,6 +30,7 @@
 import android.os.Looper;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.UserHandle;
 import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
 
@@ -35,6 +38,7 @@
 import com.android.modules.expresslog.Counter;
 import com.android.server.biometrics.BiometricSchedulerProto;
 import com.android.server.biometrics.BiometricsProto;
+import com.android.server.biometrics.Flags;
 import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDispatcher;
 
 import java.io.PrintWriter;
@@ -48,6 +52,7 @@
 import java.util.List;
 import java.util.Locale;
 import java.util.function.Consumer;
+import java.util.function.Supplier;
 
 /**
  * A scheduler for biometric HAL operations. Maintains a queue of {@link BaseClientMonitor}
@@ -56,11 +61,16 @@
  *
  * We currently assume (and require) that each biometric sensor have its own instance of a
  * {@link BiometricScheduler}.
+ *
+ * @param <T> Hal instance for starting the user.
+ * @param <U> Session associated with the current user id.
+ *
+ * TODO: (b/304604965) Update thread annotation when FLAGS_DE_HIDL is removed.
  */
 @MainThread
-public class BiometricScheduler {
+public class BiometricScheduler<T, U> {
 
-    private static final String BASE_TAG = "BiometricScheduler";
+    private static final String TAG = "BiometricScheduler";
     // Number of recent operations to keep in our logs for dumpsys
     protected static final int LOG_NUM_RECENT_OPERATIONS = 50;
 
@@ -89,30 +99,6 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface SensorType {}
 
-    public static @SensorType int sensorTypeFromFingerprintProperties(
-            @NonNull FingerprintSensorPropertiesInternal props) {
-        if (props.isAnyUdfpsType()) {
-            return SENSOR_TYPE_UDFPS;
-        }
-
-        return SENSOR_TYPE_FP_OTHER;
-    }
-
-    public static String sensorTypeToString(@SensorType int sensorType) {
-        switch (sensorType) {
-            case SENSOR_TYPE_UNKNOWN:
-                return "Unknown";
-            case SENSOR_TYPE_FACE:
-                return "Face";
-            case SENSOR_TYPE_UDFPS:
-                return "Udfps";
-            case SENSOR_TYPE_FP_OTHER:
-                return "OtherFp";
-            default:
-                return "UnknownUnknown";
-        }
-    }
-
     private static final class CrashState {
         static final int NUM_ENTRIES = 10;
         final String timestamp;
@@ -145,8 +131,8 @@
         }
     }
 
-    @NonNull protected final String mBiometricTag;
-    private final @SensorType int mSensorType;
+    @SensorType
+    private final int mSensorType;
     @Nullable private final GestureAvailabilityDispatcher mGestureAvailabilityDispatcher;
     @NonNull private final IBiometricService mBiometricService;
     @NonNull protected final Handler mHandler;
@@ -157,6 +143,43 @@
     private int mTotalOperationsHandled;
     private final int mRecentOperationsLimit;
     @NonNull private final List<Integer> mRecentOperations;
+    @Nullable private StopUserClient<U> mStopUserClient;
+    @NonNull private Supplier<Integer> mCurrentUserRetriever;
+    @Nullable private UserSwitchProvider<T, U> mUserSwitchProvider;
+
+    private class UserSwitchClientCallback implements ClientMonitorCallback {
+        @NonNull private final BaseClientMonitor mOwner;
+
+        UserSwitchClientCallback(@NonNull BaseClientMonitor owner) {
+            mOwner = owner;
+        }
+
+        @Override
+        public void onClientFinished(@NonNull BaseClientMonitor clientMonitor, boolean success) {
+            mHandler.post(() -> {
+                Slog.d(TAG, "[Client finished] " + clientMonitor + ", success: " + success);
+
+                // Set mStopUserClient to null when StopUserClient fails. Otherwise it's possible
+                // for that the queue will wait indefinitely until the field is cleared.
+                if (clientMonitor instanceof StopUserClient<?>) {
+                    if (!success) {
+                        Slog.w(TAG, "StopUserClient failed(), is the HAL stuck? "
+                                + "Clearing mStopUserClient");
+                    }
+                    mStopUserClient = null;
+                }
+                if (mCurrentOperation != null && mCurrentOperation.isFor(mOwner)) {
+                    mCurrentOperation = null;
+                } else {
+                    // can happen if the hal dies and is usually okay
+                    // do not unset the current operation that may be newer
+                    Slog.w(TAG, "operation is already null or different (reset?): "
+                            + mCurrentOperation);
+                }
+                startNextOperationIfIdle();
+            });
+        }
+    }
 
     // Internal callback, notified when an operation is complete. Notifies the requester
     // that the operation is complete, before performing internal scheduler work (such as
@@ -164,26 +187,26 @@
     private final ClientMonitorCallback mInternalCallback = new ClientMonitorCallback() {
         @Override
         public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
-            Slog.d(getTag(), "[Started] " + clientMonitor);
+            Slog.d(TAG, "[Started] " + clientMonitor);
         }
 
         @Override
         public void onClientFinished(@NonNull BaseClientMonitor clientMonitor, boolean success) {
             mHandler.post(() -> {
                 if (mCurrentOperation == null) {
-                    Slog.e(getTag(), "[Finishing] " + clientMonitor
+                    Slog.e(TAG, "[Finishing] " + clientMonitor
                             + " but current operation is null, success: " + success
                             + ", possible lifecycle bug in clientMonitor implementation?");
                     return;
                 }
 
                 if (!mCurrentOperation.isFor(clientMonitor)) {
-                    Slog.e(getTag(), "[Ignoring Finish] " + clientMonitor + " does not match"
+                    Slog.e(TAG, "[Ignoring Finish] " + clientMonitor + " does not match"
                             + " current: " + mCurrentOperation);
                     return;
                 }
 
-                Slog.d(getTag(), "[Finishing] " + clientMonitor + ", success: " + success);
+                Slog.d(TAG, "[Finishing] " + clientMonitor + ", success: " + success);
 
                 if (mGestureAvailabilityDispatcher != null) {
                     mGestureAvailabilityDispatcher.markSensorActive(
@@ -202,13 +225,11 @@
     };
 
     @VisibleForTesting
-    public BiometricScheduler(@NonNull String tag,
-            @NonNull Handler handler,
+    public BiometricScheduler(@NonNull Handler handler,
             @SensorType int sensorType,
             @Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
             @NonNull IBiometricService biometricService,
             int recentOperationsLimit) {
-        mBiometricTag = tag;
         mHandler = handler;
         mSensorType = sensorType;
         mGestureAvailabilityDispatcher = gestureAvailabilityDispatcher;
@@ -219,49 +240,140 @@
         mRecentOperations = new ArrayList<>();
     }
 
+    @VisibleForTesting
+    public BiometricScheduler(@NonNull Handler handler,
+            @SensorType int sensorType,
+            @Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
+            @NonNull IBiometricService biometricService,
+            int recentOperationsLimit,
+            @NonNull Supplier<Integer> currentUserRetriever,
+            @Nullable UserSwitchProvider<T, U> userSwitchProvider) {
+        mHandler = handler;
+        mSensorType = sensorType;
+        mGestureAvailabilityDispatcher = gestureAvailabilityDispatcher;
+        mPendingOperations = new ArrayDeque<>();
+        mBiometricService = biometricService;
+        mCrashStates = new ArrayDeque<>();
+        mRecentOperationsLimit = recentOperationsLimit;
+        mRecentOperations = new ArrayList<>();
+        mCurrentUserRetriever = currentUserRetriever;
+        mUserSwitchProvider = userSwitchProvider;
+    }
+
+    public BiometricScheduler(@NonNull Handler handler,
+            @SensorType int sensorType,
+            @Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
+            @NonNull Supplier<Integer> currentUserRetriever,
+            @NonNull UserSwitchProvider<T, U> userSwitchProvider) {
+        this(handler, sensorType, gestureAvailabilityDispatcher,
+                IBiometricService.Stub.asInterface(ServiceManager.getService(
+                        Context.BIOMETRIC_SERVICE)), LOG_NUM_RECENT_OPERATIONS,
+                currentUserRetriever, userSwitchProvider);
+    }
+
     /**
      * Creates a new scheduler.
      *
-     * @param tag for the specific instance of the scheduler. Should be unique.
      * @param sensorType the sensorType that this scheduler is handling.
      * @param gestureAvailabilityDispatcher may be null if the sensor does not support gestures
      *                                      (such as fingerprint swipe).
      */
-    public BiometricScheduler(@NonNull String tag,
-            @SensorType int sensorType,
+    public BiometricScheduler(@SensorType int sensorType,
             @Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher) {
-        this(tag, new Handler(Looper.getMainLooper()), sensorType, gestureAvailabilityDispatcher,
+        this(new Handler(Looper.getMainLooper()), sensorType, gestureAvailabilityDispatcher,
                 IBiometricService.Stub.asInterface(
                         ServiceManager.getService(Context.BIOMETRIC_SERVICE)),
                 LOG_NUM_RECENT_OPERATIONS);
     }
 
+    /**
+     * Returns sensor type for a fingerprint sensor.
+     */
+    @SensorType
+    public static int sensorTypeFromFingerprintProperties(
+            @NonNull FingerprintSensorPropertiesInternal props) {
+        if (props.isAnyUdfpsType()) {
+            return SENSOR_TYPE_UDFPS;
+        }
+
+        return SENSOR_TYPE_FP_OTHER;
+    }
+
     @VisibleForTesting
     public ClientMonitorCallback getInternalCallback() {
         return mInternalCallback;
     }
 
-    protected String getTag() {
-        return BASE_TAG + "/" + mBiometricTag;
+    protected void startNextOperationIfIdle() {
+        if (Flags.deHidl()) {
+            startNextOperation();
+        } else {
+            startNextOperationIfIdleLegacy();
+        }
     }
 
-    protected void startNextOperationIfIdle() {
+    protected void startNextOperation() {
         if (mCurrentOperation != null) {
-            Slog.v(getTag(), "Not idle, current operation: " + mCurrentOperation);
+            Slog.v(TAG, "Not idle, current operation: " + mCurrentOperation);
             return;
         }
         if (mPendingOperations.isEmpty()) {
-            Slog.d(getTag(), "No operations, returning to idle");
+            Slog.d(TAG, "No operations, returning to idle");
+            return;
+        }
+
+        final int currentUserId = mCurrentUserRetriever.get();
+        final int nextUserId = mPendingOperations.getFirst().getTargetUserId();
+
+        if (nextUserId == currentUserId || mPendingOperations.getFirst().isStartUserOperation()) {
+            startNextOperationIfIdleLegacy();
+        } else if (currentUserId == UserHandle.USER_NULL && mUserSwitchProvider != null) {
+            final BaseClientMonitor startClient =
+                    mUserSwitchProvider.getStartUserClient(nextUserId);
+            final UserSwitchClientCallback finishedCallback =
+                    new UserSwitchClientCallback(startClient);
+
+            Slog.d(TAG, "[Starting User] " + startClient);
+            mCurrentOperation = new BiometricSchedulerOperation(
+                    startClient, finishedCallback, STATE_STARTED);
+            startClient.start(finishedCallback);
+        } else if (mUserSwitchProvider != null) {
+            if (mStopUserClient != null) {
+                Slog.d(TAG, "[Waiting for StopUser] " + mStopUserClient);
+            } else {
+                mStopUserClient = mUserSwitchProvider
+                        .getStopUserClient(currentUserId);
+                final UserSwitchClientCallback finishedCallback =
+                        new UserSwitchClientCallback(mStopUserClient);
+
+                Slog.d(TAG, "[Stopping User] current: " + currentUserId
+                        + ", next: " + nextUserId + ". " + mStopUserClient);
+                mCurrentOperation = new BiometricSchedulerOperation(
+                        mStopUserClient, finishedCallback, STATE_STARTED);
+                mStopUserClient.start(finishedCallback);
+            }
+        } else {
+            Slog.e(TAG, "Cannot start next operation.");
+        }
+    }
+
+    protected void startNextOperationIfIdleLegacy() {
+        if (mCurrentOperation != null) {
+            Slog.v(TAG, "Not idle, current operation: " + mCurrentOperation);
+            return;
+        }
+        if (mPendingOperations.isEmpty()) {
+            Slog.d(TAG, "No operations, returning to idle");
             return;
         }
 
         mCurrentOperation = mPendingOperations.poll();
-        Slog.d(getTag(), "[Polled] " + mCurrentOperation);
+        Slog.d(TAG, "[Polled] " + mCurrentOperation);
 
         // If the operation at the front of the queue has been marked for cancellation, send
         // ERROR_CANCELED. No need to start this client.
         if (mCurrentOperation.isMarkedCanceling()) {
-            Slog.d(getTag(), "[Now Cancelling] " + mCurrentOperation);
+            Slog.d(TAG, "[Now Cancelling] " + mCurrentOperation);
             mCurrentOperation.cancel(mHandler, mInternalCallback);
             // Now we wait for the client to send its FinishCallback, which kicks off the next
             // operation.
@@ -289,7 +401,7 @@
                 // Note down current length of queue
                 final int pendingOperationsLength = mPendingOperations.size();
                 final BiometricSchedulerOperation lastOperation = mPendingOperations.peekLast();
-                Slog.e(getTag(), "[Unable To Start] " + mCurrentOperation
+                Slog.e(TAG, "[Unable To Start] " + mCurrentOperation
                         + ". Last pending operation: " + lastOperation);
 
                 // Then for each operation currently in the pending queue at the time of this
@@ -298,10 +410,10 @@
                 for (int i = 0; i < pendingOperationsLength; i++) {
                     final BiometricSchedulerOperation operation = mPendingOperations.pollFirst();
                     if (operation != null) {
-                        Slog.w(getTag(), "[Aborting Operation] " + operation);
+                        Slog.w(TAG, "[Aborting Operation] " + operation);
                         operation.abort();
                     } else {
-                        Slog.e(getTag(), "Null operation, index: " + i
+                        Slog.e(TAG, "Null operation, index: " + i
                                 + ", expected length: " + pendingOperationsLength);
                     }
                 }
@@ -317,9 +429,9 @@
                 mBiometricService.onReadyForAuthentication(
                         mCurrentOperation.getClientMonitor().getRequestId(), cookie);
             } catch (RemoteException e) {
-                Slog.e(getTag(), "Remote exception when contacting BiometricService", e);
+                Slog.e(TAG, "Remote exception when contacting BiometricService", e);
             }
-            Slog.d(getTag(), "Waiting for cookie before starting: " + mCurrentOperation);
+            Slog.d(TAG, "Waiting for cookie before starting: " + mCurrentOperation);
         }
     }
 
@@ -338,14 +450,14 @@
      */
     public void startPreparedClient(int cookie) {
         if (mCurrentOperation == null) {
-            Slog.e(getTag(), "Current operation is null");
+            Slog.e(TAG, "Current operation is null");
             return;
         }
 
         if (mCurrentOperation.startWithCookie(mInternalCallback, cookie)) {
-            Slog.d(getTag(), "[Started] Prepared client: " + mCurrentOperation);
+            Slog.d(TAG, "[Started] Prepared client: " + mCurrentOperation);
         } else {
-            Slog.e(getTag(), "[Unable To Start] Prepared client: " + mCurrentOperation);
+            Slog.e(TAG, "[Unable To Start] Prepared client: " + mCurrentOperation);
             mCurrentOperation = null;
             startNextOperationIfIdle();
         }
@@ -374,13 +486,13 @@
         if (clientMonitor.interruptsPrecedingClients()) {
             for (BiometricSchedulerOperation operation : mPendingOperations) {
                 if (operation.markCanceling()) {
-                    Slog.d(getTag(), "New client, marking pending op as canceling: " + operation);
+                    Slog.d(TAG, "New client, marking pending op as canceling: " + operation);
                 }
             }
         }
 
         mPendingOperations.add(new BiometricSchedulerOperation(clientMonitor, clientCallback));
-        Slog.d(getTag(), "[Added] " + clientMonitor
+        Slog.d(TAG, "[Added] " + clientMonitor
                 + ", new queue size: " + mPendingOperations.size());
 
         // If the new operation should interrupt preceding clients, and if the current operation is
@@ -389,7 +501,7 @@
                 && mCurrentOperation != null
                 && mCurrentOperation.isInterruptable()
                 && mCurrentOperation.isStarted()) {
-            Slog.d(getTag(), "[Cancelling Interruptable]: " + mCurrentOperation);
+            Slog.d(TAG, "[Cancelling Interruptable]: " + mCurrentOperation);
             mCurrentOperation.cancel(mHandler, mInternalCallback);
         } else {
             startNextOperationIfIdle();
@@ -401,16 +513,16 @@
      * @param token from the caller, should match the token passed in when requesting enrollment
      */
     public void cancelEnrollment(IBinder token, long requestId) {
-        Slog.d(getTag(), "cancelEnrollment, requestId: " + requestId);
+        Slog.d(TAG, "cancelEnrollment, requestId: " + requestId);
 
         if (mCurrentOperation != null
                 && canCancelEnrollOperation(mCurrentOperation, token, requestId)) {
-            Slog.d(getTag(), "Cancelling enrollment op: " + mCurrentOperation);
+            Slog.d(TAG, "Cancelling enrollment op: " + mCurrentOperation);
             mCurrentOperation.cancel(mHandler, mInternalCallback);
         } else {
             for (BiometricSchedulerOperation operation : mPendingOperations) {
                 if (canCancelEnrollOperation(operation, token, requestId)) {
-                    Slog.d(getTag(), "Cancelling pending enrollment op: " + operation);
+                    Slog.d(TAG, "Cancelling pending enrollment op: " + operation);
                     operation.markCanceling();
                 }
             }
@@ -423,16 +535,16 @@
      * @param requestId the id returned when requesting authentication
      */
     public void cancelAuthenticationOrDetection(IBinder token, long requestId) {
-        Slog.d(getTag(), "cancelAuthenticationOrDetection, requestId: " + requestId);
+        Slog.d(TAG, "cancelAuthenticationOrDetection, requestId: " + requestId);
 
         if (mCurrentOperation != null
                 && canCancelAuthOperation(mCurrentOperation, token, requestId)) {
-            Slog.d(getTag(), "Cancelling auth/detect op: " + mCurrentOperation);
+            Slog.d(TAG, "Cancelling auth/detect op: " + mCurrentOperation);
             mCurrentOperation.cancel(mHandler, mInternalCallback);
         } else {
             for (BiometricSchedulerOperation operation : mPendingOperations) {
                 if (canCancelAuthOperation(operation, token, requestId)) {
-                    Slog.d(getTag(), "Cancelling pending auth/detect op: " + operation);
+                    Slog.d(TAG, "Cancelling pending auth/detect op: " + operation);
                     operation.markCanceling();
                 }
             }
@@ -504,11 +616,11 @@
                 mCurrentOperation != null ? mCurrentOperation.toString() : null,
                 pendingOperations);
         mCrashStates.add(crashState);
-        Slog.e(getTag(), "Recorded crash state: " + crashState.toString());
+        Slog.e(TAG, "Recorded crash state: " + crashState.toString());
     }
 
     public void dump(PrintWriter pw) {
-        pw.println("Dump of BiometricScheduler " + getTag());
+        pw.println("Dump of BiometricScheduler " + TAG);
         pw.println("Type: " + mSensorType);
         pw.println("Current operation: " + mCurrentOperation);
         pw.println("Pending operations: " + mPendingOperations.size());
@@ -548,7 +660,7 @@
      * HAL dies.
      */
     public void reset() {
-        Slog.d(getTag(), "Resetting scheduler");
+        Slog.d(TAG, "Resetting scheduler");
         mPendingOperations.clear();
         mCurrentOperation = null;
     }
@@ -562,11 +674,11 @@
             return;
         }
         for (BiometricSchedulerOperation pendingOperation : mPendingOperations) {
-            Slog.d(getTag(), "[Watchdog cancelling pending] "
+            Slog.d(TAG, "[Watchdog cancelling pending] "
                     + pendingOperation.getClientMonitor());
             pendingOperation.markCancelingForWatchdog();
         }
-        Slog.d(getTag(), "[Watchdog cancelling current] "
+        Slog.d(TAG, "[Watchdog cancelling current] "
                 + mCurrentOperation.getClientMonitor());
         mCurrentOperation.cancel(mHandler, getInternalCallback());
     }
@@ -590,9 +702,23 @@
     /**
      * Handle stop user client when user switching occurs.
      */
-    public void onUserStopped() {}
+    public void onUserStopped() {
+        if (mStopUserClient == null) {
+            Slog.e(TAG, "Unexpected onUserStopped");
+            return;
+        }
+
+        Slog.d(TAG, "[OnUserStopped]: " + mStopUserClient);
+        mStopUserClient.onUserStopped();
+        mStopUserClient = null;
+    }
 
     public Handler getHandler() {
         return mHandler;
     }
+
+    @Nullable
+    public StopUserClient<?> getStopUserClient() {
+        return mStopUserClient;
+    }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/StopUserClient.java b/services/core/java/com/android/server/biometrics/sensors/StopUserClient.java
index e8654dc..e01c4ec 100644
--- a/services/core/java/com/android/server/biometrics/sensors/StopUserClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/StopUserClient.java
@@ -30,7 +30,10 @@
 
 /**
  * Abstract class for stopping a user.
- * @param <T> Interface for stopping the user.
+ *
+ * @param <T> Session for stopping the user. It should be either an instance of
+ *            {@link com.android.server.biometrics.sensors.fingerprint.aidl.AidlSession} or
+ *            {@link com.android.server.biometrics.sensors.face.aidl.AidlSession}.
  */
 public abstract class StopUserClient<T> extends HalClientMonitor<T> {
 
diff --git a/services/core/java/com/android/server/biometrics/sensors/UserAwareBiometricScheduler.java b/services/core/java/com/android/server/biometrics/sensors/UserAwareBiometricScheduler.java
index 3753bbd..7ca10e3 100644
--- a/services/core/java/com/android/server/biometrics/sensors/UserAwareBiometricScheduler.java
+++ b/services/core/java/com/android/server/biometrics/sensors/UserAwareBiometricScheduler.java
@@ -33,10 +33,14 @@
 
 /**
  * A user-aware scheduler that requests user-switches based on scheduled operation's targetUserId.
+ * TODO (b/304604965): Remove class when Flags.FLAG_DE_HIDL is removed.
+ *
+ * @param <T> Hal instance for starting the user.
+ * @param <U> Session associated with the current user id.
  */
-public class UserAwareBiometricScheduler extends BiometricScheduler {
+public class UserAwareBiometricScheduler<T, U> extends BiometricScheduler<T, U> {
 
-    private static final String BASE_TAG = "UaBiometricScheduler";
+    private static final String TAG = "UaBiometricScheduler";
 
     /**
      * Interface to retrieve the owner's notion of the current userId. Note that even though
@@ -66,13 +70,13 @@
         @Override
         public void onClientFinished(@NonNull BaseClientMonitor clientMonitor, boolean success) {
             mHandler.post(() -> {
-                Slog.d(getTag(), "[Client finished] " + clientMonitor + ", success: " + success);
+                Slog.d(TAG, "[Client finished] " + clientMonitor + ", success: " + success);
 
                 // Set mStopUserClient to null when StopUserClient fails. Otherwise it's possible
                 // for that the queue will wait indefinitely until the field is cleared.
                 if (clientMonitor instanceof StopUserClient<?>) {
                     if (!success) {
-                        Slog.w(getTag(), "StopUserClient failed(), is the HAL stuck? "
+                        Slog.w(TAG, "StopUserClient failed(), is the HAL stuck? "
                                 + "Clearing mStopUserClient");
                     }
                     mStopUserClient = null;
@@ -82,7 +86,7 @@
                 } else {
                     // can happen if the hal dies and is usually okay
                     // do not unset the current operation that may be newer
-                    Slog.w(getTag(), "operation is already null or different (reset?): "
+                    Slog.w(TAG, "operation is already null or different (reset?): "
                             + mCurrentOperation);
                 }
                 startNextOperationIfIdle();
@@ -98,7 +102,7 @@
             @NonNull IBiometricService biometricService,
             @NonNull CurrentUserRetriever currentUserRetriever,
             @NonNull UserSwitchCallback userSwitchCallback) {
-        super(tag, handler, sensorType, gestureAvailabilityDispatcher, biometricService,
+        super(handler, sensorType, gestureAvailabilityDispatcher, biometricService,
                 LOG_NUM_RECENT_OPERATIONS);
 
         mCurrentUserRetriever = currentUserRetriever;
@@ -117,18 +121,13 @@
     }
 
     @Override
-    protected String getTag() {
-        return BASE_TAG + "/" + mBiometricTag;
-    }
-
-    @Override
     protected void startNextOperationIfIdle() {
         if (mCurrentOperation != null) {
-            Slog.v(getTag(), "Not idle, current operation: " + mCurrentOperation);
+            Slog.v(TAG, "Not idle, current operation: " + mCurrentOperation);
             return;
         }
         if (mPendingOperations.isEmpty()) {
-            Slog.d(getTag(), "No operations, returning to idle");
+            Slog.d(TAG, "No operations, returning to idle");
             return;
         }
 
@@ -143,20 +142,20 @@
             final ClientFinishedCallback finishedCallback =
                     new ClientFinishedCallback(startClient);
 
-            Slog.d(getTag(), "[Starting User] " + startClient);
+            Slog.d(TAG, "[Starting User] " + startClient);
             mCurrentOperation = new BiometricSchedulerOperation(
                     startClient, finishedCallback, STATE_STARTED);
             startClient.start(finishedCallback);
         } else {
             if (mStopUserClient != null) {
-                Slog.d(getTag(), "[Waiting for StopUser] " + mStopUserClient);
+                Slog.d(TAG, "[Waiting for StopUser] " + mStopUserClient);
             } else {
                 mStopUserClient = mUserSwitchCallback
                         .getStopUserClient(currentUserId);
                 final ClientFinishedCallback finishedCallback =
                         new ClientFinishedCallback(mStopUserClient);
 
-                Slog.d(getTag(), "[Stopping User] current: " + currentUserId
+                Slog.d(TAG, "[Stopping User] current: " + currentUserId
                         + ", next: " + nextUserId + ". " + mStopUserClient);
                 mCurrentOperation = new BiometricSchedulerOperation(
                         mStopUserClient, finishedCallback, STATE_STARTED);
@@ -168,11 +167,11 @@
     @Override
     public void onUserStopped() {
         if (mStopUserClient == null) {
-            Slog.e(getTag(), "Unexpected onUserStopped");
+            Slog.e(TAG, "Unexpected onUserStopped");
             return;
         }
 
-        Slog.d(getTag(), "[OnUserStopped]: " + mStopUserClient);
+        Slog.d(TAG, "[OnUserStopped]: " + mStopUserClient);
         mStopUserClient.onUserStopped();
         mStopUserClient = null;
     }
diff --git a/services/core/java/com/android/server/biometrics/sensors/UserSwitchProvider.java b/services/core/java/com/android/server/biometrics/sensors/UserSwitchProvider.java
new file mode 100644
index 0000000..bc5c55b
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/UserSwitchProvider.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors;
+
+import android.annotation.NonNull;
+
+/**
+ * Interface to get the appropriate start and stop user clients.
+ *
+ * @param <T> Hal instance for starting the user.
+ * @param <U> Session associated with the current user id.
+ */
+public interface UserSwitchProvider<T, U> {
+    @NonNull
+    StartUserClient<T, U> getStartUserClient(int newUserId);
+    @NonNull
+    StopUserClient<U> getStopUserClient(int userId);
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
index 6af223b..0f964bb 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
@@ -78,6 +78,7 @@
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
+import java.util.function.Function;
 import java.util.function.Supplier;
 
 /**
@@ -99,6 +100,9 @@
             mBiometricStateCallback;
     @NonNull
     private final FaceProviderFunction mFaceProviderFunction;
+    @NonNull private final Function<String, FaceProvider> mFaceProvider;
+    @NonNull
+    private final Supplier<String[]> mAidlInstanceNameSupplier;
 
     interface FaceProviderFunction {
         FaceProvider getFaceProvider(Pair<String, SensorProps[]> filteredSensorProps,
@@ -671,23 +675,9 @@
             final List<ServiceProvider> providers = new ArrayList<>();
 
             for (String instance : instances) {
-                final String fqName = IFace.DESCRIPTOR + "/" + instance;
-                final IFace face = IFace.Stub.asInterface(
-                        Binder.allowBlocking(ServiceManager.waitForDeclaredService(fqName)));
-                if (face == null) {
-                    Slog.e(TAG, "Unable to get declared service: " + fqName);
-                    continue;
-                }
-                try {
-                    final SensorProps[] props = face.getSensorProps();
-                    final FaceProvider provider = new FaceProvider(getContext(),
-                            mBiometricStateCallback, props, instance, mLockoutResetDispatcher,
-                            BiometricContext.getInstance(getContext()),
-                            false /* resetLockoutRequiresChallenge */);
-                    providers.add(provider);
-                } catch (RemoteException e) {
-                    Slog.e(TAG, "Remote exception in getSensorProps: " + fqName);
-                }
+                final FaceProvider provider = mFaceProvider.apply(instance);
+                Slog.i(TAG, "Adding AIDL provider: " + instance);
+                providers.add(provider);
             }
 
             return providers;
@@ -700,7 +690,7 @@
 
             mRegistry.registerAll(() -> {
                 List<String> aidlSensors = new ArrayList<>();
-                final String[] instances = ServiceManager.getDeclaredInstances(IFace.DESCRIPTOR);
+                final String[] instances = mAidlInstanceNameSupplier.get();
                 if (instances != null) {
                     aidlSensors.addAll(Lists.newArrayList(instances));
                 }
@@ -813,11 +803,15 @@
 
     public FaceService(Context context) {
         this(context, null /* faceProviderFunction */, () -> IBiometricService.Stub.asInterface(
-                ServiceManager.getService(Context.BIOMETRIC_SERVICE)));
+                ServiceManager.getService(Context.BIOMETRIC_SERVICE)), null /* faceProvider */,
+                () -> ServiceManager.getDeclaredInstances(IFace.DESCRIPTOR));
     }
 
-    @VisibleForTesting FaceService(Context context, FaceProviderFunction faceProviderFunction,
-            Supplier<IBiometricService> biometricServiceSupplier) {
+    @VisibleForTesting FaceService(Context context,
+            FaceProviderFunction faceProviderFunction,
+            Supplier<IBiometricService> biometricServiceSupplier,
+            Function<String, FaceProvider> faceProvider,
+            Supplier<String[]> aidlInstanceNameSupplier) {
         super(context);
         mServiceWrapper = new FaceServiceWrapper();
         mLockoutResetDispatcher = new LockoutResetDispatcher(context);
@@ -830,6 +824,28 @@
                 mBiometricStateCallback.start(mRegistry.getProviders());
             }
         });
+        mAidlInstanceNameSupplier = aidlInstanceNameSupplier;
+
+        mFaceProvider = faceProvider != null ? faceProvider : (name) -> {
+            final String fqName = IFace.DESCRIPTOR + "/" + name;
+            final IFace face = IFace.Stub.asInterface(
+                    Binder.allowBlocking(ServiceManager.waitForDeclaredService(fqName)));
+            if (face == null) {
+                Slog.e(TAG, "Unable to get declared service: " + fqName);
+                return null;
+            }
+            try {
+                final SensorProps[] props = face.getSensorProps();
+                return new FaceProvider(getContext(),
+                        mBiometricStateCallback, props, name, mLockoutResetDispatcher,
+                        BiometricContext.getInstance(getContext()),
+                        false /* resetLockoutRequiresChallenge */);
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Remote exception in getSensorProps: " + fqName);
+            }
+
+            return null;
+        };
 
         if (Flags.deHidl()) {
             mFaceProviderFunction = faceProviderFunction != null ? faceProviderFunction :
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlSession.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlSession.java
index af46f44..3d61f99 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlSession.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlSession.java
@@ -53,12 +53,10 @@
         mAidlResponseHandler = aidlResponseHandler;
     }
 
-    /** The underlying {@link ISession}. */
     @NonNull public ISession getSession() {
         return mSession;
     }
 
-    /** The user id associated with the session. */
     public int getUserId() {
         return mUserId;
     }
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
index 9fa15b8..e4ecf1a 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
@@ -39,6 +39,7 @@
 import android.hardware.face.IFaceServiceReceiver;
 import android.os.Binder;
 import android.os.Handler;
+import android.os.HandlerThread;
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.RemoteException;
@@ -88,6 +89,8 @@
  * Provider for a single instance of the {@link IFace} HAL.
  */
 public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
+
+    private static final String TAG = "FaceProvider";
     private static final int ENROLL_TIMEOUT_SEC = 75;
 
     private boolean mTestHalEnabled;
@@ -159,7 +162,7 @@
             @NonNull BiometricContext biometricContext,
             boolean resetLockoutRequiresChallenge) {
         this(context, biometricStateCallback, props, halInstanceName, lockoutResetDispatcher,
-                biometricContext, null /* daemon */, resetLockoutRequiresChallenge,
+                biometricContext, null /* daemon */, getHandler(), resetLockoutRequiresChallenge,
                 false /* testHalEnabled */);
     }
 
@@ -169,13 +172,19 @@
             @NonNull String halInstanceName,
             @NonNull LockoutResetDispatcher lockoutResetDispatcher,
             @NonNull BiometricContext biometricContext,
-            @Nullable IFace daemon, boolean resetLockoutRequiresChallenge,
+            @Nullable IFace daemon,
+            @NonNull Handler handler,
+            boolean resetLockoutRequiresChallenge,
             boolean testHalEnabled) {
         mContext = context;
         mBiometricStateCallback = biometricStateCallback;
         mHalInstanceName = halInstanceName;
         mFaceSensors = new SensorList<>(ActivityManager.getService());
-        mHandler = new Handler(Looper.getMainLooper());
+        if (Flags.deHidl()) {
+            mHandler = handler;
+        } else {
+            mHandler = new Handler(Looper.getMainLooper());
+        }
         mUsageStats = new UsageStats(context);
         mLockoutResetDispatcher = lockoutResetDispatcher;
         mActivityTaskManager = ActivityTaskManager.getInstance();
@@ -189,6 +198,13 @@
         initSensors(resetLockoutRequiresChallenge, props);
     }
 
+    @NonNull
+    private static Handler getHandler() {
+        HandlerThread handlerThread = new HandlerThread(TAG);
+        handlerThread.start();
+        return new Handler(handlerThread.getLooper());
+    }
+
     private void initAuthenticationBroadcastReceiver() {
         new AuthenticationStatsBroadcastReceiver(
                 mContext,
@@ -230,8 +246,8 @@
                         prop.commonProps.maxEnrollmentsPerUser, componentInfo, prop.sensorType,
                         prop.supportsDetectInteraction, prop.halControlsPreview,
                         false /* resetLockoutRequiresChallenge */);
-                final Sensor sensor = new Sensor(getTag() + "/" + sensorId, this,
-                        mContext, mHandler, internalProp, mLockoutResetDispatcher,
+                final Sensor sensor = new Sensor(this,
+                        mContext, mHandler, internalProp,
                         mBiometricContext);
                 sensor.init(mLockoutResetDispatcher, this);
                 final int userId = sensor.getLazySession().get() == null ? UserHandle.USER_NULL :
@@ -250,9 +266,8 @@
 
     private void addHidlSensors(SensorProps prop, boolean resetLockoutRequiresChallenge) {
         final int sensorId = prop.commonProps.sensorId;
-        final Sensor sensor = new HidlToAidlSensorAdapter(getTag() + "/" + sensorId, this,
-                mContext, mHandler, prop, mLockoutResetDispatcher,
-                mBiometricContext, resetLockoutRequiresChallenge,
+        final Sensor sensor = new HidlToAidlSensorAdapter(this, mContext, mHandler, prop,
+                mLockoutResetDispatcher, mBiometricContext, resetLockoutRequiresChallenge,
                 () -> {
                     //TODO: update to make this testable
                     scheduleInternalCleanup(sensorId, ActivityManager.getCurrentUser(),
@@ -279,8 +294,7 @@
 
     private void addAidlSensors(SensorProps prop, boolean resetLockoutRequiresChallenge) {
         final int sensorId = prop.commonProps.sensorId;
-        final Sensor sensor = new Sensor(getTag() + "/" + sensorId, this, mContext,
-                mHandler, prop, mLockoutResetDispatcher, mBiometricContext,
+        final Sensor sensor = new Sensor(this, mContext, mHandler, prop, mBiometricContext,
                 resetLockoutRequiresChallenge);
         sensor.init(mLockoutResetDispatcher, this);
         final int userId = sensor.getLazySession().get() == null ? UserHandle.USER_NULL :
@@ -296,7 +310,7 @@
     }
 
     private String getTag() {
-        return "FaceProvider/" + mHalInstanceName;
+        return TAG + "/" + mHalInstanceName;
     }
 
     boolean hasHalInstance() {
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceStopUserClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceStopUserClient.java
index 0110ae9..e5ae8e3 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceStopUserClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceStopUserClient.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
+import android.hardware.biometrics.face.ISession;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.Slog;
@@ -30,10 +31,10 @@
 
 import java.util.function.Supplier;
 
-public class FaceStopUserClient extends StopUserClient<AidlSession> {
+public class FaceStopUserClient extends StopUserClient<ISession> {
     private static final String TAG = "FaceStopUserClient";
 
-    public FaceStopUserClient(@NonNull Context context, @NonNull Supplier<AidlSession> lazyDaemon,
+    public FaceStopUserClient(@NonNull Context context, @NonNull Supplier<ISession> lazyDaemon,
             @Nullable IBinder token, int userId, int sensorId,
             @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
             @NonNull UserStoppedCallback callback) {
@@ -49,7 +50,7 @@
     @Override
     protected void startHalOperation() {
         try {
-            getFreshDaemon().getSession().close();
+            getFreshDaemon().close();
         } catch (RemoteException e) {
             Slog.e(TAG, "Remote exception", e);
             getCallback().onClientFinished(this, false /* success */);
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 3e5c599..635e79a 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
@@ -58,6 +58,7 @@
 import com.android.server.biometrics.sensors.StartUserClient;
 import com.android.server.biometrics.sensors.StopUserClient;
 import com.android.server.biometrics.sensors.UserAwareBiometricScheduler;
+import com.android.server.biometrics.sensors.UserSwitchProvider;
 import com.android.server.biometrics.sensors.face.FaceUtils;
 
 import java.util.ArrayList;
@@ -71,15 +72,16 @@
  */
 public class Sensor {
 
+    private static final String TAG = "Sensor";
+
     private boolean mTestHalEnabled;
 
-    @NonNull private final String mTag;
     @NonNull private final FaceProvider mProvider;
     @NonNull private final Context mContext;
     @NonNull private final IBinder mToken;
     @NonNull private final Handler mHandler;
     @NonNull private final FaceSensorPropertiesInternal mSensorProperties;
-    @NonNull private BiometricScheduler mScheduler;
+    @NonNull private BiometricScheduler<IFace, ISession> mScheduler;
     @Nullable private LockoutTracker mLockoutTracker;
     @NonNull private final Map<Integer, Long> mAuthenticatorIds;
 
@@ -88,11 +90,9 @@
     @NonNull BiometricContext mBiometricContext;
 
 
-    Sensor(@NonNull String tag, @NonNull FaceProvider provider, @NonNull Context context,
+    Sensor(@NonNull FaceProvider provider, @NonNull Context context,
             @NonNull Handler handler, @NonNull FaceSensorPropertiesInternal sensorProperties,
-            @NonNull LockoutResetDispatcher lockoutResetDispatcher,
-            @NonNull BiometricContext biometricContext, @Nullable AidlSession session) {
-        mTag = tag;
+            @NonNull BiometricContext biometricContext) {
         mProvider = provider;
         mContext = context;
         mToken = new Binder();
@@ -102,105 +102,135 @@
         mAuthenticatorIds = new HashMap<>();
     }
 
-    Sensor(@NonNull String tag, @NonNull FaceProvider provider, @NonNull Context context,
-            @NonNull Handler handler, @NonNull FaceSensorPropertiesInternal sensorProperties,
-            @NonNull LockoutResetDispatcher lockoutResetDispatcher,
-            @NonNull BiometricContext biometricContext) {
-        this(tag, provider, context, handler, sensorProperties, lockoutResetDispatcher,
-                biometricContext, null);
-    }
-
-    public Sensor(@NonNull String tag, @NonNull FaceProvider provider, @NonNull Context context,
+    public Sensor(@NonNull FaceProvider provider, @NonNull Context context,
             @NonNull Handler handler, @NonNull SensorProps prop,
-            @NonNull LockoutResetDispatcher lockoutResetDispatcher,
             @NonNull BiometricContext biometricContext,
             boolean resetLockoutRequiresChallenge) {
-        this(tag, provider, context, handler,
+        this(provider, context, handler,
                 getFaceSensorPropertiesInternal(prop, resetLockoutRequiresChallenge),
-                lockoutResetDispatcher, biometricContext, null);
+                biometricContext);
     }
 
     /**
      * Initialize biometric scheduler, lockout tracker and session for the sensor.
      */
-    public void init(LockoutResetDispatcher lockoutResetDispatcher,
+    public void init(@NonNull LockoutResetDispatcher lockoutResetDispatcher,
+            @NonNull FaceProvider provider) {
+        if (Flags.deHidl()) {
+            setScheduler(getBiometricSchedulerForInit(lockoutResetDispatcher, provider));
+        } else {
+            setScheduler(getUserAwareBiometricSchedulerForInit(lockoutResetDispatcher, provider));
+        }
+        mLazySession = () -> mCurrentSession != null ? mCurrentSession : null;
+        mLockoutTracker = new LockoutCache();
+    }
+
+    private BiometricScheduler<IFace, ISession> getBiometricSchedulerForInit(
+            @NonNull LockoutResetDispatcher lockoutResetDispatcher,
+            @NonNull FaceProvider provider) {
+        return new BiometricScheduler<>(mHandler,
+                BiometricScheduler.SENSOR_TYPE_FACE,
+                null /* gestureAvailabilityDispatcher */,
+                () -> mCurrentSession != null ? mCurrentSession.getUserId() : UserHandle.USER_NULL,
+                new UserSwitchProvider<IFace, ISession>() {
+                    @NonNull
+                    @Override
+                    public StopUserClient<ISession> getStopUserClient(int userId) {
+                        return new FaceStopUserClient(mContext,
+                                () -> mLazySession.get().getSession(), mToken, userId,
+                                mSensorProperties.sensorId, BiometricLogger.ofUnknown(mContext),
+                                mBiometricContext, () -> mCurrentSession = null);
+                    }
+
+                    @NonNull
+                    @Override
+                    public StartUserClient<IFace, ISession> getStartUserClient(int newUserId) {
+                        final int sensorId = mSensorProperties.sensorId;
+                        final AidlResponseHandler resultController = new AidlResponseHandler(
+                                mContext, mScheduler, sensorId, newUserId,
+                                mLockoutTracker, lockoutResetDispatcher,
+                                mBiometricContext.getAuthSessionCoordinator(), () -> {
+                        },
+                                new AidlResponseHandler.AidlResponseHandlerCallback() {
+                                    @Override
+                                    public void onEnrollSuccess() {
+                                        mProvider.scheduleLoadAuthenticatorIdsForUser(sensorId,
+                                                newUserId);
+                                        mProvider.scheduleInvalidationRequest(sensorId,
+                                                newUserId);
+                                    }
+
+                                    @Override
+                                    public void onHardwareUnavailable() {
+                                        Slog.e(TAG, "Face sensor hardware unavailable.");
+                                        mCurrentSession = null;
+                                    }
+                                });
+
+                        return Sensor.this.getStartUserClient(resultController, sensorId,
+                                newUserId, provider);
+                    }
+                });
+    }
+
+    private UserAwareBiometricScheduler<IFace, ISession> getUserAwareBiometricSchedulerForInit(
+            LockoutResetDispatcher lockoutResetDispatcher,
             FaceProvider provider) {
-        mScheduler = new UserAwareBiometricScheduler(mTag,
+        return new UserAwareBiometricScheduler<>(TAG,
                 BiometricScheduler.SENSOR_TYPE_FACE, null /* gestureAvailabilityDispatcher */,
                 () -> mCurrentSession != null ? mCurrentSession.getUserId() : UserHandle.USER_NULL,
                 new UserAwareBiometricScheduler.UserSwitchCallback() {
                     @NonNull
                     @Override
-                    public StopUserClient<?> getStopUserClient(int userId) {
-                        return new FaceStopUserClient(mContext, mLazySession, mToken, userId,
-                                mSensorProperties.sensorId,
-                                BiometricLogger.ofUnknown(mContext), mBiometricContext,
-                                () -> mCurrentSession = null);
+                    public StopUserClient<ISession> getStopUserClient(int userId) {
+                        return new FaceStopUserClient(mContext,
+                                () -> mLazySession.get().getSession(), mToken, userId,
+                                mSensorProperties.sensorId, BiometricLogger.ofUnknown(mContext),
+                                mBiometricContext, () -> mCurrentSession = null);
                     }
 
                     @NonNull
                     @Override
-                    public StartUserClient<?, ?> getStartUserClient(int newUserId) {
+                    public StartUserClient<IFace, ISession> getStartUserClient(int newUserId) {
                         final int sensorId = mSensorProperties.sensorId;
+                        final AidlResponseHandler resultController = new AidlResponseHandler(
+                                mContext, mScheduler, sensorId, newUserId,
+                                mLockoutTracker, lockoutResetDispatcher,
+                                mBiometricContext.getAuthSessionCoordinator(), () -> {
+                                    Slog.e(TAG, "Face sensor hardware unavailable.");
+                                    mCurrentSession = null;
+                                });
 
-                        final AidlResponseHandler resultController;
-                        if (Flags.deHidl()) {
-                            resultController = new AidlResponseHandler(
-                                    mContext, mScheduler, sensorId, newUserId,
-                                    mLockoutTracker, lockoutResetDispatcher,
-                                    mBiometricContext.getAuthSessionCoordinator(), () -> {},
-                                    new AidlResponseHandler.AidlResponseHandlerCallback() {
-                                        @Override
-                                        public void onEnrollSuccess() {
-                                            mProvider.scheduleLoadAuthenticatorIdsForUser(sensorId,
-                                                    newUserId);
-                                            mProvider.scheduleInvalidationRequest(sensorId,
-                                                    newUserId);
-                                        }
-
-                                        @Override
-                                        public void onHardwareUnavailable() {
-                                            Slog.e(mTag, "Face sensor hardware unavailable.");
-                                            mCurrentSession = null;
-                                        }
-                                    });
-                        } else {
-                            resultController = new AidlResponseHandler(
-                                    mContext, mScheduler, sensorId, newUserId,
-                                    mLockoutTracker, lockoutResetDispatcher,
-                                    mBiometricContext.getAuthSessionCoordinator(), () -> {
-                                Slog.e(mTag, "Got ERROR_HW_UNAVAILABLE");
-                                mCurrentSession = null;
-                            });
-                        }
-
-                        final StartUserClient.UserStartedCallback<ISession> userStartedCallback =
-                                (userIdStarted, newSession, halInterfaceVersion) -> {
-                                    Slog.d(mTag, "New session created for user: "
-                                            + userIdStarted + " with hal version: "
-                                            + halInterfaceVersion);
-                                    mCurrentSession = new AidlSession(halInterfaceVersion,
-                                            newSession, userIdStarted, resultController);
-                                    if (FaceUtils.getLegacyInstance(sensorId)
-                                            .isInvalidationInProgress(mContext, userIdStarted)) {
-                                        Slog.w(mTag,
-                                                "Scheduling unfinished invalidation request for "
-                                                        + "sensor: "
-                                                        + sensorId
-                                                        + ", user: " + userIdStarted);
-                                        provider.scheduleInvalidationRequest(sensorId,
-                                                userIdStarted);
-                                    }
-                                };
-
-                        return new FaceStartUserClient(mContext, provider::getHalInstance,
-                                mToken, newUserId, mSensorProperties.sensorId,
-                                BiometricLogger.ofUnknown(mContext), mBiometricContext,
-                                resultController, userStartedCallback);
+                        return Sensor.this.getStartUserClient(resultController, sensorId,
+                                newUserId, provider);
                     }
                 });
-        mLazySession = () -> mCurrentSession != null ? mCurrentSession : null;
-        mLockoutTracker = new LockoutCache();
+    }
+
+    private FaceStartUserClient getStartUserClient(@NonNull AidlResponseHandler resultController,
+            int sensorId, int newUserId, @NonNull FaceProvider provider) {
+        final StartUserClient.UserStartedCallback<ISession> userStartedCallback =
+                (userIdStarted, newSession, halInterfaceVersion) -> {
+                    Slog.d(TAG, "New face session created for user: "
+                            + userIdStarted + " with hal version: "
+                            + halInterfaceVersion);
+                    mCurrentSession = new AidlSession(halInterfaceVersion,
+                            newSession, userIdStarted, resultController);
+                    if (FaceUtils.getLegacyInstance(sensorId)
+                            .isInvalidationInProgress(mContext, userIdStarted)) {
+                        Slog.w(TAG,
+                                "Scheduling unfinished invalidation request for "
+                                        + "face sensor: "
+                                        + sensorId
+                                        + ", user: " + userIdStarted);
+                        provider.scheduleInvalidationRequest(sensorId,
+                                userIdStarted);
+                    }
+                };
+
+        return new FaceStartUserClient(mContext, provider::getHalInstance, mToken, newUserId,
+                mSensorProperties.sensorId, BiometricLogger.ofUnknown(mContext), mBiometricContext,
+                resultController, userStartedCallback);
     }
 
     private static FaceSensorPropertiesInternal getFaceSensorPropertiesInternal(SensorProps prop,
@@ -213,13 +243,11 @@
                         info.softwareVersion));
             }
         }
-        final FaceSensorPropertiesInternal internalProp = new FaceSensorPropertiesInternal(
+        return new FaceSensorPropertiesInternal(
                 prop.commonProps.sensorId, prop.commonProps.sensorStrength,
                 prop.commonProps.maxEnrollmentsPerUser, componentInfo, prop.sensorType,
                 prop.supportsDetectInteraction, prop.halControlsPreview,
                 resetLockoutRequiresChallenge);
-
-        return internalProp;
     }
 
     @NonNull public Supplier<AidlSession> getLazySession() {
@@ -243,7 +271,7 @@
                 mProvider, this);
     }
 
-    @NonNull public BiometricScheduler getScheduler() {
+    @NonNull public BiometricScheduler<IFace, ISession> getScheduler() {
         return mScheduler;
     }
 
@@ -259,17 +287,17 @@
     }
 
     void setTestHalEnabled(boolean enabled) {
-        Slog.w(mTag, "setTestHalEnabled: " + enabled);
+        Slog.w(TAG, "Face setTestHalEnabled: " + enabled);
         if (enabled != mTestHalEnabled) {
             // The framework should retrieve a new session from the HAL.
             try {
                 if (mCurrentSession != null) {
                     // TODO(181984005): This should be scheduled instead of directly invoked
-                    Slog.d(mTag, "Closing old session");
+                    Slog.d(TAG, "Closing old face session");
                     mCurrentSession.getSession().close();
                 }
             } catch (RemoteException e) {
-                Slog.e(mTag, "RemoteException", e);
+                Slog.e(TAG, "RemoteException", e);
             }
             mCurrentSession = null;
         }
@@ -308,7 +336,7 @@
     public void onBinderDied() {
         final BaseClientMonitor client = mScheduler.getCurrentClient();
         if (client != null && client.isInterruptable()) {
-            Slog.e(mTag, "Sending ERROR_HW_UNAVAILABLE for client: " + client);
+            Slog.e(TAG, "Sending face hardware unavailable error for client: " + client);
             final ErrorConsumer errorConsumer = (ErrorConsumer) client;
             errorConsumer.onError(FaceManager.FACE_ERROR_HW_UNAVAILABLE,
                     0 /* vendorCode */);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
index 46ce0b6..5337666 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
@@ -120,7 +120,7 @@
     @NonNull private final FaceSensorPropertiesInternal mSensorProperties;
     @NonNull private final BiometricStateCallback mBiometricStateCallback;
     @NonNull private final Context mContext;
-    @NonNull private final BiometricScheduler mScheduler;
+    @NonNull private final BiometricScheduler<IBiometricsFace, AidlSession> mScheduler;
     @NonNull private final Handler mHandler;
     @NonNull private final Supplier<IBiometricsFace> mLazyDaemon;
     @NonNull private final LockoutHalImpl mLockoutTracker;
@@ -163,14 +163,15 @@
         private final int mSensorId;
         @NonNull private final Context mContext;
         @NonNull private final Handler mHandler;
-        @NonNull private final BiometricScheduler mScheduler;
+        @NonNull private final BiometricScheduler<IBiometricsFace, AidlSession> mScheduler;
         @Nullable private Callback mCallback;
         @NonNull private final LockoutHalImpl mLockoutTracker;
         @NonNull private final LockoutResetDispatcher mLockoutResetDispatcher;
 
 
         HalResultController(int sensorId, @NonNull Context context, @NonNull Handler handler,
-                @NonNull BiometricScheduler scheduler, @NonNull LockoutHalImpl lockoutTracker,
+                @NonNull BiometricScheduler<IBiometricsFace, AidlSession> scheduler,
+                @NonNull LockoutHalImpl lockoutTracker,
                 @NonNull LockoutResetDispatcher lockoutResetDispatcher) {
             mSensorId = sensorId;
             mContext = context;
@@ -352,7 +353,7 @@
             @NonNull FaceSensorPropertiesInternal sensorProps,
             @NonNull LockoutResetDispatcher lockoutResetDispatcher,
             @NonNull Handler handler,
-            @NonNull BiometricScheduler scheduler,
+            @NonNull BiometricScheduler<IBiometricsFace, AidlSession> scheduler,
             @NonNull BiometricContext biometricContext) {
         mSensorProperties = sensorProps;
         mContext = context;
@@ -395,7 +396,8 @@
             @NonNull LockoutResetDispatcher lockoutResetDispatcher) {
         final Handler handler = new Handler(Looper.getMainLooper());
         return new Face10(context, biometricStateCallback, sensorProps, lockoutResetDispatcher,
-                handler, new BiometricScheduler(TAG, BiometricScheduler.SENSOR_TYPE_FACE,
+                handler, new BiometricScheduler<>(
+                        BiometricScheduler.SENSOR_TYPE_FACE,
                         null /* gestureAvailabilityTracker */),
                 BiometricContext.getInstance(context));
     }
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSensorAdapter.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSensorAdapter.java
index 6355cb5..a004cae4 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSensorAdapter.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSensorAdapter.java
@@ -20,6 +20,7 @@
 import android.annotation.Nullable;
 import android.content.Context;
 import android.content.pm.UserInfo;
+import android.hardware.biometrics.face.ISession;
 import android.hardware.biometrics.face.SensorProps;
 import android.hardware.biometrics.face.V1_0.IBiometricsFace;
 import android.os.Handler;
@@ -67,8 +68,7 @@
             };
     private LockoutHalImpl mLockoutTracker;
 
-    public HidlToAidlSensorAdapter(@NonNull String tag,
-            @NonNull FaceProvider provider,
+    public HidlToAidlSensorAdapter(@NonNull FaceProvider provider,
             @NonNull Context context,
             @NonNull Handler handler,
             @NonNull SensorProps prop,
@@ -76,15 +76,14 @@
             @NonNull BiometricContext biometricContext,
             boolean resetLockoutRequiresChallenge,
             @NonNull Runnable internalCleanupAndGetFeatureRunnable) {
-        this(tag, provider, context, handler, prop, lockoutResetDispatcher, biometricContext,
+        this(provider, context, handler, prop, lockoutResetDispatcher, biometricContext,
                 resetLockoutRequiresChallenge, internalCleanupAndGetFeatureRunnable,
                 new AuthSessionCoordinator(), null /* daemon */,
                 null /* onEnrollSuccessCallback */);
     }
 
     @VisibleForTesting
-    HidlToAidlSensorAdapter(@NonNull String tag,
-            @NonNull FaceProvider provider,
+    HidlToAidlSensorAdapter(@NonNull FaceProvider provider,
             @NonNull Context context,
             @NonNull Handler handler,
             @NonNull SensorProps prop,
@@ -95,7 +94,7 @@
             @NonNull AuthSessionCoordinator authSessionCoordinator,
             @Nullable IBiometricsFace daemon,
             @Nullable AidlResponseHandler.AidlResponseHandlerCallback aidlResponseHandlerCallback) {
-        super(tag, provider, context, handler, prop, lockoutResetDispatcher, biometricContext,
+        super(provider, context, handler, prop, biometricContext,
                 resetLockoutRequiresChallenge);
         mInternalCleanupAndGetFeatureRunnable = internalCleanupAndGetFeatureRunnable;
         mFaceProvider = provider;
@@ -124,7 +123,7 @@
 
     @Override
     public void serviceDied(long cookie) {
-        Slog.d(TAG, "HAL died.");
+        Slog.d(TAG, "Face HAL died.");
         mDaemon = null;
     }
 
@@ -140,10 +139,12 @@
     }
 
     @Override
-    public void init(LockoutResetDispatcher lockoutResetDispatcher,
-            FaceProvider provider) {
-        setScheduler(new BiometricScheduler(TAG, BiometricScheduler.SENSOR_TYPE_FACE,
-                null /* gestureAvailabilityTracker */));
+    public void init(@NonNull LockoutResetDispatcher lockoutResetDispatcher,
+            @NonNull FaceProvider provider) {
+        setScheduler(new BiometricScheduler<ISession, AidlSession>(getHandler(),
+                BiometricScheduler.SENSOR_TYPE_FACE,
+                null /* gestureAvailabilityTracker */, () -> mCurrentUserId,
+                null /* userSwitchProvider */));
         setLazySession(this::getSession);
         mLockoutTracker = new LockoutHalImpl();
     }
@@ -188,7 +189,7 @@
             return mDaemon;
         }
 
-        Slog.d(TAG, "Daemon was null, reconnecting, current operation: "
+        Slog.d(TAG, "Face daemon was null, reconnecting, current operation: "
                 + getScheduler().getCurrentClient());
 
         try {
@@ -213,7 +214,7 @@
     }
 
     @VisibleForTesting void handleUserChanged(int newUserId) {
-        Slog.d(TAG, "User changed. Current user is " + newUserId);
+        Slog.d(TAG, "User changed. Current user for face sensor is " + newUserId);
         mSession = null;
         mCurrentUserId = newUserId;
     }
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSessionAdapter.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSessionAdapter.java
index 5daf2d4..fa95361 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSessionAdapter.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSessionAdapter.java
@@ -282,7 +282,7 @@
 
     @Override
     public ICancellationSignal enrollWithOptions(FaceEnrollOptions options) {
-        //Unsupported in HIDL
+        Slog.e(TAG, "enrollWithOptions unsupported in HIDL");
         return null;
     }
 
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/AidlSession.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/AidlSession.java
index 8ff105b..0d4dac0 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/AidlSession.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/AidlSession.java
@@ -51,12 +51,10 @@
         mAidlResponseHandler = aidlResponseHandler;
     }
 
-    /** The underlying {@link ISession}. */
     @NonNull public ISession getSession() {
         return mSession;
     }
 
-    /** The user id associated with the session. */
     public int getUserId() {
         return mUserId;
     }
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 88a11d9..c0388d1 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
@@ -46,6 +46,7 @@
 import android.hardware.fingerprint.IUdfpsOverlayController;
 import android.os.Binder;
 import android.os.Handler;
+import android.os.HandlerThread;
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.RemoteException;
@@ -102,6 +103,8 @@
 @SuppressWarnings("deprecation")
 public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvider {
 
+    private static final String TAG = "FingerprintProvider";
+
     private boolean mTestHalEnabled;
 
     @NonNull
@@ -172,7 +175,7 @@
             boolean resetLockoutRequiresHardwareAuthToken) {
         this(context, biometricStateCallback, authenticationStateListeners, props, halInstanceName,
                 lockoutResetDispatcher, gestureAvailabilityDispatcher, biometricContext,
-                null /* daemon */, resetLockoutRequiresHardwareAuthToken,
+                null /* daemon */, getHandler(), resetLockoutRequiresHardwareAuthToken,
                 false /* testHalEnabled */);
     }
 
@@ -184,6 +187,7 @@
             @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
             @NonNull BiometricContext biometricContext,
             @Nullable IFingerprint daemon,
+            @NonNull Handler handler,
             boolean resetLockoutRequiresHardwareAuthToken,
             boolean testHalEnabled) {
         mContext = context;
@@ -191,7 +195,11 @@
         mAuthenticationStateListeners = authenticationStateListeners;
         mHalInstanceName = halInstanceName;
         mFingerprintSensors = new SensorList<>(ActivityManager.getService());
-        mHandler = new Handler(Looper.getMainLooper());
+        if (Flags.deHidl()) {
+            mHandler = handler;
+        } else {
+            mHandler = new Handler(Looper.getMainLooper());
+        }
         mLockoutResetDispatcher = lockoutResetDispatcher;
         mActivityTaskManager = ActivityTaskManager.getInstance();
         mTaskStackListener = new BiometricTaskStackListener();
@@ -204,6 +212,13 @@
         initSensors(resetLockoutRequiresHardwareAuthToken, props, gestureAvailabilityDispatcher);
     }
 
+    @NonNull
+    private static Handler getHandler() {
+        HandlerThread handlerThread = new HandlerThread(TAG);
+        handlerThread.start();
+        return new Handler(handlerThread.getLooper());
+    }
+
     private void initAuthenticationBroadcastReceiver() {
         new AuthenticationStatsBroadcastReceiver(
                 mContext,
@@ -262,11 +277,9 @@
                                                                 location.sensorLocationY,
                                                                 location.sensorRadius))
                                                 .collect(Collectors.toList()));
-                final Sensor sensor = new Sensor(getTag() + "/" + sensorId, this, mContext,
-                        mHandler, internalProp, mLockoutResetDispatcher,
-                        gestureAvailabilityDispatcher, mBiometricContext);
-                sensor.init(gestureAvailabilityDispatcher,
-                        mLockoutResetDispatcher);
+                final Sensor sensor = new Sensor(this, mContext, mHandler, internalProp,
+                        mBiometricContext);
+                sensor.init(gestureAvailabilityDispatcher, mLockoutResetDispatcher);
                 final int sessionUserId =
                         sensor.getLazySession().get() == null ? UserHandle.USER_NULL :
                                 sensor.getLazySession().get().getUserId();
@@ -286,10 +299,8 @@
             @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
             boolean resetLockoutRequiresHardwareAuthToken) {
         final int sensorId = prop.commonProps.sensorId;
-        final Sensor sensor = new HidlToAidlSensorAdapter(getTag() + "/"
-                + sensorId, this, mContext, mHandler,
-                prop, mLockoutResetDispatcher, gestureAvailabilityDispatcher,
-                mBiometricContext, resetLockoutRequiresHardwareAuthToken,
+        final Sensor sensor = new HidlToAidlSensorAdapter(this, mContext, mHandler, prop,
+                mLockoutResetDispatcher, mBiometricContext, resetLockoutRequiresHardwareAuthToken,
                 () -> scheduleInternalCleanup(sensorId, ActivityManager.getCurrentUser(),
                         null /* callback */));
         sensor.init(gestureAvailabilityDispatcher, mLockoutResetDispatcher);
@@ -307,14 +318,11 @@
 
     private void addAidlSensors(@NonNull SensorProps prop,
             @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
-            List<SensorLocationInternal> workaroundLocations,
+            @NonNull List<SensorLocationInternal> workaroundLocations,
             boolean resetLockoutRequiresHardwareAuthToken) {
         final int sensorId = prop.commonProps.sensorId;
-        final Sensor sensor = new Sensor(getTag() + "/" + sensorId,
-                this, mContext, mHandler,
-                prop, mLockoutResetDispatcher, gestureAvailabilityDispatcher,
-                mBiometricContext, workaroundLocations,
-                resetLockoutRequiresHardwareAuthToken);
+        final Sensor sensor = new Sensor(this, mContext, mHandler, prop, mBiometricContext,
+                workaroundLocations, resetLockoutRequiresHardwareAuthToken);
         sensor.init(gestureAvailabilityDispatcher, mLockoutResetDispatcher);
         final int sessionUserId = sensor.getLazySession().get() == null ? UserHandle.USER_NULL :
                 sensor.getLazySession().get().getUserId();
@@ -329,7 +337,7 @@
     }
 
     private String getTag() {
-        return "FingerprintProvider/" + mHalInstanceName;
+        return TAG + "/" + mHalInstanceName;
     }
 
     boolean hasHalInstance() {
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintStopUserClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintStopUserClient.java
index 2cc1879..394f045 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintStopUserClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintStopUserClient.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
+import android.hardware.biometrics.fingerprint.ISession;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.Slog;
@@ -30,11 +31,11 @@
 
 import java.util.function.Supplier;
 
-public class FingerprintStopUserClient extends StopUserClient<AidlSession> {
+public class FingerprintStopUserClient extends StopUserClient<ISession> {
     private static final String TAG = "FingerprintStopUserClient";
 
     public FingerprintStopUserClient(@NonNull Context context,
-            @NonNull Supplier<AidlSession> lazyDaemon, @Nullable IBinder token, int userId,
+            @NonNull Supplier<ISession> lazyDaemon, @Nullable IBinder token, int userId,
             int sensorId,
             @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
             @NonNull UserStoppedCallback callback) {
@@ -50,7 +51,7 @@
     @Override
     protected void startHalOperation() {
         try {
-            getFreshDaemon().getSession().close();
+            getFreshDaemon().close();
         } catch (RemoteException e) {
             Slog.e(TAG, "Remote exception", e);
             getCallback().onClientFinished(this, false /* success */);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
index dd887bb..af88c62 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
@@ -59,6 +59,7 @@
 import com.android.server.biometrics.sensors.StartUserClient;
 import com.android.server.biometrics.sensors.StopUserClient;
 import com.android.server.biometrics.sensors.UserAwareBiometricScheduler;
+import com.android.server.biometrics.sensors.UserSwitchProvider;
 import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils;
 import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDispatcher;
 
@@ -77,15 +78,16 @@
 @SuppressWarnings("deprecation")
 public class Sensor {
 
+    private static final String TAG = "Sensor";
+
     private boolean mTestHalEnabled;
 
-    @NonNull private final String mTag;
     @NonNull private final FingerprintProvider mProvider;
     @NonNull private final Context mContext;
     @NonNull private final IBinder mToken;
     @NonNull private final Handler mHandler;
     @NonNull private final FingerprintSensorPropertiesInternal mSensorProperties;
-    @NonNull private BiometricScheduler mScheduler;
+    @NonNull private BiometricScheduler<IFingerprint, ISession> mScheduler;
     @NonNull private LockoutTracker mLockoutTracker;
     @NonNull private final Map<Integer, Long> mAuthenticatorIds;
     @NonNull private final BiometricContext mBiometricContext;
@@ -93,13 +95,10 @@
     @Nullable AidlSession mCurrentSession;
     @NonNull private Supplier<AidlSession> mLazySession;
 
-    public Sensor(@NonNull String tag, @NonNull FingerprintProvider provider,
+    public Sensor(@NonNull FingerprintProvider provider,
             @NonNull Context context, @NonNull Handler handler,
             @NonNull FingerprintSensorPropertiesInternal sensorProperties,
-            @NonNull LockoutResetDispatcher lockoutResetDispatcher,
-            @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
             @NonNull BiometricContext biometricContext, AidlSession session) {
-        mTag = tag;
         mProvider = provider;
         mContext = context;
         mToken = new Binder();
@@ -110,41 +109,52 @@
         mCurrentSession = session;
     }
 
-    Sensor(@NonNull String tag, @NonNull FingerprintProvider provider, @NonNull Context context,
+    Sensor(@NonNull FingerprintProvider provider, @NonNull Context context,
             @NonNull Handler handler, @NonNull FingerprintSensorPropertiesInternal sensorProperties,
-            @NonNull LockoutResetDispatcher lockoutResetDispatcher,
-            @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
             @NonNull BiometricContext biometricContext) {
-        this(tag, provider, context, handler, sensorProperties, lockoutResetDispatcher,
-                gestureAvailabilityDispatcher, biometricContext, null);
+        this(provider, context, handler, sensorProperties,
+                biometricContext, null);
     }
 
-    Sensor(@NonNull String tag, @NonNull FingerprintProvider provider, @NonNull Context context,
+    Sensor(@NonNull FingerprintProvider provider, @NonNull Context context,
             @NonNull Handler handler, @NonNull SensorProps sensorProp,
-            @NonNull LockoutResetDispatcher lockoutResetDispatcher,
-            @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
             @NonNull BiometricContext biometricContext,
             @NonNull List<SensorLocationInternal> workaroundLocation,
             boolean resetLockoutRequiresHardwareAuthToken) {
-        this(tag, provider, context, handler, getFingerprintSensorPropertiesInternal(sensorProp,
+        this(provider, context, handler, getFingerprintSensorPropertiesInternal(sensorProp,
                         workaroundLocation, resetLockoutRequiresHardwareAuthToken),
-                lockoutResetDispatcher, gestureAvailabilityDispatcher, biometricContext, null);
+                biometricContext, null);
     }
 
     /**
      * Initialize biometric scheduler, lockout tracker and session for the sensor.
      */
-    public void init(GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
-            LockoutResetDispatcher lockoutResetDispatcher) {
-        mScheduler = new UserAwareBiometricScheduler(mTag,
+    public void init(@NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
+            @NonNull LockoutResetDispatcher lockoutResetDispatcher) {
+        if (Flags.deHidl()) {
+            setScheduler(getBiometricSchedulerForInit(gestureAvailabilityDispatcher,
+                    lockoutResetDispatcher));
+        } else {
+            setScheduler(getUserAwareBiometricSchedulerForInit(gestureAvailabilityDispatcher,
+                    lockoutResetDispatcher));
+        }
+        mLockoutTracker = new LockoutCache();
+        mLazySession = () -> mCurrentSession != null ? mCurrentSession : null;
+    }
+
+    private BiometricScheduler<IFingerprint, ISession> getBiometricSchedulerForInit(
+            @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
+            @NonNull LockoutResetDispatcher lockoutResetDispatcher) {
+        return new BiometricScheduler<>(mHandler,
                 BiometricScheduler.sensorTypeFromFingerprintProperties(mSensorProperties),
                 gestureAvailabilityDispatcher,
                 () -> mCurrentSession != null ? mCurrentSession.getUserId() : UserHandle.USER_NULL,
-                new UserAwareBiometricScheduler.UserSwitchCallback() {
+                new UserSwitchProvider<IFingerprint, ISession>() {
                     @NonNull
                     @Override
-                    public StopUserClient<?> getStopUserClient(int userId) {
-                        return new FingerprintStopUserClient(mContext, mLazySession, mToken,
+                    public StopUserClient<ISession> getStopUserClient(int userId) {
+                        return new FingerprintStopUserClient(mContext,
+                                () -> mLazySession.get().getSession(), mToken,
                                 userId, mSensorProperties.sensorId,
                                 BiometricLogger.ofUnknown(mContext), mBiometricContext,
                                 () -> mCurrentSession = null);
@@ -152,69 +162,100 @@
 
                     @NonNull
                     @Override
-                    public StartUserClient<?, ?> getStartUserClient(int newUserId) {
+                    public StartUserClient<IFingerprint, ISession> getStartUserClient(
+                            int newUserId) {
                         final int sensorId = mSensorProperties.sensorId;
-
-                        final AidlResponseHandler resultController;
-
-                        if (Flags.deHidl()) {
-                            resultController = new AidlResponseHandler(
-                                    mContext, mScheduler, sensorId, newUserId,
-                                    mLockoutTracker, lockoutResetDispatcher,
-                                    mBiometricContext.getAuthSessionCoordinator(), () -> {},
-                                    new AidlResponseHandler.AidlResponseHandlerCallback() {
-                                        @Override
-                                        public void onEnrollSuccess() {
-                                            mProvider.scheduleLoadAuthenticatorIdsForUser(sensorId,
-                                                    newUserId);
-                                            mProvider.scheduleInvalidationRequest(sensorId,
-                                                    newUserId);
-                                        }
-
-                                        @Override
-                                        public void onHardwareUnavailable() {
-                                            Slog.e(mTag,
-                                                    "Fingerprint sensor hardware unavailable.");
-                                            mCurrentSession = null;
-                                        }
-                                    });
-                        } else {
-                            resultController = new AidlResponseHandler(
-                                    mContext, mScheduler, sensorId, newUserId,
-                                    mLockoutTracker, lockoutResetDispatcher,
-                                    mBiometricContext.getAuthSessionCoordinator(), () -> {
-                                Slog.e(mTag, "Got ERROR_HW_UNAVAILABLE");
-                                mCurrentSession = null;
-                            });
-                        }
-
-                        final StartUserClient.UserStartedCallback<ISession> userStartedCallback =
-                                (userIdStarted, newSession, halInterfaceVersion) -> {
-                                    Slog.d(mTag, "New session created for user: "
-                                            + userIdStarted + " with hal version: "
-                                            + halInterfaceVersion);
-                                    mCurrentSession = new AidlSession(halInterfaceVersion,
-                                            newSession, userIdStarted, resultController);
-                                    if (FingerprintUtils.getInstance(sensorId)
-                                            .isInvalidationInProgress(mContext, userIdStarted)) {
-                                        Slog.w(mTag,
-                                                "Scheduling unfinished invalidation request for "
-                                                        + "sensor: "
-                                                        + sensorId
-                                                        + ", user: " + userIdStarted);
+                        final AidlResponseHandler resultController = new AidlResponseHandler(
+                                mContext, mScheduler, sensorId, newUserId,
+                                mLockoutTracker, lockoutResetDispatcher,
+                                mBiometricContext.getAuthSessionCoordinator(), () -> {},
+                                new AidlResponseHandler.AidlResponseHandlerCallback() {
+                                    @Override
+                                    public void onEnrollSuccess() {
+                                        mProvider.scheduleLoadAuthenticatorIdsForUser(sensorId,
+                                                newUserId);
                                         mProvider.scheduleInvalidationRequest(sensorId,
-                                                userIdStarted);
+                                                newUserId);
                                     }
-                                };
 
-                        return new FingerprintStartUserClient(mContext, mProvider::getHalInstance,
-                                mToken, newUserId, mSensorProperties.sensorId,
-                                BiometricLogger.ofUnknown(mContext), mBiometricContext,
-                                resultController, userStartedCallback);
+                                    @Override
+                                    public void onHardwareUnavailable() {
+                                        Slog.e(TAG,
+                                                "Fingerprint sensor hardware unavailable.");
+                                        mCurrentSession = null;
+                                    }
+                                });
+
+                        return Sensor.this.getStartUserClient(resultController, sensorId,
+                                newUserId);
                     }
                 });
-        mLockoutTracker = new LockoutCache();
-        mLazySession = () -> mCurrentSession != null ? mCurrentSession : null;
+    }
+
+    private UserAwareBiometricScheduler<ISession, AidlSession>
+            getUserAwareBiometricSchedulerForInit(
+                    GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
+                    LockoutResetDispatcher lockoutResetDispatcher) {
+        return new UserAwareBiometricScheduler<>(TAG,
+                BiometricScheduler.sensorTypeFromFingerprintProperties(mSensorProperties),
+                gestureAvailabilityDispatcher,
+                () -> mCurrentSession != null ? mCurrentSession.getUserId() : UserHandle.USER_NULL,
+                new UserAwareBiometricScheduler.UserSwitchCallback() {
+                    @NonNull
+                    @Override
+                    public StopUserClient<ISession> getStopUserClient(int userId) {
+                        return new FingerprintStopUserClient(mContext,
+                                () -> mLazySession.get().getSession(), mToken,
+                                userId, mSensorProperties.sensorId,
+                                BiometricLogger.ofUnknown(mContext), mBiometricContext,
+                                () -> mCurrentSession = null);
+                    }
+
+                    @NonNull
+                    @Override
+                    public StartUserClient<IFingerprint, ISession> getStartUserClient(
+                            int newUserId) {
+                        final int sensorId = mSensorProperties.sensorId;
+
+                        final AidlResponseHandler resultController = new AidlResponseHandler(
+                                mContext, mScheduler, sensorId, newUserId,
+                                mLockoutTracker, lockoutResetDispatcher,
+                                mBiometricContext.getAuthSessionCoordinator(), () -> {
+                                    Slog.e(TAG, "Fingerprint hardware unavailable.");
+                                    mCurrentSession = null;
+                                });
+
+                        return Sensor.this.getStartUserClient(resultController, sensorId,
+                                newUserId);
+                    }
+                });
+    }
+
+    private FingerprintStartUserClient getStartUserClient(AidlResponseHandler resultController,
+            int sensorId, int newUserId) {
+        final StartUserClient.UserStartedCallback<ISession> userStartedCallback =
+                (userIdStarted, newSession, halInterfaceVersion) -> {
+                    Slog.d(TAG, "New fingerprint session created for user: "
+                            + userIdStarted + " with hal version: "
+                            + halInterfaceVersion);
+                    mCurrentSession = new AidlSession(halInterfaceVersion,
+                            newSession, userIdStarted, resultController);
+                    if (FingerprintUtils.getInstance(sensorId)
+                            .isInvalidationInProgress(mContext, userIdStarted)) {
+                        Slog.w(TAG,
+                                "Scheduling unfinished invalidation request for "
+                                        + "fingerprint sensor: "
+                                        + sensorId
+                                        + ", user: " + userIdStarted);
+                        mProvider.scheduleInvalidationRequest(sensorId,
+                                userIdStarted);
+                    }
+                };
+
+        return new FingerprintStartUserClient(mContext, mProvider::getHalInstance,
+                mToken, newUserId, mSensorProperties.sensorId,
+                BiometricLogger.ofUnknown(mContext), mBiometricContext,
+                resultController, userStartedCallback);
     }
 
     protected static FingerprintSensorPropertiesInternal getFingerprintSensorPropertiesInternal(
@@ -267,7 +308,7 @@
                 biometricStateCallback, mProvider, this);
     }
 
-    @NonNull public BiometricScheduler getScheduler() {
+    @NonNull public BiometricScheduler<IFingerprint, ISession> getScheduler() {
         return mScheduler;
     }
 
@@ -283,17 +324,17 @@
     }
 
     void setTestHalEnabled(boolean enabled) {
-        Slog.w(mTag, "setTestHalEnabled: " + enabled);
+        Slog.w(TAG, "Fingerprint setTestHalEnabled: " + enabled);
         if (enabled != mTestHalEnabled) {
             // The framework should retrieve a new session from the HAL.
             try {
                 if (mCurrentSession != null) {
                     // TODO(181984005): This should be scheduled instead of directly invoked
-                    Slog.d(mTag, "Closing old session");
+                    Slog.d(TAG, "Closing old fingerprint session");
                     mCurrentSession.getSession().close();
                 }
             } catch (RemoteException e) {
-                Slog.e(mTag, "RemoteException", e);
+                Slog.e(TAG, "RemoteException", e);
             }
             mCurrentSession = null;
         }
@@ -335,7 +376,7 @@
     public void onBinderDied() {
         final BaseClientMonitor client = mScheduler.getCurrentClient();
         if (client instanceof ErrorConsumer) {
-            Slog.e(mTag, "Sending ERROR_HW_UNAVAILABLE for client: " + client);
+            Slog.e(TAG, "Sending fingerprint hardware unavailable error for client: " + client);
             final ErrorConsumer errorConsumer = (ErrorConsumer) client;
             errorConsumer.onError(FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE,
                     0 /* vendorCode */);
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 d3cecd0..4accf8f 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
@@ -119,7 +119,7 @@
     @NonNull private final AuthenticationStateListeners mAuthenticationStateListeners;
     private final ActivityTaskManager mActivityTaskManager;
     @NonNull private final FingerprintSensorPropertiesInternal mSensorProperties;
-    private final BiometricScheduler mScheduler;
+    private final BiometricScheduler<IBiometricsFingerprint, AidlSession> mScheduler;
     private final Handler mHandler;
     private final LockoutResetDispatcher mLockoutResetDispatcher;
     private final LockoutFrameworkImpl mLockoutTracker;
@@ -198,11 +198,11 @@
         private final int mSensorId;
         @NonNull private final Context mContext;
         @NonNull final Handler mHandler;
-        @NonNull final BiometricScheduler mScheduler;
+        @NonNull final BiometricScheduler<IBiometricsFingerprint, AidlSession> mScheduler;
         @Nullable private Callback mCallback;
 
         HalResultController(int sensorId, @NonNull Context context, @NonNull Handler handler,
-                @NonNull BiometricScheduler scheduler) {
+                @NonNull BiometricScheduler<IBiometricsFingerprint, AidlSession> scheduler) {
             mSensorId = sensorId;
             mContext = context;
             mHandler = handler;
@@ -336,7 +336,7 @@
             @NonNull BiometricStateCallback biometricStateCallback,
             @NonNull AuthenticationStateListeners authenticationStateListeners,
             @NonNull FingerprintSensorPropertiesInternal sensorProps,
-            @NonNull BiometricScheduler scheduler,
+            @NonNull BiometricScheduler<IBiometricsFingerprint, AidlSession> scheduler,
             @NonNull Handler handler,
             @NonNull LockoutResetDispatcher lockoutResetDispatcher,
             @NonNull HalResultController controller,
@@ -389,8 +389,8 @@
             @NonNull Handler handler,
             @NonNull LockoutResetDispatcher lockoutResetDispatcher,
             @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher) {
-        final BiometricScheduler scheduler =
-                new BiometricScheduler(TAG,
+        final BiometricScheduler<IBiometricsFingerprint, AidlSession> scheduler =
+                new BiometricScheduler<>(
                         BiometricScheduler.sensorTypeFromFingerprintProperties(sensorProps),
                         gestureAvailabilityDispatcher);
         final HalResultController controller = new HalResultController(sensorProps.sensorId,
@@ -533,8 +533,8 @@
     private void scheduleUpdateActiveUserWithoutHandler(int targetUserId, boolean force) {
         final boolean hasEnrolled =
                 !getEnrolledFingerprints(mSensorProperties.sensorId, targetUserId).isEmpty();
-        final FingerprintUpdateActiveUserClient client =
-                new FingerprintUpdateActiveUserClient(mContext, mLazyDaemon, targetUserId,
+        final FingerprintUpdateActiveUserClientLegacy client =
+                new FingerprintUpdateActiveUserClientLegacy(mContext, mLazyDaemon, targetUserId,
                         mContext.getOpPackageName(), mSensorProperties.sensorId,
                         createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
                                 BiometricsProtoEnums.CLIENT_UNKNOWN,
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java
index 88dae6f..9232e11 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java
@@ -140,9 +140,9 @@
     private static class TestableBiometricScheduler extends BiometricScheduler {
         @NonNull private Fingerprint21UdfpsMock mFingerprint21;
 
-        TestableBiometricScheduler(@NonNull String tag, @NonNull Handler handler,
+        TestableBiometricScheduler(
                 @Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher) {
-            super(tag, BiometricScheduler.SENSOR_TYPE_FP_OTHER, gestureAvailabilityDispatcher);
+            super(BiometricScheduler.SENSOR_TYPE_FP_OTHER, gestureAvailabilityDispatcher);
         }
 
         void init(@NonNull Fingerprint21UdfpsMock fingerprint21) {
@@ -258,7 +258,7 @@
 
         final Handler handler = new Handler(Looper.getMainLooper());
         final TestableBiometricScheduler scheduler =
-                new TestableBiometricScheduler(TAG, handler, gestureAvailabilityDispatcher);
+                new TestableBiometricScheduler(gestureAvailabilityDispatcher);
         final MockHalResultController controller =
                 new MockHalResultController(sensorProps.sensorId, context, handler, scheduler);
         return new Fingerprint21UdfpsMock(context, biometricStateCallback,
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintUpdateActiveUserClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintUpdateActiveUserClient.java
index 5c5b992..59e64cd 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintUpdateActiveUserClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintUpdateActiveUserClient.java
@@ -18,7 +18,7 @@
 
 import android.annotation.NonNull;
 import android.content.Context;
-import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
+import android.hardware.biometrics.fingerprint.ISession;
 import android.os.Build;
 import android.os.Environment;
 import android.os.RemoteException;
@@ -39,8 +39,8 @@
 /**
  * Sets the HAL's current active user, and updates the framework's authenticatorId cache.
  */
-public class FingerprintUpdateActiveUserClient extends
-        StartUserClient<IBiometricsFingerprint, AidlSession> {
+public class FingerprintUpdateActiveUserClient extends StartUserClient<ISession,
+        AidlSession> {
 
     private static final String TAG = "FingerprintUpdateActiveUserClient";
     private static final String FP_DATA_DIR = "fpdata";
@@ -52,19 +52,7 @@
     private File mDirectory;
 
     FingerprintUpdateActiveUserClient(@NonNull Context context,
-            @NonNull Supplier<IBiometricsFingerprint> lazyDaemon, int userId,
-            @NonNull String owner, int sensorId,
-            @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
-            @NonNull Supplier<Integer> currentUserId,
-            boolean hasEnrolledBiometrics, @NonNull Map<Integer, Long> authenticatorIds,
-            boolean forceUpdateAuthenticatorId) {
-        this(context, lazyDaemon, userId, owner, sensorId, logger, biometricContext, currentUserId,
-                hasEnrolledBiometrics, authenticatorIds, forceUpdateAuthenticatorId,
-                (newUserId, newUser, halInterfaceVersion) -> {});
-    }
-
-    FingerprintUpdateActiveUserClient(@NonNull Context context,
-            @NonNull Supplier<IBiometricsFingerprint> lazyDaemon, int userId,
+            @NonNull Supplier<ISession> lazyDaemon, int userId,
             @NonNull String owner, int sensorId,
             @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
             @NonNull Supplier<Integer> currentUserId,
@@ -132,9 +120,10 @@
         try {
             final int targetId = getTargetUserId();
             Slog.d(TAG, "Setting active user: " + targetId);
-            getFreshDaemon().setActiveGroup(targetId, mDirectory.getAbsolutePath());
+            HidlToAidlSessionAdapter sessionAdapter = (HidlToAidlSessionAdapter) getFreshDaemon();
+            sessionAdapter.setActiveGroup(targetId, mDirectory.getAbsolutePath());
             mAuthenticatorIds.put(targetId, mHasEnrolledBiometrics
-                    ? getFreshDaemon().getAuthenticatorId() : 0L);
+                    ? sessionAdapter.getAuthenticatorIdForUpdateClient() : 0L);
             mUserStartedCallback.onUserStarted(targetId, null, 0);
             mCallback.onClientFinished(this, true /* success */);
         } catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintUpdateActiveUserClientLegacy.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintUpdateActiveUserClientLegacy.java
new file mode 100644
index 0000000..fc85402
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintUpdateActiveUserClientLegacy.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors.fingerprint.hidl;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
+import android.os.Build;
+import android.os.Environment;
+import android.os.RemoteException;
+import android.os.SELinux;
+import android.util.Slog;
+
+import com.android.server.biometrics.BiometricsProto;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
+import com.android.server.biometrics.sensors.HalClientMonitor;
+
+import java.io.File;
+import java.util.Map;
+import java.util.function.Supplier;
+
+/**
+ * TODO(b/304604965): Delete this class once Flags.DE_HIDL is ready for release.
+ */
+public class FingerprintUpdateActiveUserClientLegacy extends
+        HalClientMonitor<IBiometricsFingerprint> {
+    private static final String TAG = "FingerprintUpdateActiveUserClient";
+    private static final String FP_DATA_DIR = "fpdata";
+
+    private final Supplier<Integer> mCurrentUserId;
+    private final boolean mForceUpdateAuthenticatorId;
+    private final boolean mHasEnrolledBiometrics;
+    private final Map<Integer, Long> mAuthenticatorIds;
+    private File mDirectory;
+
+    FingerprintUpdateActiveUserClientLegacy(@NonNull Context context,
+            @NonNull Supplier<IBiometricsFingerprint> lazyDaemon, int userId,
+            @NonNull String owner, int sensorId,
+            @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
+            @NonNull Supplier<Integer> currentUserId,
+            boolean hasEnrolledBiometrics, @NonNull Map<Integer, Long> authenticatorIds,
+            boolean forceUpdateAuthenticatorId) {
+        super(context, lazyDaemon, null /* token */, null /* listener */, userId, owner,
+                0 /* cookie */, sensorId, logger, biometricContext);
+        mCurrentUserId = currentUserId;
+        mForceUpdateAuthenticatorId = forceUpdateAuthenticatorId;
+        mHasEnrolledBiometrics = hasEnrolledBiometrics;
+        mAuthenticatorIds = authenticatorIds;
+    }
+
+    @Override
+    public void start(@NonNull ClientMonitorCallback callback) {
+        super.start(callback);
+
+        if (mCurrentUserId.get() == getTargetUserId() && !mForceUpdateAuthenticatorId) {
+            Slog.d(TAG, "Already user: " + mCurrentUserId + ", returning");
+            callback.onClientFinished(this, true /* success */);
+            return;
+        }
+
+        int firstSdkInt = Build.VERSION.DEVICE_INITIAL_SDK_INT;
+        if (firstSdkInt < Build.VERSION_CODES.BASE) {
+            Slog.e(TAG, "First SDK version " + firstSdkInt + " is invalid; must be "
+                    + "at least VERSION_CODES.BASE");
+        }
+        File baseDir;
+        if (firstSdkInt <= Build.VERSION_CODES.O_MR1) {
+            baseDir = Environment.getUserSystemDirectory(getTargetUserId());
+        } else {
+            baseDir = Environment.getDataVendorDeDirectory(getTargetUserId());
+        }
+
+        mDirectory = new File(baseDir, FP_DATA_DIR);
+        if (!mDirectory.exists()) {
+            if (!mDirectory.mkdir()) {
+                Slog.e(TAG, "Cannot make directory: " + mDirectory.getAbsolutePath());
+                callback.onClientFinished(this, false /* success */);
+                return;
+            }
+            // Calling mkdir() from this process will create a directory with our
+            // permissions (inherited from the containing dir). This command fixes
+            // the label.
+            if (!SELinux.restorecon(mDirectory)) {
+                Slog.e(TAG, "Restorecons failed. Directory will have wrong label.");
+                callback.onClientFinished(this, false /* success */);
+                return;
+            }
+        }
+
+        startHalOperation();
+    }
+
+    @Override
+    public void unableToStart() {
+        // Nothing to do here
+    }
+
+    @Override
+    protected void startHalOperation() {
+        try {
+            final int targetId = getTargetUserId();
+            Slog.d(TAG, "Setting active user: " + targetId);
+            getFreshDaemon().setActiveGroup(targetId, mDirectory.getAbsolutePath());
+            mAuthenticatorIds.put(targetId, mHasEnrolledBiometrics
+                    ? getFreshDaemon().getAuthenticatorId() : 0L);
+            mCallback.onClientFinished(this, true /* success */);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Failed to setActiveGroup: " + e);
+            mCallback.onClientFinished(this, false /* success */);
+        }
+    }
+
+    @Override
+    public int getProtoEnum() {
+        return BiometricsProto.CM_UPDATE_ACTIVE_USER;
+    }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSensorAdapter.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSensorAdapter.java
index 0bb61415..47fdcdb 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSensorAdapter.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSensorAdapter.java
@@ -20,6 +20,7 @@
 import android.annotation.Nullable;
 import android.content.Context;
 import android.content.pm.UserInfo;
+import android.hardware.biometrics.fingerprint.ISession;
 import android.hardware.biometrics.fingerprint.SensorProps;
 import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
 import android.os.Handler;
@@ -39,7 +40,7 @@
 import com.android.server.biometrics.sensors.LockoutTracker;
 import com.android.server.biometrics.sensors.StartUserClient;
 import com.android.server.biometrics.sensors.StopUserClient;
-import com.android.server.biometrics.sensors.UserAwareBiometricScheduler;
+import com.android.server.biometrics.sensors.UserSwitchProvider;
 import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils;
 import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDispatcher;
 import com.android.server.biometrics.sensors.fingerprint.aidl.AidlResponseHandler;
@@ -71,37 +72,33 @@
                 }
             };
 
-    public HidlToAidlSensorAdapter(@NonNull String tag, @NonNull FingerprintProvider provider,
-            @NonNull Context context, @NonNull Handler handler,
+    public HidlToAidlSensorAdapter(@NonNull FingerprintProvider provider,
+            @NonNull Context context,
+            @NonNull Handler handler,
             @NonNull SensorProps prop,
             @NonNull LockoutResetDispatcher lockoutResetDispatcher,
-            @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
             @NonNull BiometricContext biometricContext,
             boolean resetLockoutRequiresHardwareAuthToken,
             @NonNull Runnable internalCleanupRunnable) {
-        this(tag, provider, context, handler, prop, lockoutResetDispatcher,
-                gestureAvailabilityDispatcher, biometricContext,
+        this(provider, context, handler, prop, lockoutResetDispatcher, biometricContext,
                 resetLockoutRequiresHardwareAuthToken, internalCleanupRunnable,
                 new AuthSessionCoordinator(), null /* daemon */,
                 null /* onEnrollSuccessCallback */);
     }
 
     @VisibleForTesting
-    HidlToAidlSensorAdapter(@NonNull String tag, @NonNull FingerprintProvider provider,
+    HidlToAidlSensorAdapter(@NonNull FingerprintProvider provider,
             @NonNull Context context, @NonNull Handler handler,
             @NonNull SensorProps prop,
             @NonNull LockoutResetDispatcher lockoutResetDispatcher,
-            @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
             @NonNull BiometricContext biometricContext,
             boolean resetLockoutRequiresHardwareAuthToken,
             @NonNull Runnable internalCleanupRunnable,
             @NonNull AuthSessionCoordinator authSessionCoordinator,
             @Nullable IBiometricsFingerprint daemon,
             @Nullable AidlResponseHandler.AidlResponseHandlerCallback aidlResponseHandlerCallback) {
-        super(tag, provider, context, handler, getFingerprintSensorPropertiesInternal(prop,
+        super(provider, context, handler, getFingerprintSensorPropertiesInternal(prop,
                         new ArrayList<>(), resetLockoutRequiresHardwareAuthToken),
-                lockoutResetDispatcher,
-                gestureAvailabilityDispatcher,
                 biometricContext, null /* session */);
         mLockoutResetDispatcher = lockoutResetDispatcher;
         mInternalCleanupRunnable = internalCleanupRunnable;
@@ -127,7 +124,7 @@
 
     @Override
     public void serviceDied(long cookie) {
-        Slog.d(TAG, "HAL died.");
+        Slog.d(TAG, "Fingerprint HAL died.");
         mSession = null;
         mDaemon = null;
     }
@@ -139,19 +136,20 @@
     }
 
     @Override
-    public void init(GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
-            LockoutResetDispatcher lockoutResetDispatcher) {
+    public void init(@NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
+            @NonNull LockoutResetDispatcher lockoutResetDispatcher) {
         setLazySession(this::getSession);
-        setScheduler(new UserAwareBiometricScheduler(TAG,
+        setScheduler(new BiometricScheduler<ISession, AidlSession>(getHandler(),
                 BiometricScheduler.sensorTypeFromFingerprintProperties(getSensorProperties()),
-                gestureAvailabilityDispatcher, () -> mCurrentUserId, getUserSwitchCallback()));
+                gestureAvailabilityDispatcher, () -> mCurrentUserId, getUserSwitchProvider()));
         mLockoutTracker = new LockoutFrameworkImpl(getContext(),
                 userId -> mLockoutResetDispatcher.notifyLockoutResetCallbacks(
-                        getSensorProperties().sensorId));
+                        getSensorProperties().sensorId), getHandler());
     }
 
     @Override
     @Nullable
+    @VisibleForTesting
     protected AidlSession getSessionForUser(int userId) {
         if (mSession != null && mSession.getUserId() == userId) {
             return mSession;
@@ -217,21 +215,18 @@
         }
 
         mDaemon.asBinder().linkToDeath(this, 0 /* flags */);
-
-        Slog.d(TAG, "Fingerprint HAL ready");
-
         scheduleLoadAuthenticatorIds();
         mInternalCleanupRunnable.run();
         return mDaemon;
     }
 
-    private UserAwareBiometricScheduler.UserSwitchCallback getUserSwitchCallback() {
-        return new UserAwareBiometricScheduler.UserSwitchCallback() {
+    private UserSwitchProvider<ISession, AidlSession> getUserSwitchProvider() {
+        return new UserSwitchProvider<>() {
             @NonNull
             @Override
-            public StopUserClient<?> getStopUserClient(int userId) {
-                return new StopUserClient<IBiometricsFingerprint>(getContext(),
-                        HidlToAidlSensorAdapter.this::getIBiometricsFingerprint,
+            public StopUserClient<AidlSession> getStopUserClient(int userId) {
+                return new StopUserClient<>(getContext(),
+                        HidlToAidlSensorAdapter.this::getSession,
                         null /* token */, userId, getSensorProperties().sensorId,
                         BiometricLogger.ofUnknown(getContext()), getBiometricContext(),
                         () -> {
@@ -258,7 +253,7 @@
 
             @NonNull
             @Override
-            public StartUserClient<?, ?> getStartUserClient(int newUserId) {
+            public StartUserClient<ISession, AidlSession> getStartUserClient(int newUserId) {
                 return getFingerprintUpdateActiveUserClient(newUserId,
                         false /* forceUpdateAuthenticatorId */);
             }
@@ -268,7 +263,7 @@
     private FingerprintUpdateActiveUserClient getFingerprintUpdateActiveUserClient(int newUserId,
             boolean forceUpdateAuthenticatorIds) {
         return new FingerprintUpdateActiveUserClient(getContext(),
-                this::getIBiometricsFingerprint, newUserId, TAG,
+                () -> getSession().getSession(), newUserId, TAG,
                 getSensorProperties().sensorId, BiometricLogger.ofUnknown(getContext()),
                 getBiometricContext(), () -> mCurrentUserId,
                 !FingerprintUtils.getInstance(getSensorProperties().sensorId)
@@ -290,7 +285,7 @@
     }
 
     @VisibleForTesting void handleUserChanged(int newUserId) {
-        Slog.d(TAG, "User changed. Current user is " + newUserId);
+        Slog.d(TAG, "User changed. Current user for fingerprint sensor is " + newUserId);
         mSession = null;
         mCurrentUserId = newUserId;
     }
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSessionAdapter.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSessionAdapter.java
index 2fc00e1..b469752 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSessionAdapter.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSessionAdapter.java
@@ -209,6 +209,14 @@
         return null;
     }
 
+    public long getAuthenticatorIdForUpdateClient() throws RemoteException {
+        return mSession.get().getAuthenticatorId();
+    }
+
+    public void setActiveGroup(int userId, String absolutePath) throws RemoteException {
+        mSession.get().setActiveGroup(userId, absolutePath);
+    }
+
     private void setCallback(AidlResponseHandler aidlResponseHandler) {
         mHidlToAidlCallbackConverter = new HidlToAidlCallbackConverter(aidlResponseHandler);
         try {
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/LockoutFrameworkImpl.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/LockoutFrameworkImpl.java
index 2f77275..0e05a79 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/LockoutFrameworkImpl.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/LockoutFrameworkImpl.java
@@ -19,6 +19,7 @@
 import static android.Manifest.permission.RESET_FINGERPRINT_LOCKOUT;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.AlarmManager;
 import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
@@ -81,19 +82,30 @@
             @NonNull LockoutResetCallback lockoutResetCallback) {
         this(context, lockoutResetCallback, (userId) -> PendingIntent.getBroadcast(context, userId,
                 new Intent(ACTION_LOCKOUT_RESET).putExtra(KEY_LOCKOUT_RESET_USER, userId),
-                PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE));
+                PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE),
+                null /* handler */);
+    }
+
+    public LockoutFrameworkImpl(@NonNull Context context,
+            @NonNull LockoutResetCallback lockoutResetCallback,
+            @NonNull Handler handler) {
+        this(context, lockoutResetCallback, (userId) -> PendingIntent.getBroadcast(context, userId,
+                new Intent(ACTION_LOCKOUT_RESET).putExtra(KEY_LOCKOUT_RESET_USER, userId),
+                PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE),
+                handler);
     }
 
     @VisibleForTesting
     LockoutFrameworkImpl(@NonNull Context context,
             @NonNull LockoutResetCallback lockoutResetCallback,
-            @NonNull Function<Integer, PendingIntent> lockoutResetIntent) {
+            @NonNull Function<Integer, PendingIntent> lockoutResetIntent,
+            @Nullable Handler handler) {
         mLockoutResetCallback = lockoutResetCallback;
         mTimedLockoutCleared = new SparseBooleanArray();
         mFailedAttempts = new SparseIntArray();
         mAlarmManager = context.getSystemService(AlarmManager.class);
         mLockoutReceiver = new LockoutReceiver();
-        mHandler = new Handler(Looper.getMainLooper());
+        mHandler = handler == null ? new Handler(Looper.getMainLooper()) : handler;
         mLockoutResetIntent = lockoutResetIntent;
 
         context.registerReceiver(mLockoutReceiver, new IntentFilter(ACTION_LOCKOUT_RESET),
diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java
index 56a94ec0..49f6070 100644
--- a/services/core/java/com/android/server/clipboard/ClipboardService.java
+++ b/services/core/java/com/android/server/clipboard/ClipboardService.java
@@ -1424,7 +1424,11 @@
         String defaultIme = Settings.Secure.getStringForUser(getContext().getContentResolver(),
                 Settings.Secure.DEFAULT_INPUT_METHOD, userId);
         if (!TextUtils.isEmpty(defaultIme)) {
-            final String imePkg = ComponentName.unflattenFromString(defaultIme).getPackageName();
+            final ComponentName imeComponent = ComponentName.unflattenFromString(defaultIme);
+            if (imeComponent == null) {
+                return false;
+            }
+            final String imePkg = imeComponent.getPackageName();
             return imePkg.equals(packageName);
         }
         return false;
diff --git a/services/core/java/com/android/server/criticalevents/CriticalEventLog.java b/services/core/java/com/android/server/criticalevents/CriticalEventLog.java
index 0814375..816c349 100644
--- a/services/core/java/com/android/server/criticalevents/CriticalEventLog.java
+++ b/services/core/java/com/android/server/criticalevents/CriticalEventLog.java
@@ -29,6 +29,7 @@
 import com.android.server.criticalevents.nano.CriticalEventProto;
 import com.android.server.criticalevents.nano.CriticalEventProto.AppNotResponding;
 import com.android.server.criticalevents.nano.CriticalEventProto.HalfWatchdog;
+import com.android.server.criticalevents.nano.CriticalEventProto.InstallPackages;
 import com.android.server.criticalevents.nano.CriticalEventProto.JavaCrash;
 import com.android.server.criticalevents.nano.CriticalEventProto.NativeCrash;
 import com.android.server.criticalevents.nano.CriticalEventProto.SystemServerStarted;
@@ -142,6 +143,13 @@
         return System.currentTimeMillis();
     }
 
+    /** Logs when one or more packages are installed. */
+    public void logInstallPackagesStarted() {
+        CriticalEventProto event = new CriticalEventProto();
+        event.setInstallPackages(new InstallPackages());
+        log(event);
+    }
+
     /** Logs when system server started. */
     public void logSystemServerStarted() {
         CriticalEventProto event = new CriticalEventProto();
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index 3024dd2..8910b6e 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -82,6 +82,7 @@
     public static final int AUTO_BRIGHTNESS_MODE_DEFAULT = 0;
     public static final int AUTO_BRIGHTNESS_MODE_IDLE = 1;
     public static final int AUTO_BRIGHTNESS_MODE_DOZE = 2;
+    public static final int AUTO_BRIGHTNESS_MODE_MAX = AUTO_BRIGHTNESS_MODE_DOZE;
 
     // How long the current sensor reading is assumed to be valid beyond the current time.
     // This provides a bit of prediction, as well as ensures that the weight for the last sample is
diff --git a/services/core/java/com/android/server/display/DeviceStateToLayoutMap.java b/services/core/java/com/android/server/display/DeviceStateToLayoutMap.java
index 1ac3a12..e54f30f 100644
--- a/services/core/java/com/android/server/display/DeviceStateToLayoutMap.java
+++ b/services/core/java/com/android/server/display/DeviceStateToLayoutMap.java
@@ -27,6 +27,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.display.config.layout.Layouts;
 import com.android.server.display.config.layout.XmlParser;
+import com.android.server.display.feature.DisplayManagerFlags;
 import com.android.server.display.layout.DisplayIdProducer;
 import com.android.server.display.layout.Layout;
 
@@ -63,24 +64,40 @@
     private static final String CONFIG_FILE_PATH =
             "etc/displayconfig/display_layout_configuration.xml";
 
+    private static final String DATA_CONFIG_FILE_PATH =
+            "system/displayconfig/display_layout_configuration.xml";
+
     private final SparseArray<Layout> mLayoutMap = new SparseArray<>();
     private final DisplayIdProducer mIdProducer;
+    private final boolean mIsPortInDisplayLayoutEnabled;
 
-    DeviceStateToLayoutMap(DisplayIdProducer idProducer) {
-        this(idProducer, Environment.buildPath(
-                Environment.getVendorDirectory(), CONFIG_FILE_PATH));
+    DeviceStateToLayoutMap(DisplayIdProducer idProducer, DisplayManagerFlags flags) {
+        this(idProducer, flags, getConfigFile());
     }
 
-    DeviceStateToLayoutMap(DisplayIdProducer idProducer, File configFile) {
+    DeviceStateToLayoutMap(DisplayIdProducer idProducer, DisplayManagerFlags flags,
+            File configFile) {
+        mIsPortInDisplayLayoutEnabled = flags.isPortInDisplayLayoutEnabled();
         mIdProducer = idProducer;
         loadLayoutsFromConfig(configFile);
         createLayout(STATE_DEFAULT);
     }
 
+    static private File getConfigFile() {
+        final File configFileFromDataDir = Environment.buildPath(Environment.getDataDirectory(),
+                DATA_CONFIG_FILE_PATH);
+        if (configFileFromDataDir.exists()) {
+            return configFileFromDataDir;
+        } else {
+            return Environment.buildPath(Environment.getVendorDirectory(), CONFIG_FILE_PATH);
+        }
+    }
+
     public void dumpLocked(IndentingPrintWriter ipw) {
         ipw.println("DeviceStateToLayoutMap:");
         ipw.increaseIndent();
 
+        ipw.println("mIsPortInDisplayLayoutEnabled=" + mIsPortInDisplayLayoutEnabled);
         ipw.println("Registered Layouts:");
         for (int i = 0; i < mLayoutMap.size(); i++) {
             ipw.println("state(" + mLayoutMap.keyAt(i) + "): " + mLayoutMap.valueAt(i));
@@ -120,13 +137,15 @@
                 final Layout layout = createLayout(state);
                 for (com.android.server.display.config.layout.Display d: l.getDisplay()) {
                     assert layout != null;
+                    final DisplayAddress address = getDisplayAddressForLayoutDisplay(d);
+
                     int position = getPosition(d.getPosition());
                     BigInteger leadDisplayPhysicalId = d.getLeadDisplayAddress();
                     DisplayAddress leadDisplayAddress = leadDisplayPhysicalId == null ? null
                             : DisplayAddress.fromPhysicalDisplayId(
                                     leadDisplayPhysicalId.longValue());
                     layout.createDisplayLocked(
-                            DisplayAddress.fromPhysicalDisplayId(d.getAddress().longValue()),
+                            address,
                             d.isDefaultDisplay(),
                             d.isEnabled(),
                             d.getDisplayGroup(),
@@ -146,6 +165,20 @@
         }
     }
 
+    private DisplayAddress getDisplayAddressForLayoutDisplay(
+            @NonNull com.android.server.display.config.layout.Display display) {
+        BigInteger xmlAddress = display.getAddress_optional();
+        if (xmlAddress != null) {
+            return DisplayAddress.fromPhysicalDisplayId(xmlAddress.longValue());
+        }
+        if (!mIsPortInDisplayLayoutEnabled || display.getPort_optional() == null) {
+            throw new IllegalArgumentException(
+                  "Must specify a display identifier in display layout configuration: " + display);
+        }
+        return DisplayAddress.fromPortAndModel((int) display.getPort_optional().longValue(),
+                /* model= */ null);
+    }
+
     private int getPosition(@NonNull String position) {
         int positionInt = POSITION_UNKNOWN;
         if (FRONT_STRING.equals(position)) {
diff --git a/services/core/java/com/android/server/display/DisplayBrightnessState.java b/services/core/java/com/android/server/display/DisplayBrightnessState.java
index 9fcaa1e..d50a43a 100644
--- a/services/core/java/com/android/server/display/DisplayBrightnessState.java
+++ b/services/core/java/com/android/server/display/DisplayBrightnessState.java
@@ -33,6 +33,7 @@
     private final float mSdrBrightness;
 
     private final float mMaxBrightness;
+    private final float mMinBrightness;
     private final BrightnessReason mBrightnessReason;
     private final String mDisplayBrightnessStrategyName;
     private final boolean mShouldUseAutoBrightness;
@@ -50,6 +51,7 @@
         mShouldUseAutoBrightness = builder.getShouldUseAutoBrightness();
         mIsSlowChange = builder.isSlowChange();
         mMaxBrightness = builder.getMaxBrightness();
+        mMinBrightness = builder.getMinBrightness();
         mCustomAnimationRate = builder.getCustomAnimationRate();
         mShouldUpdateScreenBrightnessSetting = builder.shouldUpdateScreenBrightnessSetting();
     }
@@ -105,6 +107,13 @@
     }
 
     /**
+     * @return minimum allowed brightness
+     */
+    public float getMinBrightness() {
+        return mMinBrightness;
+    }
+
+    /**
      * @return custom animation rate
      */
     public float getCustomAnimationRate() {
@@ -131,6 +140,7 @@
         stringBuilder.append(getShouldUseAutoBrightness());
         stringBuilder.append("\n    isSlowChange:").append(mIsSlowChange);
         stringBuilder.append("\n    maxBrightness:").append(mMaxBrightness);
+        stringBuilder.append("\n    minBrightness:").append(mMinBrightness);
         stringBuilder.append("\n    customAnimationRate:").append(mCustomAnimationRate);
         stringBuilder.append("\n    shouldUpdateScreenBrightnessSetting:")
                 .append(mShouldUpdateScreenBrightnessSetting);
@@ -160,6 +170,7 @@
                 && mShouldUseAutoBrightness == otherState.getShouldUseAutoBrightness()
                 && mIsSlowChange == otherState.isSlowChange()
                 && mMaxBrightness == otherState.getMaxBrightness()
+                && mMinBrightness == otherState.getMinBrightness()
                 && mCustomAnimationRate == otherState.getCustomAnimationRate()
                 && mShouldUpdateScreenBrightnessSetting
                     == otherState.shouldUpdateScreenBrightnessSetting();
@@ -168,7 +179,8 @@
     @Override
     public int hashCode() {
         return Objects.hash(mBrightness, mSdrBrightness, mBrightnessReason,
-                mShouldUseAutoBrightness, mIsSlowChange, mMaxBrightness, mCustomAnimationRate,
+                mShouldUseAutoBrightness, mIsSlowChange, mMaxBrightness, mMinBrightness,
+                mCustomAnimationRate,
                 mShouldUpdateScreenBrightnessSetting);
     }
 
@@ -190,6 +202,7 @@
         private boolean mShouldUseAutoBrightness;
         private boolean mIsSlowChange;
         private float mMaxBrightness;
+        private float mMinBrightness;
         private float mCustomAnimationRate = CUSTOM_ANIMATION_RATE_NOT_SET;
         private boolean mShouldUpdateScreenBrightnessSetting;
 
@@ -208,6 +221,7 @@
             builder.setShouldUseAutoBrightness(state.getShouldUseAutoBrightness());
             builder.setIsSlowChange(state.isSlowChange());
             builder.setMaxBrightness(state.getMaxBrightness());
+            builder.setMinBrightness(state.getMinBrightness());
             builder.setCustomAnimationRate(state.getCustomAnimationRate());
             builder.setShouldUpdateScreenBrightnessSetting(
                     state.shouldUpdateScreenBrightnessSetting());
@@ -334,6 +348,20 @@
             return mMaxBrightness;
         }
 
+        /**
+         * See {@link DisplayBrightnessState#getMinBrightness()}.
+         */
+        public Builder setMinBrightness(float minBrightness) {
+            this.mMinBrightness = minBrightness;
+            return this;
+        }
+
+        /**
+         * See {@link DisplayBrightnessState#getMinBrightness()}.
+         */
+        public float getMinBrightness() {
+            return mMinBrightness;
+        }
 
         /**
          * See {@link DisplayBrightnessState#getCustomAnimationRate()}.
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index bd22e1d..4c4cf608 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -16,6 +16,7 @@
 
 package com.android.server.display;
 
+import static com.android.server.display.BrightnessMappingStrategy.INVALID_NITS;
 import static com.android.server.display.utils.DeviceConfigParsingUtils.ambientBrightnessThresholdsIntToFloat;
 import static com.android.server.display.utils.DeviceConfigParsingUtils.displayBrightnessThresholdsIntToFloat;
 
@@ -567,7 +568,8 @@
 
     public static final int DEFAULT_LOW_REFRESH_RATE = 60;
 
-    private static final float BRIGHTNESS_DEFAULT = 0.5f;
+    @VisibleForTesting
+    static final float BRIGHTNESS_DEFAULT = 0.5f;
     private static final String ETC_DIR = "etc";
     private static final String DISPLAY_CONFIG_DIR = "displayconfig";
     private static final String CONFIG_FILE_FORMAT = "display_%s.xml";
@@ -597,8 +599,6 @@
     // so -2 is used instead
     private static final float INVALID_BRIGHTNESS_IN_CONFIG = -2f;
 
-    static final float NITS_INVALID = -1;
-
     // Length of the ambient light horizon used to calculate the long term estimate of ambient
     // light.
     private static final int AMBIENT_LIGHT_LONG_HORIZON_MILLIS = 10000;
@@ -1031,11 +1031,12 @@
     /**
      * Calculates the nits value for the specified backlight value if a mapping exists.
      *
-     * @return The mapped nits or {@link #NITS_INVALID} if no mapping exits.
+     * @return The mapped nits or {@link BrightnessMappingStrategy.INVALID_NITS} if no mapping
+     * exits.
      */
     public float getNitsFromBacklight(float backlight) {
         if (mBacklightToNitsSpline == null) {
-            return NITS_INVALID;
+            return INVALID_NITS;
         }
         backlight = Math.max(backlight, mBacklightMinimum);
         return mBacklightToNitsSpline.interpolate(backlight);
@@ -1061,7 +1062,7 @@
 
         float backlight = getBacklightFromBrightness(brightness);
         float nits = getNitsFromBacklight(backlight);
-        if (nits == NITS_INVALID) {
+        if (nits == INVALID_NITS) {
             return PowerManager.BRIGHTNESS_INVALID;
         }
 
diff --git a/services/core/java/com/android/server/display/DisplayDeviceRepository.java b/services/core/java/com/android/server/display/DisplayDeviceRepository.java
index 67e612d..6164154 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceRepository.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceRepository.java
@@ -129,7 +129,9 @@
     public DisplayDevice getByAddressLocked(@NonNull DisplayAddress address) {
         for (int i = mDisplayDevices.size() - 1; i >= 0; i--) {
             final DisplayDevice device = mDisplayDevices.get(i);
-            if (address.equals(device.getDisplayDeviceInfoLocked().address)) {
+            final DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
+            if (address.equals(info.address)
+                    || DisplayAddress.Physical.isPortMatch(address, info.address)) {
                 return device;
             }
         }
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index e38d08f..9cf9119 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -1615,6 +1615,10 @@
                 if ((flags & VIRTUAL_DISPLAY_FLAG_TRUSTED) == 0) {
                     Slog.w(TAG, "Display created with home support but lacks "
                             + "VIRTUAL_DISPLAY_FLAG_TRUSTED, ignoring the home support request.");
+                } else if ((flags & VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR) != 0) {
+                    Slog.w(TAG, "Display created with home support but has "
+                            + "VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR, ignoring the home support "
+                            + "request.");
                 } else {
                     mWindowManagerInternal.setHomeSupportedOnDisplay(displayUniqueId,
                             Display.TYPE_VIRTUAL, true);
@@ -3386,17 +3390,10 @@
         // with the corresponding displaydevice.
         HighBrightnessModeMetadata hbmMetadata =
                 mHighBrightnessModeMetadataMapper.getHighBrightnessModeMetadataLocked(display);
-        if (mConfigParameterProvider.isNewPowerControllerFeatureEnabled()) {
-            displayPowerController = new DisplayPowerController2(
-                    mContext, /* injector= */ null, mDisplayPowerCallbacks, mPowerHandler,
-                    mSensorManager, mDisplayBlanker, display, mBrightnessTracker, brightnessSetting,
-                    () -> handleBrightnessChange(display), hbmMetadata, mBootCompleted, mFlags);
-        } else {
-            displayPowerController = new DisplayPowerController(
-                    mContext, /* injector= */ null, mDisplayPowerCallbacks, mPowerHandler,
-                    mSensorManager, mDisplayBlanker, display, mBrightnessTracker, brightnessSetting,
-                    () -> handleBrightnessChange(display), hbmMetadata, mBootCompleted, mFlags);
-        }
+        displayPowerController = new DisplayPowerController(
+                mContext, /* injector= */ null, mDisplayPowerCallbacks, mPowerHandler,
+                mSensorManager, mDisplayBlanker, display, mBrightnessTracker, brightnessSetting,
+                () -> handleBrightnessChange(display), hbmMetadata, mBootCompleted, mFlags);
         mDisplayPowerControllers.append(display.getDisplayIdLocked(), displayPowerController);
         return displayPowerController;
     }
@@ -4573,8 +4570,10 @@
                     if ((flags & DisplayDeviceInfo.FLAG_NEVER_BLANK) == 0) {
                         final DisplayPowerControllerInterface displayPowerController =
                                 mDisplayPowerControllers.get(id);
-                        ready &= displayPowerController.requestPowerState(request,
-                                waitForNegativeProximity);
+                        if (displayPowerController != null) {
+                            ready &= displayPowerController.requestPowerState(request,
+                                    waitForNegativeProximity);
+                        }
                     }
                 }
 
diff --git a/services/core/java/com/android/server/display/DisplayOffloadSessionImpl.java b/services/core/java/com/android/server/display/DisplayOffloadSessionImpl.java
index 4e341a9..a43f93a 100644
--- a/services/core/java/com/android/server/display/DisplayOffloadSessionImpl.java
+++ b/services/core/java/com/android/server/display/DisplayOffloadSessionImpl.java
@@ -16,6 +16,8 @@
 
 package com.android.server.display;
 
+import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_MAX;
+
 import android.annotation.Nullable;
 import android.hardware.display.DisplayManagerInternal;
 import android.os.PowerManager;
@@ -65,6 +67,22 @@
         return true;
     }
 
+    @Override
+    public float[] getAutoBrightnessLevels(int mode) {
+        if (mode < 0 || mode > AUTO_BRIGHTNESS_MODE_MAX) {
+            throw new IllegalArgumentException("Unknown auto-brightness mode: " + mode);
+        }
+        return mDisplayPowerController.getAutoBrightnessLevels(mode);
+    }
+
+    @Override
+    public float[] getAutoBrightnessLuxLevels(int mode) {
+        if (mode < 0 || mode > AUTO_BRIGHTNESS_MODE_MAX) {
+            throw new IllegalArgumentException("Unknown auto-brightness mode: " + mode);
+        }
+        return mDisplayPowerController.getAutoBrightnessLuxLevels(mode);
+    }
+
     /**
      * Start the offload session. The method returns if the session is already active.
      * @return Whether the session was started successfully
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 06e5f99..087cacf 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012 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.
@@ -17,11 +17,12 @@
 package com.android.server.display;
 
 import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DEFAULT;
+import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DOZE;
 import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_IDLE;
+import static com.android.server.display.config.DisplayBrightnessMappingConfig.autoBrightnessPresetToString;
 
 import android.animation.Animator;
 import android.animation.ObjectAnimator;
-import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SuppressLint;
 import android.annotation.UserIdInt;
@@ -31,8 +32,6 @@
 import android.content.res.Resources;
 import android.database.ContentObserver;
 import android.hardware.Sensor;
-import android.hardware.SensorEvent;
-import android.hardware.SensorEventListener;
 import android.hardware.SensorManager;
 import android.hardware.display.AmbientBrightnessDayStats;
 import android.hardware.display.BrightnessChangeEvent;
@@ -45,6 +44,7 @@
 import android.metrics.LogMaker;
 import android.net.Uri;
 import android.os.Handler;
+import android.os.HandlerExecutor;
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
@@ -56,12 +56,12 @@
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.util.FloatProperty;
+import android.util.IndentingPrintWriter;
 import android.util.MathUtils;
 import android.util.MutableFloat;
 import android.util.MutableInt;
 import android.util.Slog;
 import android.util.SparseArray;
-import android.util.TimeUtils;
 import android.view.Display;
 
 import com.android.internal.R;
@@ -78,10 +78,15 @@
 import com.android.server.display.RampAnimator.DualRampAnimator;
 import com.android.server.display.brightness.BrightnessEvent;
 import com.android.server.display.brightness.BrightnessReason;
+import com.android.server.display.brightness.BrightnessUtils;
+import com.android.server.display.brightness.DisplayBrightnessController;
+import com.android.server.display.brightness.clamper.BrightnessClamperController;
+import com.android.server.display.brightness.strategy.AutomaticBrightnessStrategy;
 import com.android.server.display.color.ColorDisplayService.ColorDisplayServiceInternal;
 import com.android.server.display.color.ColorDisplayService.ReduceBrightColorsListener;
 import com.android.server.display.feature.DisplayManagerFlags;
 import com.android.server.display.layout.Layout;
+import com.android.server.display.state.DisplayStateController;
 import com.android.server.display.utils.DebugUtils;
 import com.android.server.display.utils.SensorUtils;
 import com.android.server.display.whitebalance.DisplayWhiteBalanceController;
@@ -119,12 +124,12 @@
     private static final String SCREEN_ON_BLOCKED_TRACE_NAME = "Screen on blocked";
     private static final String SCREEN_OFF_BLOCKED_TRACE_NAME = "Screen off blocked";
 
-    private static final String TAG = "DisplayPowerController";
+    private static final String TAG = "DisplayPowerController2";
     // To enable these logs, run:
-    // 'adb shell setprop persist.log.tag.DisplayPowerController DEBUG && adb reboot'
+    // 'adb shell setprop persist.log.tag.DisplayPowerController2 DEBUG && adb reboot'
     private static final boolean DEBUG = DebugUtils.isDebuggable(TAG);
-
-    private static final boolean DEBUG_PRETEND_PROXIMITY_SENSOR_ABSENT = false;
+    private static final String SCREEN_ON_BLOCKED_BY_DISPLAYOFFLOAD_TRACE_NAME =
+            "Screen on blocked by displayoffload";
 
     // If true, uses the color fade on animation.
     // We might want to turn this off if we cannot get a guarantee that the screen
@@ -138,36 +143,28 @@
     private static final int COLOR_FADE_OFF_ANIMATION_DURATION_MILLIS = 400;
 
     private static final int MSG_UPDATE_POWER_STATE = 1;
-    private static final int MSG_PROXIMITY_SENSOR_DEBOUNCED = 2;
-    private static final int MSG_SCREEN_ON_UNBLOCKED = 3;
-    private static final int MSG_SCREEN_OFF_UNBLOCKED = 4;
-    private static final int MSG_CONFIGURE_BRIGHTNESS = 5;
-    private static final int MSG_SET_TEMPORARY_BRIGHTNESS = 6;
-    private static final int MSG_SET_TEMPORARY_AUTO_BRIGHTNESS_ADJUSTMENT = 7;
-    private static final int MSG_IGNORE_PROXIMITY = 8;
-    private static final int MSG_STOP = 9;
-    private static final int MSG_UPDATE_BRIGHTNESS = 10;
-    private static final int MSG_UPDATE_RBC = 11;
-    private static final int MSG_BRIGHTNESS_RAMP_DONE = 12;
-    private static final int MSG_STATSD_HBM_BRIGHTNESS = 13;
-    private static final int MSG_SWITCH_USER = 14;
-    private static final int MSG_BOOT_COMPLETED = 15;
-    private static final int MSG_SET_DWBC_STRONG_MODE = 16;
-    private static final int MSG_SET_DWBC_COLOR_OVERRIDE = 17;
-    private static final int MSG_SET_DWBC_LOGGING_ENABLED = 18;
+    private static final int MSG_SCREEN_ON_UNBLOCKED = 2;
+    private static final int MSG_SCREEN_OFF_UNBLOCKED = 3;
+    private static final int MSG_CONFIGURE_BRIGHTNESS = 4;
+    private static final int MSG_SET_TEMPORARY_BRIGHTNESS = 5;
+    private static final int MSG_SET_TEMPORARY_AUTO_BRIGHTNESS_ADJUSTMENT = 6;
+    private static final int MSG_STOP = 7;
+    private static final int MSG_UPDATE_BRIGHTNESS = 8;
+    private static final int MSG_UPDATE_RBC = 9;
+    private static final int MSG_BRIGHTNESS_RAMP_DONE = 10;
+    private static final int MSG_STATSD_HBM_BRIGHTNESS = 11;
+    private static final int MSG_SWITCH_USER = 12;
+    private static final int MSG_BOOT_COMPLETED = 13;
+    private static final int MSG_SET_DWBC_STRONG_MODE = 14;
+    private static final int MSG_SET_DWBC_COLOR_OVERRIDE = 15;
+    private static final int MSG_SET_DWBC_LOGGING_ENABLED = 16;
+    private static final int MSG_SET_BRIGHTNESS_FROM_OFFLOAD = 17;
+    private static final int MSG_OFFLOADING_SCREEN_ON_UNBLOCKED = 18;
 
-    private static final int PROXIMITY_UNKNOWN = -1;
-    private static final int PROXIMITY_NEGATIVE = 0;
-    private static final int PROXIMITY_POSITIVE = 1;
 
-    // Proximity sensor debounce delay in milliseconds for positive or negative transitions.
-    private static final int PROXIMITY_SENSOR_POSITIVE_DEBOUNCE_DELAY = 0;
-    private static final int PROXIMITY_SENSOR_NEGATIVE_DEBOUNCE_DELAY = 250;
 
     private static final int BRIGHTNESS_CHANGE_STATSD_REPORT_INTERVAL_MS = 500;
 
-    // Trigger proximity if distance is less than 5 cm.
-    private static final float TYPICAL_PROXIMITY_THRESHOLD = 5.0f;
 
     // State machine constants for tracking initial brightness ramp skipping when enabled.
     private static final int RAMP_STATE_SKIP_NONE = 0;
@@ -181,6 +178,7 @@
     private static final int REPORTED_TO_POLICY_SCREEN_TURNING_OFF = 3;
 
     private static final int RINGBUFFER_MAX = 100;
+    private static final int RINGBUFFER_RBC_MAX = 20;
 
     private static final float[] BRIGHTNESS_RANGE_BOUNDARIES = {
         0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20, 30, 40, 50, 60, 70, 80,
@@ -236,10 +234,6 @@
     // Our handler.
     private final DisplayControllerHandler mHandler;
 
-    // Asynchronous callbacks into the power manager service.
-    // Only invoked from the handler thread while no locks are held.
-    private final DisplayPowerCallbacks mCallbacks;
-
     // Battery stats.
     @Nullable
     private final IBatteryStats mBatteryStats;
@@ -253,10 +247,10 @@
     // The display blanker.
     private final DisplayBlanker mBlanker;
 
-    // The LogicalDisplay tied to this DisplayPowerController.
+    // The LogicalDisplay tied to this DisplayPowerController2.
     private final LogicalDisplay mLogicalDisplay;
 
-    // The ID of the LogicalDisplay tied to this DisplayPowerController.
+    // The ID of the LogicalDisplay tied to this DisplayPowerController2.
     private final int mDisplayId;
 
     // The ID of the display which this display follows for brightness purposes.
@@ -272,34 +266,12 @@
     // Tracker for brightness settings changes.
     private final SettingsObserver mSettingsObserver;
 
-    // The proximity sensor, or null if not available or needed.
-    private Sensor mProximitySensor;
-
     // The doze screen brightness.
     private final float mScreenBrightnessDozeConfig;
 
-    // The dim screen brightness.
-    private final float mScreenBrightnessDimConfig;
-
-    // The minimum dim amount to use if the screen brightness is already below
-    // mScreenBrightnessDimConfig.
-    private final float mScreenBrightnessMinimumDimAmount;
-
-    private final float mScreenBrightnessDefault;
-
     // True if auto-brightness should be used.
     private boolean mUseSoftwareAutoBrightnessConfig;
 
-    // True if should use light sensor to automatically determine doze screen brightness.
-    private final boolean mAllowAutoBrightnessWhileDozingConfig;
-
-    // True if we want to persist the brightness value in nits even if the underlying display
-    // device changes.
-    private final boolean mPersistBrightnessNitsForDefaultDisplay;
-
-    // True if the brightness config has changed and the short-term model needs to be reset
-    private boolean mShouldResetShortTermModel;
-
     // Whether or not the color fade on screen on / off is enabled.
     private final boolean mColorFadeEnabled;
 
@@ -340,10 +312,6 @@
     @GuardedBy("mLock")
     private DisplayPowerRequest mPendingRequestLocked;
 
-    // True if a request has been made to wait for the proximity sensor to go negative.
-    @GuardedBy("mLock")
-    private boolean mPendingWaitForNegativeProximityLocked;
-
     // True if the pending power request or wait for negative proximity flag
     // has been changed since the last update occurred.
     @GuardedBy("mLock")
@@ -370,67 +338,36 @@
     // Must only be accessed on the handler thread.
     private DisplayPowerState mPowerState;
 
-    // True if the device should wait for negative proximity sensor before
-    // waking up the screen.  This is set to false as soon as a negative
-    // proximity sensor measurement is observed or when the device is forced to
-    // go to sleep by the user.  While true, the screen remains off.
-    private boolean mWaitingForNegativeProximity;
 
-    // True if the device should not take into account the proximity sensor
-    // until either the proximity sensor state changes, or there is no longer a
-    // request to listen to proximity sensor.
-    private boolean mIgnoreProximityUntilChanged;
-
-    // The actual proximity sensor threshold value.
-    private float mProximityThreshold;
-
-    // Set to true if the proximity sensor listener has been registered
-    // with the sensor manager.
-    private boolean mProximitySensorEnabled;
-
-    // The debounced proximity sensor state.
-    private int mProximity = PROXIMITY_UNKNOWN;
-
-    // The raw non-debounced proximity sensor state.
-    private int mPendingProximity = PROXIMITY_UNKNOWN;
-    private long mPendingProximityDebounceTime = -1; // -1 if fully debounced
-
-    // True if the screen was turned off because of the proximity sensor.
-    // When the screen turns on again, we report user activity to the power manager.
-    private boolean mScreenOffBecauseOfProximity;
 
     // The currently active screen on unblocker.  This field is non-null whenever
     // we are waiting for a callback to release it and unblock the screen.
     private ScreenOnUnblocker mPendingScreenOnUnblocker;
     private ScreenOffUnblocker mPendingScreenOffUnblocker;
+    private Runnable mPendingScreenOnUnblockerByDisplayOffload;
 
     // True if we were in the process of turning off the screen.
     // This allows us to recover more gracefully from situations where we abort
     // turning off the screen.
     private boolean mPendingScreenOff;
 
-    // True if we have unfinished business and are holding a suspend blocker.
-    private boolean mUnfinishedBusiness;
-
     // The elapsed real time when the screen on was blocked.
     private long mScreenOnBlockStartRealTime;
     private long mScreenOffBlockStartRealTime;
+    private long mScreenOnBlockByDisplayOffloadStartRealTime;
 
     // Screen state we reported to policy. Must be one of REPORTED_TO_POLICY_* fields.
     private int mReportedScreenStateToPolicy = REPORTED_TO_POLICY_UNREPORTED;
 
+    // Used to deduplicate the displayoffload blocking screen on logic. One block per turning on.
+    // This value is reset when screen on is reported or the blocking is cancelled.
+    private boolean mScreenTurningOnWasBlockedByDisplayOffload;
+
     // If the last recorded screen state was dozing or not.
     private boolean mDozing;
 
-    // Remembers whether certain kinds of brightness adjustments
-    // were recently applied so that we can decide how to transition.
-    private boolean mAppliedAutoBrightness;
     private boolean mAppliedDimming;
-    private boolean mAppliedLowPower;
-    private boolean mAppliedScreenBrightnessOverride;
-    private boolean mAppliedTemporaryBrightness;
-    private boolean mAppliedTemporaryAutoBrightnessAdjustment;
-    private boolean mAppliedBrightnessBoost;
+
     private boolean mAppliedThrottling;
 
     // Reason for which the brightness was last changed. See {@link BrightnessReason} for more
@@ -456,7 +393,7 @@
     private final boolean mSkipScreenOnBrightnessRamp;
 
     // Display white balance components.
-    // Critical methods must be called on DPC handler thread.
+    // Critical methods must be called on DPC2 handler thread.
     @Nullable
     private final DisplayWhiteBalanceSettings mDisplayWhiteBalanceSettings;
     @Nullable
@@ -468,21 +405,39 @@
 
     private final BrightnessRangeController mBrightnessRangeController;
 
-    @Nullable
-    private final HighBrightnessModeMetadata mHighBrightnessModeMetadata;
-
     private final BrightnessThrottler mBrightnessThrottler;
 
-    private final BrightnessSetting mBrightnessSetting;
+    private final BrightnessClamperController mBrightnessClamperController;
 
     private final Runnable mOnBrightnessChangeRunnable;
 
     private final BrightnessEvent mLastBrightnessEvent;
     private final BrightnessEvent mTempBrightnessEvent;
 
+    private final DisplayBrightnessController mDisplayBrightnessController;
+
     // Keeps a record of brightness changes for dumpsys.
     private RingBuffer<BrightnessEvent> mBrightnessEventRingBuffer;
 
+    // Keeps a record of rbc changes for dumpsys.
+    private final RingBuffer<BrightnessEvent> mRbcEventRingBuffer =
+            new RingBuffer<>(BrightnessEvent.class, RINGBUFFER_RBC_MAX);
+
+    // Controls and tracks all the wakelocks that are acquired/released by the system. Also acts as
+    // a medium of communication between this class and the PowerManagerService.
+    private final WakelockController mWakelockController;
+
+    // Tracks and manages the proximity state of the associated display.
+    private final DisplayPowerProximityStateController mDisplayPowerProximityStateController;
+
+    // Tracks and manages the display state of the associated display.
+    private final DisplayStateController mDisplayStateController;
+
+
+    // Responsible for evaluating and tracking the automatic brightness relevant states.
+    // Todo: This is a temporary workaround. Ideally DPC2 should never talk to the strategies
+    private final AutomaticBrightnessStrategy mAutomaticBrightnessStrategy;
+
     // A record of state for skipping brightness ramps.
     private int mSkipRampState = RAMP_STATE_SKIP_NONE;
 
@@ -500,81 +455,18 @@
     private Sensor mLightSensor;
     private Sensor mScreenOffBrightnessSensor;
 
-    // The current brightness configuration.
-    @Nullable
-    private BrightnessConfiguration mBrightnessConfiguration;
-
-    // The last brightness that was set by the user and not temporary. Set to
-    // PowerManager.BRIGHTNESS_INVALID_FLOAT when a brightness has yet to be recorded.
-    private float mLastUserSetScreenBrightness = Float.NaN;
-
-    // The screen brightness setting has changed but not taken effect yet. If this is different
-    // from the current screen brightness setting then this is coming from something other than us
-    // and should be considered a user interaction.
-    private float mPendingScreenBrightnessSetting;
-
-    // The last observed screen brightness setting, either set by us or by the settings app on
-    // behalf of the user.
-    private float mCurrentScreenBrightnessSetting;
-
-    // The temporary screen brightness. Typically set when a user is interacting with the
-    // brightness slider but hasn't settled on a choice yet. Set to
-    // PowerManager.BRIGHTNESS_INVALID_FLOAT when there's no temporary brightness set.
-    private float mTemporaryScreenBrightness;
-
-    // This brightness value is set in concurrent displays mode. It is the brightness value
-    // of the lead display that this DPC should follow.
-    private float mBrightnessToFollow;
-
-    // Indicates whether we should ramp slowly to the brightness value to follow.
-    private boolean mBrightnessToFollowSlowChange;
-
-    // The last auto brightness adjustment that was set by the user and not temporary. Set to
-    // Float.NaN when an auto-brightness adjustment hasn't been recorded yet.
-    private float mAutoBrightnessAdjustment;
-
-    // The pending auto brightness adjustment that will take effect on the next power state update.
-    private float mPendingAutoBrightnessAdjustment;
-
-    // The temporary auto brightness adjustment. Typically set when a user is interacting with the
-    // adjustment slider but hasn't settled on a choice yet. Set to
-    // PowerManager.BRIGHTNESS_INVALID_FLOAT when there's no temporary adjustment set.
-    private float mTemporaryAutoBrightnessAdjustment;
-
-    private boolean mUseAutoBrightness;
-
     private boolean mIsRbcActive;
 
-    // Whether there's a callback to tell listeners the display has changed scheduled to run. When
-    // true it implies a wakelock is being held to guarantee the update happens before we collapse
-    // into suspend and so needs to be cleaned up if the thread is exiting.
-    // Should only be accessed on the Handler thread.
-    private boolean mOnStateChangedPending;
-
-    // Count of proximity messages currently on this DPC's Handler. Used to keep track of how many
-    // suspend blocker acquisitions are pending when shutting down this DPC.
-    // Should only be accessed on the Handler thread.
-    private int mOnProximityPositiveMessages;
-    private int mOnProximityNegativeMessages;
-
     // Animators.
     private ObjectAnimator mColorFadeOnAnimator;
     private ObjectAnimator mColorFadeOffAnimator;
     private DualRampAnimator<DisplayPowerState> mScreenBrightnessRampAnimator;
-    private BrightnessSetting.BrightnessSettingListener mBrightnessSettingListener;
 
-    // True if this DisplayPowerController has been stopped and should no longer be running.
+    // True if this DisplayPowerController2 has been stopped and should no longer be running.
     private boolean mStopped;
 
     private DisplayDeviceConfig mDisplayDeviceConfig;
 
-    // Identifiers for suspend blocker acquisition requests
-    private final String mSuspendBlockerIdUnfinishedBusiness;
-    private final String mSuspendBlockerIdOnStateChanged;
-    private final String mSuspendBlockerIdProxPositive;
-    private final String mSuspendBlockerIdProxNegative;
-    private final String mSuspendBlockerIdProxDebounce;
-
     private boolean mIsEnabled;
     private boolean mIsInTransition;
     private boolean mIsDisplayInternal;
@@ -585,13 +477,13 @@
     // DPCs following the brightness of this DPC. This is used in concurrent displays mode - there
     // is one lead display, the additional displays follow the brightness value of the lead display.
     @GuardedBy("mLock")
-    private final SparseArray<DisplayPowerControllerInterface> mDisplayBrightnessFollowers =
-            new SparseArray<>();
+    private SparseArray<DisplayPowerControllerInterface> mDisplayBrightnessFollowers =
+            new SparseArray();
 
     private boolean mBootCompleted;
     private final DisplayManagerFlags mFlags;
-    private int mDozeStateOverride = Display.STATE_UNKNOWN;
-    private DisplayManagerInternal.DisplayOffloadSession mDisplayOffloadSession;
+
+    private DisplayOffloadSession mDisplayOffloadSession;
 
     /**
      * Creates the display power controller.
@@ -607,26 +499,28 @@
         mClock = mInjector.getClock();
         mLogicalDisplay = logicalDisplay;
         mDisplayId = mLogicalDisplay.getDisplayIdLocked();
-        mTag = TAG + "[" + mDisplayId + "]";
-        mHighBrightnessModeMetadata = hbmMetadata;
-        mSuspendBlockerIdUnfinishedBusiness = getSuspendBlockerUnfinishedBusinessId(mDisplayId);
-        mSuspendBlockerIdOnStateChanged = getSuspendBlockerOnStateChangedId(mDisplayId);
-        mSuspendBlockerIdProxPositive = getSuspendBlockerProxPositiveId(mDisplayId);
-        mSuspendBlockerIdProxNegative = getSuspendBlockerProxNegativeId(mDisplayId);
-        mSuspendBlockerIdProxDebounce = getSuspendBlockerProxDebounceId(mDisplayId);
-
-        mDisplayDevice = mLogicalDisplay.getPrimaryDisplayDeviceLocked();
-        mUniqueDisplayId = logicalDisplay.getPrimaryDisplayDeviceLocked().getUniqueId();
-        mDisplayStatsId = mUniqueDisplayId.hashCode();
+        mSensorManager = sensorManager;
+        mHandler = new DisplayControllerHandler(handler.getLooper());
+        mDisplayDeviceConfig = logicalDisplay.getPrimaryDisplayDeviceLocked()
+                .getDisplayDeviceConfig();
         mIsEnabled = logicalDisplay.isEnabledLocked();
         mIsInTransition = logicalDisplay.isInTransitionLocked();
         mIsDisplayInternal = logicalDisplay.getPrimaryDisplayDeviceLocked()
                 .getDisplayDeviceInfoLocked().type == Display.TYPE_INTERNAL;
-        mHandler = new DisplayControllerHandler(handler.getLooper());
-        mLastBrightnessEvent = new BrightnessEvent(mDisplayId);
-        mTempBrightnessEvent = new BrightnessEvent(mDisplayId);
+        mWakelockController = mInjector.getWakelockController(mDisplayId, callbacks);
+        mDisplayPowerProximityStateController = mInjector.getDisplayPowerProximityStateController(
+                mWakelockController, mDisplayDeviceConfig, mHandler.getLooper(),
+                () -> updatePowerState(), mDisplayId, mSensorManager);
+        mDisplayStateController = new DisplayStateController(mDisplayPowerProximityStateController);
+        mTag = TAG + "[" + mDisplayId + "]";
         mThermalBrightnessThrottlingDataId =
                 logicalDisplay.getDisplayInfoLocked().thermalBrightnessThrottlingDataId;
+        mDisplayDevice = mLogicalDisplay.getPrimaryDisplayDeviceLocked();
+        mUniqueDisplayId = logicalDisplay.getPrimaryDisplayDeviceLocked().getUniqueId();
+        mDisplayStatsId = mUniqueDisplayId.hashCode();
+
+        mLastBrightnessEvent = new BrightnessEvent(mDisplayId);
+        mTempBrightnessEvent = new BrightnessEvent(mDisplayId);
 
         if (mDisplayId == Display.DEFAULT_DISPLAY) {
             mBatteryStats = BatteryStatsService.getService();
@@ -635,14 +529,10 @@
         }
 
         mSettingsObserver = new SettingsObserver(mHandler);
-        mCallbacks = callbacks;
-        mSensorManager = sensorManager;
         mWindowManagerPolicy = LocalServices.getService(WindowManagerPolicy.class);
         mBlanker = blanker;
         mContext = context;
         mBrightnessTracker = brightnessTracker;
-        // TODO: b/186428377 update brightness setting when display changes
-        mBrightnessSetting = brightnessSetting;
         mOnBrightnessChangeRunnable = onBrightnessChangeRunnable;
 
         PowerManager pm = context.getSystemService(PowerManager.class);
@@ -650,30 +540,12 @@
         final Resources resources = context.getResources();
 
         // DOZE AND DIM SETTINGS
-        mScreenBrightnessDozeConfig = clampAbsoluteBrightness(
+        mScreenBrightnessDozeConfig = BrightnessUtils.clampAbsoluteBrightness(
                 pm.getBrightnessConstraint(PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_DOZE));
-        mScreenBrightnessDimConfig = clampAbsoluteBrightness(
-                pm.getBrightnessConstraint(PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_DIM));
-        mScreenBrightnessMinimumDimAmount = resources.getFloat(
-                com.android.internal.R.dimen.config_screenBrightnessMinimumDimAmountFloat);
-
-
-        // NORMAL SCREEN SETTINGS
-        mScreenBrightnessDefault = clampAbsoluteBrightness(
-                mLogicalDisplay.getDisplayInfoLocked().brightnessDefault);
-
-        mAllowAutoBrightnessWhileDozingConfig = resources.getBoolean(
-                com.android.internal.R.bool.config_allowAutoBrightnessWhileDozing);
-
-        mPersistBrightnessNitsForDefaultDisplay = resources.getBoolean(
-                com.android.internal.R.bool.config_persistBrightnessNitsForDefaultDisplay);
-
-        mDisplayDeviceConfig = logicalDisplay.getPrimaryDisplayDeviceLocked()
-                .getDisplayDeviceConfig();
-
         loadBrightnessRampRates();
         mSkipScreenOnBrightnessRamp = resources.getBoolean(
-                com.android.internal.R.bool.config_skipScreenOnBrightnessRamp);
+                R.bool.config_skipScreenOnBrightnessRamp);
+
         Runnable modeChangeCallback = () -> {
             sendUpdatePowerState();
             postBrightnessChangeRunnable();
@@ -683,23 +555,38 @@
             }
         };
 
-        HighBrightnessModeController hbmController = createHbmControllerLocked(modeChangeCallback);
+        HighBrightnessModeController hbmController = createHbmControllerLocked(hbmMetadata,
+                modeChangeCallback);
+        mBrightnessThrottler = createBrightnessThrottlerLocked();
 
-        mBrightnessRangeController = new BrightnessRangeController(hbmController,
+        mBrightnessRangeController = mInjector.getBrightnessRangeController(hbmController,
                 modeChangeCallback, mDisplayDeviceConfig, mHandler, flags,
                 mDisplayDevice.getDisplayTokenLocked(),
                 mDisplayDevice.getDisplayDeviceInfoLocked());
 
-        mBrightnessThrottler = createBrightnessThrottlerLocked();
+        mDisplayBrightnessController =
+                new DisplayBrightnessController(context, null,
+                        mDisplayId, mLogicalDisplay.getDisplayInfoLocked().brightnessDefault,
+                        brightnessSetting, () -> postBrightnessChangeRunnable(),
+                        new HandlerExecutor(mHandler), flags);
 
+        mBrightnessClamperController = mInjector.getBrightnessClamperController(
+                mHandler, modeChangeCallback::run,
+                new BrightnessClamperController.DisplayDeviceData(
+                        mUniqueDisplayId,
+                        mThermalBrightnessThrottlingDataId,
+                        logicalDisplay.getPowerThrottlingDataIdLocked(),
+                        mDisplayDeviceConfig), mContext, flags);
         // Seed the cached brightness
         saveBrightnessInfo(getScreenBrightnessSetting());
+        mAutomaticBrightnessStrategy =
+                mDisplayBrightnessController.getAutomaticBrightnessStrategy();
 
         DisplayWhiteBalanceSettings displayWhiteBalanceSettings = null;
         DisplayWhiteBalanceController displayWhiteBalanceController = null;
         if (mDisplayId == Display.DEFAULT_DISPLAY) {
             try {
-                displayWhiteBalanceController = injector.getDisplayWhiteBalanceController(
+                displayWhiteBalanceController = mInjector.getDisplayWhiteBalanceController(
                         mHandler, mSensorManager, resources);
                 displayWhiteBalanceSettings = new DisplayWhiteBalanceSettings(mContext, mHandler);
                 displayWhiteBalanceSettings.setCallbacks(this);
@@ -715,21 +602,24 @@
 
         if (mDisplayId == Display.DEFAULT_DISPLAY) {
             mCdsi = LocalServices.getService(ColorDisplayServiceInternal.class);
-            boolean active = mCdsi.setReduceBrightColorsListener(new ReduceBrightColorsListener() {
-                @Override
-                public void onReduceBrightColorsActivationChanged(boolean activated,
-                        boolean userInitiated) {
-                    applyReduceBrightColorsSplineAdjustment();
+            if (mCdsi != null) {
+                boolean active = mCdsi.setReduceBrightColorsListener(
+                        new ReduceBrightColorsListener() {
+                            @Override
+                            public void onReduceBrightColorsActivationChanged(boolean activated,
+                                    boolean userInitiated) {
+                                applyReduceBrightColorsSplineAdjustment();
 
-                }
+                            }
 
-                @Override
-                public void onReduceBrightColorsStrengthChanged(int strength) {
+                            @Override
+                            public void onReduceBrightColorsStrengthChanged(int strength) {
+                                applyReduceBrightColorsSplineAdjustment();
+                            }
+                        });
+                if (active) {
                     applyReduceBrightColorsSplineAdjustment();
                 }
-            });
-            if (active) {
-                applyReduceBrightColorsSplineAdjustment();
             }
         } else {
             mCdsi = null;
@@ -737,27 +627,17 @@
 
         setUpAutoBrightness(context, handler);
 
-        mColorFadeEnabled = !ActivityManager.isLowRamDeviceStatic()
+        mColorFadeEnabled = mInjector.isColorFadeEnabled()
                 && !resources.getBoolean(
                   com.android.internal.R.bool.config_displayColorFadeDisabled);
         mColorFadeFadesConfig = resources.getBoolean(
-                com.android.internal.R.bool.config_animateScreenLights);
+                R.bool.config_animateScreenLights);
 
         mDisplayBlanksAfterDozeConfig = resources.getBoolean(
-                com.android.internal.R.bool.config_displayBlanksAfterDoze);
+                R.bool.config_displayBlanksAfterDoze);
 
         mBrightnessBucketsInDozeConfig = resources.getBoolean(
-                com.android.internal.R.bool.config_displayBrightnessBucketsInDoze);
-
-        loadProximitySensor();
-
-        loadNitBasedBrightnessSetting();
-        mBrightnessToFollow = PowerManager.BRIGHTNESS_INVALID_FLOAT;
-        mAutoBrightnessAdjustment = getAutoBrightnessAdjustmentSetting();
-        mTemporaryScreenBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
-        mPendingScreenBrightnessSetting = PowerManager.BRIGHTNESS_INVALID_FLOAT;
-        mTemporaryAutoBrightnessAdjustment = PowerManager.BRIGHTNESS_INVALID_FLOAT;
-        mPendingAutoBrightnessAdjustment = PowerManager.BRIGHTNESS_INVALID_FLOAT;
+                R.bool.config_displayBrightnessBucketsInDoze);
 
         mBootCompleted = bootCompleted;
     }
@@ -785,7 +665,7 @@
      */
     @Override
     public boolean isProximitySensorAvailable() {
-        return mProximitySensor != null;
+        return mDisplayPowerProximityStateController.isProximitySensorAvailable();
     }
 
     /**
@@ -818,64 +698,6 @@
         }
     }
 
-    @Override
-    public int getDisplayId() {
-        return mDisplayId;
-    }
-
-    @Override
-    public int getLeadDisplayId() {
-        return mLeadDisplayId;
-    }
-
-    @Override
-    public void setBrightnessToFollow(float leadDisplayBrightness, float nits, float ambientLux,
-            boolean slowChange) {
-        mBrightnessRangeController.onAmbientLuxChange(ambientLux);
-        if (nits == BrightnessMappingStrategy.INVALID_NITS) {
-            mBrightnessToFollow = leadDisplayBrightness;
-        } else {
-            float brightness = getBrightnessFromNits(nits);
-            if (isValidBrightnessValue(brightness)) {
-                mBrightnessToFollow = brightness;
-            } else {
-                // The device does not support nits
-                mBrightnessToFollow = leadDisplayBrightness;
-            }
-        }
-        mBrightnessToFollowSlowChange = slowChange;
-        sendUpdatePowerState();
-    }
-
-    @Override
-    public void addDisplayBrightnessFollower(@NonNull DisplayPowerControllerInterface follower) {
-        synchronized (mLock) {
-            mDisplayBrightnessFollowers.append(follower.getDisplayId(), follower);
-            sendUpdatePowerStateLocked();
-        }
-    }
-
-    @Override
-    public void removeDisplayBrightnessFollower(@NonNull DisplayPowerControllerInterface follower) {
-        synchronized (mLock) {
-            mDisplayBrightnessFollowers.remove(follower.getDisplayId());
-            mHandler.postAtTime(() -> follower.setBrightnessToFollow(
-                    PowerManager.BRIGHTNESS_INVALID_FLOAT, BrightnessMappingStrategy.INVALID_NITS,
-                    /* ambientLux= */ 0, /* slowChange= */ false), mClock.uptimeMillis());
-        }
-    }
-
-    @GuardedBy("mLock")
-    private void clearDisplayBrightnessFollowersLocked() {
-        for (int i = 0; i < mDisplayBrightnessFollowers.size(); i++) {
-            DisplayPowerControllerInterface follower = mDisplayBrightnessFollowers.valueAt(i);
-            mHandler.postAtTime(() -> follower.setBrightnessToFollow(
-                    PowerManager.BRIGHTNESS_INVALID_FLOAT, BrightnessMappingStrategy.INVALID_NITS,
-                    /* ambientLux= */ 0, /* slowChange= */ false), mClock.uptimeMillis());
-        }
-        mDisplayBrightnessFollowers.clear();
-    }
-
     @Nullable
     @Override
     public ParceledListSlice<AmbientBrightnessDayStats> getAmbientBrightnessStats(
@@ -923,13 +745,8 @@
                 return true;
             }
 
-            boolean changed = false;
-
-            if (waitForNegativeProximity
-                    && !mPendingWaitForNegativeProximityLocked) {
-                mPendingWaitForNegativeProximityLocked = true;
-                changed = true;
-            }
+            boolean changed = mDisplayPowerProximityStateController
+                    .setPendingWaitForNegativeProximityLocked(waitForNegativeProximity);
 
             if (mPendingRequestLocked == null) {
                 mPendingRequestLocked = new DisplayPowerRequest(request);
@@ -953,18 +770,23 @@
 
     @Override
     public void overrideDozeScreenState(int displayState) {
-        synchronized (mLock) {
-            if (mDisplayOffloadSession == null ||
-                    !DisplayOffloadSession.isSupportedOffloadState(displayState)) {
+        mHandler.postAtTime(() -> {
+            if (mDisplayOffloadSession == null
+                    || !(DisplayOffloadSession.isSupportedOffloadState(displayState)
+                    || displayState == Display.STATE_UNKNOWN)) {
                 return;
             }
-            mDozeStateOverride = displayState;
+            mDisplayStateController.overrideDozeScreenState(displayState);
             sendUpdatePowerState();
-        }
+        }, mClock.uptimeMillis());
     }
 
     @Override
     public void setDisplayOffloadSession(DisplayOffloadSession session) {
+        if (session == mDisplayOffloadSession) {
+            return;
+        }
+        unblockScreenOnByDisplayOffload();
         mDisplayOffloadSession = session;
     }
 
@@ -981,14 +803,14 @@
      * when displays get swapped on foldable devices.  For example, different brightness properties
      * of each display need to be properly reflected in AutomaticBrightnessController.
      *
-     * Make sure DisplayManagerService.mSyncRoot is held when this is called
+     * Make sure DisplayManagerService.mSyncRoot lock is held when this is called
      */
     @Override
     public void onDisplayChanged(HighBrightnessModeMetadata hbmMetadata, int leadDisplayId) {
         mLeadDisplayId = leadDisplayId;
         final DisplayDevice device = mLogicalDisplay.getPrimaryDisplayDeviceLocked();
         if (device == null) {
-            Slog.wtf(mTag, "Display Device is null in DisplayPowerController for display: "
+            Slog.wtf(mTag, "Display Device is null in DisplayPowerController2 for display: "
                     + mLogicalDisplay.getDisplayIdLocked());
             return;
         }
@@ -1004,6 +826,9 @@
                 .getDisplayDeviceInfoLocked().type == Display.TYPE_INTERNAL;
         final String thermalBrightnessThrottlingDataId =
                 mLogicalDisplay.getDisplayInfoLocked().thermalBrightnessThrottlingDataId;
+        final String powerThrottlingDataId =
+                mLogicalDisplay.getPowerThrottlingDataIdLocked();
+
         mHandler.postAtTime(() -> {
             boolean changed = false;
             if (mDisplayDevice != device) {
@@ -1014,9 +839,9 @@
                 mDisplayDeviceConfig = config;
                 mThermalBrightnessThrottlingDataId = thermalBrightnessThrottlingDataId;
                 loadFromDisplayDeviceConfig(token, info, hbmMetadata);
-                loadNitBasedBrightnessSetting();
+                mDisplayPowerProximityStateController.notifyDisplayDeviceChanged(config);
 
-                /// Since the underlying display-device changed, we really don't know the
+                // Since the underlying display-device changed, we really don't know the
                 // last command that was sent to change it's state. Let's assume it is unknown so
                 // that we trigger a change immediately.
                 mPowerState.resetScreenState();
@@ -1034,7 +859,16 @@
                 mIsEnabled = isEnabled;
                 mIsInTransition = isInTransition;
             }
+
             mIsDisplayInternal = isDisplayInternal;
+            // using local variables here, when mBrightnessThrottler is removed,
+            // mThermalBrightnessThrottlingDataId could be removed as well
+            // changed = true will be not needed - clampers are maintaining their state and
+            // will call updatePowerState if needed.
+            mBrightnessClamperController.onDisplayChanged(
+                    new BrightnessClamperController.DisplayDeviceData(uniqueId,
+                        thermalBrightnessThrottlingDataId, powerThrottlingDataId, config));
+
             if (changed) {
                 updatePowerState();
             }
@@ -1044,7 +878,7 @@
     /**
      * Unregisters all listeners and interrupts all running threads; halting future work.
      *
-     * This method should be called when the DisplayPowerController is no longer in use; i.e. when
+     * This method should be called when the DisplayPowerController2 is no longer in use; i.e. when
      * the {@link #mDisplayId display} has been removed.
      */
     @Override
@@ -1060,9 +894,7 @@
                 mAutomaticBrightnessController.stop();
             }
 
-            if (mBrightnessSetting != null) {
-                mBrightnessSetting.unregisterListener(mBrightnessSettingListener);
-            }
+            mDisplayBrightnessController.stop();
 
             mContext.getContentResolver().unregisterContentObserver(mSettingsObserver);
         }
@@ -1073,11 +905,11 @@
         // All properties that depend on the associated DisplayDevice and the DDC must be
         // updated here.
         loadBrightnessRampRates();
-        loadProximitySensor();
         loadNitsRange(mContext.getResources());
         setUpAutoBrightness(mContext, mHandler);
         reloadReduceBrightColours();
         setAnimatorRampSpeeds(/* isIdleMode= */ false);
+
         mBrightnessRangeController.loadFromConfig(hbmMetadata, token, info, mDisplayDeviceConfig);
         mBrightnessThrottler.loadThermalBrightnessThrottlingDataFromDisplayDeviceConfig(
                 mDisplayDeviceConfig.getThermalBrightnessThrottlingDataMapByThrottlingId(),
@@ -1126,22 +958,30 @@
         noteScreenBrightness(mPowerState.getScreenBrightness());
 
         // Initialize all of the brightness tracking state
-        final float brightness = convertToAdjustedNits(mPowerState.getScreenBrightness());
+        final float brightness = mDisplayBrightnessController.convertToAdjustedNits(
+                mPowerState.getScreenBrightness());
         if (mBrightnessTracker != null && brightness >= PowerManager.BRIGHTNESS_MIN) {
             mBrightnessTracker.start(brightness);
         }
-        mBrightnessSettingListener = brightnessValue -> {
+
+        BrightnessSetting.BrightnessSettingListener brightnessSettingListener = brightnessValue -> {
             Message msg = mHandler.obtainMessage(MSG_UPDATE_BRIGHTNESS, brightnessValue);
             mHandler.sendMessageAtTime(msg, mClock.uptimeMillis());
         };
+        mDisplayBrightnessController
+                .registerBrightnessSettingChangeListener(brightnessSettingListener);
 
-        mBrightnessSetting.registerListener(mBrightnessSettingListener);
         mContext.getContentResolver().registerContentObserver(
                 Settings.System.getUriFor(Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ),
                 false /*notifyForDescendants*/, mSettingsObserver, UserHandle.USER_ALL);
         mContext.getContentResolver().registerContentObserver(
                 Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_MODE),
                 false /*notifyForDescendants*/, mSettingsObserver, UserHandle.USER_ALL);
+        if (mFlags.areAutoBrightnessModesEnabled()) {
+            mContext.getContentResolver().registerContentObserver(
+                    Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_FOR_ALS),
+                    /* notifyForDescendants= */ false, mSettingsObserver, UserHandle.USER_CURRENT);
+        }
         handleBrightnessModeChange();
     }
 
@@ -1165,12 +1005,21 @@
         if (isIdleScreenBrightnessEnabled) {
             BrightnessMappingStrategy idleModeBrightnessMapper =
                     BrightnessMappingStrategy.create(context, mDisplayDeviceConfig,
-                            AUTO_BRIGHTNESS_MODE_IDLE, mDisplayWhiteBalanceController);
+                            AUTO_BRIGHTNESS_MODE_IDLE,
+                            mDisplayWhiteBalanceController);
             if (idleModeBrightnessMapper != null) {
-                brightnessMappers.append(AUTO_BRIGHTNESS_MODE_IDLE, idleModeBrightnessMapper);
+                brightnessMappers.append(AUTO_BRIGHTNESS_MODE_IDLE,
+                        idleModeBrightnessMapper);
             }
         }
 
+        BrightnessMappingStrategy dozeModeBrightnessMapper =
+                BrightnessMappingStrategy.create(context, mDisplayDeviceConfig,
+                        AUTO_BRIGHTNESS_MODE_DOZE, mDisplayWhiteBalanceController);
+        if (mFlags.areAutoBrightnessModesEnabled() && dozeModeBrightnessMapper != null) {
+            brightnessMappers.put(AUTO_BRIGHTNESS_MODE_DOZE, dozeModeBrightnessMapper);
+        }
+
         float userLux = BrightnessMappingStrategy.INVALID_LUX;
         float userNits = BrightnessMappingStrategy.INVALID_NITS;
         if (mAutomaticBrightnessController != null) {
@@ -1180,7 +1029,7 @@
 
         if (defaultModeBrightnessMapper != null) {
             final float dozeScaleFactor = context.getResources().getFraction(
-                    com.android.internal.R.fraction.config_screenAutoBrightnessDozeScaleFactor,
+                    R.fraction.config_screenAutoBrightnessDozeScaleFactor,
                     1, 1);
 
             // Ambient Lux - Active Mode Brightness Thresholds
@@ -1264,14 +1113,14 @@
             long darkeningLightDebounceIdle = mDisplayDeviceConfig
                     .getAutoBrightnessDarkeningLightDebounceIdle();
             boolean autoBrightnessResetAmbientLuxAfterWarmUp = context.getResources().getBoolean(
-                    com.android.internal.R.bool.config_autoBrightnessResetAmbientLuxAfterWarmUp);
+                    R.bool.config_autoBrightnessResetAmbientLuxAfterWarmUp);
 
             int lightSensorWarmUpTimeConfig = context.getResources().getInteger(
-                    com.android.internal.R.integer.config_lightSensorWarmupTime);
+                    R.integer.config_lightSensorWarmupTime);
             int lightSensorRate = context.getResources().getInteger(
-                    com.android.internal.R.integer.config_autoBrightnessLightSensorRate);
+                    R.integer.config_autoBrightnessLightSensorRate);
             int initialLightSensorRate = context.getResources().getInteger(
-                    com.android.internal.R.integer.config_autoBrightnessInitialLightSensorRate);
+                    R.integer.config_autoBrightnessInitialLightSensorRate);
             if (initialLightSensorRate == -1) {
                 initialLightSensorRate = lightSensorRate;
             } else if (initialLightSensorRate > lightSensorRate) {
@@ -1301,7 +1150,11 @@
                     screenBrightnessThresholdsIdle, mContext, mBrightnessRangeController,
                     mBrightnessThrottler, mDisplayDeviceConfig.getAmbientHorizonShort(),
                     mDisplayDeviceConfig.getAmbientHorizonLong(), userLux, userNits);
+            mDisplayBrightnessController.setAutomaticBrightnessController(
+                    mAutomaticBrightnessController);
 
+            mAutomaticBrightnessStrategy
+                    .setAutomaticBrightnessController(mAutomaticBrightnessController);
             mBrightnessEventRingBuffer =
                     new RingBuffer<>(BrightnessEvent.class, RINGBUFFER_MAX);
 
@@ -1351,7 +1204,7 @@
         } else {
             Slog.w(mTag, "Screen brightness nits configuration is unavailable; falling back");
             mNitsRange = BrightnessMappingStrategy.getFloatArray(resources
-                    .obtainTypedArray(com.android.internal.R.array.config_screenBrightnessNits));
+                    .obtainTypedArray(R.array.config_screenBrightnessNits));
         }
     }
 
@@ -1369,7 +1222,6 @@
             mAutomaticBrightnessController.switchMode(mode);
             setAnimatorRampSpeeds(isIdle);
         }
-
         Message msg = mHandler.obtainMessage();
         msg.what = MSG_SET_DWBC_STRONG_MODE;
         msg.arg1 = isIdle ? 1 : 0;
@@ -1421,28 +1273,14 @@
 
     /** Clean up all resources that are accessed via the {@link #mHandler} thread. */
     private void cleanupHandlerThreadAfterStop() {
-        setProximitySensorEnabled(false);
+        mDisplayPowerProximityStateController.cleanup();
         mBrightnessRangeController.stop();
         mBrightnessThrottler.stop();
+        mBrightnessClamperController.stop();
         mHandler.removeCallbacksAndMessages(null);
 
         // Release any outstanding wakelocks we're still holding because of pending messages.
-        if (mUnfinishedBusiness) {
-            mCallbacks.releaseSuspendBlocker(mSuspendBlockerIdUnfinishedBusiness);
-            mUnfinishedBusiness = false;
-        }
-        if (mOnStateChangedPending) {
-            mCallbacks.releaseSuspendBlocker(mSuspendBlockerIdOnStateChanged);
-            mOnStateChangedPending = false;
-        }
-        for (int i = 0; i < mOnProximityPositiveMessages; i++) {
-            mCallbacks.releaseSuspendBlocker(mSuspendBlockerIdProxPositive);
-        }
-        mOnProximityPositiveMessages = 0;
-        for (int i = 0; i < mOnProximityNegativeMessages; i++) {
-            mCallbacks.releaseSuspendBlocker(mSuspendBlockerIdProxNegative);
-        }
-        mOnProximityNegativeMessages = 0;
+        mWakelockController.releaseAll();
 
         final float brightness = mPowerState != null
                 ? mPowerState.getScreenBrightness()
@@ -1457,10 +1295,10 @@
         if (mScreenOffBrightnessSensorController != null) {
             mScreenOffBrightnessSensorController.stop();
         }
+
         if (mDisplayWhiteBalanceController != null) {
             mDisplayWhiteBalanceController.setEnabled(false);
         }
-
     }
 
     // Call from handler thread
@@ -1476,7 +1314,6 @@
         final boolean mustNotify;
         final int previousPolicy;
         boolean mustInitialize = false;
-        int brightnessAdjustmentFlags = 0;
         mBrightnessReasonTemp.set(null);
         mTempBrightnessEvent.reset();
         SparseArray<DisplayPowerControllerInterface> displayBrightnessFollowers;
@@ -1491,7 +1328,7 @@
 
             if (mPowerRequest == null) {
                 mPowerRequest = new DisplayPowerRequest(mPendingRequestLocked);
-                updatePendingProximityRequestsLocked();
+                mDisplayPowerProximityStateController.updatePendingProximityRequestsLocked();
                 mPendingRequestChangedLocked = false;
                 mustInitialize = true;
                 // Assume we're on and bright until told otherwise, since that's the state we turn
@@ -1500,7 +1337,7 @@
             } else if (mPendingRequestChangedLocked) {
                 previousPolicy = mPowerRequest.policy;
                 mPowerRequest.copyFrom(mPendingRequestLocked);
-                updatePendingProximityRequestsLocked();
+                mDisplayPowerProximityStateController.updatePendingProximityRequestsLocked();
                 mPendingRequestChangedLocked = false;
                 mDisplayReadyLocked = false;
             } else {
@@ -1512,105 +1349,8 @@
             displayBrightnessFollowers = mDisplayBrightnessFollowers.clone();
         }
 
-        // Compute the basic display state using the policy.
-        // We might override this below based on other factors.
-        // Initialise brightness as invalid.
-        int state;
-        float brightnessState = PowerManager.BRIGHTNESS_INVALID_FLOAT;
-        boolean performScreenOffTransition = false;
-        switch (mPowerRequest.policy) {
-            case DisplayPowerRequest.POLICY_OFF:
-                state = Display.STATE_OFF;
-                performScreenOffTransition = true;
-                break;
-            case DisplayPowerRequest.POLICY_DOZE:
-                if (mDozeStateOverride != Display.STATE_UNKNOWN) {
-                    state = mDozeStateOverride;
-                } else if (mPowerRequest.dozeScreenState != Display.STATE_UNKNOWN) {
-                    state = mPowerRequest.dozeScreenState;
-                } else {
-                    state = Display.STATE_DOZE;
-                }
-                if (!mAllowAutoBrightnessWhileDozingConfig) {
-                    brightnessState = mPowerRequest.dozeScreenBrightness;
-                    mBrightnessReasonTemp.setReason(BrightnessReason.REASON_DOZE);
-                }
-                break;
-            case DisplayPowerRequest.POLICY_DIM:
-            case DisplayPowerRequest.POLICY_BRIGHT:
-            default:
-                state = Display.STATE_ON;
-                break;
-        }
-        assert (state != Display.STATE_UNKNOWN);
-
-        if (mScreenOffBrightnessSensorController != null) {
-            mScreenOffBrightnessSensorController.setLightSensorEnabled(mUseAutoBrightness
-                    && mIsEnabled && (state == Display.STATE_OFF || (state == Display.STATE_DOZE
-                    && !mAllowAutoBrightnessWhileDozingConfig))
-                    && mLeadDisplayId == Layout.NO_LEAD_DISPLAY);
-        }
-
-        boolean skipRampBecauseOfProximityChangeToNegative = false;
-        // Apply the proximity sensor.
-        if (mProximitySensor != null) {
-            if (mPowerRequest.useProximitySensor && state != Display.STATE_OFF) {
-                // At this point the policy says that the screen should be on, but we've been
-                // asked to listen to the prox sensor to adjust the display state, so lets make
-                // sure the sensor is on.
-                setProximitySensorEnabled(true);
-                if (!mScreenOffBecauseOfProximity
-                        && mProximity == PROXIMITY_POSITIVE
-                        && !mIgnoreProximityUntilChanged) {
-                    // Prox sensor already reporting "near" so we should turn off the screen.
-                    // Also checked that we aren't currently set to ignore the proximity sensor
-                    // temporarily.
-                    mScreenOffBecauseOfProximity = true;
-                    sendOnProximityPositiveWithWakelock();
-                }
-            } else if (mWaitingForNegativeProximity
-                    && mScreenOffBecauseOfProximity
-                    && mProximity == PROXIMITY_POSITIVE
-                    && state != Display.STATE_OFF) {
-                // The policy says that we should have the screen on, but it's off due to the prox
-                // and we've been asked to wait until the screen is far from the user to turn it
-                // back on. Let keep the prox sensor on so we can tell when it's far again.
-                setProximitySensorEnabled(true);
-            } else {
-                // We haven't been asked to use the prox sensor and we're not waiting on the screen
-                // to turn back on...so lets shut down the prox sensor.
-                setProximitySensorEnabled(false);
-                mWaitingForNegativeProximity = false;
-            }
-
-            if (mScreenOffBecauseOfProximity
-                    && (mProximity != PROXIMITY_POSITIVE || mIgnoreProximityUntilChanged)) {
-                // The screen *was* off due to prox being near, but now it's "far" so lets turn
-                // the screen back on.  Also turn it back on if we've been asked to ignore the
-                // prox sensor temporarily.
-                mScreenOffBecauseOfProximity = false;
-                skipRampBecauseOfProximityChangeToNegative = true;
-                sendOnProximityNegativeWithWakelock();
-            }
-        } else {
-            setProximitySensorEnabled(false);
-            mWaitingForNegativeProximity = false;
-            mIgnoreProximityUntilChanged = false;
-
-            if (mScreenOffBecauseOfProximity) {
-                // The screen *was* off due to prox being near, but now there's no prox sensor, so
-                // let's turn the screen back on.
-                mScreenOffBecauseOfProximity = false;
-                skipRampBecauseOfProximityChangeToNegative = true;
-                sendOnProximityNegativeWithWakelock();
-            }
-        }
-
-        if (!mIsEnabled
-                || mIsInTransition
-                || mScreenOffBecauseOfProximity) {
-            state = Display.STATE_OFF;
-        }
+        int state = mDisplayStateController
+                .updateDisplayState(mPowerRequest, mIsEnabled, mIsInTransition);
 
         // Initialize things the first time the power state is changed.
         if (mustInitialize) {
@@ -1620,157 +1360,100 @@
         // Animate the screen state change unless already animating.
         // The transition may be deferred, so after this point we will use the
         // actual state instead of the desired one.
-        final int oldState = mPowerState.getScreenState();
-        animateScreenStateChange(state, performScreenOffTransition);
+        animateScreenStateChange(state, mDisplayStateController.shouldPerformScreenOffTransition());
         state = mPowerState.getScreenState();
-        boolean slowChange = false;
 
-        if (state == Display.STATE_OFF) {
-            brightnessState = PowerManager.BRIGHTNESS_OFF_FLOAT;
-            mBrightnessReasonTemp.setReason(BrightnessReason.REASON_SCREEN_OFF);
+        // Switch to doze auto-brightness mode if needed
+        if (mFlags.areAutoBrightnessModesEnabled() && mAutomaticBrightnessController != null
+                && !mAutomaticBrightnessController.isInIdleMode()) {
+            setAutomaticScreenBrightnessMode(Display.isDozeState(state)
+                    ? AUTO_BRIGHTNESS_MODE_DOZE : AUTO_BRIGHTNESS_MODE_DEFAULT);
         }
 
-        if (Float.isNaN(brightnessState) && isValidBrightnessValue(mBrightnessToFollow)) {
-            brightnessState = mBrightnessToFollow;
-            slowChange = mBrightnessToFollowSlowChange;
-            mBrightnessReasonTemp.setReason(BrightnessReason.REASON_FOLLOWER);
+        final boolean userSetBrightnessChanged = mDisplayBrightnessController
+                .updateUserSetScreenBrightness();
+
+        DisplayBrightnessState displayBrightnessState = mDisplayBrightnessController
+                .updateBrightness(mPowerRequest, state);
+        float brightnessState = displayBrightnessState.getBrightness();
+        float rawBrightnessState = displayBrightnessState.getBrightness();
+        mBrightnessReasonTemp.set(displayBrightnessState.getBrightnessReason());
+        boolean slowChange = displayBrightnessState.isSlowChange();
+        // custom transition duration
+        float customAnimationRate = displayBrightnessState.getCustomAnimationRate();
+
+        // Set up the ScreenOff controller used when coming out of SCREEN_OFF and the ALS sensor
+        // doesn't yet have a valid lux value to use with auto-brightness.
+        if (mScreenOffBrightnessSensorController != null) {
+            mScreenOffBrightnessSensorController
+                    .setLightSensorEnabled(displayBrightnessState.getShouldUseAutoBrightness()
+                    && mIsEnabled && (state == Display.STATE_OFF
+                    || (state == Display.STATE_DOZE
+                    && !mDisplayBrightnessController.isAllowAutoBrightnessWhileDozingConfig()))
+                    && mLeadDisplayId == Layout.NO_LEAD_DISPLAY);
         }
 
-        if ((Float.isNaN(brightnessState))
-                && isValidBrightnessValue(mPowerRequest.screenBrightnessOverride)) {
-            brightnessState = mPowerRequest.screenBrightnessOverride;
-            mBrightnessReasonTemp.setReason(BrightnessReason.REASON_OVERRIDE);
-            mAppliedScreenBrightnessOverride = true;
-        } else {
-            mAppliedScreenBrightnessOverride = false;
-        }
-
-        final boolean autoBrightnessEnabledInDoze =
-                mAllowAutoBrightnessWhileDozingConfig && Display.isDozeState(state);
-        final boolean autoBrightnessEnabled = mUseAutoBrightness
-                && (state == Display.STATE_ON || autoBrightnessEnabledInDoze)
-                && mBrightnessReasonTemp.getReason() != BrightnessReason.REASON_OVERRIDE
-                && mAutomaticBrightnessController != null;
-        final boolean autoBrightnessDisabledDueToDisplayOff = mUseAutoBrightness
-                && !(state == Display.STATE_ON || autoBrightnessEnabledInDoze);
-        final int autoBrightnessState = autoBrightnessEnabled
-                    && mBrightnessReasonTemp.getReason() != BrightnessReason.REASON_FOLLOWER
-                ? AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED
-                : autoBrightnessDisabledDueToDisplayOff
-                        ? AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE
-                        : AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED;
-
-        final boolean userSetBrightnessChanged = updateUserSetScreenBrightness();
-
-        // Use the temporary screen brightness if there isn't an override, either from
-        // WindowManager or based on the display state.
-        if (isValidBrightnessValue(mTemporaryScreenBrightness)) {
-            brightnessState = mTemporaryScreenBrightness;
-            mAppliedTemporaryBrightness = true;
-            mBrightnessReasonTemp.setReason(BrightnessReason.REASON_TEMPORARY);
-        } else {
-            mAppliedTemporaryBrightness = false;
-        }
-
-        final boolean autoBrightnessAdjustmentChanged = updateAutoBrightnessAdjustment();
-
-        // Use the autobrightness adjustment override if set.
-        final float autoBrightnessAdjustment;
-        if (!Float.isNaN(mTemporaryAutoBrightnessAdjustment)) {
-            autoBrightnessAdjustment = mTemporaryAutoBrightnessAdjustment;
-            brightnessAdjustmentFlags = BrightnessReason.ADJUSTMENT_AUTO_TEMP;
-            mAppliedTemporaryAutoBrightnessAdjustment = true;
-        } else {
-            autoBrightnessAdjustment = mAutoBrightnessAdjustment;
-            brightnessAdjustmentFlags = BrightnessReason.ADJUSTMENT_AUTO;
-            mAppliedTemporaryAutoBrightnessAdjustment = false;
-        }
-        // Apply brightness boost.
-        // We do this here after deciding whether auto-brightness is enabled so that we don't
-        // disable the light sensor during this temporary state.  That way when boost ends we will
-        // be able to resume normal auto-brightness behavior without any delay.
-        if (mPowerRequest.boostScreenBrightness
-                && brightnessState != PowerManager.BRIGHTNESS_OFF_FLOAT) {
-            brightnessState = PowerManager.BRIGHTNESS_MAX;
-            mBrightnessReasonTemp.setReason(BrightnessReason.REASON_BOOST);
-            mAppliedBrightnessBoost = true;
-        } else {
-            mAppliedBrightnessBoost = false;
-        }
+        // Take note if the short term model was already active before applying the current
+        // request changes.
+        final boolean wasShortTermModelActive =
+                mAutomaticBrightnessStrategy.isShortTermModelActive();
+        mAutomaticBrightnessStrategy.setAutoBrightnessState(state,
+                mDisplayBrightnessController.isAllowAutoBrightnessWhileDozingConfig(),
+                mBrightnessReasonTemp.getReason(), mPowerRequest.policy,
+                mDisplayBrightnessController.getLastUserSetScreenBrightness(),
+                userSetBrightnessChanged);
 
         // If the brightness is already set then it's been overridden by something other than the
         // user, or is a temporary adjustment.
         boolean userInitiatedChange = (Float.isNaN(brightnessState))
-                && (autoBrightnessAdjustmentChanged || userSetBrightnessChanged);
-        boolean wasShortTermModelActive = false;
-        // Configure auto-brightness.
-        if (mAutomaticBrightnessController != null) {
-            wasShortTermModelActive = mAutomaticBrightnessController.hasUserDataPoints();
-            mAutomaticBrightnessController.configure(autoBrightnessState,
-                    mBrightnessConfiguration,
-                    mLastUserSetScreenBrightness,
-                    userSetBrightnessChanged, autoBrightnessAdjustment,
-                    autoBrightnessAdjustmentChanged, mPowerRequest.policy,
-                    mShouldResetShortTermModel);
-            mShouldResetShortTermModel = false;
-        }
-        mBrightnessRangeController.setAutoBrightnessEnabled(autoBrightnessEnabled
+                && (mAutomaticBrightnessStrategy.getAutoBrightnessAdjustmentChanged()
+                || userSetBrightnessChanged);
+
+        mBrightnessRangeController.setAutoBrightnessEnabled(
+                mAutomaticBrightnessStrategy.isAutoBrightnessEnabled()
                 ? AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED
-                : autoBrightnessDisabledDueToDisplayOff
+                : mAutomaticBrightnessStrategy.isAutoBrightnessDisabledDueToDisplayOff()
                         ? AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE
                         : AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED);
 
-        if (mBrightnessTracker != null) {
-            mBrightnessTracker.setShouldCollectColorSample(mBrightnessConfiguration != null
-                    && mBrightnessConfiguration.shouldCollectColorSamples());
-        }
-
-        boolean updateScreenBrightnessSetting = false;
-        float rawBrightnessState = brightnessState;
-
+        boolean updateScreenBrightnessSetting =
+                displayBrightnessState.shouldUpdateScreenBrightnessSetting();
+        float currentBrightnessSetting = mDisplayBrightnessController.getCurrentBrightness();
         // Apply auto-brightness.
+        int brightnessAdjustmentFlags = 0;
         if (Float.isNaN(brightnessState)) {
-            float newAutoBrightnessAdjustment = autoBrightnessAdjustment;
-            if (autoBrightnessEnabled) {
-                rawBrightnessState = mAutomaticBrightnessController
-                        .getRawAutomaticScreenBrightness();
-                brightnessState = mAutomaticBrightnessController.getAutomaticScreenBrightness(
+            if (mAutomaticBrightnessStrategy.isAutoBrightnessEnabled()) {
+                brightnessState = mAutomaticBrightnessStrategy.getAutomaticScreenBrightness(
                         mTempBrightnessEvent);
-                newAutoBrightnessAdjustment =
-                        mAutomaticBrightnessController.getAutomaticScreenBrightnessAdjustment();
-            }
-            if (isValidBrightnessValue(brightnessState)
-                    || brightnessState == PowerManager.BRIGHTNESS_OFF_FLOAT) {
-                // Use current auto-brightness value and slowly adjust to changes.
-                brightnessState = clampScreenBrightness(brightnessState);
-                if (mAppliedAutoBrightness && !autoBrightnessAdjustmentChanged) {
-                    slowChange = true; // slowly adapt to auto-brightness
+                if (BrightnessUtils.isValidBrightnessValue(brightnessState)
+                        || brightnessState == PowerManager.BRIGHTNESS_OFF_FLOAT) {
+                    rawBrightnessState = mAutomaticBrightnessController
+                            .getRawAutomaticScreenBrightness();
+                    brightnessState = clampScreenBrightness(brightnessState);
+                    // slowly adapt to auto-brightness
+                    // TODO(b/253226419): slowChange should be decided by strategy.updateBrightness
+                    slowChange = mAutomaticBrightnessStrategy.hasAppliedAutoBrightness()
+                            && !mAutomaticBrightnessStrategy.getAutoBrightnessAdjustmentChanged();
+                    brightnessAdjustmentFlags =
+                            mAutomaticBrightnessStrategy.getAutoBrightnessAdjustmentReasonsFlags();
+                    updateScreenBrightnessSetting = currentBrightnessSetting != brightnessState;
+                    mAutomaticBrightnessStrategy.setAutoBrightnessApplied(true);
+                    mBrightnessReasonTemp.setReason(BrightnessReason.REASON_AUTOMATIC);
+                    if (mScreenOffBrightnessSensorController != null) {
+                        mScreenOffBrightnessSensorController.setLightSensorEnabled(false);
+                    }
+                } else {
+                    mAutomaticBrightnessStrategy.setAutoBrightnessApplied(false);
                 }
-                updateScreenBrightnessSetting = mCurrentScreenBrightnessSetting != brightnessState;
-                mAppliedAutoBrightness = true;
-                mBrightnessReasonTemp.setReason(BrightnessReason.REASON_AUTOMATIC);
-                if (mScreenOffBrightnessSensorController != null) {
-                    mScreenOffBrightnessSensorController.setLightSensorEnabled(false);
-                }
-            } else {
-                mAppliedAutoBrightness = false;
-            }
-            if (autoBrightnessAdjustment != newAutoBrightnessAdjustment) {
-                // If the autobrightness controller has decided to change the adjustment value
-                // used, make sure that's reflected in settings.
-                putAutoBrightnessAdjustmentSetting(newAutoBrightnessAdjustment);
-            } else {
-                // Adjustment values resulted in no change
-                brightnessAdjustmentFlags = 0;
             }
         } else {
             // Any non-auto-brightness values such as override or temporary should still be subject
             // to clamping so that they don't go beyond the current max as specified by HBM
             // Controller.
             brightnessState = clampScreenBrightness(brightnessState);
-            mAppliedAutoBrightness = false;
-            brightnessAdjustmentFlags = 0;
+            mAutomaticBrightnessStrategy.setAutoBrightnessApplied(false);
         }
+
         // Use default brightness when dozing unless overridden.
         if ((Float.isNaN(brightnessState))
                 && Display.isDozeState(state)) {
@@ -1781,14 +1464,15 @@
 
         // The ALS is not available yet - use the screen off sensor to determine the initial
         // brightness
-        if (Float.isNaN(brightnessState) && autoBrightnessEnabled
+        if (Float.isNaN(brightnessState) && mAutomaticBrightnessStrategy.isAutoBrightnessEnabled()
                 && mScreenOffBrightnessSensorController != null) {
             rawBrightnessState =
                     mScreenOffBrightnessSensorController.getAutomaticScreenBrightness();
             brightnessState = rawBrightnessState;
-            if (isValidBrightnessValue(brightnessState)) {
+            if (BrightnessUtils.isValidBrightnessValue(brightnessState)) {
                 brightnessState = clampScreenBrightness(brightnessState);
-                updateScreenBrightnessSetting = mCurrentScreenBrightnessSetting != brightnessState;
+                updateScreenBrightnessSetting = mDisplayBrightnessController.getCurrentBrightness()
+                        != brightnessState;
                 mBrightnessReasonTemp.setReason(
                         BrightnessReason.REASON_SCREEN_OFF_BRIGHTNESS_SENSOR);
             }
@@ -1796,9 +1480,9 @@
 
         // Apply manual brightness.
         if (Float.isNaN(brightnessState)) {
-            rawBrightnessState = mCurrentScreenBrightnessSetting;
+            rawBrightnessState = currentBrightnessSetting;
             brightnessState = clampScreenBrightness(rawBrightnessState);
-            if (brightnessState != mCurrentScreenBrightnessSetting) {
+            if (brightnessState != currentBrightnessSetting) {
                 // The manually chosen screen brightness is outside of the currently allowed
                 // range (i.e., high-brightness-mode), make sure we tell the rest of the system
                 // by updating the setting.
@@ -1811,7 +1495,8 @@
                 : mAutomaticBrightnessController.getAmbientLux();
         for (int i = 0; i < displayBrightnessFollowers.size(); i++) {
             DisplayPowerControllerInterface follower = displayBrightnessFollowers.valueAt(i);
-            follower.setBrightnessToFollow(rawBrightnessState, convertToNits(rawBrightnessState),
+            follower.setBrightnessToFollow(rawBrightnessState,
+                    mDisplayBrightnessController.convertToNits(rawBrightnessState),
                     ambientLux, slowChange);
         }
 
@@ -1823,64 +1508,25 @@
         // Note throttling effectively changes the allowed brightness range, so, similarly to HBM,
         // we broadcast this change through setting.
         final float unthrottledBrightnessState = brightnessState;
-        if (mBrightnessThrottler.isThrottled()) {
-            mTempBrightnessEvent.setThermalMax(mBrightnessThrottler.getBrightnessCap());
-            brightnessState = Math.min(brightnessState, mBrightnessThrottler.getBrightnessCap());
-            mBrightnessReasonTemp.addModifier(BrightnessReason.MODIFIER_THROTTLED);
-            if (!mAppliedThrottling) {
-                // Brightness throttling is needed, so do so quickly.
-                // Later, when throttling is removed, we let other mechanisms decide on speed.
-                slowChange = false;
-            }
-            mAppliedThrottling = true;
-        } else if (mAppliedThrottling) {
-            mAppliedThrottling = false;
-        }
+        DisplayBrightnessState clampedState = mBrightnessClamperController.clamp(mPowerRequest,
+                brightnessState, slowChange);
+
+        brightnessState = clampedState.getBrightness();
+        slowChange = clampedState.isSlowChange();
+        // faster rate wins, at this point customAnimationRate == -1, strategy does not control
+        // customAnimationRate. Should be revisited if strategy start setting this value
+        customAnimationRate = Math.max(customAnimationRate, clampedState.getCustomAnimationRate());
+        mBrightnessReasonTemp.addModifier(clampedState.getBrightnessReason().getModifier());
 
         if (updateScreenBrightnessSetting) {
             // Tell the rest of the system about the new brightness in case we had to change it
             // for things like auto-brightness or high-brightness-mode. Note that we do this
-            // before applying the low power or dim transformations so that the slider
-            // accurately represents the full possible range, even if they range changes what
-            // it means in absolute terms.
-            updateScreenBrightnessSetting(brightnessState);
-        }
-
-        // Apply dimming by at least some minimum amount when user activity
-        // timeout is about to expire.
-        if (mPowerRequest.policy == DisplayPowerRequest.POLICY_DIM) {
-            if (brightnessState > PowerManager.BRIGHTNESS_MIN) {
-                brightnessState = Math.max(
-                        Math.min(brightnessState - mScreenBrightnessMinimumDimAmount,
-                                mScreenBrightnessDimConfig),
-                        PowerManager.BRIGHTNESS_MIN);
-                mBrightnessReasonTemp.addModifier(BrightnessReason.MODIFIER_DIMMED);
-            }
-            if (!mAppliedDimming) {
-                slowChange = false;
-            }
-            mAppliedDimming = true;
-        } else if (mAppliedDimming) {
-            slowChange = false;
-            mAppliedDimming = false;
-        }
-        // If low power mode is enabled, scale brightness by screenLowPowerBrightnessFactor
-        // as long as it is above the minimum threshold.
-        if (mPowerRequest.lowPowerMode) {
-            if (brightnessState > PowerManager.BRIGHTNESS_MIN) {
-                final float brightnessFactor =
-                        Math.min(mPowerRequest.screenLowPowerBrightnessFactor, 1);
-                final float lowPowerBrightnessFloat = (brightnessState * brightnessFactor);
-                brightnessState = Math.max(lowPowerBrightnessFloat, PowerManager.BRIGHTNESS_MIN);
-                mBrightnessReasonTemp.addModifier(BrightnessReason.MODIFIER_LOW_POWER);
-            }
-            if (!mAppliedLowPower) {
-                slowChange = false;
-            }
-            mAppliedLowPower = true;
-        } else if (mAppliedLowPower) {
-            slowChange = false;
-            mAppliedLowPower = false;
+            // only considering maxBrightness (ignoring brightness modifiers like low power or dim)
+            // so that the slider accurately represents the full possible range,
+            // even if they range changes what it means in absolute terms.
+            mDisplayBrightnessController.updateScreenBrightnessSetting(
+                    MathUtils.constrain(unthrottledBrightnessState,
+                            clampedState.getMinBrightness(), clampedState.getMaxBrightness()));
         }
 
         // The current brightness to use has been calculated at this point, and HbmController should
@@ -1889,13 +1535,15 @@
         // brightness sources (such as an app override) are not saved to the setting, but should be
         // reflected in HBM calculations.
         mBrightnessRangeController.onBrightnessChanged(brightnessState, unthrottledBrightnessState,
-                mBrightnessThrottler.getBrightnessMaxReason());
+                mBrightnessClamperController.getBrightnessMaxReason());
 
         // Animate the screen brightness when the screen is on or dozing.
-        // Skip the animation when the screen is off.
+        // Skip the animation when the screen is off or suspended.
         boolean brightnessAdjusted = false;
         final boolean brightnessIsTemporary =
-                mAppliedTemporaryBrightness || mAppliedTemporaryAutoBrightnessAdjustment;
+                (mBrightnessReasonTemp.getReason() == BrightnessReason.REASON_TEMPORARY)
+                        || mAutomaticBrightnessStrategy
+                        .isTemporaryAutoBrightnessAdjustmentApplied();
         if (!mPendingScreenOff) {
             if (mSkipScreenOnBrightnessRamp) {
                 if (state == Display.STATE_ON) {
@@ -1916,7 +1564,8 @@
             }
 
             final boolean initialRampSkip = (state == Display.STATE_ON && mSkipRampState
-                    != RAMP_STATE_SKIP_NONE) || skipRampBecauseOfProximityChangeToNegative;
+                    != RAMP_STATE_SKIP_NONE) || mDisplayPowerProximityStateController
+                    .shouldSkipRampBecauseOfProximityChangeToNegative();
             // While dozing, sometimes the brightness is split into buckets. Rather than animating
             // through the buckets, which is unlikely to be smooth in the first place, just jump
             // right to the suggested brightness.
@@ -1950,13 +1599,25 @@
                 // We want to scale HDR brightness level with the SDR level, we also need to restore
                 // SDR brightness immediately when entering dim or low power mode.
                 animateValue = mBrightnessRangeController.getHdrBrightnessValue();
+                customAnimationRate = Math.max(customAnimationRate,
+                        mBrightnessRangeController.getHdrTransitionRate());
                 mBrightnessReasonTemp.addModifier(BrightnessReason.MODIFIER_HDR);
             }
 
+            // if doze or suspend state is requested, we want to finish brightnes animation fast
+            // to allow state animation to start
+            if (mPowerRequest.policy == DisplayManagerInternal.DisplayPowerRequest.POLICY_DOZE
+                    && (mPowerRequest.dozeScreenState == Display.STATE_UNKNOWN  // dozing
+                    || mPowerRequest.dozeScreenState == Display.STATE_DOZE_SUSPEND
+                    || mPowerRequest.dozeScreenState == Display.STATE_ON_SUSPEND)) {
+                customAnimationRate = DisplayBrightnessState.CUSTOM_ANIMATION_RATE_NOT_SET;
+                slowChange = false;
+            }
+
             final float currentBrightness = mPowerState.getScreenBrightness();
             final float currentSdrBrightness = mPowerState.getSdrScreenBrightness();
 
-            if (isValidBrightnessValue(animateValue)
+            if (BrightnessUtils.isValidBrightnessValue(animateValue)
                     && (animateValue != currentBrightness
                     || sdrAnimateValue != currentSdrBrightness)) {
                 boolean skipAnimation = initialRampSkip || hasBrightnessBuckets
@@ -1986,6 +1647,9 @@
                 if (skipAnimation) {
                     animateScreenBrightness(animateValue, sdrAnimateValue,
                             SCREEN_ANIMATION_RATE_MINIMUM);
+                } else if (customAnimationRate > 0) {
+                    animateScreenBrightness(animateValue, sdrAnimateValue,
+                            customAnimationRate, /* ignoreAnimationLimits = */true);
                 } else {
                     boolean isIncreasing = animateValue > currentBrightness;
                     final float rampSpeed;
@@ -2007,13 +1671,15 @@
             }
 
             notifyBrightnessTrackerChanged(brightnessState, userInitiatedChange,
-                    wasShortTermModelActive, autoBrightnessEnabled, brightnessIsTemporary);
+                    wasShortTermModelActive, mAutomaticBrightnessStrategy.isAutoBrightnessEnabled(),
+                    brightnessIsTemporary, displayBrightnessState.getShouldUseAutoBrightness());
 
             // We save the brightness info *after* the brightness setting has been changed and
             // adjustments made so that the brightness info reflects the latest value.
-            brightnessAdjusted = saveBrightnessInfo(getScreenBrightnessSetting(), animateValue);
+            brightnessAdjusted = saveBrightnessInfo(getScreenBrightnessSetting(),
+                    animateValue, clampedState);
         } else {
-            brightnessAdjusted = saveBrightnessInfo(getScreenBrightnessSetting());
+            brightnessAdjusted = saveBrightnessInfo(getScreenBrightnessSetting(), clampedState);
         }
 
         // Only notify if the brightness adjustment is not temporary (i.e. slider has been released)
@@ -2049,13 +1715,20 @@
                 ? mCdsi.getReduceBrightColorsStrength() : -1);
         mTempBrightnessEvent.setPowerFactor(mPowerRequest.screenLowPowerBrightnessFactor);
         mTempBrightnessEvent.setWasShortTermModelActive(wasShortTermModelActive);
-        mTempBrightnessEvent.setAutomaticBrightnessEnabled(mUseAutoBrightness);
+        mTempBrightnessEvent.setDisplayBrightnessStrategyName(displayBrightnessState
+                .getDisplayBrightnessStrategyName());
+        mTempBrightnessEvent.setAutomaticBrightnessEnabled(
+                displayBrightnessState.getShouldUseAutoBrightness());
         // Temporary is what we use during slider interactions. We avoid logging those so that
         // we don't spam logcat when the slider is being used.
         boolean tempToTempTransition =
                 mTempBrightnessEvent.getReason().getReason() == BrightnessReason.REASON_TEMPORARY
                         && mLastBrightnessEvent.getReason().getReason()
                         == BrightnessReason.REASON_TEMPORARY;
+        // Purely for dumpsys;
+        final boolean isRbcEvent =
+                mLastBrightnessEvent.isRbcEnabled() != mTempBrightnessEvent.isRbcEnabled();
+
         if ((!mTempBrightnessEvent.equalsMainData(mLastBrightnessEvent) && !tempToTempTransition)
                 || brightnessAdjustmentFlags != 0) {
             mTempBrightnessEvent.setInitialBrightness(mLastBrightnessEvent.getBrightness());
@@ -2075,6 +1748,10 @@
             if (mBrightnessEventRingBuffer != null) {
                 mBrightnessEventRingBuffer.append(newEvent);
             }
+            if (isRbcEvent) {
+                mRbcEventRingBuffer.append(newEvent);
+            }
+
         }
 
         // Update display white-balance.
@@ -2092,6 +1769,7 @@
         // reporting the display is ready because we only need to ensure the screen is in the
         // right power state even as it continues to converge on the desired brightness.
         final boolean ready = mPendingScreenOnUnblocker == null
+                && mPendingScreenOnUnblockerByDisplayOffload == null
                 && (!mColorFadeEnabled || (!mColorFadeOnAnimator.isStarted()
                         && !mColorFadeOffAnimator.isStarted()))
                 && mPowerState.waitUntilClean(mCleanListener);
@@ -2106,12 +1784,8 @@
         }
 
         // Grab a wake lock if we have unfinished business.
-        if (!finished && !mUnfinishedBusiness) {
-            if (DEBUG) {
-                Slog.d(mTag, "Unfinished business...");
-            }
-            mCallbacks.acquireSuspendBlocker(mSuspendBlockerIdUnfinishedBusiness);
-            mUnfinishedBusiness = true;
+        if (!finished) {
+            mWakelockController.acquireWakelock(WakelockController.WAKE_LOCK_UNFINISHED_BUSINESS);
         }
 
         // Notify the power manager when ready.
@@ -2130,12 +1804,8 @@
         }
 
         // Release the wake lock when we have no unfinished business.
-        if (finished && mUnfinishedBusiness) {
-            if (DEBUG) {
-                Slog.d(mTag, "Finished business...");
-            }
-            mUnfinishedBusiness = false;
-            mCallbacks.releaseSuspendBlocker(mSuspendBlockerIdUnfinishedBusiness);
+        if (finished) {
+            mWakelockController.releaseWakelock(WakelockController.WAKE_LOCK_UNFINISHED_BUSINESS);
         }
 
         // Record if dozing for future comparison.
@@ -2166,9 +1836,9 @@
 
     private void setDwbcLoggingEnabled(int arg) {
         if (mDisplayWhiteBalanceController != null) {
-            final boolean shouldEnable = (arg == 1);
-            mDisplayWhiteBalanceController.setLoggingEnabled(shouldEnable);
-            mDisplayWhiteBalanceSettings.setLoggingEnabled(shouldEnable);
+            final boolean enabled = (arg == 1);
+            mDisplayWhiteBalanceController.setLoggingEnabled(enabled);
+            mDisplayWhiteBalanceSettings.setLoggingEnabled(enabled);
         }
     }
 
@@ -2183,7 +1853,7 @@
      */
     @Override
     public void ignoreProximitySensorUntilChanged() {
-        mHandler.sendEmptyMessage(MSG_IGNORE_PROXIMITY);
+        mDisplayPowerProximityStateController.ignoreProximitySensorUntilChanged();
     }
 
     @Override
@@ -2210,7 +1880,27 @@
 
     @Override
     public void setBrightnessFromOffload(float brightness) {
-        // The old DPC is no longer supported
+        Message msg = mHandler.obtainMessage(MSG_SET_BRIGHTNESS_FROM_OFFLOAD,
+                Float.floatToIntBits(brightness), 0 /*unused*/);
+        mHandler.sendMessageAtTime(msg, mClock.uptimeMillis());
+    }
+
+    @Override
+    public float[] getAutoBrightnessLevels(
+            @AutomaticBrightnessController.AutomaticBrightnessMode int mode) {
+        int preset = Settings.System.getIntForUser(mContext.getContentResolver(),
+                Settings.System.SCREEN_BRIGHTNESS_FOR_ALS,
+                Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_NORMAL, UserHandle.USER_CURRENT);
+        return mDisplayDeviceConfig.getAutoBrightnessBrighteningLevels(mode, preset);
+    }
+
+    @Override
+    public float[] getAutoBrightnessLuxLevels(
+            @AutomaticBrightnessController.AutomaticBrightnessMode int mode) {
+        int preset = Settings.System.getIntForUser(mContext.getContentResolver(),
+                Settings.System.SCREEN_BRIGHTNESS_FOR_ALS,
+                Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_NORMAL, UserHandle.USER_CURRENT);
+        return mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsLux(mode, preset);
     }
 
     @Override
@@ -2227,18 +1917,29 @@
         }
     }
 
-    private boolean saveBrightnessInfo(float brightness) {
-        return saveBrightnessInfo(brightness, brightness);
+    @Override
+    public void onBootCompleted() {
+        Message msg = mHandler.obtainMessage(MSG_BOOT_COMPLETED);
+        mHandler.sendMessageAtTime(msg, mClock.uptimeMillis());
     }
 
-    private boolean saveBrightnessInfo(float brightness, float adjustedBrightness) {
+    private boolean saveBrightnessInfo(float brightness) {
+        return saveBrightnessInfo(brightness, /* state= */ null);
+    }
+
+    private boolean saveBrightnessInfo(float brightness, @Nullable DisplayBrightnessState state) {
+        return saveBrightnessInfo(brightness, brightness, state);
+    }
+
+    private boolean saveBrightnessInfo(float brightness, float adjustedBrightness,
+            @Nullable DisplayBrightnessState state) {
         synchronized (mCachedBrightnessInfo) {
-            final float minBrightness = Math.min(
-                    mBrightnessRangeController.getCurrentBrightnessMin(),
-                    mBrightnessThrottler.getBrightnessCap());
+            float stateMax = state != null ? state.getMaxBrightness() : PowerManager.BRIGHTNESS_MAX;
+            float stateMin = state != null ? state.getMinBrightness() : PowerManager.BRIGHTNESS_MAX;
+            final float minBrightness = Math.max(stateMin, Math.min(
+                    mBrightnessRangeController.getCurrentBrightnessMin(), stateMax));
             final float maxBrightness = Math.min(
-                    mBrightnessRangeController.getCurrentBrightnessMax(),
-                    mBrightnessThrottler.getBrightnessCap());
+                    mBrightnessRangeController.getCurrentBrightnessMax(), stateMax);
             boolean changed = false;
 
             changed |=
@@ -2261,8 +1962,7 @@
                             mBrightnessRangeController.getTransitionPoint());
             changed |=
                     mCachedBrightnessInfo.checkAndSetInt(mCachedBrightnessInfo.brightnessMaxReason,
-                            mBrightnessThrottler.getBrightnessMaxReason());
-
+                            mBrightnessClamperController.getBrightnessMaxReason());
             return changed;
         }
     }
@@ -2274,27 +1974,18 @@
     }
 
     private HighBrightnessModeController createHbmControllerLocked(
-            Runnable modeChangeCallback) {
-        final DisplayDevice device = mLogicalDisplay.getPrimaryDisplayDeviceLocked();
-        final DisplayDeviceConfig ddConfig = device.getDisplayDeviceConfig();
-        final IBinder displayToken =
-                mLogicalDisplay.getPrimaryDisplayDeviceLocked().getDisplayTokenLocked();
-        final String displayUniqueId =
-                mLogicalDisplay.getPrimaryDisplayDeviceLocked().getUniqueId();
+            HighBrightnessModeMetadata hbmMetadata, Runnable modeChangeCallback) {
+        final DisplayDeviceConfig ddConfig = mDisplayDevice.getDisplayDeviceConfig();
+        final IBinder displayToken = mDisplayDevice.getDisplayTokenLocked();
+        final String displayUniqueId = mDisplayDevice.getUniqueId();
         final DisplayDeviceConfig.HighBrightnessModeData hbmData =
                 ddConfig != null ? ddConfig.getHighBrightnessModeData() : null;
-        final DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
+        final DisplayDeviceInfo info = mDisplayDevice.getDisplayDeviceInfoLocked();
         return mInjector.getHighBrightnessModeController(mHandler, info.width, info.height,
                 displayToken, displayUniqueId, PowerManager.BRIGHTNESS_MIN,
-                PowerManager.BRIGHTNESS_MAX, hbmData,
-                new HighBrightnessModeController.HdrBrightnessDeviceConfig() {
-                    @Override
-                    public float getHdrBrightnessFromSdr(
-                            float sdrBrightness, float maxDesiredHdrSdrRatio) {
-                        return mDisplayDeviceConfig.getHdrBrightnessFromSdr(
-                                sdrBrightness, maxDesiredHdrSdrRatio);
-                    }
-                }, modeChangeCallback, mHighBrightnessModeMetadata, mContext);
+                PowerManager.BRIGHTNESS_MAX, hbmData, (sdrBrightness, maxDesiredHdrSdrRatio) ->
+                        mDisplayDeviceConfig.getHdrBrightnessFromSdr(sdrBrightness,
+                                maxDesiredHdrSdrRatio), modeChangeCallback, hbmMetadata, mContext);
     }
 
     private BrightnessThrottler createBrightnessThrottlerLocked() {
@@ -2345,18 +2036,72 @@
         }
     }
 
+    private void blockScreenOnByDisplayOffload(DisplayOffloadSession displayOffloadSession) {
+        if (mPendingScreenOnUnblockerByDisplayOffload != null || displayOffloadSession == null) {
+            return;
+        }
+        mScreenTurningOnWasBlockedByDisplayOffload = true;
+
+        Trace.asyncTraceBegin(
+                Trace.TRACE_TAG_POWER, SCREEN_ON_BLOCKED_BY_DISPLAYOFFLOAD_TRACE_NAME, 0);
+        mScreenOnBlockByDisplayOffloadStartRealTime = SystemClock.elapsedRealtime();
+
+        mPendingScreenOnUnblockerByDisplayOffload =
+                () -> onDisplayOffloadUnblockScreenOn(displayOffloadSession);
+        if (!displayOffloadSession.blockScreenOn(mPendingScreenOnUnblockerByDisplayOffload)) {
+            mPendingScreenOnUnblockerByDisplayOffload = null;
+            long delay =
+                    SystemClock.elapsedRealtime() - mScreenOnBlockByDisplayOffloadStartRealTime;
+            Slog.w(mTag, "Tried blocking screen on for offloading but failed. So, end trace after "
+                    + delay + " ms.");
+            Trace.asyncTraceEnd(
+                    Trace.TRACE_TAG_POWER, SCREEN_ON_BLOCKED_BY_DISPLAYOFFLOAD_TRACE_NAME, 0);
+            return;
+        }
+        Slog.i(mTag, "Blocking screen on for offloading.");
+    }
+
+    private void onDisplayOffloadUnblockScreenOn(DisplayOffloadSession displayOffloadSession) {
+        Message msg = mHandler.obtainMessage(MSG_OFFLOADING_SCREEN_ON_UNBLOCKED,
+                displayOffloadSession);
+        mHandler.sendMessage(msg);
+    }
+
+    private void unblockScreenOnByDisplayOffload() {
+        if (mPendingScreenOnUnblockerByDisplayOffload == null) {
+            return;
+        }
+        mPendingScreenOnUnblockerByDisplayOffload = null;
+        long delay = SystemClock.elapsedRealtime() - mScreenOnBlockByDisplayOffloadStartRealTime;
+        Slog.i(mTag, "Unblocked screen on for offloading after " + delay + " ms");
+        Trace.asyncTraceEnd(
+                Trace.TRACE_TAG_POWER, SCREEN_ON_BLOCKED_BY_DISPLAYOFFLOAD_TRACE_NAME, 0);
+    }
+
     private boolean setScreenState(int state) {
         return setScreenState(state, false /*reportOnly*/);
     }
 
     private boolean setScreenState(int state, boolean reportOnly) {
         final boolean isOff = (state == Display.STATE_OFF);
+        final boolean isOn = (state == Display.STATE_ON);
+        final boolean changed = mPowerState.getScreenState() != state;
 
-        if (mPowerState.getScreenState() != state
-                || mReportedScreenStateToPolicy == REPORTED_TO_POLICY_UNREPORTED) {
+        // If the screen is turning on, give displayoffload a chance to do something before the
+        // screen actually turns on.
+        // TODO(b/316941732): add tests for this displayoffload screen-on blocker.
+        if (isOn && changed && !mScreenTurningOnWasBlockedByDisplayOffload) {
+            blockScreenOnByDisplayOffload(mDisplayOffloadSession);
+        } else if (!isOn && mScreenTurningOnWasBlockedByDisplayOffload) {
+            // No longer turning screen on, so unblock previous screen on blocking immediately.
+            unblockScreenOnByDisplayOffload();
+            mScreenTurningOnWasBlockedByDisplayOffload = false;
+        }
+
+        if (changed || mReportedScreenStateToPolicy == REPORTED_TO_POLICY_UNREPORTED) {
             // If we are trying to turn screen off, give policy a chance to do something before we
             // actually turn the screen off.
-            if (isOff && !mScreenOffBecauseOfProximity) {
+            if (isOff && !mDisplayPowerProximityStateController.isScreenOffBecauseOfProximity()) {
                 if (mReportedScreenStateToPolicy == REPORTED_TO_POLICY_SCREEN_ON
                         || mReportedScreenStateToPolicy == REPORTED_TO_POLICY_UNREPORTED) {
                     setReportedScreenState(REPORTED_TO_POLICY_SCREEN_TURNING_OFF);
@@ -2369,8 +2114,9 @@
                 }
             }
 
-            if (!reportOnly && mPowerState.getScreenState() != state
-                    && readyToUpdateDisplayState()) {
+            if (!reportOnly && changed && readyToUpdateDisplayState()
+                    && mPendingScreenOffUnblocker == null
+                    && mPendingScreenOnUnblockerByDisplayOffload == null) {
                 Trace.traceCounter(Trace.TRACE_TAG_POWER, "ScreenState", state);
 
                 String propertyKey = "debug.tracing.screen_state";
@@ -2396,7 +2142,7 @@
         // it is only removed once the window manager tells us that the activity has
         // finished drawing underneath.
         if (isOff && mReportedScreenStateToPolicy != REPORTED_TO_POLICY_SCREEN_OFF
-                && !mScreenOffBecauseOfProximity) {
+                && !mDisplayPowerProximityStateController.isScreenOffBecauseOfProximity()) {
             setReportedScreenState(REPORTED_TO_POLICY_SCREEN_OFF);
             unblockScreenOn();
             mWindowManagerPolicy.screenTurnedOff(mDisplayId, mIsInTransition);
@@ -2422,12 +2168,16 @@
         }
 
         // Return true if the screen isn't blocked.
-        return mPendingScreenOnUnblocker == null;
+        return mPendingScreenOnUnblocker == null
+                && mPendingScreenOnUnblockerByDisplayOffload == null;
     }
 
     private void setReportedScreenState(int state) {
         Trace.traceCounter(Trace.TRACE_TAG_POWER, "ReportedScreenStateToPolicy", state);
         mReportedScreenStateToPolicy = state;
+        if (state == REPORTED_TO_POLICY_SCREEN_ON) {
+            mScreenTurningOnWasBlockedByDisplayOffload = false;
+        }
     }
 
     private void loadAmbientLightSensor() {
@@ -2442,18 +2192,6 @@
                 mDisplayDeviceConfig.getScreenOffBrightnessSensor(), SensorUtils.NO_FALLBACK);
     }
 
-    private void loadProximitySensor() {
-        if (DEBUG_PRETEND_PROXIMITY_SENSOR_ABSENT || mDisplayId != Display.DEFAULT_DISPLAY) {
-            return;
-        }
-        mProximitySensor = SensorUtils.findSensor(mSensorManager,
-                mDisplayDeviceConfig.getProximitySensor(), Sensor.TYPE_PROXIMITY);
-        if (mProximitySensor != null) {
-            mProximityThreshold = Math.min(mProximitySensor.getMaximumRange(),
-                    TYPICAL_PROXIMITY_THRESHOLD);
-        }
-    }
-
     private float clampScreenBrightness(float value) {
         if (Float.isNaN(value)) {
             value = PowerManager.BRIGHTNESS_MIN;
@@ -2462,18 +2200,18 @@
                 mBrightnessRangeController.getCurrentBrightnessMax());
     }
 
-    // Checks whether the brightness is within the valid brightness range, not including off.
-    private boolean isValidBrightnessValue(float brightness) {
-        return brightness >= PowerManager.BRIGHTNESS_MIN
-                && brightness <= PowerManager.BRIGHTNESS_MAX;
+    private void animateScreenBrightness(float target, float sdrTarget, float rate) {
+        animateScreenBrightness(target, sdrTarget, rate, /* ignoreAnimationLimits = */false);
     }
 
-    private void animateScreenBrightness(float target, float sdrTarget, float rate) {
+    private void animateScreenBrightness(float target, float sdrTarget, float rate,
+            boolean ignoreAnimationLimits) {
         if (DEBUG) {
             Slog.d(mTag, "Animating brightness: target=" + target + ", sdrTarget=" + sdrTarget
                     + ", rate=" + rate);
         }
-        if (mScreenBrightnessRampAnimator.animateTo(target, sdrTarget, rate, false)) {
+        if (mScreenBrightnessRampAnimator.animateTo(target, sdrTarget, rate,
+                ignoreAnimationLimits)) {
             Trace.traceCounter(Trace.TRACE_TAG_POWER, "TargetScreenBrightness", (int) target);
 
             String propertyKey = "debug.tracing.screen_brightness";
@@ -2641,102 +2379,11 @@
 
     private final Runnable mCleanListener = this::sendUpdatePowerState;
 
-    private void setProximitySensorEnabled(boolean enable) {
-        if (enable) {
-            if (!mProximitySensorEnabled) {
-                // Register the listener.
-                // Proximity sensor state already cleared initially.
-                mProximitySensorEnabled = true;
-                mIgnoreProximityUntilChanged = false;
-                mSensorManager.registerListener(mProximitySensorListener, mProximitySensor,
-                        SensorManager.SENSOR_DELAY_NORMAL, mHandler);
-            }
-        } else {
-            if (mProximitySensorEnabled) {
-                // Unregister the listener.
-                // Clear the proximity sensor state for next time.
-                mProximitySensorEnabled = false;
-                mProximity = PROXIMITY_UNKNOWN;
-                mIgnoreProximityUntilChanged = false;
-                mPendingProximity = PROXIMITY_UNKNOWN;
-                mHandler.removeMessages(MSG_PROXIMITY_SENSOR_DEBOUNCED);
-                mSensorManager.unregisterListener(mProximitySensorListener);
-                clearPendingProximityDebounceTime(); // release wake lock (must be last)
-            }
-        }
-    }
-
-    private void handleProximitySensorEvent(long time, boolean positive) {
-        if (mProximitySensorEnabled) {
-            if (mPendingProximity == PROXIMITY_NEGATIVE && !positive) {
-                return; // no change
-            }
-            if (mPendingProximity == PROXIMITY_POSITIVE && positive) {
-                return; // no change
-            }
-
-            // Only accept a proximity sensor reading if it remains
-            // stable for the entire debounce delay.  We hold a wake lock while
-            // debouncing the sensor.
-            mHandler.removeMessages(MSG_PROXIMITY_SENSOR_DEBOUNCED);
-            if (positive) {
-                mPendingProximity = PROXIMITY_POSITIVE;
-                setPendingProximityDebounceTime(
-                        time + PROXIMITY_SENSOR_POSITIVE_DEBOUNCE_DELAY); // acquire wake lock
-            } else {
-                mPendingProximity = PROXIMITY_NEGATIVE;
-                setPendingProximityDebounceTime(
-                        time + PROXIMITY_SENSOR_NEGATIVE_DEBOUNCE_DELAY); // acquire wake lock
-            }
-
-            // Debounce the new sensor reading.
-            debounceProximitySensor();
-        }
-    }
-
-    private void debounceProximitySensor() {
-        if (mProximitySensorEnabled
-                && mPendingProximity != PROXIMITY_UNKNOWN
-                && mPendingProximityDebounceTime >= 0) {
-            final long now = mClock.uptimeMillis();
-            if (mPendingProximityDebounceTime <= now) {
-                if (mProximity != mPendingProximity) {
-                    // if the status of the sensor changed, stop ignoring.
-                    mIgnoreProximityUntilChanged = false;
-                    Slog.i(mTag, "No longer ignoring proximity [" + mPendingProximity + "]");
-                }
-                // Sensor reading accepted.  Apply the change then release the wake lock.
-                mProximity = mPendingProximity;
-                updatePowerState();
-                clearPendingProximityDebounceTime(); // release wake lock (must be last)
-            } else {
-                // Need to wait a little longer.
-                // Debounce again later.  We continue holding a wake lock while waiting.
-                Message msg = mHandler.obtainMessage(MSG_PROXIMITY_SENSOR_DEBOUNCED);
-                mHandler.sendMessageAtTime(msg, mPendingProximityDebounceTime);
-            }
-        }
-    }
-
-    private void clearPendingProximityDebounceTime() {
-        if (mPendingProximityDebounceTime >= 0) {
-            mPendingProximityDebounceTime = -1;
-            mCallbacks.releaseSuspendBlocker(mSuspendBlockerIdProxDebounce);
-        }
-    }
-
-    private void setPendingProximityDebounceTime(long debounceTime) {
-        if (mPendingProximityDebounceTime < 0) {
-            mCallbacks.acquireSuspendBlocker(mSuspendBlockerIdProxDebounce);
-        }
-        mPendingProximityDebounceTime = debounceTime;
-    }
-
     private void sendOnStateChangedWithWakelock() {
-        if (!mOnStateChangedPending) {
-            mOnStateChangedPending = true;
-            mCallbacks.acquireSuspendBlocker(mSuspendBlockerIdOnStateChanged);
-            mHandler.post(mOnStateChangedRunnable);
+        boolean wakeLockAcquired = mWakelockController.acquireWakelock(
+                WakelockController.WAKE_LOCK_STATE_CHANGED);
+        if (wakeLockAcquired) {
+            mHandler.post(mWakelockController.getOnStateChangedRunnable());
         }
     }
 
@@ -2748,12 +2395,15 @@
     }
 
     private void handleSettingsChange(boolean userSwitch) {
-        mPendingScreenBrightnessSetting = getScreenBrightnessSetting();
-        mPendingAutoBrightnessAdjustment = getAutoBrightnessAdjustmentSetting();
+        mDisplayBrightnessController
+                .setPendingScreenBrightness(mDisplayBrightnessController
+                        .getScreenBrightnessSetting());
+        mAutomaticBrightnessStrategy.updatePendingAutoBrightnessAdjustments(userSwitch);
         if (userSwitch) {
             // Don't treat user switches as user initiated change.
-            setCurrentScreenBrightness(mPendingScreenBrightnessSetting);
-            updateAutoBrightnessAdjustment();
+            mDisplayBrightnessController
+                    .setAndNotifyCurrentScreenBrightness(mDisplayBrightnessController
+                            .getPendingScreenBrightness());
             if (mAutomaticBrightnessController != null) {
                 mAutomaticBrightnessController.resetShortTermModel();
             }
@@ -2767,129 +2417,59 @@
                 Settings.System.SCREEN_BRIGHTNESS_MODE,
                 Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL, UserHandle.USER_CURRENT);
         mHandler.postAtTime(() -> {
-            mUseAutoBrightness = screenBrightnessModeSetting
-                    == Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC;
+            mAutomaticBrightnessStrategy.setUseAutoBrightness(screenBrightnessModeSetting
+                    == Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
             updatePowerState();
         }, mClock.uptimeMillis());
     }
 
-    private float getAutoBrightnessAdjustmentSetting() {
-        final float adj = Settings.System.getFloatForUser(mContext.getContentResolver(),
-                Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, 0.0f, UserHandle.USER_CURRENT);
-        return Float.isNaN(adj) ? 0.0f : clampAutoBrightnessAdjustment(adj);
-    }
 
     @Override
     public float getScreenBrightnessSetting() {
-        float brightness = mBrightnessSetting.getBrightness();
-        if (Float.isNaN(brightness)) {
-            brightness = mScreenBrightnessDefault;
-        }
-        return clampAbsoluteBrightness(brightness);
-    }
-
-    private void loadNitBasedBrightnessSetting() {
-        if (mDisplayId == Display.DEFAULT_DISPLAY && mPersistBrightnessNitsForDefaultDisplay) {
-            float brightnessNitsForDefaultDisplay =
-                    mBrightnessSetting.getBrightnessNitsForDefaultDisplay();
-            if (brightnessNitsForDefaultDisplay >= 0) {
-                float brightnessForDefaultDisplay = getBrightnessFromNits(
-                        brightnessNitsForDefaultDisplay);
-                if (isValidBrightnessValue(brightnessForDefaultDisplay)) {
-                    mBrightnessSetting.setBrightness(brightnessForDefaultDisplay);
-                    mCurrentScreenBrightnessSetting = brightnessForDefaultDisplay;
-                    return;
-                }
-            }
-        }
-        mCurrentScreenBrightnessSetting = getScreenBrightnessSetting();
+        return mDisplayBrightnessController.getScreenBrightnessSetting();
     }
 
     @Override
     public void setBrightness(float brightnessValue, int userSerial) {
-        // Update the setting, which will eventually call back into DPC to have us actually update
-        // the display with the new value.
-        float clampedBrightnessValue = clampScreenBrightness(brightnessValue);
-        mBrightnessSetting.setUserSerial(userSerial);
-        mBrightnessSetting.setBrightness(clampedBrightnessValue);
-        if (mDisplayId == Display.DEFAULT_DISPLAY && mPersistBrightnessNitsForDefaultDisplay) {
-            float nits = convertToNits(clampedBrightnessValue);
-            if (nits >= 0) {
-                mBrightnessSetting.setBrightnessNitsForDefaultDisplay(nits);
-            }
-        }
+        mDisplayBrightnessController.setBrightness(clampScreenBrightness(brightnessValue),
+                userSerial);
     }
 
     @Override
-    public void onBootCompleted() {
-        Message msg = mHandler.obtainMessage(MSG_BOOT_COMPLETED);
-        mHandler.sendMessageAtTime(msg, mClock.uptimeMillis());
+    public int getDisplayId() {
+        return mDisplayId;
     }
 
-    private void updateScreenBrightnessSetting(float brightnessValue) {
-        if (!isValidBrightnessValue(brightnessValue)
-                || brightnessValue == mCurrentScreenBrightnessSetting) {
-            return;
-        }
-        setCurrentScreenBrightness(brightnessValue);
-        setBrightness(brightnessValue);
+    @Override
+    public int getLeadDisplayId() {
+        return mLeadDisplayId;
     }
 
-    private void setCurrentScreenBrightness(float brightnessValue) {
-        if (brightnessValue != mCurrentScreenBrightnessSetting) {
-            mCurrentScreenBrightnessSetting = brightnessValue;
-            postBrightnessChangeRunnable();
+    @Override
+    public void setBrightnessToFollow(float leadDisplayBrightness, float nits, float ambientLux,
+            boolean slowChange) {
+        mBrightnessRangeController.onAmbientLuxChange(ambientLux);
+        if (nits == BrightnessMappingStrategy.INVALID_NITS) {
+            mDisplayBrightnessController.setBrightnessToFollow(leadDisplayBrightness, slowChange);
+        } else {
+            float brightness = mDisplayBrightnessController.getBrightnessFromNits(nits);
+            if (BrightnessUtils.isValidBrightnessValue(brightness)) {
+                mDisplayBrightnessController.setBrightnessToFollow(brightness, slowChange);
+            } else {
+                // The device does not support nits
+                mDisplayBrightnessController.setBrightnessToFollow(leadDisplayBrightness,
+                        slowChange);
+            }
         }
-    }
-
-    private void putAutoBrightnessAdjustmentSetting(float adjustment) {
-        if (mDisplayId == Display.DEFAULT_DISPLAY) {
-            mAutoBrightnessAdjustment = adjustment;
-            Settings.System.putFloatForUser(mContext.getContentResolver(),
-                    Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, adjustment,
-                    UserHandle.USER_CURRENT);
-        }
-    }
-
-    private boolean updateAutoBrightnessAdjustment() {
-        if (Float.isNaN(mPendingAutoBrightnessAdjustment)) {
-            return false;
-        }
-        if (mAutoBrightnessAdjustment == mPendingAutoBrightnessAdjustment) {
-            mPendingAutoBrightnessAdjustment = Float.NaN;
-            return false;
-        }
-        mAutoBrightnessAdjustment = mPendingAutoBrightnessAdjustment;
-        mPendingAutoBrightnessAdjustment = Float.NaN;
-        mTemporaryAutoBrightnessAdjustment = Float.NaN;
-        return true;
-    }
-
-    // We want to return true if the user has set the screen brightness.
-    // RBC on, off, and intensity changes will return false.
-    // Slider interactions whilst in RBC will return true, just as when in non-rbc.
-    private boolean updateUserSetScreenBrightness() {
-        if ((Float.isNaN(mPendingScreenBrightnessSetting)
-                || mPendingScreenBrightnessSetting < 0.0f)) {
-            return false;
-        }
-        if (mCurrentScreenBrightnessSetting == mPendingScreenBrightnessSetting) {
-            mPendingScreenBrightnessSetting = PowerManager.BRIGHTNESS_INVALID_FLOAT;
-            mTemporaryScreenBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
-            return false;
-        }
-        setCurrentScreenBrightness(mPendingScreenBrightnessSetting);
-        mLastUserSetScreenBrightness = mPendingScreenBrightnessSetting;
-        mPendingScreenBrightnessSetting = PowerManager.BRIGHTNESS_INVALID_FLOAT;
-        mTemporaryScreenBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
-        return true;
+        sendUpdatePowerState();
     }
 
     private void notifyBrightnessTrackerChanged(float brightness, boolean userInitiated,
             boolean wasShortTermModelActive, boolean autobrightnessEnabled,
-            boolean brightnessIsTemporary) {
-        final float brightnessInNits = convertToAdjustedNits(brightness);
+            boolean brightnessIsTemporary, boolean shouldUseAutoBrightness) {
 
+        final float brightnessInNits =
+                mDisplayBrightnessController.convertToAdjustedNits(brightness);
         // Don't report brightness to brightnessTracker:
         // If brightness is temporary (ie the slider has not been released)
         // or if we are in idle screen brightness mode.
@@ -2901,12 +2481,13 @@
                 || mAutomaticBrightnessController.isInIdleMode()
                 || !autobrightnessEnabled
                 || mBrightnessTracker == null
-                || !mUseAutoBrightness
+                || !shouldUseAutoBrightness
                 || brightnessInNits < 0.0f) {
             return;
         }
 
-        if (userInitiated && !mAutomaticBrightnessController.hasValidAmbientLux()) {
+        if (userInitiated && (mAutomaticBrightnessController == null
+                || !mAutomaticBrightnessController.hasValidAmbientLux())) {
             // If we don't have a valid lux reading we can't report a valid
             // slider event so notify as if the system changed the brightness.
             userInitiated = false;
@@ -2925,96 +2506,33 @@
                 mAutomaticBrightnessController.getLastSensorTimestamps());
     }
 
-    private float convertToNits(float brightness) {
-        if (mAutomaticBrightnessController == null) {
-            return BrightnessMappingStrategy.INVALID_NITS;
+    @Override
+    public void addDisplayBrightnessFollower(DisplayPowerControllerInterface follower) {
+        synchronized (mLock) {
+            mDisplayBrightnessFollowers.append(follower.getDisplayId(), follower);
+            sendUpdatePowerStateLocked();
         }
-        return mAutomaticBrightnessController.convertToNits(brightness);
     }
 
-    private float convertToAdjustedNits(float brightness) {
-        if (mAutomaticBrightnessController == null) {
-            return BrightnessMappingStrategy.INVALID_NITS;
+    @Override
+    public void removeDisplayBrightnessFollower(DisplayPowerControllerInterface follower) {
+        synchronized (mLock) {
+            mDisplayBrightnessFollowers.remove(follower.getDisplayId());
+            mHandler.postAtTime(() -> follower.setBrightnessToFollow(
+                    PowerManager.BRIGHTNESS_INVALID_FLOAT, BrightnessMappingStrategy.INVALID_NITS,
+                    /* ambientLux= */ 0, /* slowChange= */ false), mClock.uptimeMillis());
         }
-        return mAutomaticBrightnessController.convertToAdjustedNits(brightness);
-    }
-
-    private float getBrightnessFromNits(float nits) {
-        if (mAutomaticBrightnessController == null) {
-            return PowerManager.BRIGHTNESS_INVALID_FLOAT;
-        }
-        return mAutomaticBrightnessController.getBrightnessFromNits(nits);
     }
 
     @GuardedBy("mLock")
-    private void updatePendingProximityRequestsLocked() {
-        mWaitingForNegativeProximity |= mPendingWaitForNegativeProximityLocked;
-        mPendingWaitForNegativeProximityLocked = false;
-
-        if (mIgnoreProximityUntilChanged) {
-            // Also, lets stop waiting for negative proximity if we're ignoring it.
-            mWaitingForNegativeProximity = false;
+    private void clearDisplayBrightnessFollowersLocked() {
+        for (int i = 0; i < mDisplayBrightnessFollowers.size(); i++) {
+            DisplayPowerControllerInterface follower = mDisplayBrightnessFollowers.valueAt(i);
+            mHandler.postAtTime(() -> follower.setBrightnessToFollow(
+                    PowerManager.BRIGHTNESS_INVALID_FLOAT, BrightnessMappingStrategy.INVALID_NITS,
+                    /* ambientLux= */ 0, /* slowChange= */ false), mClock.uptimeMillis());
         }
-    }
-
-    private void ignoreProximitySensorUntilChangedInternal() {
-        if (!mIgnoreProximityUntilChanged
-                && mProximity == PROXIMITY_POSITIVE) {
-            // Only ignore if it is still reporting positive (near)
-            mIgnoreProximityUntilChanged = true;
-            Slog.i(mTag, "Ignoring proximity");
-            updatePowerState();
-        }
-    }
-
-    private final Runnable mOnStateChangedRunnable = new Runnable() {
-        @Override
-        public void run() {
-            mOnStateChangedPending = false;
-            mCallbacks.onStateChanged();
-            mCallbacks.releaseSuspendBlocker(mSuspendBlockerIdOnStateChanged);
-        }
-    };
-
-    private void sendOnProximityPositiveWithWakelock() {
-        mCallbacks.acquireSuspendBlocker(mSuspendBlockerIdProxPositive);
-        mHandler.post(mOnProximityPositiveRunnable);
-        mOnProximityPositiveMessages++;
-    }
-
-    private final Runnable mOnProximityPositiveRunnable = new Runnable() {
-        @Override
-        public void run() {
-            mOnProximityPositiveMessages--;
-            mCallbacks.onProximityPositive();
-            mCallbacks.releaseSuspendBlocker(mSuspendBlockerIdProxPositive);
-        }
-    };
-
-    private void sendOnProximityNegativeWithWakelock() {
-        mOnProximityNegativeMessages++;
-        mCallbacks.acquireSuspendBlocker(mSuspendBlockerIdProxNegative);
-        mHandler.post(mOnProximityNegativeRunnable);
-    }
-
-    private final Runnable mOnProximityNegativeRunnable = new Runnable() {
-        @Override
-        public void run() {
-            mOnProximityNegativeMessages--;
-            mCallbacks.onProximityNegative();
-            mCallbacks.releaseSuspendBlocker(mSuspendBlockerIdProxNegative);
-        }
-    };
-
-    /**
-     * Indicates whether the display state is ready to update. If this is the default display, we
-     * want to update it right away so that we can draw the boot animation on it. If it is not
-     * the default display, drawing the boot animation on it would look incorrect, so we need
-     * to wait until boot is completed.
-     * @return True if the display state is ready to update
-     */
-    private boolean readyToUpdateDisplayState() {
-        return mDisplayId == Display.DEFAULT_DISPLAY || mBootCompleted;
+        mDisplayBrightnessFollowers.clear();
     }
 
     @Override
@@ -3026,31 +2544,23 @@
             pw.println("  mLeadDisplayId=" + mLeadDisplayId);
             pw.println("  mLightSensor=" + mLightSensor);
             pw.println("  mDisplayBrightnessFollowers=" + mDisplayBrightnessFollowers);
-            pw.println("  mDozeStateOverride=" + mDozeStateOverride);
 
             pw.println();
             pw.println("Display Power Controller Locked State:");
             pw.println("  mDisplayReadyLocked=" + mDisplayReadyLocked);
             pw.println("  mPendingRequestLocked=" + mPendingRequestLocked);
             pw.println("  mPendingRequestChangedLocked=" + mPendingRequestChangedLocked);
-            pw.println("  mPendingWaitForNegativeProximityLocked="
-                    + mPendingWaitForNegativeProximityLocked);
             pw.println("  mPendingUpdatePowerStateLocked=" + mPendingUpdatePowerStateLocked);
         }
 
         pw.println();
         pw.println("Display Power Controller Configuration:");
-        pw.println("  mScreenBrightnessRangeDefault=" + mScreenBrightnessDefault);
         pw.println("  mScreenBrightnessDozeConfig=" + mScreenBrightnessDozeConfig);
-        pw.println("  mScreenBrightnessDimConfig=" + mScreenBrightnessDimConfig);
         pw.println("  mUseSoftwareAutoBrightnessConfig=" + mUseSoftwareAutoBrightnessConfig);
-        pw.println("  mAllowAutoBrightnessWhileDozingConfig="
-                + mAllowAutoBrightnessWhileDozingConfig);
-        pw.println("  mPersistBrightnessNitsForDefaultDisplay="
-                + mPersistBrightnessNitsForDefaultDisplay);
         pw.println("  mSkipScreenOnBrightnessRamp=" + mSkipScreenOnBrightnessRamp);
         pw.println("  mColorFadeFadesConfig=" + mColorFadeFadesConfig);
         pw.println("  mColorFadeEnabled=" + mColorFadeEnabled);
+        pw.println("  mIsDisplayInternal=" + mIsDisplayInternal);
         synchronized (mCachedBrightnessInfo) {
             pw.println("  mCachedBrightnessInfo.brightness="
                     + mCachedBrightnessInfo.brightness.value);
@@ -3068,7 +2578,6 @@
         }
         pw.println("  mDisplayBlanksAfterDozeConfig=" + mDisplayBlanksAfterDozeConfig);
         pw.println("  mBrightnessBucketsInDozeConfig=" + mBrightnessBucketsInDozeConfig);
-
         mHandler.runWithScissors(() -> dumpLocal(pw), 1000);
     }
 
@@ -3076,35 +2585,9 @@
         pw.println();
         pw.println("Display Power Controller Thread State:");
         pw.println("  mPowerRequest=" + mPowerRequest);
-        pw.println("  mUnfinishedBusiness=" + mUnfinishedBusiness);
-        pw.println("  mWaitingForNegativeProximity=" + mWaitingForNegativeProximity);
-        pw.println("  mProximitySensor=" + mProximitySensor);
-        pw.println("  mProximitySensorEnabled=" + mProximitySensorEnabled);
-        pw.println("  mProximityThreshold=" + mProximityThreshold);
-        pw.println("  mProximity=" + proximityToString(mProximity));
-        pw.println("  mPendingProximity=" + proximityToString(mPendingProximity));
-        pw.println("  mPendingProximityDebounceTime="
-                + TimeUtils.formatUptime(mPendingProximityDebounceTime));
-        pw.println("  mScreenOffBecauseOfProximity=" + mScreenOffBecauseOfProximity);
-        pw.println("  mLastUserSetScreenBrightness=" + mLastUserSetScreenBrightness);
-        pw.println("  mPendingScreenBrightnessSetting="
-                + mPendingScreenBrightnessSetting);
-        pw.println("  mTemporaryScreenBrightness=" + mTemporaryScreenBrightness);
-        pw.println("  mBrightnessToFollow=" + mBrightnessToFollow);
-        pw.println("  mBrightnessToFollowSlowChange=" + mBrightnessToFollowSlowChange);
-        pw.println("  mAutoBrightnessAdjustment=" + mAutoBrightnessAdjustment);
         pw.println("  mBrightnessReason=" + mBrightnessReason);
-        pw.println("  mTemporaryAutoBrightnessAdjustment=" + mTemporaryAutoBrightnessAdjustment);
-        pw.println("  mPendingAutoBrightnessAdjustment=" + mPendingAutoBrightnessAdjustment);
-        pw.println("  mAppliedAutoBrightness=" + mAppliedAutoBrightness);
         pw.println("  mAppliedDimming=" + mAppliedDimming);
-        pw.println("  mAppliedLowPower=" + mAppliedLowPower);
         pw.println("  mAppliedThrottling=" + mAppliedThrottling);
-        pw.println("  mAppliedScreenBrightnessOverride=" + mAppliedScreenBrightnessOverride);
-        pw.println("  mAppliedTemporaryBrightness=" + mAppliedTemporaryBrightness);
-        pw.println("  mAppliedTemporaryAutoBrightnessAdjustment="
-                + mAppliedTemporaryAutoBrightnessAdjustment);
-        pw.println("  mAppliedBrightnessBoost=" + mAppliedBrightnessBoost);
         pw.println("  mDozing=" + mDozing);
         pw.println("  mSkipRampState=" + skipRampStateToString(mSkipRampState));
         pw.println("  mScreenOnBlockStartRealTime=" + mScreenOnBlockStartRealTime);
@@ -3115,9 +2598,8 @@
         pw.println("  mReportedToPolicy="
                 + reportedToPolicyToString(mReportedScreenStateToPolicy));
         pw.println("  mIsRbcActive=" + mIsRbcActive);
-        pw.println("  mOnStateChangePending=" + mOnStateChangedPending);
-        pw.println("  mOnProximityPositiveMessages=" + mOnProximityPositiveMessages);
-        pw.println("  mOnProximityNegativeMessages=" + mOnProximityNegativeMessages);
+        IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "    ");
+        mAutomaticBrightnessStrategy.dump(ipw);
 
         if (mScreenBrightnessRampAnimator != null) {
             pw.println("  mScreenBrightnessRampAnimator.isAnimating()="
@@ -3142,6 +2624,8 @@
             dumpBrightnessEvents(pw);
         }
 
+        dumpRbcEvents(pw);
+
         if (mScreenOffBrightnessSensorController != null) {
             mScreenOffBrightnessSensorController.dump(pw);
         }
@@ -3159,21 +2643,30 @@
             mDisplayWhiteBalanceController.dump(pw);
             mDisplayWhiteBalanceSettings.dump(pw);
         }
-    }
 
-    private static String proximityToString(int state) {
-        switch (state) {
-            case PROXIMITY_UNKNOWN:
-                return "Unknown";
-            case PROXIMITY_NEGATIVE:
-                return "Negative";
-            case PROXIMITY_POSITIVE:
-                return "Positive";
-            default:
-                return Integer.toString(state);
+        pw.println();
+
+        if (mWakelockController != null) {
+            mWakelockController.dumpLocal(pw);
+        }
+
+        pw.println();
+        if (mDisplayBrightnessController != null) {
+            mDisplayBrightnessController.dump(pw);
+        }
+
+        pw.println();
+        if (mDisplayStateController != null) {
+            mDisplayStateController.dumpsys(pw);
+        }
+
+        pw.println();
+        if (mBrightnessClamperController != null) {
+            mBrightnessClamperController.dump(ipw);
         }
     }
 
+
     private static String reportedToPolicyToString(int state) {
         switch (state) {
             case REPORTED_TO_POLICY_SCREEN_OFF:
@@ -3214,14 +2707,20 @@
         }
     }
 
-    private static float clampAbsoluteBrightness(float value) {
-        return MathUtils.constrain(value, PowerManager.BRIGHTNESS_MIN,
-                PowerManager.BRIGHTNESS_MAX);
+    private void dumpRbcEvents(PrintWriter pw) {
+        int size = mRbcEventRingBuffer.size();
+        if (size < 1) {
+            pw.println("No Reduce Bright Colors Adjustments");
+            return;
+        }
+
+        pw.println("Reduce Bright Colors Adjustments Last " + size + " Events: ");
+        BrightnessEvent[] eventArray = mRbcEventRingBuffer.toArray();
+        for (int i = 0; i < mRbcEventRingBuffer.size(); i++) {
+            pw.println("  " + eventArray[i]);
+        }
     }
 
-    private static float clampAutoBrightnessAdjustment(float value) {
-        return MathUtils.constrain(value, -1.0f, 1.0f);
-    }
 
     private void noteScreenState(int screenState) {
         // Log screen state change with display id
@@ -3349,20 +2848,21 @@
         // It's easier to check if the brightness is at maximum level using the brightness
         // value untouched by any modifiers
         boolean brightnessIsMax = unmodifiedBrightness == event.getHbmMax();
-        float brightnessInNits = convertToAdjustedNits(event.getBrightness());
+        float brightnessInNits =
+                mDisplayBrightnessController.convertToAdjustedNits(event.getBrightness());
         float appliedLowPowerMode = event.isLowPowerModeSet() ? event.getPowerFactor() : -1f;
         int appliedRbcStrength  = event.isRbcEnabled() ? event.getRbcStrength() : -1;
         float appliedHbmMaxNits =
                 event.getHbmMode() == BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF
-                ? -1f : convertToAdjustedNits(event.getHbmMax());
+                ? -1f : mDisplayBrightnessController.convertToAdjustedNits(event.getHbmMax());
         // thermalCapNits set to -1 if not currently capping max brightness
         float appliedThermalCapNits =
                 event.getThermalMax() == PowerManager.BRIGHTNESS_MAX
-                ? -1f : convertToAdjustedNits(event.getThermalMax());
-
+                ? -1f : mDisplayBrightnessController.convertToAdjustedNits(event.getThermalMax());
         if (mIsDisplayInternal) {
             FrameworkStatsLog.write(FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED,
-                    convertToAdjustedNits(event.getInitialBrightness()),
+                    mDisplayBrightnessController
+                            .convertToAdjustedNits(event.getInitialBrightness()),
                     brightnessInNits,
                     event.getLux(),
                     event.getPhysicalDisplayId(),
@@ -3379,7 +2879,8 @@
                     event.getHbmMode() == BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT,
                     event.getHbmMode() == BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR,
                     (modifier & BrightnessReason.MODIFIER_LOW_POWER) > 0,
-                    mBrightnessThrottler.getBrightnessMaxReason(),
+                    mBrightnessClamperController.getBrightnessMaxReason(),
+                    // TODO: (flc) add brightnessMinReason here too.
                     (modifier & BrightnessReason.MODIFIER_DIMMED) > 0,
                     event.isRbcEnabled(),
                     (flags & BrightnessEvent.FLAG_INVALID_LUX) > 0,
@@ -3390,6 +2891,17 @@
         }
     }
 
+    /**
+     * Indicates whether the display state is ready to update. If this is the default display, we
+     * want to update it right away so that we can draw the boot animation on it. If it is not
+     * the default display, drawing the boot animation on it would look incorrect, so we need
+     * to wait until boot is completed.
+     * @return True if the display state is ready to update
+     */
+    private boolean readyToUpdateDisplayState() {
+        return mDisplayId == Display.DEFAULT_DISPLAY || mBootCompleted;
+    }
+
     private final class DisplayControllerHandler extends Handler {
         DisplayControllerHandler(Looper looper) {
             super(looper, null, true /*async*/);
@@ -3402,10 +2914,6 @@
                     updatePowerState();
                     break;
 
-                case MSG_PROXIMITY_SENSOR_DEBOUNCED:
-                    debounceProximitySensor();
-                    break;
-
                 case MSG_SCREEN_ON_UNBLOCKED:
                     if (mPendingScreenOnUnblocker == msg.obj) {
                         unblockScreenOn();
@@ -3418,27 +2926,38 @@
                         updatePowerState();
                     }
                     break;
+                case MSG_OFFLOADING_SCREEN_ON_UNBLOCKED:
+                    if (mDisplayOffloadSession == msg.obj) {
+                        unblockScreenOnByDisplayOffload();
+                        updatePowerState();
+                    }
+                    break;
                 case MSG_CONFIGURE_BRIGHTNESS:
-                    mBrightnessConfiguration = (BrightnessConfiguration) msg.obj;
-                    mShouldResetShortTermModel = msg.arg1 == 1;
+                    BrightnessConfiguration brightnessConfiguration =
+                            (BrightnessConfiguration) msg.obj;
+                    mAutomaticBrightnessStrategy.setBrightnessConfiguration(brightnessConfiguration,
+                            msg.arg1 == 1);
+                    if (mBrightnessTracker != null) {
+                        mBrightnessTracker
+                                .setShouldCollectColorSample(brightnessConfiguration != null
+                                        && brightnessConfiguration.shouldCollectColorSamples());
+                    }
                     updatePowerState();
                     break;
 
                 case MSG_SET_TEMPORARY_BRIGHTNESS:
                     // TODO: Should we have a a timeout for the temporary brightness?
-                    mTemporaryScreenBrightness = Float.intBitsToFloat(msg.arg1);
+                    mDisplayBrightnessController
+                            .setTemporaryBrightness(Float.intBitsToFloat(msg.arg1));
                     updatePowerState();
                     break;
 
                 case MSG_SET_TEMPORARY_AUTO_BRIGHTNESS_ADJUSTMENT:
-                    mTemporaryAutoBrightnessAdjustment = Float.intBitsToFloat(msg.arg1);
+                    mAutomaticBrightnessStrategy
+                            .setTemporaryAutoBrightnessAdjustment(Float.intBitsToFloat(msg.arg1));
                     updatePowerState();
                     break;
 
-                case MSG_IGNORE_PROXIMITY:
-                    ignoreProximitySensorUntilChangedInternal();
-                    break;
-
                 case MSG_STOP:
                     cleanupHandlerThreadAfterStop();
                     break;
@@ -3486,27 +3005,15 @@
                 case MSG_SET_DWBC_LOGGING_ENABLED:
                     setDwbcLoggingEnabled(msg.arg1);
                     break;
+                case MSG_SET_BRIGHTNESS_FROM_OFFLOAD:
+                    mDisplayBrightnessController.setBrightnessFromOffload(
+                            Float.intBitsToFloat(msg.arg1));
+                    updatePowerState();
+                    break;
             }
         }
     }
 
-    private final SensorEventListener mProximitySensorListener = new SensorEventListener() {
-        @Override
-        public void onSensorChanged(SensorEvent event) {
-            if (mProximitySensorEnabled) {
-                final long time = mClock.uptimeMillis();
-                final float distance = event.values[0];
-                boolean positive = distance >= 0.0f && distance < mProximityThreshold;
-                handleProximitySensorEvent(time, positive);
-            }
-        }
-
-        @Override
-        public void onAccuracyChanged(Sensor sensor, int accuracy) {
-            // Not used.
-        }
-    };
-
 
     private final class SettingsObserver extends ContentObserver {
         SettingsObserver(Handler handler) {
@@ -3517,6 +3024,16 @@
         public void onChange(boolean selfChange, Uri uri) {
             if (uri.equals(Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_MODE))) {
                 handleBrightnessModeChange();
+            } else if (uri.equals(Settings.System.getUriFor(
+                    Settings.System.SCREEN_BRIGHTNESS_FOR_ALS))) {
+                int preset = Settings.System.getIntForUser(mContext.getContentResolver(),
+                        Settings.System.SCREEN_BRIGHTNESS_FOR_ALS,
+                        Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_NORMAL,
+                        UserHandle.USER_CURRENT);
+                Slog.i(mTag, "Setting up auto-brightness for preset "
+                        + autoBrightnessPresetToString(preset));
+                setUpAutoBrightness(mContext, mHandler);
+                sendUpdatePowerState();
             } else {
                 handleSettingsChange(false /* userSwitch */);
             }
@@ -3567,28 +3084,6 @@
         msg.sendToTarget();
     }
 
-    @VisibleForTesting
-    String getSuspendBlockerUnfinishedBusinessId(int displayId) {
-        return "[" + displayId + "]unfinished business";
-    }
-
-    String getSuspendBlockerOnStateChangedId(int displayId) {
-        return "[" + displayId + "]on state changed";
-    }
-
-    String getSuspendBlockerProxPositiveId(int displayId) {
-        return "[" + displayId + "]prox positive";
-    }
-
-    String getSuspendBlockerProxNegativeId(int displayId) {
-        return "[" + displayId + "]prox negative";
-    }
-
-    @VisibleForTesting
-    String getSuspendBlockerProxDebounceId(int displayId) {
-        return "[" + displayId + "]prox debounce";
-    }
-
     /** Functional interface for providing time. */
     @VisibleForTesting
     interface Clock {
@@ -3615,6 +3110,20 @@
             return new DualRampAnimator(dps, firstProperty, secondProperty);
         }
 
+        WakelockController getWakelockController(int displayId,
+                DisplayPowerCallbacks displayPowerCallbacks) {
+            return new WakelockController(displayId, displayPowerCallbacks);
+        }
+
+        DisplayPowerProximityStateController getDisplayPowerProximityStateController(
+                WakelockController wakelockController, DisplayDeviceConfig displayDeviceConfig,
+                Looper looper, Runnable nudgeUpdatePowerState,
+                int displayId, SensorManager sensorManager) {
+            return new DisplayPowerProximityStateController(wakelockController, displayDeviceConfig,
+                    looper, nudgeUpdatePowerState,
+                    displayId, sensorManager, /* injector= */ null);
+        }
+
         AutomaticBrightnessController getAutomaticBrightnessController(
                 AutomaticBrightnessController.Callbacks callbacks, Looper looper,
                 SensorManager sensorManager, Sensor lightSensor,
@@ -3697,11 +3206,32 @@
                     hbmChangeCallback, hbmMetadata, context);
         }
 
+        BrightnessRangeController getBrightnessRangeController(
+                HighBrightnessModeController hbmController, Runnable modeChangeCallback,
+                DisplayDeviceConfig displayDeviceConfig, Handler handler,
+                DisplayManagerFlags flags, IBinder displayToken, DisplayDeviceInfo info) {
+            return new BrightnessRangeController(hbmController,
+                    modeChangeCallback, displayDeviceConfig, handler, flags, displayToken, info);
+        }
+
+        BrightnessClamperController getBrightnessClamperController(Handler handler,
+                BrightnessClamperController.ClamperChangeListener clamperChangeListener,
+                BrightnessClamperController.DisplayDeviceData data, Context context,
+                DisplayManagerFlags flags) {
+
+            return new BrightnessClamperController(handler, clamperChangeListener, data, context,
+                    flags);
+        }
+
         DisplayWhiteBalanceController getDisplayWhiteBalanceController(Handler handler,
                 SensorManager sensorManager, Resources resources) {
             return DisplayWhiteBalanceFactory.create(handler,
                     sensorManager, resources);
         }
+
+        boolean isColorFadeEnabled() {
+            return !ActivityManager.isLowRamDeviceStatic();
+        }
     }
 
     static class CachedBrightnessInfo {
diff --git a/services/core/java/com/android/server/display/DisplayPowerController2.java b/services/core/java/com/android/server/display/DisplayPowerController2.java
deleted file mode 100644
index 519224a..0000000
--- a/services/core/java/com/android/server/display/DisplayPowerController2.java
+++ /dev/null
@@ -1,3248 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.display;
-
-import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DEFAULT;
-import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DOZE;
-import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_IDLE;
-import static com.android.server.display.config.DisplayBrightnessMappingConfig.autoBrightnessPresetToString;
-
-import android.animation.Animator;
-import android.animation.ObjectAnimator;
-import android.annotation.Nullable;
-import android.annotation.SuppressLint;
-import android.annotation.UserIdInt;
-import android.app.ActivityManager;
-import android.content.Context;
-import android.content.pm.ParceledListSlice;
-import android.content.res.Resources;
-import android.database.ContentObserver;
-import android.hardware.Sensor;
-import android.hardware.SensorManager;
-import android.hardware.display.AmbientBrightnessDayStats;
-import android.hardware.display.BrightnessChangeEvent;
-import android.hardware.display.BrightnessConfiguration;
-import android.hardware.display.BrightnessInfo;
-import android.hardware.display.DisplayManagerInternal;
-import android.hardware.display.DisplayManagerInternal.DisplayOffloadSession;
-import android.hardware.display.DisplayManagerInternal.DisplayPowerCallbacks;
-import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
-import android.metrics.LogMaker;
-import android.net.Uri;
-import android.os.Handler;
-import android.os.HandlerExecutor;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
-import android.os.PowerManager;
-import android.os.RemoteException;
-import android.os.SystemClock;
-import android.os.SystemProperties;
-import android.os.Trace;
-import android.os.UserHandle;
-import android.provider.Settings;
-import android.util.FloatProperty;
-import android.util.IndentingPrintWriter;
-import android.util.MathUtils;
-import android.util.MutableFloat;
-import android.util.MutableInt;
-import android.util.Slog;
-import android.util.SparseArray;
-import android.view.Display;
-
-import com.android.internal.R;
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.app.IBatteryStats;
-import com.android.internal.display.BrightnessSynchronizer;
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.internal.util.FrameworkStatsLog;
-import com.android.internal.util.RingBuffer;
-import com.android.server.LocalServices;
-import com.android.server.am.BatteryStatsService;
-import com.android.server.display.RampAnimator.DualRampAnimator;
-import com.android.server.display.brightness.BrightnessEvent;
-import com.android.server.display.brightness.BrightnessReason;
-import com.android.server.display.brightness.BrightnessUtils;
-import com.android.server.display.brightness.DisplayBrightnessController;
-import com.android.server.display.brightness.clamper.BrightnessClamperController;
-import com.android.server.display.brightness.strategy.AutomaticBrightnessStrategy;
-import com.android.server.display.color.ColorDisplayService.ColorDisplayServiceInternal;
-import com.android.server.display.color.ColorDisplayService.ReduceBrightColorsListener;
-import com.android.server.display.feature.DisplayManagerFlags;
-import com.android.server.display.layout.Layout;
-import com.android.server.display.state.DisplayStateController;
-import com.android.server.display.utils.DebugUtils;
-import com.android.server.display.utils.SensorUtils;
-import com.android.server.display.whitebalance.DisplayWhiteBalanceController;
-import com.android.server.display.whitebalance.DisplayWhiteBalanceFactory;
-import com.android.server.display.whitebalance.DisplayWhiteBalanceSettings;
-import com.android.server.policy.WindowManagerPolicy;
-
-import java.io.PrintWriter;
-import java.util.Objects;
-
-/**
- * Controls the power state of the display.
- *
- * Handles the proximity sensor, light sensor, and animations between states
- * including the screen off animation.
- *
- * This component acts independently of the rest of the power manager service.
- * In particular, it does not share any state and it only communicates
- * via asynchronous callbacks to inform the power manager that something has
- * changed.
- *
- * Everything this class does internally is serialized on its handler although
- * it may be accessed by other threads from the outside.
- *
- * Note that the power manager service guarantees that it will hold a suspend
- * blocker as long as the display is not ready.  So most of the work done here
- * does not need to worry about holding a suspend blocker unless it happens
- * independently of the display ready signal.
- *
- * For debugging, you can make the color fade and brightness animations run
- * slower by changing the "animator duration scale" option in Development Settings.
- */
-final class DisplayPowerController2 implements AutomaticBrightnessController.Callbacks,
-        DisplayWhiteBalanceController.Callbacks, DisplayPowerControllerInterface {
-    private static final String SCREEN_ON_BLOCKED_TRACE_NAME = "Screen on blocked";
-    private static final String SCREEN_OFF_BLOCKED_TRACE_NAME = "Screen off blocked";
-
-    private static final String TAG = "DisplayPowerController2";
-    // To enable these logs, run:
-    // 'adb shell setprop persist.log.tag.DisplayPowerController2 DEBUG && adb reboot'
-    private static final boolean DEBUG = DebugUtils.isDebuggable(TAG);
-    private static final String SCREEN_ON_BLOCKED_BY_DISPLAYOFFLOAD_TRACE_NAME =
-            "Screen on blocked by displayoffload";
-
-    // If true, uses the color fade on animation.
-    // We might want to turn this off if we cannot get a guarantee that the screen
-    // actually turns on and starts showing new content after the call to set the
-    // screen state returns.  Playing the animation can also be somewhat slow.
-    private static final boolean USE_COLOR_FADE_ON_ANIMATION = false;
-
-    private static final float SCREEN_ANIMATION_RATE_MINIMUM = 0.0f;
-
-    private static final int COLOR_FADE_ON_ANIMATION_DURATION_MILLIS = 250;
-    private static final int COLOR_FADE_OFF_ANIMATION_DURATION_MILLIS = 400;
-
-    private static final int MSG_UPDATE_POWER_STATE = 1;
-    private static final int MSG_SCREEN_ON_UNBLOCKED = 2;
-    private static final int MSG_SCREEN_OFF_UNBLOCKED = 3;
-    private static final int MSG_CONFIGURE_BRIGHTNESS = 4;
-    private static final int MSG_SET_TEMPORARY_BRIGHTNESS = 5;
-    private static final int MSG_SET_TEMPORARY_AUTO_BRIGHTNESS_ADJUSTMENT = 6;
-    private static final int MSG_STOP = 7;
-    private static final int MSG_UPDATE_BRIGHTNESS = 8;
-    private static final int MSG_UPDATE_RBC = 9;
-    private static final int MSG_BRIGHTNESS_RAMP_DONE = 10;
-    private static final int MSG_STATSD_HBM_BRIGHTNESS = 11;
-    private static final int MSG_SWITCH_USER = 12;
-    private static final int MSG_BOOT_COMPLETED = 13;
-    private static final int MSG_SET_DWBC_STRONG_MODE = 14;
-    private static final int MSG_SET_DWBC_COLOR_OVERRIDE = 15;
-    private static final int MSG_SET_DWBC_LOGGING_ENABLED = 16;
-    private static final int MSG_SET_BRIGHTNESS_FROM_OFFLOAD = 17;
-    private static final int MSG_OFFLOADING_SCREEN_ON_UNBLOCKED = 18;
-
-
-
-    private static final int BRIGHTNESS_CHANGE_STATSD_REPORT_INTERVAL_MS = 500;
-
-
-    // State machine constants for tracking initial brightness ramp skipping when enabled.
-    private static final int RAMP_STATE_SKIP_NONE = 0;
-    private static final int RAMP_STATE_SKIP_INITIAL = 1;
-    private static final int RAMP_STATE_SKIP_AUTOBRIGHT = 2;
-
-    private static final int REPORTED_TO_POLICY_UNREPORTED = -1;
-    private static final int REPORTED_TO_POLICY_SCREEN_OFF = 0;
-    private static final int REPORTED_TO_POLICY_SCREEN_TURNING_ON = 1;
-    private static final int REPORTED_TO_POLICY_SCREEN_ON = 2;
-    private static final int REPORTED_TO_POLICY_SCREEN_TURNING_OFF = 3;
-
-    private static final int RINGBUFFER_MAX = 100;
-    private static final int RINGBUFFER_RBC_MAX = 20;
-
-    private static final float[] BRIGHTNESS_RANGE_BOUNDARIES = {
-        0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20, 30, 40, 50, 60, 70, 80,
-        90, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000, 1200,
-        1400, 1600, 1800, 2000, 2250, 2500, 2750, 3000};
-    private static final int[] BRIGHTNESS_RANGE_INDEX = {
-        FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_UNKNOWN,
-        FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_0_1,
-        FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_1_2,
-        FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_2_3,
-        FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_3_4,
-        FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_4_5,
-        FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_5_6,
-        FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_6_7,
-        FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_7_8,
-        FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_8_9,
-        FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_9_10,
-        FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_10_20,
-        FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_20_30,
-        FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_30_40,
-        FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_40_50,
-        FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_50_60,
-        FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_60_70,
-        FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_70_80,
-        FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_80_90,
-        FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_90_100,
-        FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_100_200,
-        FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_200_300,
-        FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_300_400,
-        FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_400_500,
-        FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_500_600,
-        FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_600_700,
-        FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_700_800,
-        FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_800_900,
-        FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_900_1000,
-        FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_1000_1200,
-        FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_1200_1400,
-        FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_1400_1600,
-        FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_1600_1800,
-        FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_1800_2000,
-        FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_2000_2250,
-        FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_2250_2500,
-        FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_2500_2750,
-        FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_2750_3000,
-    };
-
-    private final String mTag;
-
-    private final Object mLock = new Object();
-
-    private final Context mContext;
-
-    // Our handler.
-    private final DisplayControllerHandler mHandler;
-
-    // Battery stats.
-    @Nullable
-    private final IBatteryStats mBatteryStats;
-
-    // The sensor manager.
-    private final SensorManager mSensorManager;
-
-    // The window manager policy.
-    private final WindowManagerPolicy mWindowManagerPolicy;
-
-    // The display blanker.
-    private final DisplayBlanker mBlanker;
-
-    // The LogicalDisplay tied to this DisplayPowerController2.
-    private final LogicalDisplay mLogicalDisplay;
-
-    // The ID of the LogicalDisplay tied to this DisplayPowerController2.
-    private final int mDisplayId;
-
-    // The ID of the display which this display follows for brightness purposes.
-    private int mLeadDisplayId = Layout.NO_LEAD_DISPLAY;
-
-    // The unique ID of the primary display device currently tied to this logical display
-    private String mUniqueDisplayId;
-
-    // Tracker for brightness changes.
-    @Nullable
-    private final BrightnessTracker mBrightnessTracker;
-
-    // Tracker for brightness settings changes.
-    private final SettingsObserver mSettingsObserver;
-
-    // The doze screen brightness.
-    private final float mScreenBrightnessDozeConfig;
-
-    // True if auto-brightness should be used.
-    private boolean mUseSoftwareAutoBrightnessConfig;
-
-    // Whether or not the color fade on screen on / off is enabled.
-    private final boolean mColorFadeEnabled;
-
-    @GuardedBy("mCachedBrightnessInfo")
-    private final CachedBrightnessInfo mCachedBrightnessInfo = new CachedBrightnessInfo();
-
-    private DisplayDevice mDisplayDevice;
-
-    // True if we should fade the screen while turning it off, false if we should play
-    // a stylish color fade animation instead.
-    private final boolean mColorFadeFadesConfig;
-
-    // True if we need to fake a transition to off when coming out of a doze state.
-    // Some display hardware will blank itself when coming out of doze in order to hide
-    // artifacts. For these displays we fake a transition into OFF so that policy can appropriately
-    // blank itself and begin an appropriate power on animation.
-    private final boolean mDisplayBlanksAfterDozeConfig;
-
-    // True if there are only buckets of brightness values when the display is in the doze state,
-    // rather than a full range of values. If this is true, then we'll avoid animating the screen
-    // brightness since it'd likely be multiple jarring brightness transitions instead of just one
-    // to reach the final state.
-    private final boolean mBrightnessBucketsInDozeConfig;
-
-    private final Clock mClock;
-    private final Injector mInjector;
-
-    // Maximum time a ramp animation can take.
-    private long mBrightnessRampIncreaseMaxTimeMillis;
-    private long mBrightnessRampDecreaseMaxTimeMillis;
-
-    // Maximum time a ramp animation can take in idle mode.
-    private long mBrightnessRampIncreaseMaxTimeIdleMillis;
-    private long mBrightnessRampDecreaseMaxTimeIdleMillis;
-
-    // The pending power request.
-    // Initially null until the first call to requestPowerState.
-    @GuardedBy("mLock")
-    private DisplayPowerRequest mPendingRequestLocked;
-
-    // True if the pending power request or wait for negative proximity flag
-    // has been changed since the last update occurred.
-    @GuardedBy("mLock")
-    private boolean mPendingRequestChangedLocked;
-
-    // Set to true when the important parts of the pending power request have been applied.
-    // The important parts are mainly the screen state.  Brightness changes may occur
-    // concurrently.
-    @GuardedBy("mLock")
-    private boolean mDisplayReadyLocked;
-
-    // Set to true if a power state update is required.
-    @GuardedBy("mLock")
-    private boolean mPendingUpdatePowerStateLocked;
-
-    /* The following state must only be accessed by the handler thread. */
-
-    // The currently requested power state.
-    // The power controller will progressively update its internal state to match
-    // the requested power state.  Initially null until the first update.
-    private DisplayPowerRequest mPowerRequest;
-
-    // The current power state.
-    // Must only be accessed on the handler thread.
-    private DisplayPowerState mPowerState;
-
-
-
-    // The currently active screen on unblocker.  This field is non-null whenever
-    // we are waiting for a callback to release it and unblock the screen.
-    private ScreenOnUnblocker mPendingScreenOnUnblocker;
-    private ScreenOffUnblocker mPendingScreenOffUnblocker;
-    private Runnable mPendingScreenOnUnblockerByDisplayOffload;
-
-    // True if we were in the process of turning off the screen.
-    // This allows us to recover more gracefully from situations where we abort
-    // turning off the screen.
-    private boolean mPendingScreenOff;
-
-    // The elapsed real time when the screen on was blocked.
-    private long mScreenOnBlockStartRealTime;
-    private long mScreenOffBlockStartRealTime;
-    private long mScreenOnBlockByDisplayOffloadStartRealTime;
-
-    // Screen state we reported to policy. Must be one of REPORTED_TO_POLICY_* fields.
-    private int mReportedScreenStateToPolicy = REPORTED_TO_POLICY_UNREPORTED;
-
-    // Used to deduplicate the displayoffload blocking screen on logic. One block per turning on.
-    // This value is reset when screen on is reported or the blocking is cancelled.
-    private boolean mScreenTurningOnWasBlockedByDisplayOffload;
-
-    // If the last recorded screen state was dozing or not.
-    private boolean mDozing;
-
-    private boolean mAppliedDimming;
-
-    private boolean mAppliedThrottling;
-
-    // Reason for which the brightness was last changed. See {@link BrightnessReason} for more
-    // information.
-    // At the time of this writing, this value is changed within updatePowerState() only, which is
-    // limited to the thread used by DisplayControllerHandler.
-    private final BrightnessReason mBrightnessReason = new BrightnessReason();
-    private final BrightnessReason mBrightnessReasonTemp = new BrightnessReason();
-
-    // Brightness animation ramp rates in brightness units per second
-    private float mBrightnessRampRateFastDecrease;
-    private float mBrightnessRampRateFastIncrease;
-    private float mBrightnessRampRateSlowDecrease;
-    private float mBrightnessRampRateSlowIncrease;
-    private float mBrightnessRampRateSlowDecreaseIdle;
-    private float mBrightnessRampRateSlowIncreaseIdle;
-
-    // Report HBM brightness change to StatsD
-    private int mDisplayStatsId;
-    private float mLastStatsBrightness = PowerManager.BRIGHTNESS_MIN;
-
-    // Whether or not to skip the initial brightness ramps into STATE_ON.
-    private final boolean mSkipScreenOnBrightnessRamp;
-
-    // Display white balance components.
-    // Critical methods must be called on DPC2 handler thread.
-    @Nullable
-    private final DisplayWhiteBalanceSettings mDisplayWhiteBalanceSettings;
-    @Nullable
-    private final DisplayWhiteBalanceController mDisplayWhiteBalanceController;
-
-    @Nullable
-    private final ColorDisplayServiceInternal mCdsi;
-    private float[] mNitsRange;
-
-    private final BrightnessRangeController mBrightnessRangeController;
-
-    private final BrightnessThrottler mBrightnessThrottler;
-
-    private final BrightnessClamperController mBrightnessClamperController;
-
-    private final Runnable mOnBrightnessChangeRunnable;
-
-    private final BrightnessEvent mLastBrightnessEvent;
-    private final BrightnessEvent mTempBrightnessEvent;
-
-    private final DisplayBrightnessController mDisplayBrightnessController;
-
-    // Keeps a record of brightness changes for dumpsys.
-    private RingBuffer<BrightnessEvent> mBrightnessEventRingBuffer;
-
-    // Keeps a record of rbc changes for dumpsys.
-    private final RingBuffer<BrightnessEvent> mRbcEventRingBuffer =
-            new RingBuffer<>(BrightnessEvent.class, RINGBUFFER_RBC_MAX);
-
-    // Controls and tracks all the wakelocks that are acquired/released by the system. Also acts as
-    // a medium of communication between this class and the PowerManagerService.
-    private final WakelockController mWakelockController;
-
-    // Tracks and manages the proximity state of the associated display.
-    private final DisplayPowerProximityStateController mDisplayPowerProximityStateController;
-
-    // Tracks and manages the display state of the associated display.
-    private final DisplayStateController mDisplayStateController;
-
-
-    // Responsible for evaluating and tracking the automatic brightness relevant states.
-    // Todo: This is a temporary workaround. Ideally DPC2 should never talk to the strategies
-    private final AutomaticBrightnessStrategy mAutomaticBrightnessStrategy;
-
-    // A record of state for skipping brightness ramps.
-    private int mSkipRampState = RAMP_STATE_SKIP_NONE;
-
-    // The first autobrightness value set when entering RAMP_STATE_SKIP_INITIAL.
-    private float mInitialAutoBrightness;
-
-    // The controller for the automatic brightness level.
-    @Nullable
-    private AutomaticBrightnessController mAutomaticBrightnessController;
-
-    // The controller for the sensor used to estimate ambient lux while the display is off.
-    @Nullable
-    private ScreenOffBrightnessSensorController mScreenOffBrightnessSensorController;
-
-    private Sensor mLightSensor;
-    private Sensor mScreenOffBrightnessSensor;
-
-    private boolean mIsRbcActive;
-
-    // Animators.
-    private ObjectAnimator mColorFadeOnAnimator;
-    private ObjectAnimator mColorFadeOffAnimator;
-    private DualRampAnimator<DisplayPowerState> mScreenBrightnessRampAnimator;
-
-    // True if this DisplayPowerController2 has been stopped and should no longer be running.
-    private boolean mStopped;
-
-    private DisplayDeviceConfig mDisplayDeviceConfig;
-
-    private boolean mIsEnabled;
-    private boolean mIsInTransition;
-    private boolean mIsDisplayInternal;
-
-    // The id of the thermal brightness throttling policy that should be used.
-    private String mThermalBrightnessThrottlingDataId;
-
-    // DPCs following the brightness of this DPC. This is used in concurrent displays mode - there
-    // is one lead display, the additional displays follow the brightness value of the lead display.
-    @GuardedBy("mLock")
-    private SparseArray<DisplayPowerControllerInterface> mDisplayBrightnessFollowers =
-            new SparseArray();
-
-    private boolean mBootCompleted;
-    private final DisplayManagerFlags mFlags;
-
-    private DisplayOffloadSession mDisplayOffloadSession;
-
-    /**
-     * Creates the display power controller.
-     */
-    DisplayPowerController2(Context context, Injector injector,
-            DisplayPowerCallbacks callbacks, Handler handler,
-            SensorManager sensorManager, DisplayBlanker blanker, LogicalDisplay logicalDisplay,
-            BrightnessTracker brightnessTracker, BrightnessSetting brightnessSetting,
-            Runnable onBrightnessChangeRunnable, HighBrightnessModeMetadata hbmMetadata,
-            boolean bootCompleted, DisplayManagerFlags flags) {
-        mFlags = flags;
-        mInjector = injector != null ? injector : new Injector();
-        mClock = mInjector.getClock();
-        mLogicalDisplay = logicalDisplay;
-        mDisplayId = mLogicalDisplay.getDisplayIdLocked();
-        mSensorManager = sensorManager;
-        mHandler = new DisplayControllerHandler(handler.getLooper());
-        mDisplayDeviceConfig = logicalDisplay.getPrimaryDisplayDeviceLocked()
-                .getDisplayDeviceConfig();
-        mIsEnabled = logicalDisplay.isEnabledLocked();
-        mIsInTransition = logicalDisplay.isInTransitionLocked();
-        mIsDisplayInternal = logicalDisplay.getPrimaryDisplayDeviceLocked()
-                .getDisplayDeviceInfoLocked().type == Display.TYPE_INTERNAL;
-        mWakelockController = mInjector.getWakelockController(mDisplayId, callbacks);
-        mDisplayPowerProximityStateController = mInjector.getDisplayPowerProximityStateController(
-                mWakelockController, mDisplayDeviceConfig, mHandler.getLooper(),
-                () -> updatePowerState(), mDisplayId, mSensorManager);
-        mDisplayStateController = new DisplayStateController(mDisplayPowerProximityStateController);
-        mTag = TAG + "[" + mDisplayId + "]";
-        mThermalBrightnessThrottlingDataId =
-                logicalDisplay.getDisplayInfoLocked().thermalBrightnessThrottlingDataId;
-        mDisplayDevice = mLogicalDisplay.getPrimaryDisplayDeviceLocked();
-        mUniqueDisplayId = logicalDisplay.getPrimaryDisplayDeviceLocked().getUniqueId();
-        mDisplayStatsId = mUniqueDisplayId.hashCode();
-
-        mLastBrightnessEvent = new BrightnessEvent(mDisplayId);
-        mTempBrightnessEvent = new BrightnessEvent(mDisplayId);
-
-        if (mDisplayId == Display.DEFAULT_DISPLAY) {
-            mBatteryStats = BatteryStatsService.getService();
-        } else {
-            mBatteryStats = null;
-        }
-
-        mSettingsObserver = new SettingsObserver(mHandler);
-        mWindowManagerPolicy = LocalServices.getService(WindowManagerPolicy.class);
-        mBlanker = blanker;
-        mContext = context;
-        mBrightnessTracker = brightnessTracker;
-        mOnBrightnessChangeRunnable = onBrightnessChangeRunnable;
-
-        PowerManager pm = context.getSystemService(PowerManager.class);
-
-        final Resources resources = context.getResources();
-
-        // DOZE AND DIM SETTINGS
-        mScreenBrightnessDozeConfig = BrightnessUtils.clampAbsoluteBrightness(
-                pm.getBrightnessConstraint(PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_DOZE));
-        loadBrightnessRampRates();
-        mSkipScreenOnBrightnessRamp = resources.getBoolean(
-                R.bool.config_skipScreenOnBrightnessRamp);
-
-        Runnable modeChangeCallback = () -> {
-            sendUpdatePowerState();
-            postBrightnessChangeRunnable();
-            // TODO(b/192258832): Switch the HBMChangeCallback to a listener pattern.
-            if (mAutomaticBrightnessController != null) {
-                mAutomaticBrightnessController.update();
-            }
-        };
-
-        HighBrightnessModeController hbmController = createHbmControllerLocked(hbmMetadata,
-                modeChangeCallback);
-        mBrightnessThrottler = createBrightnessThrottlerLocked();
-
-        mBrightnessRangeController = mInjector.getBrightnessRangeController(hbmController,
-                modeChangeCallback, mDisplayDeviceConfig, mHandler, flags,
-                mDisplayDevice.getDisplayTokenLocked(),
-                mDisplayDevice.getDisplayDeviceInfoLocked());
-
-        mDisplayBrightnessController =
-                new DisplayBrightnessController(context, null,
-                        mDisplayId, mLogicalDisplay.getDisplayInfoLocked().brightnessDefault,
-                        brightnessSetting, () -> postBrightnessChangeRunnable(),
-                        new HandlerExecutor(mHandler), flags);
-
-        mBrightnessClamperController = mInjector.getBrightnessClamperController(
-                mHandler, modeChangeCallback::run,
-                new BrightnessClamperController.DisplayDeviceData(
-                mUniqueDisplayId,
-                mThermalBrightnessThrottlingDataId,
-                logicalDisplay.getPowerThrottlingDataIdLocked(),
-                mDisplayDeviceConfig), mContext, flags);
-        // Seed the cached brightness
-        saveBrightnessInfo(getScreenBrightnessSetting());
-        mAutomaticBrightnessStrategy =
-                mDisplayBrightnessController.getAutomaticBrightnessStrategy();
-
-        DisplayWhiteBalanceSettings displayWhiteBalanceSettings = null;
-        DisplayWhiteBalanceController displayWhiteBalanceController = null;
-        if (mDisplayId == Display.DEFAULT_DISPLAY) {
-            try {
-                displayWhiteBalanceController = mInjector.getDisplayWhiteBalanceController(
-                        mHandler, mSensorManager, resources);
-                displayWhiteBalanceSettings = new DisplayWhiteBalanceSettings(mContext, mHandler);
-                displayWhiteBalanceSettings.setCallbacks(this);
-                displayWhiteBalanceController.setCallbacks(this);
-            } catch (Exception e) {
-                Slog.e(mTag, "failed to set up display white-balance: " + e);
-            }
-        }
-        mDisplayWhiteBalanceSettings = displayWhiteBalanceSettings;
-        mDisplayWhiteBalanceController = displayWhiteBalanceController;
-
-        loadNitsRange(resources);
-
-        if (mDisplayId == Display.DEFAULT_DISPLAY) {
-            mCdsi = LocalServices.getService(ColorDisplayServiceInternal.class);
-            if (mCdsi != null) {
-                boolean active = mCdsi.setReduceBrightColorsListener(
-                        new ReduceBrightColorsListener() {
-                            @Override
-                            public void onReduceBrightColorsActivationChanged(boolean activated,
-                                    boolean userInitiated) {
-                                applyReduceBrightColorsSplineAdjustment();
-
-                            }
-
-                            @Override
-                            public void onReduceBrightColorsStrengthChanged(int strength) {
-                                applyReduceBrightColorsSplineAdjustment();
-                            }
-                        });
-                if (active) {
-                    applyReduceBrightColorsSplineAdjustment();
-                }
-            }
-        } else {
-            mCdsi = null;
-        }
-
-        setUpAutoBrightness(context, handler);
-
-        mColorFadeEnabled = mInjector.isColorFadeEnabled()
-                && !resources.getBoolean(
-                  com.android.internal.R.bool.config_displayColorFadeDisabled);
-        mColorFadeFadesConfig = resources.getBoolean(
-                R.bool.config_animateScreenLights);
-
-        mDisplayBlanksAfterDozeConfig = resources.getBoolean(
-                R.bool.config_displayBlanksAfterDoze);
-
-        mBrightnessBucketsInDozeConfig = resources.getBoolean(
-                R.bool.config_displayBrightnessBucketsInDoze);
-
-        mBootCompleted = bootCompleted;
-    }
-
-    private void applyReduceBrightColorsSplineAdjustment() {
-        mHandler.obtainMessage(MSG_UPDATE_RBC).sendToTarget();
-        sendUpdatePowerState();
-    }
-
-    private void handleRbcChanged() {
-        if (mAutomaticBrightnessController == null) {
-            return;
-        }
-
-        float[] adjustedNits = new float[mNitsRange.length];
-        for (int i = 0; i < mNitsRange.length; i++) {
-            adjustedNits[i] = mCdsi.getReduceBrightColorsAdjustedBrightnessNits(mNitsRange[i]);
-        }
-        mIsRbcActive = mCdsi.isReduceBrightColorsActivated();
-        mAutomaticBrightnessController.recalculateSplines(mIsRbcActive, adjustedNits);
-    }
-
-    /**
-     * Returns true if the proximity sensor screen-off function is available.
-     */
-    @Override
-    public boolean isProximitySensorAvailable() {
-        return mDisplayPowerProximityStateController.isProximitySensorAvailable();
-    }
-
-    /**
-     * Get the {@link BrightnessChangeEvent}s for the specified user.
-     *
-     * @param userId         userId to fetch data for
-     * @param includePackage if false will null out the package name in events
-     */
-    @Nullable
-    @Override
-    public ParceledListSlice<BrightnessChangeEvent> getBrightnessEvents(
-            @UserIdInt int userId, boolean includePackage) {
-        if (mBrightnessTracker == null) {
-            return null;
-        }
-        return mBrightnessTracker.getEvents(userId, includePackage);
-    }
-
-    @Override
-    public void onSwitchUser(@UserIdInt int newUserId) {
-        Message msg = mHandler.obtainMessage(MSG_SWITCH_USER, newUserId);
-        mHandler.sendMessage(msg);
-    }
-
-    private void handleOnSwitchUser(@UserIdInt int newUserId) {
-        handleSettingsChange(true /* userSwitch */);
-        handleBrightnessModeChange();
-        if (mBrightnessTracker != null) {
-            mBrightnessTracker.onSwitchUser(newUserId);
-        }
-    }
-
-    @Nullable
-    @Override
-    public ParceledListSlice<AmbientBrightnessDayStats> getAmbientBrightnessStats(
-            @UserIdInt int userId) {
-        if (mBrightnessTracker == null) {
-            return null;
-        }
-        return mBrightnessTracker.getAmbientBrightnessStats(userId);
-    }
-
-    /**
-     * Persist the brightness slider events and ambient brightness stats to disk.
-     */
-    @Override
-    public void persistBrightnessTrackerState() {
-        if (mBrightnessTracker != null) {
-            mBrightnessTracker.persistBrightnessTrackerState();
-        }
-    }
-
-    /**
-     * Requests a new power state.
-     * The controller makes a copy of the provided object and then
-     * begins adjusting the power state to match what was requested.
-     *
-     * @param request                  The requested power state.
-     * @param waitForNegativeProximity If true, issues a request to wait for
-     *                                 negative proximity before turning the screen back on,
-     *                                 assuming the screen
-     *                                 was turned off by the proximity sensor.
-     * @return True if display is ready, false if there are important changes that must
-     * be made asynchronously (such as turning the screen on), in which case the caller
-     * should grab a wake lock, watch for {@link DisplayPowerCallbacks#onStateChanged()}
-     * then try the request again later until the state converges.
-     */
-    public boolean requestPowerState(DisplayPowerRequest request,
-            boolean waitForNegativeProximity) {
-        if (DEBUG) {
-            Slog.d(mTag, "requestPowerState: "
-                    + request + ", waitForNegativeProximity=" + waitForNegativeProximity);
-        }
-
-        synchronized (mLock) {
-            if (mStopped) {
-                return true;
-            }
-
-            boolean changed = mDisplayPowerProximityStateController
-                    .setPendingWaitForNegativeProximityLocked(waitForNegativeProximity);
-
-            if (mPendingRequestLocked == null) {
-                mPendingRequestLocked = new DisplayPowerRequest(request);
-                changed = true;
-            } else if (!mPendingRequestLocked.equals(request)) {
-                mPendingRequestLocked.copyFrom(request);
-                changed = true;
-            }
-
-            if (changed) {
-                mDisplayReadyLocked = false;
-                if (!mPendingRequestChangedLocked) {
-                    mPendingRequestChangedLocked = true;
-                    sendUpdatePowerStateLocked();
-                }
-            }
-
-            return mDisplayReadyLocked;
-        }
-    }
-
-    @Override
-    public void overrideDozeScreenState(int displayState) {
-        mHandler.postAtTime(() -> {
-            if (mDisplayOffloadSession == null
-                    || !(DisplayOffloadSession.isSupportedOffloadState(displayState)
-                    || displayState == Display.STATE_UNKNOWN)) {
-                return;
-            }
-            mDisplayStateController.overrideDozeScreenState(displayState);
-            sendUpdatePowerState();
-        }, mClock.uptimeMillis());
-    }
-
-    @Override
-    public void setDisplayOffloadSession(DisplayOffloadSession session) {
-        if (session == mDisplayOffloadSession) {
-            return;
-        }
-        unblockScreenOnByDisplayOffload();
-        mDisplayOffloadSession = session;
-    }
-
-    @Override
-    public BrightnessConfiguration getDefaultBrightnessConfiguration() {
-        if (mAutomaticBrightnessController == null) {
-            return null;
-        }
-        return mAutomaticBrightnessController.getDefaultConfig();
-    }
-
-    /**
-     * Notified when the display is changed. We use this to apply any changes that might be needed
-     * when displays get swapped on foldable devices.  For example, different brightness properties
-     * of each display need to be properly reflected in AutomaticBrightnessController.
-     *
-     * Make sure DisplayManagerService.mSyncRoot lock is held when this is called
-     */
-    @Override
-    public void onDisplayChanged(HighBrightnessModeMetadata hbmMetadata, int leadDisplayId) {
-        mLeadDisplayId = leadDisplayId;
-        final DisplayDevice device = mLogicalDisplay.getPrimaryDisplayDeviceLocked();
-        if (device == null) {
-            Slog.wtf(mTag, "Display Device is null in DisplayPowerController2 for display: "
-                    + mLogicalDisplay.getDisplayIdLocked());
-            return;
-        }
-
-        final String uniqueId = device.getUniqueId();
-        final DisplayDeviceConfig config = device.getDisplayDeviceConfig();
-        final IBinder token = device.getDisplayTokenLocked();
-        final DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
-        final boolean isEnabled = mLogicalDisplay.isEnabledLocked();
-        final boolean isInTransition = mLogicalDisplay.isInTransitionLocked();
-        final boolean isDisplayInternal = mLogicalDisplay.getPrimaryDisplayDeviceLocked() != null
-                && mLogicalDisplay.getPrimaryDisplayDeviceLocked()
-                .getDisplayDeviceInfoLocked().type == Display.TYPE_INTERNAL;
-        final String thermalBrightnessThrottlingDataId =
-                mLogicalDisplay.getDisplayInfoLocked().thermalBrightnessThrottlingDataId;
-        final String powerThrottlingDataId =
-                mLogicalDisplay.getPowerThrottlingDataIdLocked();
-
-        mHandler.postAtTime(() -> {
-            boolean changed = false;
-            if (mDisplayDevice != device) {
-                changed = true;
-                mDisplayDevice = device;
-                mUniqueDisplayId = uniqueId;
-                mDisplayStatsId = mUniqueDisplayId.hashCode();
-                mDisplayDeviceConfig = config;
-                mThermalBrightnessThrottlingDataId = thermalBrightnessThrottlingDataId;
-                loadFromDisplayDeviceConfig(token, info, hbmMetadata);
-                mDisplayPowerProximityStateController.notifyDisplayDeviceChanged(config);
-
-                // Since the underlying display-device changed, we really don't know the
-                // last command that was sent to change it's state. Let's assume it is unknown so
-                // that we trigger a change immediately.
-                mPowerState.resetScreenState();
-            } else if (!Objects.equals(mThermalBrightnessThrottlingDataId,
-                    thermalBrightnessThrottlingDataId)) {
-                changed = true;
-                mThermalBrightnessThrottlingDataId = thermalBrightnessThrottlingDataId;
-                mBrightnessThrottler.loadThermalBrightnessThrottlingDataFromDisplayDeviceConfig(
-                        config.getThermalBrightnessThrottlingDataMapByThrottlingId(),
-                        mThermalBrightnessThrottlingDataId,
-                        mUniqueDisplayId);
-            }
-            if (mIsEnabled != isEnabled || mIsInTransition != isInTransition) {
-                changed = true;
-                mIsEnabled = isEnabled;
-                mIsInTransition = isInTransition;
-            }
-
-            mIsDisplayInternal = isDisplayInternal;
-            // using local variables here, when mBrightnessThrottler is removed,
-            // mThermalBrightnessThrottlingDataId could be removed as well
-            // changed = true will be not needed - clampers are maintaining their state and
-            // will call updatePowerState if needed.
-            mBrightnessClamperController.onDisplayChanged(
-                    new BrightnessClamperController.DisplayDeviceData(uniqueId,
-                        thermalBrightnessThrottlingDataId, powerThrottlingDataId, config));
-
-            if (changed) {
-                updatePowerState();
-            }
-        }, mClock.uptimeMillis());
-    }
-
-    /**
-     * Unregisters all listeners and interrupts all running threads; halting future work.
-     *
-     * This method should be called when the DisplayPowerController2 is no longer in use; i.e. when
-     * the {@link #mDisplayId display} has been removed.
-     */
-    @Override
-    public void stop() {
-        synchronized (mLock) {
-            clearDisplayBrightnessFollowersLocked();
-
-            mStopped = true;
-            Message msg = mHandler.obtainMessage(MSG_STOP);
-            mHandler.sendMessageAtTime(msg, mClock.uptimeMillis());
-
-            if (mAutomaticBrightnessController != null) {
-                mAutomaticBrightnessController.stop();
-            }
-
-            mDisplayBrightnessController.stop();
-
-            mContext.getContentResolver().unregisterContentObserver(mSettingsObserver);
-        }
-    }
-
-    private void loadFromDisplayDeviceConfig(IBinder token, DisplayDeviceInfo info,
-            HighBrightnessModeMetadata hbmMetadata) {
-        // All properties that depend on the associated DisplayDevice and the DDC must be
-        // updated here.
-        loadBrightnessRampRates();
-        loadNitsRange(mContext.getResources());
-        setUpAutoBrightness(mContext, mHandler);
-        reloadReduceBrightColours();
-        setAnimatorRampSpeeds(/* isIdleMode= */ false);
-
-        mBrightnessRangeController.loadFromConfig(hbmMetadata, token, info, mDisplayDeviceConfig);
-        mBrightnessThrottler.loadThermalBrightnessThrottlingDataFromDisplayDeviceConfig(
-                mDisplayDeviceConfig.getThermalBrightnessThrottlingDataMapByThrottlingId(),
-                mThermalBrightnessThrottlingDataId, mUniqueDisplayId);
-    }
-
-    private void sendUpdatePowerState() {
-        synchronized (mLock) {
-            sendUpdatePowerStateLocked();
-        }
-    }
-
-    @GuardedBy("mLock")
-    private void sendUpdatePowerStateLocked() {
-        if (!mStopped && !mPendingUpdatePowerStateLocked) {
-            mPendingUpdatePowerStateLocked = true;
-            Message msg = mHandler.obtainMessage(MSG_UPDATE_POWER_STATE);
-            mHandler.sendMessageAtTime(msg, mClock.uptimeMillis());
-        }
-    }
-
-    private void initialize(int displayState) {
-        mPowerState = mInjector.getDisplayPowerState(mBlanker,
-                mColorFadeEnabled ? new ColorFade(mDisplayId) : null, mDisplayId, displayState);
-
-        if (mColorFadeEnabled) {
-            mColorFadeOnAnimator = ObjectAnimator.ofFloat(
-                    mPowerState, DisplayPowerState.COLOR_FADE_LEVEL, 0.0f, 1.0f);
-            mColorFadeOnAnimator.setDuration(COLOR_FADE_ON_ANIMATION_DURATION_MILLIS);
-            mColorFadeOnAnimator.addListener(mAnimatorListener);
-
-            mColorFadeOffAnimator = ObjectAnimator.ofFloat(
-                    mPowerState, DisplayPowerState.COLOR_FADE_LEVEL, 1.0f, 0.0f);
-            mColorFadeOffAnimator.setDuration(COLOR_FADE_OFF_ANIMATION_DURATION_MILLIS);
-            mColorFadeOffAnimator.addListener(mAnimatorListener);
-        }
-
-        mScreenBrightnessRampAnimator = mInjector.getDualRampAnimator(mPowerState,
-                DisplayPowerState.SCREEN_BRIGHTNESS_FLOAT,
-                DisplayPowerState.SCREEN_SDR_BRIGHTNESS_FLOAT);
-        setAnimatorRampSpeeds(mAutomaticBrightnessController != null
-                && mAutomaticBrightnessController.isInIdleMode());
-        mScreenBrightnessRampAnimator.setListener(mRampAnimatorListener);
-
-        noteScreenState(mPowerState.getScreenState());
-        noteScreenBrightness(mPowerState.getScreenBrightness());
-
-        // Initialize all of the brightness tracking state
-        final float brightness = mDisplayBrightnessController.convertToAdjustedNits(
-                mPowerState.getScreenBrightness());
-        if (mBrightnessTracker != null && brightness >= PowerManager.BRIGHTNESS_MIN) {
-            mBrightnessTracker.start(brightness);
-        }
-
-        BrightnessSetting.BrightnessSettingListener brightnessSettingListener = brightnessValue -> {
-            Message msg = mHandler.obtainMessage(MSG_UPDATE_BRIGHTNESS, brightnessValue);
-            mHandler.sendMessageAtTime(msg, mClock.uptimeMillis());
-        };
-        mDisplayBrightnessController
-                .registerBrightnessSettingChangeListener(brightnessSettingListener);
-
-        mContext.getContentResolver().registerContentObserver(
-                Settings.System.getUriFor(Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ),
-                false /*notifyForDescendants*/, mSettingsObserver, UserHandle.USER_ALL);
-        mContext.getContentResolver().registerContentObserver(
-                Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_MODE),
-                false /*notifyForDescendants*/, mSettingsObserver, UserHandle.USER_ALL);
-        if (mFlags.areAutoBrightnessModesEnabled()) {
-            mContext.getContentResolver().registerContentObserver(
-                    Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_FOR_ALS),
-                    /* notifyForDescendants= */ false, mSettingsObserver, UserHandle.USER_CURRENT);
-        }
-        handleBrightnessModeChange();
-    }
-
-    private void setUpAutoBrightness(Context context, Handler handler) {
-        mUseSoftwareAutoBrightnessConfig = mDisplayDeviceConfig.isAutoBrightnessAvailable();
-
-        if (!mUseSoftwareAutoBrightnessConfig) {
-            return;
-        }
-
-        SparseArray<BrightnessMappingStrategy> brightnessMappers = new SparseArray<>();
-
-        BrightnessMappingStrategy defaultModeBrightnessMapper =
-                mInjector.getDefaultModeBrightnessMapper(context, mDisplayDeviceConfig,
-                        mDisplayWhiteBalanceController);
-        brightnessMappers.append(AUTO_BRIGHTNESS_MODE_DEFAULT,
-                defaultModeBrightnessMapper);
-
-        final boolean isIdleScreenBrightnessEnabled = context.getResources().getBoolean(
-                R.bool.config_enableIdleScreenBrightnessMode);
-        if (isIdleScreenBrightnessEnabled) {
-            BrightnessMappingStrategy idleModeBrightnessMapper =
-                    BrightnessMappingStrategy.create(context, mDisplayDeviceConfig,
-                            AUTO_BRIGHTNESS_MODE_IDLE,
-                            mDisplayWhiteBalanceController);
-            if (idleModeBrightnessMapper != null) {
-                brightnessMappers.append(AUTO_BRIGHTNESS_MODE_IDLE,
-                        idleModeBrightnessMapper);
-            }
-        }
-
-        BrightnessMappingStrategy dozeModeBrightnessMapper =
-                BrightnessMappingStrategy.create(context, mDisplayDeviceConfig,
-                        AUTO_BRIGHTNESS_MODE_DOZE, mDisplayWhiteBalanceController);
-        if (mFlags.areAutoBrightnessModesEnabled() && dozeModeBrightnessMapper != null) {
-            brightnessMappers.put(AUTO_BRIGHTNESS_MODE_DOZE, dozeModeBrightnessMapper);
-        }
-
-        float userLux = BrightnessMappingStrategy.INVALID_LUX;
-        float userNits = BrightnessMappingStrategy.INVALID_NITS;
-        if (mAutomaticBrightnessController != null) {
-            userLux = mAutomaticBrightnessController.getUserLux();
-            userNits = mAutomaticBrightnessController.getUserNits();
-        }
-
-        if (defaultModeBrightnessMapper != null) {
-            final float dozeScaleFactor = context.getResources().getFraction(
-                    R.fraction.config_screenAutoBrightnessDozeScaleFactor,
-                    1, 1);
-
-            // Ambient Lux - Active Mode Brightness Thresholds
-            float[] ambientBrighteningThresholds =
-                    mDisplayDeviceConfig.getAmbientBrighteningPercentages();
-            float[] ambientDarkeningThresholds =
-                    mDisplayDeviceConfig.getAmbientDarkeningPercentages();
-            float[] ambientBrighteningLevels =
-                    mDisplayDeviceConfig.getAmbientBrighteningLevels();
-            float[] ambientDarkeningLevels =
-                    mDisplayDeviceConfig.getAmbientDarkeningLevels();
-            float ambientDarkeningMinThreshold =
-                    mDisplayDeviceConfig.getAmbientLuxDarkeningMinThreshold();
-            float ambientBrighteningMinThreshold =
-                    mDisplayDeviceConfig.getAmbientLuxBrighteningMinThreshold();
-            HysteresisLevels ambientBrightnessThresholds = mInjector.getHysteresisLevels(
-                    ambientBrighteningThresholds, ambientDarkeningThresholds,
-                    ambientBrighteningLevels, ambientDarkeningLevels, ambientDarkeningMinThreshold,
-                    ambientBrighteningMinThreshold);
-
-            // Display - Active Mode Brightness Thresholds
-            float[] screenBrighteningThresholds =
-                    mDisplayDeviceConfig.getScreenBrighteningPercentages();
-            float[] screenDarkeningThresholds =
-                    mDisplayDeviceConfig.getScreenDarkeningPercentages();
-            float[] screenBrighteningLevels =
-                    mDisplayDeviceConfig.getScreenBrighteningLevels();
-            float[] screenDarkeningLevels =
-                    mDisplayDeviceConfig.getScreenDarkeningLevels();
-            float screenDarkeningMinThreshold =
-                    mDisplayDeviceConfig.getScreenDarkeningMinThreshold();
-            float screenBrighteningMinThreshold =
-                    mDisplayDeviceConfig.getScreenBrighteningMinThreshold();
-            HysteresisLevels screenBrightnessThresholds = mInjector.getHysteresisLevels(
-                    screenBrighteningThresholds, screenDarkeningThresholds,
-                    screenBrighteningLevels, screenDarkeningLevels, screenDarkeningMinThreshold,
-                    screenBrighteningMinThreshold, true);
-
-            // Ambient Lux - Idle Screen Brightness Thresholds
-            float ambientDarkeningMinThresholdIdle =
-                    mDisplayDeviceConfig.getAmbientLuxDarkeningMinThresholdIdle();
-            float ambientBrighteningMinThresholdIdle =
-                    mDisplayDeviceConfig.getAmbientLuxBrighteningMinThresholdIdle();
-            float[] ambientBrighteningThresholdsIdle =
-                    mDisplayDeviceConfig.getAmbientBrighteningPercentagesIdle();
-            float[] ambientDarkeningThresholdsIdle =
-                    mDisplayDeviceConfig.getAmbientDarkeningPercentagesIdle();
-            float[] ambientBrighteningLevelsIdle =
-                    mDisplayDeviceConfig.getAmbientBrighteningLevelsIdle();
-            float[] ambientDarkeningLevelsIdle =
-                    mDisplayDeviceConfig.getAmbientDarkeningLevelsIdle();
-            HysteresisLevels ambientBrightnessThresholdsIdle = mInjector.getHysteresisLevels(
-                    ambientBrighteningThresholdsIdle, ambientDarkeningThresholdsIdle,
-                    ambientBrighteningLevelsIdle, ambientDarkeningLevelsIdle,
-                    ambientDarkeningMinThresholdIdle, ambientBrighteningMinThresholdIdle);
-
-            // Display - Idle Screen Brightness Thresholds
-            float screenDarkeningMinThresholdIdle =
-                    mDisplayDeviceConfig.getScreenDarkeningMinThresholdIdle();
-            float screenBrighteningMinThresholdIdle =
-                    mDisplayDeviceConfig.getScreenBrighteningMinThresholdIdle();
-            float[] screenBrighteningThresholdsIdle =
-                    mDisplayDeviceConfig.getScreenBrighteningPercentagesIdle();
-            float[] screenDarkeningThresholdsIdle =
-                    mDisplayDeviceConfig.getScreenDarkeningPercentagesIdle();
-            float[] screenBrighteningLevelsIdle =
-                    mDisplayDeviceConfig.getScreenBrighteningLevelsIdle();
-            float[] screenDarkeningLevelsIdle =
-                    mDisplayDeviceConfig.getScreenDarkeningLevelsIdle();
-            HysteresisLevels screenBrightnessThresholdsIdle = mInjector.getHysteresisLevels(
-                    screenBrighteningThresholdsIdle, screenDarkeningThresholdsIdle,
-                    screenBrighteningLevelsIdle, screenDarkeningLevelsIdle,
-                    screenDarkeningMinThresholdIdle, screenBrighteningMinThresholdIdle);
-
-            long brighteningLightDebounce = mDisplayDeviceConfig
-                    .getAutoBrightnessBrighteningLightDebounce();
-            long darkeningLightDebounce = mDisplayDeviceConfig
-                    .getAutoBrightnessDarkeningLightDebounce();
-            long brighteningLightDebounceIdle = mDisplayDeviceConfig
-                    .getAutoBrightnessBrighteningLightDebounceIdle();
-            long darkeningLightDebounceIdle = mDisplayDeviceConfig
-                    .getAutoBrightnessDarkeningLightDebounceIdle();
-            boolean autoBrightnessResetAmbientLuxAfterWarmUp = context.getResources().getBoolean(
-                    R.bool.config_autoBrightnessResetAmbientLuxAfterWarmUp);
-
-            int lightSensorWarmUpTimeConfig = context.getResources().getInteger(
-                    R.integer.config_lightSensorWarmupTime);
-            int lightSensorRate = context.getResources().getInteger(
-                    R.integer.config_autoBrightnessLightSensorRate);
-            int initialLightSensorRate = context.getResources().getInteger(
-                    R.integer.config_autoBrightnessInitialLightSensorRate);
-            if (initialLightSensorRate == -1) {
-                initialLightSensorRate = lightSensorRate;
-            } else if (initialLightSensorRate > lightSensorRate) {
-                Slog.w(mTag, "Expected config_autoBrightnessInitialLightSensorRate ("
-                        + initialLightSensorRate + ") to be less than or equal to "
-                        + "config_autoBrightnessLightSensorRate (" + lightSensorRate + ").");
-            }
-
-            loadAmbientLightSensor();
-            // BrightnessTracker should only use one light sensor, we want to use the light sensor
-            // from the default display and not e.g. temporary displays when switching layouts.
-            if (mBrightnessTracker != null && mDisplayId == Display.DEFAULT_DISPLAY) {
-                mBrightnessTracker.setLightSensor(mLightSensor);
-            }
-
-            if (mAutomaticBrightnessController != null) {
-                mAutomaticBrightnessController.stop();
-            }
-            mAutomaticBrightnessController = mInjector.getAutomaticBrightnessController(
-                    this, handler.getLooper(), mSensorManager, mLightSensor,
-                    brightnessMappers, lightSensorWarmUpTimeConfig, PowerManager.BRIGHTNESS_MIN,
-                    PowerManager.BRIGHTNESS_MAX, dozeScaleFactor, lightSensorRate,
-                    initialLightSensorRate, brighteningLightDebounce, darkeningLightDebounce,
-                    brighteningLightDebounceIdle, darkeningLightDebounceIdle,
-                    autoBrightnessResetAmbientLuxAfterWarmUp, ambientBrightnessThresholds,
-                    screenBrightnessThresholds, ambientBrightnessThresholdsIdle,
-                    screenBrightnessThresholdsIdle, mContext, mBrightnessRangeController,
-                    mBrightnessThrottler, mDisplayDeviceConfig.getAmbientHorizonShort(),
-                    mDisplayDeviceConfig.getAmbientHorizonLong(), userLux, userNits);
-            mDisplayBrightnessController.setAutomaticBrightnessController(
-                    mAutomaticBrightnessController);
-
-            mAutomaticBrightnessStrategy
-                    .setAutomaticBrightnessController(mAutomaticBrightnessController);
-            mBrightnessEventRingBuffer =
-                    new RingBuffer<>(BrightnessEvent.class, RINGBUFFER_MAX);
-
-            if (mScreenOffBrightnessSensorController != null) {
-                mScreenOffBrightnessSensorController.stop();
-                mScreenOffBrightnessSensorController = null;
-            }
-            loadScreenOffBrightnessSensor();
-            int[] sensorValueToLux = mDisplayDeviceConfig.getScreenOffBrightnessSensorValueToLux();
-            if (mScreenOffBrightnessSensor != null && sensorValueToLux != null) {
-                mScreenOffBrightnessSensorController =
-                        mInjector.getScreenOffBrightnessSensorController(
-                                mSensorManager,
-                                mScreenOffBrightnessSensor,
-                                mHandler,
-                                SystemClock::uptimeMillis,
-                                sensorValueToLux,
-                                defaultModeBrightnessMapper);
-            }
-        } else {
-            mUseSoftwareAutoBrightnessConfig = false;
-        }
-    }
-
-    private void loadBrightnessRampRates() {
-        mBrightnessRampRateFastDecrease = mDisplayDeviceConfig.getBrightnessRampFastDecrease();
-        mBrightnessRampRateFastIncrease = mDisplayDeviceConfig.getBrightnessRampFastIncrease();
-        mBrightnessRampRateSlowDecrease = mDisplayDeviceConfig.getBrightnessRampSlowDecrease();
-        mBrightnessRampRateSlowIncrease = mDisplayDeviceConfig.getBrightnessRampSlowIncrease();
-        mBrightnessRampRateSlowDecreaseIdle =
-                mDisplayDeviceConfig.getBrightnessRampSlowDecreaseIdle();
-        mBrightnessRampRateSlowIncreaseIdle =
-                mDisplayDeviceConfig.getBrightnessRampSlowIncreaseIdle();
-        mBrightnessRampDecreaseMaxTimeMillis =
-                mDisplayDeviceConfig.getBrightnessRampDecreaseMaxMillis();
-        mBrightnessRampIncreaseMaxTimeMillis =
-                mDisplayDeviceConfig.getBrightnessRampIncreaseMaxMillis();
-        mBrightnessRampDecreaseMaxTimeIdleMillis =
-                mDisplayDeviceConfig.getBrightnessRampDecreaseMaxIdleMillis();
-        mBrightnessRampIncreaseMaxTimeIdleMillis =
-                mDisplayDeviceConfig.getBrightnessRampIncreaseMaxIdleMillis();
-    }
-
-    private void loadNitsRange(Resources resources) {
-        if (mDisplayDeviceConfig != null && mDisplayDeviceConfig.getNits() != null) {
-            mNitsRange = mDisplayDeviceConfig.getNits();
-        } else {
-            Slog.w(mTag, "Screen brightness nits configuration is unavailable; falling back");
-            mNitsRange = BrightnessMappingStrategy.getFloatArray(resources
-                    .obtainTypedArray(R.array.config_screenBrightnessNits));
-        }
-    }
-
-    private void reloadReduceBrightColours() {
-        if (mCdsi != null && mCdsi.isReduceBrightColorsActivated()) {
-            applyReduceBrightColorsSplineAdjustment();
-        }
-    }
-
-    @Override
-    public void setAutomaticScreenBrightnessMode(
-            @AutomaticBrightnessController.AutomaticBrightnessMode int mode) {
-        boolean isIdle = mode == AUTO_BRIGHTNESS_MODE_IDLE;
-        if (mAutomaticBrightnessController != null) {
-            mAutomaticBrightnessController.switchMode(mode);
-            setAnimatorRampSpeeds(isIdle);
-        }
-        Message msg = mHandler.obtainMessage();
-        msg.what = MSG_SET_DWBC_STRONG_MODE;
-        msg.arg1 = isIdle ? 1 : 0;
-        mHandler.sendMessageAtTime(msg, mClock.uptimeMillis());
-    }
-
-    private void setAnimatorRampSpeeds(boolean isIdle) {
-        if (mScreenBrightnessRampAnimator == null) {
-            return;
-        }
-        if (mFlags.isAdaptiveTone1Enabled() && isIdle) {
-            mScreenBrightnessRampAnimator.setAnimationTimeLimits(
-                    mBrightnessRampIncreaseMaxTimeIdleMillis,
-                    mBrightnessRampDecreaseMaxTimeIdleMillis);
-        } else {
-            mScreenBrightnessRampAnimator.setAnimationTimeLimits(
-                    mBrightnessRampIncreaseMaxTimeMillis,
-                    mBrightnessRampDecreaseMaxTimeMillis);
-        }
-    }
-
-    private final Animator.AnimatorListener mAnimatorListener = new Animator.AnimatorListener() {
-        @Override
-        public void onAnimationStart(Animator animation) {
-        }
-
-        @Override
-        public void onAnimationEnd(Animator animation) {
-            sendUpdatePowerState();
-        }
-
-        @Override
-        public void onAnimationRepeat(Animator animation) {
-        }
-
-        @Override
-        public void onAnimationCancel(Animator animation) {
-        }
-    };
-
-    private final RampAnimator.Listener mRampAnimatorListener = new RampAnimator.Listener() {
-        @Override
-        public void onAnimationEnd() {
-            sendUpdatePowerState();
-            Message msg = mHandler.obtainMessage(MSG_BRIGHTNESS_RAMP_DONE);
-            mHandler.sendMessageAtTime(msg, mClock.uptimeMillis());
-        }
-    };
-
-    /** Clean up all resources that are accessed via the {@link #mHandler} thread. */
-    private void cleanupHandlerThreadAfterStop() {
-        mDisplayPowerProximityStateController.cleanup();
-        mBrightnessRangeController.stop();
-        mBrightnessThrottler.stop();
-        mBrightnessClamperController.stop();
-        mHandler.removeCallbacksAndMessages(null);
-
-        // Release any outstanding wakelocks we're still holding because of pending messages.
-        mWakelockController.releaseAll();
-
-        final float brightness = mPowerState != null
-                ? mPowerState.getScreenBrightness()
-                : PowerManager.BRIGHTNESS_MIN;
-        reportStats(brightness);
-
-        if (mPowerState != null) {
-            mPowerState.stop();
-            mPowerState = null;
-        }
-
-        if (mScreenOffBrightnessSensorController != null) {
-            mScreenOffBrightnessSensorController.stop();
-        }
-
-        if (mDisplayWhiteBalanceController != null) {
-            mDisplayWhiteBalanceController.setEnabled(false);
-        }
-    }
-
-    // Call from handler thread
-    private void updatePowerState() {
-        Trace.traceBegin(Trace.TRACE_TAG_POWER,
-                "DisplayPowerController#updatePowerState");
-        updatePowerStateInternal();
-        Trace.traceEnd(Trace.TRACE_TAG_POWER);
-    }
-
-    private void updatePowerStateInternal() {
-        // Update the power state request.
-        final boolean mustNotify;
-        final int previousPolicy;
-        boolean mustInitialize = false;
-        mBrightnessReasonTemp.set(null);
-        mTempBrightnessEvent.reset();
-        SparseArray<DisplayPowerControllerInterface> displayBrightnessFollowers;
-        synchronized (mLock) {
-            if (mStopped) {
-                return;
-            }
-            mPendingUpdatePowerStateLocked = false;
-            if (mPendingRequestLocked == null) {
-                return; // wait until first actual power request
-            }
-
-            if (mPowerRequest == null) {
-                mPowerRequest = new DisplayPowerRequest(mPendingRequestLocked);
-                mDisplayPowerProximityStateController.updatePendingProximityRequestsLocked();
-                mPendingRequestChangedLocked = false;
-                mustInitialize = true;
-                // Assume we're on and bright until told otherwise, since that's the state we turn
-                // on in.
-                previousPolicy = DisplayPowerRequest.POLICY_BRIGHT;
-            } else if (mPendingRequestChangedLocked) {
-                previousPolicy = mPowerRequest.policy;
-                mPowerRequest.copyFrom(mPendingRequestLocked);
-                mDisplayPowerProximityStateController.updatePendingProximityRequestsLocked();
-                mPendingRequestChangedLocked = false;
-                mDisplayReadyLocked = false;
-            } else {
-                previousPolicy = mPowerRequest.policy;
-            }
-
-            mustNotify = !mDisplayReadyLocked;
-
-            displayBrightnessFollowers = mDisplayBrightnessFollowers.clone();
-        }
-
-        int state = mDisplayStateController
-                .updateDisplayState(mPowerRequest, mIsEnabled, mIsInTransition);
-
-        // Initialize things the first time the power state is changed.
-        if (mustInitialize) {
-            initialize(readyToUpdateDisplayState() ? state : Display.STATE_UNKNOWN);
-        }
-
-        // Animate the screen state change unless already animating.
-        // The transition may be deferred, so after this point we will use the
-        // actual state instead of the desired one.
-        animateScreenStateChange(state, mDisplayStateController.shouldPerformScreenOffTransition());
-        state = mPowerState.getScreenState();
-
-        // Switch to doze auto-brightness mode if needed
-        if (mFlags.areAutoBrightnessModesEnabled() && mAutomaticBrightnessController != null
-                && !mAutomaticBrightnessController.isInIdleMode()) {
-            setAutomaticScreenBrightnessMode(Display.isDozeState(state)
-                    ? AUTO_BRIGHTNESS_MODE_DOZE : AUTO_BRIGHTNESS_MODE_DEFAULT);
-        }
-
-        final boolean userSetBrightnessChanged = mDisplayBrightnessController
-                .updateUserSetScreenBrightness();
-
-        DisplayBrightnessState displayBrightnessState = mDisplayBrightnessController
-                .updateBrightness(mPowerRequest, state);
-        float brightnessState = displayBrightnessState.getBrightness();
-        float rawBrightnessState = displayBrightnessState.getBrightness();
-        mBrightnessReasonTemp.set(displayBrightnessState.getBrightnessReason());
-        boolean slowChange = displayBrightnessState.isSlowChange();
-        // custom transition duration
-        float customAnimationRate = displayBrightnessState.getCustomAnimationRate();
-
-        // Set up the ScreenOff controller used when coming out of SCREEN_OFF and the ALS sensor
-        // doesn't yet have a valid lux value to use with auto-brightness.
-        if (mScreenOffBrightnessSensorController != null) {
-            mScreenOffBrightnessSensorController
-                    .setLightSensorEnabled(displayBrightnessState.getShouldUseAutoBrightness()
-                    && mIsEnabled && (state == Display.STATE_OFF
-                    || (state == Display.STATE_DOZE
-                    && !mDisplayBrightnessController.isAllowAutoBrightnessWhileDozingConfig()))
-                    && mLeadDisplayId == Layout.NO_LEAD_DISPLAY);
-        }
-
-        // Take note if the short term model was already active before applying the current
-        // request changes.
-        final boolean wasShortTermModelActive =
-                mAutomaticBrightnessStrategy.isShortTermModelActive();
-        mAutomaticBrightnessStrategy.setAutoBrightnessState(state,
-                mDisplayBrightnessController.isAllowAutoBrightnessWhileDozingConfig(),
-                mBrightnessReasonTemp.getReason(), mPowerRequest.policy,
-                mDisplayBrightnessController.getLastUserSetScreenBrightness(),
-                userSetBrightnessChanged);
-
-        // If the brightness is already set then it's been overridden by something other than the
-        // user, or is a temporary adjustment.
-        boolean userInitiatedChange = (Float.isNaN(brightnessState))
-                && (mAutomaticBrightnessStrategy.getAutoBrightnessAdjustmentChanged()
-                || userSetBrightnessChanged);
-
-        mBrightnessRangeController.setAutoBrightnessEnabled(
-                mAutomaticBrightnessStrategy.isAutoBrightnessEnabled()
-                ? AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED
-                : mAutomaticBrightnessStrategy.isAutoBrightnessDisabledDueToDisplayOff()
-                        ? AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE
-                        : AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED);
-
-        boolean updateScreenBrightnessSetting =
-                displayBrightnessState.shouldUpdateScreenBrightnessSetting();
-        float currentBrightnessSetting = mDisplayBrightnessController.getCurrentBrightness();
-        // Apply auto-brightness.
-        int brightnessAdjustmentFlags = 0;
-        if (Float.isNaN(brightnessState)) {
-            if (mAutomaticBrightnessStrategy.isAutoBrightnessEnabled()) {
-                brightnessState = mAutomaticBrightnessStrategy.getAutomaticScreenBrightness(
-                        mTempBrightnessEvent);
-                if (BrightnessUtils.isValidBrightnessValue(brightnessState)
-                        || brightnessState == PowerManager.BRIGHTNESS_OFF_FLOAT) {
-                    rawBrightnessState = mAutomaticBrightnessController
-                            .getRawAutomaticScreenBrightness();
-                    brightnessState = clampScreenBrightness(brightnessState);
-                    // slowly adapt to auto-brightness
-                    // TODO(b/253226419): slowChange should be decided by strategy.updateBrightness
-                    slowChange = mAutomaticBrightnessStrategy.hasAppliedAutoBrightness()
-                            && !mAutomaticBrightnessStrategy.getAutoBrightnessAdjustmentChanged();
-                    brightnessAdjustmentFlags =
-                            mAutomaticBrightnessStrategy.getAutoBrightnessAdjustmentReasonsFlags();
-                    updateScreenBrightnessSetting = currentBrightnessSetting != brightnessState;
-                    mAutomaticBrightnessStrategy.setAutoBrightnessApplied(true);
-                    mBrightnessReasonTemp.setReason(BrightnessReason.REASON_AUTOMATIC);
-                    if (mScreenOffBrightnessSensorController != null) {
-                        mScreenOffBrightnessSensorController.setLightSensorEnabled(false);
-                    }
-                } else {
-                    mAutomaticBrightnessStrategy.setAutoBrightnessApplied(false);
-                }
-            }
-        } else {
-            // Any non-auto-brightness values such as override or temporary should still be subject
-            // to clamping so that they don't go beyond the current max as specified by HBM
-            // Controller.
-            brightnessState = clampScreenBrightness(brightnessState);
-            mAutomaticBrightnessStrategy.setAutoBrightnessApplied(false);
-        }
-
-        // Use default brightness when dozing unless overridden.
-        if ((Float.isNaN(brightnessState))
-                && Display.isDozeState(state)) {
-            rawBrightnessState = mScreenBrightnessDozeConfig;
-            brightnessState = clampScreenBrightness(rawBrightnessState);
-            mBrightnessReasonTemp.setReason(BrightnessReason.REASON_DOZE_DEFAULT);
-        }
-
-        // The ALS is not available yet - use the screen off sensor to determine the initial
-        // brightness
-        if (Float.isNaN(brightnessState) && mAutomaticBrightnessStrategy.isAutoBrightnessEnabled()
-                && mScreenOffBrightnessSensorController != null) {
-            rawBrightnessState =
-                    mScreenOffBrightnessSensorController.getAutomaticScreenBrightness();
-            brightnessState = rawBrightnessState;
-            if (BrightnessUtils.isValidBrightnessValue(brightnessState)) {
-                brightnessState = clampScreenBrightness(brightnessState);
-                updateScreenBrightnessSetting = mDisplayBrightnessController.getCurrentBrightness()
-                        != brightnessState;
-                mBrightnessReasonTemp.setReason(
-                        BrightnessReason.REASON_SCREEN_OFF_BRIGHTNESS_SENSOR);
-            }
-        }
-
-        // Apply manual brightness.
-        if (Float.isNaN(brightnessState)) {
-            rawBrightnessState = currentBrightnessSetting;
-            brightnessState = clampScreenBrightness(rawBrightnessState);
-            if (brightnessState != currentBrightnessSetting) {
-                // The manually chosen screen brightness is outside of the currently allowed
-                // range (i.e., high-brightness-mode), make sure we tell the rest of the system
-                // by updating the setting.
-                updateScreenBrightnessSetting = true;
-            }
-            mBrightnessReasonTemp.setReason(BrightnessReason.REASON_MANUAL);
-        }
-
-        float ambientLux = mAutomaticBrightnessController == null ? 0
-                : mAutomaticBrightnessController.getAmbientLux();
-        for (int i = 0; i < displayBrightnessFollowers.size(); i++) {
-            DisplayPowerControllerInterface follower = displayBrightnessFollowers.valueAt(i);
-            follower.setBrightnessToFollow(rawBrightnessState,
-                    mDisplayBrightnessController.convertToNits(rawBrightnessState),
-                    ambientLux, slowChange);
-        }
-
-        // Now that a desired brightness has been calculated, apply brightness throttling. The
-        // dimming and low power transformations that follow can only dim brightness further.
-        //
-        // We didn't do this earlier through brightness clamping because we need to know both
-        // unthrottled (unclamped/ideal) and throttled brightness levels for subsequent operations.
-        // Note throttling effectively changes the allowed brightness range, so, similarly to HBM,
-        // we broadcast this change through setting.
-        final float unthrottledBrightnessState = brightnessState;
-
-        DisplayBrightnessState clampedState = mBrightnessClamperController.clamp(mPowerRequest,
-                brightnessState, slowChange);
-
-        brightnessState = clampedState.getBrightness();
-        slowChange = clampedState.isSlowChange();
-        // faster rate wins, at this point customAnimationRate == -1, strategy does not control
-        // customAnimationRate. Should be revisited if strategy start setting this value
-        customAnimationRate = Math.max(customAnimationRate, clampedState.getCustomAnimationRate());
-        mBrightnessReasonTemp.addModifier(clampedState.getBrightnessReason().getModifier());
-
-        if (updateScreenBrightnessSetting) {
-            // Tell the rest of the system about the new brightness in case we had to change it
-            // for things like auto-brightness or high-brightness-mode. Note that we do this
-            // only considering maxBrightness (ignroing brightness modifiers like low power or dim)
-            // so that the slider accurately represents the full possible range,
-            // even if they range changes what it means in absolute terms.
-            mDisplayBrightnessController.updateScreenBrightnessSetting(
-                    Math.min(unthrottledBrightnessState, clampedState.getMaxBrightness()));
-        }
-
-        // The current brightness to use has been calculated at this point, and HbmController should
-        // be notified so that it can accurately calculate HDR or HBM levels. We specifically do it
-        // here instead of having HbmController listen to the brightness setting because certain
-        // brightness sources (such as an app override) are not saved to the setting, but should be
-        // reflected in HBM calculations.
-        mBrightnessRangeController.onBrightnessChanged(brightnessState, unthrottledBrightnessState,
-                mBrightnessClamperController.getBrightnessMaxReason());
-
-        // Animate the screen brightness when the screen is on or dozing.
-        // Skip the animation when the screen is off or suspended.
-        boolean brightnessAdjusted = false;
-        final boolean brightnessIsTemporary =
-                (mBrightnessReasonTemp.getReason() == BrightnessReason.REASON_TEMPORARY)
-                        || mAutomaticBrightnessStrategy
-                        .isTemporaryAutoBrightnessAdjustmentApplied();
-        if (!mPendingScreenOff) {
-            if (mSkipScreenOnBrightnessRamp) {
-                if (state == Display.STATE_ON) {
-                    if (mSkipRampState == RAMP_STATE_SKIP_NONE && mDozing) {
-                        mInitialAutoBrightness = brightnessState;
-                        mSkipRampState = RAMP_STATE_SKIP_INITIAL;
-                    } else if (mSkipRampState == RAMP_STATE_SKIP_INITIAL
-                            && mUseSoftwareAutoBrightnessConfig
-                            && !BrightnessSynchronizer.floatEquals(brightnessState,
-                            mInitialAutoBrightness)) {
-                        mSkipRampState = RAMP_STATE_SKIP_AUTOBRIGHT;
-                    } else if (mSkipRampState == RAMP_STATE_SKIP_AUTOBRIGHT) {
-                        mSkipRampState = RAMP_STATE_SKIP_NONE;
-                    }
-                } else {
-                    mSkipRampState = RAMP_STATE_SKIP_NONE;
-                }
-            }
-
-            final boolean initialRampSkip = (state == Display.STATE_ON && mSkipRampState
-                    != RAMP_STATE_SKIP_NONE) || mDisplayPowerProximityStateController
-                    .shouldSkipRampBecauseOfProximityChangeToNegative();
-            // While dozing, sometimes the brightness is split into buckets. Rather than animating
-            // through the buckets, which is unlikely to be smooth in the first place, just jump
-            // right to the suggested brightness.
-            final boolean hasBrightnessBuckets =
-                    Display.isDozeState(state) && mBrightnessBucketsInDozeConfig;
-            // If the color fade is totally covering the screen then we can change the backlight
-            // level without it being a noticeable jump since any actual content isn't yet visible.
-            final boolean isDisplayContentVisible =
-                    mColorFadeEnabled && mPowerState.getColorFadeLevel() == 1.0f;
-            // We only want to animate the brightness if it is between 0.0f and 1.0f.
-            // brightnessState can contain the values -1.0f and NaN, which we do not want to
-            // animate to. To avoid this, we check the value first.
-            // If the brightnessState is off (-1.0f) we still want to animate to the minimum
-            // brightness (0.0f) to accommodate for LED displays, which can appear bright to the
-            // user even when the display is all black. We also clamp here in case some
-            // transformations to the brightness have pushed it outside of the currently
-            // allowed range.
-            float animateValue = clampScreenBrightness(brightnessState);
-
-            // If there are any HDR layers on the screen, we have a special brightness value that we
-            // use instead. We still preserve the calculated brightness for Standard Dynamic Range
-            // (SDR) layers, but the main brightness value will be the one for HDR.
-            float sdrAnimateValue = animateValue;
-            // TODO(b/216365040): The decision to prevent HBM for HDR in low power mode should be
-            // done in HighBrightnessModeController.
-            if (mBrightnessRangeController.getHighBrightnessMode()
-                    == BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR
-                    && (mBrightnessReasonTemp.getModifier() & BrightnessReason.MODIFIER_DIMMED) == 0
-                    && (mBrightnessReasonTemp.getModifier() & BrightnessReason.MODIFIER_LOW_POWER)
-                    == 0) {
-                // We want to scale HDR brightness level with the SDR level, we also need to restore
-                // SDR brightness immediately when entering dim or low power mode.
-                animateValue = mBrightnessRangeController.getHdrBrightnessValue();
-                customAnimationRate = Math.max(customAnimationRate,
-                        mBrightnessRangeController.getHdrTransitionRate());
-                mBrightnessReasonTemp.addModifier(BrightnessReason.MODIFIER_HDR);
-            }
-
-            // if doze or suspend state is requested, we want to finish brightnes animation fast
-            // to allow state animation to start
-            if (mPowerRequest.policy == DisplayManagerInternal.DisplayPowerRequest.POLICY_DOZE
-                    && (mPowerRequest.dozeScreenState == Display.STATE_UNKNOWN  // dozing
-                    || mPowerRequest.dozeScreenState == Display.STATE_DOZE_SUSPEND
-                    || mPowerRequest.dozeScreenState == Display.STATE_ON_SUSPEND)) {
-                customAnimationRate = DisplayBrightnessState.CUSTOM_ANIMATION_RATE_NOT_SET;
-                slowChange = false;
-            }
-
-            final float currentBrightness = mPowerState.getScreenBrightness();
-            final float currentSdrBrightness = mPowerState.getSdrScreenBrightness();
-
-            if (BrightnessUtils.isValidBrightnessValue(animateValue)
-                    && (animateValue != currentBrightness
-                    || sdrAnimateValue != currentSdrBrightness)) {
-                boolean skipAnimation = initialRampSkip || hasBrightnessBuckets
-                        || !isDisplayContentVisible || brightnessIsTemporary;
-                final boolean isHdrOnlyChange = BrightnessSynchronizer.floatEquals(
-                        sdrAnimateValue, currentSdrBrightness);
-                if (mFlags.isFastHdrTransitionsEnabled() && !skipAnimation && isHdrOnlyChange) {
-                    // SDR brightness is unchanged, so animate quickly as this is only impacting
-                    // a likely minority amount of display content
-                    // ie, the highlights of an HDR video or UltraHDR image
-                    slowChange = false;
-
-                    // Going from HDR to no HDR; visually this should be a "no-op" anyway
-                    // as the remaining SDR content's brightness should be holding steady
-                    // due to the sdr brightness not shifting
-                    if (BrightnessSynchronizer.floatEquals(sdrAnimateValue, animateValue)) {
-                        skipAnimation = true;
-                    }
-
-                    // Going from no HDR to HDR; visually this is a significant scene change
-                    // and the animation just prevents advanced clients from doing their own
-                    // handling of enter/exit animations if they would like to do such a thing
-                    if (BrightnessSynchronizer.floatEquals(sdrAnimateValue, currentBrightness)) {
-                        skipAnimation = true;
-                    }
-                }
-                if (skipAnimation) {
-                    animateScreenBrightness(animateValue, sdrAnimateValue,
-                            SCREEN_ANIMATION_RATE_MINIMUM);
-                } else if (customAnimationRate > 0) {
-                    animateScreenBrightness(animateValue, sdrAnimateValue,
-                            customAnimationRate, /* ignoreAnimationLimits = */true);
-                } else {
-                    boolean isIncreasing = animateValue > currentBrightness;
-                    final float rampSpeed;
-                    final boolean idle = mAutomaticBrightnessController != null
-                            && mAutomaticBrightnessController.isInIdleMode();
-                    if (isIncreasing && slowChange) {
-                        rampSpeed = idle ? mBrightnessRampRateSlowIncreaseIdle
-                                : mBrightnessRampRateSlowIncrease;
-                    } else if (isIncreasing && !slowChange) {
-                        rampSpeed = mBrightnessRampRateFastIncrease;
-                    } else if (!isIncreasing && slowChange) {
-                        rampSpeed = idle ? mBrightnessRampRateSlowDecreaseIdle
-                                : mBrightnessRampRateSlowDecrease;
-                    } else {
-                        rampSpeed = mBrightnessRampRateFastDecrease;
-                    }
-                    animateScreenBrightness(animateValue, sdrAnimateValue, rampSpeed);
-                }
-            }
-
-            notifyBrightnessTrackerChanged(brightnessState, userInitiatedChange,
-                    wasShortTermModelActive, mAutomaticBrightnessStrategy.isAutoBrightnessEnabled(),
-                    brightnessIsTemporary, displayBrightnessState.getShouldUseAutoBrightness());
-
-            // We save the brightness info *after* the brightness setting has been changed and
-            // adjustments made so that the brightness info reflects the latest value.
-            brightnessAdjusted = saveBrightnessInfo(getScreenBrightnessSetting(),
-                    animateValue, clampedState);
-        } else {
-            brightnessAdjusted = saveBrightnessInfo(getScreenBrightnessSetting(), clampedState);
-        }
-
-        // Only notify if the brightness adjustment is not temporary (i.e. slider has been released)
-        if (brightnessAdjusted && !brightnessIsTemporary) {
-            postBrightnessChangeRunnable();
-        }
-
-        // Log any changes to what is currently driving the brightness setting.
-        if (!mBrightnessReasonTemp.equals(mBrightnessReason) || brightnessAdjustmentFlags != 0) {
-            Slog.v(mTag, "Brightness [" + brightnessState + "] reason changing to: '"
-                    + mBrightnessReasonTemp.toString(brightnessAdjustmentFlags)
-                    + "', previous reason: '" + mBrightnessReason + "'.");
-            mBrightnessReason.set(mBrightnessReasonTemp);
-        } else if (mBrightnessReasonTemp.getReason() == BrightnessReason.REASON_MANUAL
-                && userSetBrightnessChanged) {
-            Slog.v(mTag, "Brightness [" + brightnessState + "] manual adjustment.");
-        }
-
-
-        // Log brightness events when a detail of significance has changed. Generally this is the
-        // brightness itself changing, but also includes data like HBM cap, thermal throttling
-        // brightness cap, RBC state, etc.
-        mTempBrightnessEvent.setTime(System.currentTimeMillis());
-        mTempBrightnessEvent.setBrightness(brightnessState);
-        mTempBrightnessEvent.setPhysicalDisplayId(mUniqueDisplayId);
-        mTempBrightnessEvent.setReason(mBrightnessReason);
-        mTempBrightnessEvent.setHbmMax(mBrightnessRangeController.getCurrentBrightnessMax());
-        mTempBrightnessEvent.setHbmMode(mBrightnessRangeController.getHighBrightnessMode());
-        mTempBrightnessEvent.setFlags(mTempBrightnessEvent.getFlags()
-                | (mIsRbcActive ? BrightnessEvent.FLAG_RBC : 0)
-                | (mPowerRequest.lowPowerMode ? BrightnessEvent.FLAG_LOW_POWER_MODE : 0));
-        mTempBrightnessEvent.setRbcStrength(mCdsi != null
-                ? mCdsi.getReduceBrightColorsStrength() : -1);
-        mTempBrightnessEvent.setPowerFactor(mPowerRequest.screenLowPowerBrightnessFactor);
-        mTempBrightnessEvent.setWasShortTermModelActive(wasShortTermModelActive);
-        mTempBrightnessEvent.setDisplayBrightnessStrategyName(displayBrightnessState
-                .getDisplayBrightnessStrategyName());
-        mTempBrightnessEvent.setAutomaticBrightnessEnabled(
-                displayBrightnessState.getShouldUseAutoBrightness());
-        // Temporary is what we use during slider interactions. We avoid logging those so that
-        // we don't spam logcat when the slider is being used.
-        boolean tempToTempTransition =
-                mTempBrightnessEvent.getReason().getReason() == BrightnessReason.REASON_TEMPORARY
-                        && mLastBrightnessEvent.getReason().getReason()
-                        == BrightnessReason.REASON_TEMPORARY;
-        // Purely for dumpsys;
-        final boolean isRbcEvent =
-                mLastBrightnessEvent.isRbcEnabled() != mTempBrightnessEvent.isRbcEnabled();
-
-        if ((!mTempBrightnessEvent.equalsMainData(mLastBrightnessEvent) && !tempToTempTransition)
-                || brightnessAdjustmentFlags != 0) {
-            mTempBrightnessEvent.setInitialBrightness(mLastBrightnessEvent.getBrightness());
-            mLastBrightnessEvent.copyFrom(mTempBrightnessEvent);
-            BrightnessEvent newEvent = new BrightnessEvent(mTempBrightnessEvent);
-            // Adjustment flags (and user-set flag) only get added after the equality checks since
-            // they are transient.
-            newEvent.setAdjustmentFlags(brightnessAdjustmentFlags);
-            newEvent.setFlags(newEvent.getFlags() | (userSetBrightnessChanged
-                    ? BrightnessEvent.FLAG_USER_SET : 0));
-            Slog.i(mTag, newEvent.toString(/* includeTime= */ false));
-
-            if (userSetBrightnessChanged
-                    || newEvent.getReason().getReason() != BrightnessReason.REASON_TEMPORARY) {
-                logBrightnessEvent(newEvent, unthrottledBrightnessState);
-            }
-            if (mBrightnessEventRingBuffer != null) {
-                mBrightnessEventRingBuffer.append(newEvent);
-            }
-            if (isRbcEvent) {
-                mRbcEventRingBuffer.append(newEvent);
-            }
-
-        }
-
-        // Update display white-balance.
-        if (mDisplayWhiteBalanceController != null) {
-            if (state == Display.STATE_ON && mDisplayWhiteBalanceSettings.isEnabled()) {
-                mDisplayWhiteBalanceController.setEnabled(true);
-                mDisplayWhiteBalanceController.updateDisplayColorTemperature();
-            } else {
-                mDisplayWhiteBalanceController.setEnabled(false);
-            }
-        }
-
-        // Determine whether the display is ready for use in the newly requested state.
-        // Note that we do not wait for the brightness ramp animation to complete before
-        // reporting the display is ready because we only need to ensure the screen is in the
-        // right power state even as it continues to converge on the desired brightness.
-        final boolean ready = mPendingScreenOnUnblocker == null
-                && mPendingScreenOnUnblockerByDisplayOffload == null
-                && (!mColorFadeEnabled || (!mColorFadeOnAnimator.isStarted()
-                        && !mColorFadeOffAnimator.isStarted()))
-                && mPowerState.waitUntilClean(mCleanListener);
-        final boolean finished = ready
-                && !mScreenBrightnessRampAnimator.isAnimating();
-
-        // Notify policy about screen turned on.
-        if (ready && state != Display.STATE_OFF
-                && mReportedScreenStateToPolicy == REPORTED_TO_POLICY_SCREEN_TURNING_ON) {
-            setReportedScreenState(REPORTED_TO_POLICY_SCREEN_ON);
-            mWindowManagerPolicy.screenTurnedOn(mDisplayId);
-        }
-
-        // Grab a wake lock if we have unfinished business.
-        if (!finished) {
-            mWakelockController.acquireWakelock(WakelockController.WAKE_LOCK_UNFINISHED_BUSINESS);
-        }
-
-        // Notify the power manager when ready.
-        if (ready && mustNotify) {
-            // Send state change.
-            synchronized (mLock) {
-                if (!mPendingRequestChangedLocked) {
-                    mDisplayReadyLocked = true;
-
-                    if (DEBUG) {
-                        Slog.d(mTag, "Display ready!");
-                    }
-                }
-            }
-            sendOnStateChangedWithWakelock();
-        }
-
-        // Release the wake lock when we have no unfinished business.
-        if (finished) {
-            mWakelockController.releaseWakelock(WakelockController.WAKE_LOCK_UNFINISHED_BUSINESS);
-        }
-
-        // Record if dozing for future comparison.
-        mDozing = state != Display.STATE_ON;
-
-        if (previousPolicy != mPowerRequest.policy) {
-            logDisplayPolicyChanged(mPowerRequest.policy);
-        }
-    }
-
-    private void setDwbcOverride(float cct) {
-        if (mDisplayWhiteBalanceController != null) {
-            mDisplayWhiteBalanceController.setAmbientColorTemperatureOverride(cct);
-            // The ambient color temperature override is only applied when the ambient color
-            // temperature changes or is updated, so it doesn't necessarily change the screen color
-            // temperature immediately. So, let's make it!
-            // We can call this directly, since we're already on the handler thread.
-            updatePowerState();
-        }
-    }
-
-    private void setDwbcStrongMode(int arg) {
-        if (mDisplayWhiteBalanceController != null) {
-            final boolean isIdle = (arg == 1);
-            mDisplayWhiteBalanceController.setStrongModeEnabled(isIdle);
-        }
-    }
-
-    private void setDwbcLoggingEnabled(int arg) {
-        if (mDisplayWhiteBalanceController != null) {
-            final boolean enabled = (arg == 1);
-            mDisplayWhiteBalanceController.setLoggingEnabled(enabled);
-            mDisplayWhiteBalanceSettings.setLoggingEnabled(enabled);
-        }
-    }
-
-    @Override
-    public void updateBrightness() {
-        sendUpdatePowerState();
-    }
-
-    /**
-     * Ignores the proximity sensor until the sensor state changes, but only if the sensor is
-     * currently enabled and forcing the screen to be dark.
-     */
-    @Override
-    public void ignoreProximitySensorUntilChanged() {
-        mDisplayPowerProximityStateController.ignoreProximitySensorUntilChanged();
-    }
-
-    @Override
-    public void setBrightnessConfiguration(BrightnessConfiguration c,
-            boolean shouldResetShortTermModel) {
-        Message msg = mHandler.obtainMessage(MSG_CONFIGURE_BRIGHTNESS,
-                shouldResetShortTermModel ? 1 : 0, /* unused */ 0, c);
-        msg.sendToTarget();
-    }
-
-    @Override
-    public void setTemporaryBrightness(float brightness) {
-        Message msg = mHandler.obtainMessage(MSG_SET_TEMPORARY_BRIGHTNESS,
-                Float.floatToIntBits(brightness), 0 /*unused*/);
-        msg.sendToTarget();
-    }
-
-    @Override
-    public void setTemporaryAutoBrightnessAdjustment(float adjustment) {
-        Message msg = mHandler.obtainMessage(MSG_SET_TEMPORARY_AUTO_BRIGHTNESS_ADJUSTMENT,
-                Float.floatToIntBits(adjustment), 0 /*unused*/);
-        msg.sendToTarget();
-    }
-
-    @Override
-    public void setBrightnessFromOffload(float brightness) {
-        Message msg = mHandler.obtainMessage(MSG_SET_BRIGHTNESS_FROM_OFFLOAD,
-                Float.floatToIntBits(brightness), 0 /*unused*/);
-        mHandler.sendMessageAtTime(msg, mClock.uptimeMillis());
-    }
-
-    @Override
-    public BrightnessInfo getBrightnessInfo() {
-        synchronized (mCachedBrightnessInfo) {
-            return new BrightnessInfo(
-                    mCachedBrightnessInfo.brightness.value,
-                    mCachedBrightnessInfo.adjustedBrightness.value,
-                    mCachedBrightnessInfo.brightnessMin.value,
-                    mCachedBrightnessInfo.brightnessMax.value,
-                    mCachedBrightnessInfo.hbmMode.value,
-                    mCachedBrightnessInfo.hbmTransitionPoint.value,
-                    mCachedBrightnessInfo.brightnessMaxReason.value);
-        }
-    }
-
-    @Override
-    public void onBootCompleted() {
-        Message msg = mHandler.obtainMessage(MSG_BOOT_COMPLETED);
-        mHandler.sendMessageAtTime(msg, mClock.uptimeMillis());
-    }
-
-    private boolean saveBrightnessInfo(float brightness) {
-        return saveBrightnessInfo(brightness, /* state= */ null);
-    }
-
-    private boolean saveBrightnessInfo(float brightness, @Nullable DisplayBrightnessState state) {
-        return saveBrightnessInfo(brightness, brightness, state);
-    }
-
-    private boolean saveBrightnessInfo(float brightness, float adjustedBrightness,
-            @Nullable DisplayBrightnessState state) {
-        synchronized (mCachedBrightnessInfo) {
-            float stateMax = state != null ? state.getMaxBrightness() : PowerManager.BRIGHTNESS_MAX;
-            final float minBrightness = Math.min(
-                    mBrightnessRangeController.getCurrentBrightnessMin(), stateMax);
-            final float maxBrightness = Math.min(
-                    mBrightnessRangeController.getCurrentBrightnessMax(), stateMax);
-            boolean changed = false;
-
-            changed |=
-                    mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.brightness,
-                            brightness);
-            changed |=
-                    mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.adjustedBrightness,
-                            adjustedBrightness);
-            changed |=
-                    mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.brightnessMin,
-                            minBrightness);
-            changed |=
-                    mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.brightnessMax,
-                            maxBrightness);
-            changed |=
-                    mCachedBrightnessInfo.checkAndSetInt(mCachedBrightnessInfo.hbmMode,
-                            mBrightnessRangeController.getHighBrightnessMode());
-            changed |=
-                    mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.hbmTransitionPoint,
-                            mBrightnessRangeController.getTransitionPoint());
-            changed |=
-                    mCachedBrightnessInfo.checkAndSetInt(mCachedBrightnessInfo.brightnessMaxReason,
-                            mBrightnessClamperController.getBrightnessMaxReason());
-
-            return changed;
-        }
-    }
-
-    void postBrightnessChangeRunnable() {
-        if (!mHandler.hasCallbacks(mOnBrightnessChangeRunnable)) {
-            mHandler.post(mOnBrightnessChangeRunnable);
-        }
-    }
-
-    private HighBrightnessModeController createHbmControllerLocked(
-            HighBrightnessModeMetadata hbmMetadata, Runnable modeChangeCallback) {
-        final DisplayDeviceConfig ddConfig = mDisplayDevice.getDisplayDeviceConfig();
-        final IBinder displayToken = mDisplayDevice.getDisplayTokenLocked();
-        final String displayUniqueId = mDisplayDevice.getUniqueId();
-        final DisplayDeviceConfig.HighBrightnessModeData hbmData =
-                ddConfig != null ? ddConfig.getHighBrightnessModeData() : null;
-        final DisplayDeviceInfo info = mDisplayDevice.getDisplayDeviceInfoLocked();
-        return mInjector.getHighBrightnessModeController(mHandler, info.width, info.height,
-                displayToken, displayUniqueId, PowerManager.BRIGHTNESS_MIN,
-                PowerManager.BRIGHTNESS_MAX, hbmData, (sdrBrightness, maxDesiredHdrSdrRatio) ->
-                        mDisplayDeviceConfig.getHdrBrightnessFromSdr(sdrBrightness,
-                                maxDesiredHdrSdrRatio), modeChangeCallback, hbmMetadata, mContext);
-    }
-
-    private BrightnessThrottler createBrightnessThrottlerLocked() {
-        final DisplayDevice device = mLogicalDisplay.getPrimaryDisplayDeviceLocked();
-        final DisplayDeviceConfig ddConfig = device.getDisplayDeviceConfig();
-        return new BrightnessThrottler(mHandler,
-                () -> {
-                    sendUpdatePowerState();
-                    postBrightnessChangeRunnable();
-                }, mUniqueDisplayId,
-                mLogicalDisplay.getDisplayInfoLocked().thermalBrightnessThrottlingDataId,
-                ddConfig.getThermalBrightnessThrottlingDataMapByThrottlingId());
-    }
-
-    private void blockScreenOn() {
-        if (mPendingScreenOnUnblocker == null) {
-            Trace.asyncTraceBegin(Trace.TRACE_TAG_POWER, SCREEN_ON_BLOCKED_TRACE_NAME, 0);
-            mPendingScreenOnUnblocker = new ScreenOnUnblocker();
-            mScreenOnBlockStartRealTime = SystemClock.elapsedRealtime();
-            Slog.i(mTag, "Blocking screen on until initial contents have been drawn.");
-        }
-    }
-
-    private void unblockScreenOn() {
-        if (mPendingScreenOnUnblocker != null) {
-            mPendingScreenOnUnblocker = null;
-            long delay = SystemClock.elapsedRealtime() - mScreenOnBlockStartRealTime;
-            Slog.i(mTag, "Unblocked screen on after " + delay + " ms");
-            Trace.asyncTraceEnd(Trace.TRACE_TAG_POWER, SCREEN_ON_BLOCKED_TRACE_NAME, 0);
-        }
-    }
-
-    private void blockScreenOff() {
-        if (mPendingScreenOffUnblocker == null) {
-            Trace.asyncTraceBegin(Trace.TRACE_TAG_POWER, SCREEN_OFF_BLOCKED_TRACE_NAME, 0);
-            mPendingScreenOffUnblocker = new ScreenOffUnblocker();
-            mScreenOffBlockStartRealTime = SystemClock.elapsedRealtime();
-            Slog.i(mTag, "Blocking screen off");
-        }
-    }
-
-    private void unblockScreenOff() {
-        if (mPendingScreenOffUnblocker != null) {
-            mPendingScreenOffUnblocker = null;
-            long delay = SystemClock.elapsedRealtime() - mScreenOffBlockStartRealTime;
-            Slog.i(mTag, "Unblocked screen off after " + delay + " ms");
-            Trace.asyncTraceEnd(Trace.TRACE_TAG_POWER, SCREEN_OFF_BLOCKED_TRACE_NAME, 0);
-        }
-    }
-
-    private void blockScreenOnByDisplayOffload(DisplayOffloadSession displayOffloadSession) {
-        if (mPendingScreenOnUnblockerByDisplayOffload != null || displayOffloadSession == null) {
-            return;
-        }
-        mScreenTurningOnWasBlockedByDisplayOffload = true;
-
-        Trace.asyncTraceBegin(
-                Trace.TRACE_TAG_POWER, SCREEN_ON_BLOCKED_BY_DISPLAYOFFLOAD_TRACE_NAME, 0);
-        mScreenOnBlockByDisplayOffloadStartRealTime = SystemClock.elapsedRealtime();
-
-        mPendingScreenOnUnblockerByDisplayOffload =
-                () -> onDisplayOffloadUnblockScreenOn(displayOffloadSession);
-        if (!displayOffloadSession.blockScreenOn(mPendingScreenOnUnblockerByDisplayOffload)) {
-            mPendingScreenOnUnblockerByDisplayOffload = null;
-            long delay =
-                    SystemClock.elapsedRealtime() - mScreenOnBlockByDisplayOffloadStartRealTime;
-            Slog.w(mTag, "Tried blocking screen on for offloading but failed. So, end trace after "
-                    + delay + " ms.");
-            Trace.asyncTraceEnd(
-                    Trace.TRACE_TAG_POWER, SCREEN_ON_BLOCKED_BY_DISPLAYOFFLOAD_TRACE_NAME, 0);
-            return;
-        }
-        Slog.i(mTag, "Blocking screen on for offloading.");
-    }
-
-    private void onDisplayOffloadUnblockScreenOn(DisplayOffloadSession displayOffloadSession) {
-        Message msg = mHandler.obtainMessage(MSG_OFFLOADING_SCREEN_ON_UNBLOCKED,
-                displayOffloadSession);
-        mHandler.sendMessage(msg);
-    }
-
-    private void unblockScreenOnByDisplayOffload() {
-        if (mPendingScreenOnUnblockerByDisplayOffload == null) {
-            return;
-        }
-        mPendingScreenOnUnblockerByDisplayOffload = null;
-        long delay = SystemClock.elapsedRealtime() - mScreenOnBlockByDisplayOffloadStartRealTime;
-        Slog.i(mTag, "Unblocked screen on for offloading after " + delay + " ms");
-        Trace.asyncTraceEnd(
-                Trace.TRACE_TAG_POWER, SCREEN_ON_BLOCKED_BY_DISPLAYOFFLOAD_TRACE_NAME, 0);
-    }
-
-    private boolean setScreenState(int state) {
-        return setScreenState(state, false /*reportOnly*/);
-    }
-
-    private boolean setScreenState(int state, boolean reportOnly) {
-        final boolean isOff = (state == Display.STATE_OFF);
-        final boolean isOn = (state == Display.STATE_ON);
-        final boolean changed = mPowerState.getScreenState() != state;
-
-        // If the screen is turning on, give displayoffload a chance to do something before the
-        // screen actually turns on.
-        // TODO(b/316941732): add tests for this displayoffload screen-on blocker.
-        if (isOn && changed && !mScreenTurningOnWasBlockedByDisplayOffload) {
-            blockScreenOnByDisplayOffload(mDisplayOffloadSession);
-        } else if (!isOn && mScreenTurningOnWasBlockedByDisplayOffload) {
-            // No longer turning screen on, so unblock previous screen on blocking immediately.
-            unblockScreenOnByDisplayOffload();
-            mScreenTurningOnWasBlockedByDisplayOffload = false;
-        }
-
-        if (changed || mReportedScreenStateToPolicy == REPORTED_TO_POLICY_UNREPORTED) {
-            // If we are trying to turn screen off, give policy a chance to do something before we
-            // actually turn the screen off.
-            if (isOff && !mDisplayPowerProximityStateController.isScreenOffBecauseOfProximity()) {
-                if (mReportedScreenStateToPolicy == REPORTED_TO_POLICY_SCREEN_ON
-                        || mReportedScreenStateToPolicy == REPORTED_TO_POLICY_UNREPORTED) {
-                    setReportedScreenState(REPORTED_TO_POLICY_SCREEN_TURNING_OFF);
-                    blockScreenOff();
-                    mWindowManagerPolicy.screenTurningOff(mDisplayId, mPendingScreenOffUnblocker);
-                    unblockScreenOff();
-                } else if (mPendingScreenOffUnblocker != null) {
-                    // Abort doing the state change until screen off is unblocked.
-                    return false;
-                }
-            }
-
-            if (!reportOnly && changed && readyToUpdateDisplayState()
-                    && mPendingScreenOffUnblocker == null
-                    && mPendingScreenOnUnblockerByDisplayOffload == null) {
-                Trace.traceCounter(Trace.TRACE_TAG_POWER, "ScreenState", state);
-
-                String propertyKey = "debug.tracing.screen_state";
-                String propertyValue = String.valueOf(state);
-                try {
-                    // TODO(b/153319140) remove when we can get this from the above trace invocation
-                    SystemProperties.set(propertyKey, propertyValue);
-                } catch (RuntimeException e) {
-                    Slog.e(mTag, "Failed to set a system property: key=" + propertyKey
-                            + " value=" + propertyValue + " " + e.getMessage());
-                }
-
-                mPowerState.setScreenState(state);
-                // Tell battery stats about the transition.
-                noteScreenState(state);
-            }
-        }
-
-        // Tell the window manager policy when the screen is turned off or on unless it's due
-        // to the proximity sensor.  We temporarily block turning the screen on until the
-        // window manager is ready by leaving a black surface covering the screen.
-        // This surface is essentially the final state of the color fade animation and
-        // it is only removed once the window manager tells us that the activity has
-        // finished drawing underneath.
-        if (isOff && mReportedScreenStateToPolicy != REPORTED_TO_POLICY_SCREEN_OFF
-                && !mDisplayPowerProximityStateController.isScreenOffBecauseOfProximity()) {
-            setReportedScreenState(REPORTED_TO_POLICY_SCREEN_OFF);
-            unblockScreenOn();
-            mWindowManagerPolicy.screenTurnedOff(mDisplayId, mIsInTransition);
-        } else if (!isOff
-                && mReportedScreenStateToPolicy == REPORTED_TO_POLICY_SCREEN_TURNING_OFF) {
-
-            // We told policy already that screen was turning off, but now we changed our minds.
-            // Complete the full state transition on -> turningOff -> off.
-            unblockScreenOff();
-            mWindowManagerPolicy.screenTurnedOff(mDisplayId, mIsInTransition);
-            setReportedScreenState(REPORTED_TO_POLICY_SCREEN_OFF);
-        }
-        if (!isOff
-                && (mReportedScreenStateToPolicy == REPORTED_TO_POLICY_SCREEN_OFF
-                || mReportedScreenStateToPolicy == REPORTED_TO_POLICY_UNREPORTED)) {
-            setReportedScreenState(REPORTED_TO_POLICY_SCREEN_TURNING_ON);
-            if (mPowerState.getColorFadeLevel() == 0.0f) {
-                blockScreenOn();
-            } else {
-                unblockScreenOn();
-            }
-            mWindowManagerPolicy.screenTurningOn(mDisplayId, mPendingScreenOnUnblocker);
-        }
-
-        // Return true if the screen isn't blocked.
-        return mPendingScreenOnUnblocker == null
-                && mPendingScreenOnUnblockerByDisplayOffload == null;
-    }
-
-    private void setReportedScreenState(int state) {
-        Trace.traceCounter(Trace.TRACE_TAG_POWER, "ReportedScreenStateToPolicy", state);
-        mReportedScreenStateToPolicy = state;
-        if (state == REPORTED_TO_POLICY_SCREEN_ON) {
-            mScreenTurningOnWasBlockedByDisplayOffload = false;
-        }
-    }
-
-    private void loadAmbientLightSensor() {
-        final int fallbackType = mDisplayId == Display.DEFAULT_DISPLAY
-                ? Sensor.TYPE_LIGHT : SensorUtils.NO_FALLBACK;
-        mLightSensor = SensorUtils.findSensor(mSensorManager,
-                mDisplayDeviceConfig.getAmbientLightSensor(), fallbackType);
-    }
-
-    private void loadScreenOffBrightnessSensor() {
-        mScreenOffBrightnessSensor = SensorUtils.findSensor(mSensorManager,
-                mDisplayDeviceConfig.getScreenOffBrightnessSensor(), SensorUtils.NO_FALLBACK);
-    }
-
-    private float clampScreenBrightness(float value) {
-        if (Float.isNaN(value)) {
-            value = PowerManager.BRIGHTNESS_MIN;
-        }
-        return MathUtils.constrain(value, mBrightnessRangeController.getCurrentBrightnessMin(),
-                mBrightnessRangeController.getCurrentBrightnessMax());
-    }
-
-    private void animateScreenBrightness(float target, float sdrTarget, float rate) {
-        animateScreenBrightness(target, sdrTarget, rate, /* ignoreAnimationLimits = */false);
-    }
-
-    private void animateScreenBrightness(float target, float sdrTarget, float rate,
-            boolean ignoreAnimationLimits) {
-        if (DEBUG) {
-            Slog.d(mTag, "Animating brightness: target=" + target + ", sdrTarget=" + sdrTarget
-                    + ", rate=" + rate);
-        }
-        if (mScreenBrightnessRampAnimator.animateTo(target, sdrTarget, rate,
-                ignoreAnimationLimits)) {
-            Trace.traceCounter(Trace.TRACE_TAG_POWER, "TargetScreenBrightness", (int) target);
-
-            String propertyKey = "debug.tracing.screen_brightness";
-            String propertyValue = String.valueOf(target);
-            try {
-                // TODO(b/153319140) remove when we can get this from the above trace invocation
-                SystemProperties.set(propertyKey, propertyValue);
-            } catch (RuntimeException e) {
-                Slog.e(mTag, "Failed to set a system property: key=" + propertyKey
-                        + " value=" + propertyValue + " " + e.getMessage());
-            }
-
-            noteScreenBrightness(target);
-        }
-    }
-
-    private void animateScreenStateChange(int target, boolean performScreenOffTransition) {
-        // If there is already an animation in progress, don't interfere with it.
-        if (mColorFadeEnabled
-                && (mColorFadeOnAnimator.isStarted() || mColorFadeOffAnimator.isStarted())) {
-            if (target != Display.STATE_ON) {
-                return;
-            }
-            // If display state changed to on, proceed and stop the color fade and turn screen on.
-            mPendingScreenOff = false;
-        }
-
-        if (mDisplayBlanksAfterDozeConfig
-                && Display.isDozeState(mPowerState.getScreenState())
-                && !Display.isDozeState(target)) {
-            // Skip the screen off animation and add a black surface to hide the
-            // contents of the screen.
-            mPowerState.prepareColorFade(mContext,
-                    mColorFadeFadesConfig ? ColorFade.MODE_FADE : ColorFade.MODE_WARM_UP);
-            if (mColorFadeOffAnimator != null) {
-                mColorFadeOffAnimator.end();
-            }
-            // Some display hardware will blank itself on the transition between doze and non-doze
-            // but still on display states. In this case we want to report to policy that the
-            // display has turned off so it can prepare the appropriate power on animation, but we
-            // don't want to actually transition to the fully off state since that takes
-            // significantly longer to transition from.
-            setScreenState(Display.STATE_OFF, target != Display.STATE_OFF /*reportOnly*/);
-        }
-
-        // If we were in the process of turning off the screen but didn't quite
-        // finish.  Then finish up now to prevent a jarring transition back
-        // to screen on if we skipped blocking screen on as usual.
-        if (mPendingScreenOff && target != Display.STATE_OFF) {
-            setScreenState(Display.STATE_OFF);
-            mPendingScreenOff = false;
-            mPowerState.dismissColorFadeResources();
-        }
-
-        if (target == Display.STATE_ON) {
-            // Want screen on.  The contents of the screen may not yet
-            // be visible if the color fade has not been dismissed because
-            // its last frame of animation is solid black.
-            if (!setScreenState(Display.STATE_ON)) {
-                return; // screen on blocked
-            }
-            if (USE_COLOR_FADE_ON_ANIMATION && mColorFadeEnabled && mPowerRequest.isBrightOrDim()) {
-                // Perform screen on animation.
-                if (mPowerState.getColorFadeLevel() == 1.0f) {
-                    mPowerState.dismissColorFade();
-                } else if (mPowerState.prepareColorFade(mContext,
-                        mColorFadeFadesConfig
-                                ? ColorFade.MODE_FADE : ColorFade.MODE_WARM_UP)) {
-                    mColorFadeOnAnimator.start();
-                } else {
-                    mColorFadeOnAnimator.end();
-                }
-            } else {
-                // Skip screen on animation.
-                mPowerState.setColorFadeLevel(1.0f);
-                mPowerState.dismissColorFade();
-            }
-        } else if (target == Display.STATE_DOZE) {
-            // Want screen dozing.
-            // Wait for brightness animation to complete beforehand when entering doze
-            // from screen on to prevent a perceptible jump because brightness may operate
-            // differently when the display is configured for dozing.
-            if (mScreenBrightnessRampAnimator.isAnimating()
-                    && mPowerState.getScreenState() == Display.STATE_ON) {
-                return;
-            }
-
-            // Set screen state.
-            if (!setScreenState(Display.STATE_DOZE)) {
-                return; // screen on blocked
-            }
-
-            // Dismiss the black surface without fanfare.
-            mPowerState.setColorFadeLevel(1.0f);
-            mPowerState.dismissColorFade();
-        } else if (target == Display.STATE_DOZE_SUSPEND) {
-            // Want screen dozing and suspended.
-            // Wait for brightness animation to complete beforehand unless already
-            // suspended because we may not be able to change it after suspension.
-            if (mScreenBrightnessRampAnimator.isAnimating()
-                    && mPowerState.getScreenState() != Display.STATE_DOZE_SUSPEND) {
-                return;
-            }
-
-            // If not already suspending, temporarily set the state to doze until the
-            // screen on is unblocked, then suspend.
-            if (mPowerState.getScreenState() != Display.STATE_DOZE_SUSPEND) {
-                if (!setScreenState(Display.STATE_DOZE)) {
-                    return; // screen on blocked
-                }
-                setScreenState(Display.STATE_DOZE_SUSPEND); // already on so can't block
-            }
-
-            // Dismiss the black surface without fanfare.
-            mPowerState.setColorFadeLevel(1.0f);
-            mPowerState.dismissColorFade();
-        } else if (target == Display.STATE_ON_SUSPEND) {
-            // Want screen full-power and suspended.
-            // Wait for brightness animation to complete beforehand unless already
-            // suspended because we may not be able to change it after suspension.
-            if (mScreenBrightnessRampAnimator.isAnimating()
-                    && mPowerState.getScreenState() != Display.STATE_ON_SUSPEND) {
-                return;
-            }
-
-            // If not already suspending, temporarily set the state to on until the
-            // screen on is unblocked, then suspend.
-            if (mPowerState.getScreenState() != Display.STATE_ON_SUSPEND) {
-                if (!setScreenState(Display.STATE_ON)) {
-                    return;
-                }
-                setScreenState(Display.STATE_ON_SUSPEND);
-            }
-
-            // Dismiss the black surface without fanfare.
-            mPowerState.setColorFadeLevel(1.0f);
-            mPowerState.dismissColorFade();
-        } else {
-            // Want screen off.
-            mPendingScreenOff = true;
-            if (!mColorFadeEnabled) {
-                mPowerState.setColorFadeLevel(0.0f);
-            }
-
-            if (mPowerState.getColorFadeLevel() == 0.0f) {
-                // Turn the screen off.
-                // A black surface is already hiding the contents of the screen.
-                setScreenState(Display.STATE_OFF);
-                mPendingScreenOff = false;
-                mPowerState.dismissColorFadeResources();
-            } else if (performScreenOffTransition
-                    && mPowerState.prepareColorFade(mContext,
-                    mColorFadeFadesConfig
-                            ? ColorFade.MODE_FADE : ColorFade.MODE_COOL_DOWN)
-                    && mPowerState.getScreenState() != Display.STATE_OFF) {
-                // Perform the screen off animation.
-                mColorFadeOffAnimator.start();
-            } else {
-                // Skip the screen off animation and add a black surface to hide the
-                // contents of the screen.
-                mColorFadeOffAnimator.end();
-            }
-        }
-    }
-
-    private final Runnable mCleanListener = this::sendUpdatePowerState;
-
-    private void sendOnStateChangedWithWakelock() {
-        boolean wakeLockAcquired = mWakelockController.acquireWakelock(
-                WakelockController.WAKE_LOCK_STATE_CHANGED);
-        if (wakeLockAcquired) {
-            mHandler.post(mWakelockController.getOnStateChangedRunnable());
-        }
-    }
-
-    private void logDisplayPolicyChanged(int newPolicy) {
-        LogMaker log = new LogMaker(MetricsEvent.DISPLAY_POLICY);
-        log.setType(MetricsEvent.TYPE_UPDATE);
-        log.setSubtype(newPolicy);
-        MetricsLogger.action(log);
-    }
-
-    private void handleSettingsChange(boolean userSwitch) {
-        mDisplayBrightnessController
-                .setPendingScreenBrightness(mDisplayBrightnessController
-                        .getScreenBrightnessSetting());
-        mAutomaticBrightnessStrategy.updatePendingAutoBrightnessAdjustments(userSwitch);
-        if (userSwitch) {
-            // Don't treat user switches as user initiated change.
-            mDisplayBrightnessController
-                    .setAndNotifyCurrentScreenBrightness(mDisplayBrightnessController
-                            .getPendingScreenBrightness());
-            if (mAutomaticBrightnessController != null) {
-                mAutomaticBrightnessController.resetShortTermModel();
-            }
-        }
-        sendUpdatePowerState();
-    }
-
-    private void handleBrightnessModeChange() {
-        final int screenBrightnessModeSetting = Settings.System.getIntForUser(
-                mContext.getContentResolver(),
-                Settings.System.SCREEN_BRIGHTNESS_MODE,
-                Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL, UserHandle.USER_CURRENT);
-        mHandler.postAtTime(() -> {
-            mAutomaticBrightnessStrategy.setUseAutoBrightness(screenBrightnessModeSetting
-                    == Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
-            updatePowerState();
-        }, mClock.uptimeMillis());
-    }
-
-
-    @Override
-    public float getScreenBrightnessSetting() {
-        return mDisplayBrightnessController.getScreenBrightnessSetting();
-    }
-
-    @Override
-    public void setBrightness(float brightnessValue, int userSerial) {
-        mDisplayBrightnessController.setBrightness(clampScreenBrightness(brightnessValue),
-                userSerial);
-    }
-
-    @Override
-    public int getDisplayId() {
-        return mDisplayId;
-    }
-
-    @Override
-    public int getLeadDisplayId() {
-        return mLeadDisplayId;
-    }
-
-    @Override
-    public void setBrightnessToFollow(float leadDisplayBrightness, float nits, float ambientLux,
-            boolean slowChange) {
-        mBrightnessRangeController.onAmbientLuxChange(ambientLux);
-        if (nits == BrightnessMappingStrategy.INVALID_NITS) {
-            mDisplayBrightnessController.setBrightnessToFollow(leadDisplayBrightness, slowChange);
-        } else {
-            float brightness = mDisplayBrightnessController.getBrightnessFromNits(nits);
-            if (BrightnessUtils.isValidBrightnessValue(brightness)) {
-                mDisplayBrightnessController.setBrightnessToFollow(brightness, slowChange);
-            } else {
-                // The device does not support nits
-                mDisplayBrightnessController.setBrightnessToFollow(leadDisplayBrightness,
-                        slowChange);
-            }
-        }
-        sendUpdatePowerState();
-    }
-
-    private void notifyBrightnessTrackerChanged(float brightness, boolean userInitiated,
-            boolean wasShortTermModelActive, boolean autobrightnessEnabled,
-            boolean brightnessIsTemporary, boolean shouldUseAutoBrightness) {
-
-        final float brightnessInNits =
-                mDisplayBrightnessController.convertToAdjustedNits(brightness);
-        // Don't report brightness to brightnessTracker:
-        // If brightness is temporary (ie the slider has not been released)
-        // or if we are in idle screen brightness mode.
-        // or display is not on
-        // or we shouldn't be using autobrightness
-        // or the nits is invalid.
-        if (brightnessIsTemporary
-                || mAutomaticBrightnessController == null
-                || mAutomaticBrightnessController.isInIdleMode()
-                || !autobrightnessEnabled
-                || mBrightnessTracker == null
-                || !shouldUseAutoBrightness
-                || brightnessInNits < 0.0f) {
-            return;
-        }
-
-        if (userInitiated && (mAutomaticBrightnessController == null
-                || !mAutomaticBrightnessController.hasValidAmbientLux())) {
-            // If we don't have a valid lux reading we can't report a valid
-            // slider event so notify as if the system changed the brightness.
-            userInitiated = false;
-        }
-
-        // We only want to track changes on devices that can actually map the display backlight
-        // values into a physical brightness unit since the value provided by the API is in
-        // nits and not using the arbitrary backlight units.
-        final float powerFactor = mPowerRequest.lowPowerMode
-                ? mPowerRequest.screenLowPowerBrightnessFactor
-                : 1.0f;
-        mBrightnessTracker.notifyBrightnessChanged(brightnessInNits, userInitiated,
-                powerFactor, wasShortTermModelActive,
-                mAutomaticBrightnessController.isDefaultConfig(), mUniqueDisplayId,
-                mAutomaticBrightnessController.getLastSensorValues(),
-                mAutomaticBrightnessController.getLastSensorTimestamps());
-    }
-
-    @Override
-    public void addDisplayBrightnessFollower(DisplayPowerControllerInterface follower) {
-        synchronized (mLock) {
-            mDisplayBrightnessFollowers.append(follower.getDisplayId(), follower);
-            sendUpdatePowerStateLocked();
-        }
-    }
-
-    @Override
-    public void removeDisplayBrightnessFollower(DisplayPowerControllerInterface follower) {
-        synchronized (mLock) {
-            mDisplayBrightnessFollowers.remove(follower.getDisplayId());
-            mHandler.postAtTime(() -> follower.setBrightnessToFollow(
-                    PowerManager.BRIGHTNESS_INVALID_FLOAT, BrightnessMappingStrategy.INVALID_NITS,
-                    /* ambientLux= */ 0, /* slowChange= */ false), mClock.uptimeMillis());
-        }
-    }
-
-    @GuardedBy("mLock")
-    private void clearDisplayBrightnessFollowersLocked() {
-        for (int i = 0; i < mDisplayBrightnessFollowers.size(); i++) {
-            DisplayPowerControllerInterface follower = mDisplayBrightnessFollowers.valueAt(i);
-            mHandler.postAtTime(() -> follower.setBrightnessToFollow(
-                    PowerManager.BRIGHTNESS_INVALID_FLOAT, BrightnessMappingStrategy.INVALID_NITS,
-                    /* ambientLux= */ 0, /* slowChange= */ false), mClock.uptimeMillis());
-        }
-        mDisplayBrightnessFollowers.clear();
-    }
-
-    @Override
-    public void dump(final PrintWriter pw) {
-        synchronized (mLock) {
-            pw.println();
-            pw.println("Display Power Controller:");
-            pw.println("  mDisplayId=" + mDisplayId);
-            pw.println("  mLeadDisplayId=" + mLeadDisplayId);
-            pw.println("  mLightSensor=" + mLightSensor);
-            pw.println("  mDisplayBrightnessFollowers=" + mDisplayBrightnessFollowers);
-
-            pw.println();
-            pw.println("Display Power Controller Locked State:");
-            pw.println("  mDisplayReadyLocked=" + mDisplayReadyLocked);
-            pw.println("  mPendingRequestLocked=" + mPendingRequestLocked);
-            pw.println("  mPendingRequestChangedLocked=" + mPendingRequestChangedLocked);
-            pw.println("  mPendingUpdatePowerStateLocked=" + mPendingUpdatePowerStateLocked);
-        }
-
-        pw.println();
-        pw.println("Display Power Controller Configuration:");
-        pw.println("  mScreenBrightnessDozeConfig=" + mScreenBrightnessDozeConfig);
-        pw.println("  mUseSoftwareAutoBrightnessConfig=" + mUseSoftwareAutoBrightnessConfig);
-        pw.println("  mSkipScreenOnBrightnessRamp=" + mSkipScreenOnBrightnessRamp);
-        pw.println("  mColorFadeFadesConfig=" + mColorFadeFadesConfig);
-        pw.println("  mColorFadeEnabled=" + mColorFadeEnabled);
-        pw.println("  mIsDisplayInternal=" + mIsDisplayInternal);
-        synchronized (mCachedBrightnessInfo) {
-            pw.println("  mCachedBrightnessInfo.brightness="
-                    + mCachedBrightnessInfo.brightness.value);
-            pw.println("  mCachedBrightnessInfo.adjustedBrightness="
-                    + mCachedBrightnessInfo.adjustedBrightness.value);
-            pw.println("  mCachedBrightnessInfo.brightnessMin="
-                    + mCachedBrightnessInfo.brightnessMin.value);
-            pw.println("  mCachedBrightnessInfo.brightnessMax="
-                    + mCachedBrightnessInfo.brightnessMax.value);
-            pw.println("  mCachedBrightnessInfo.hbmMode=" + mCachedBrightnessInfo.hbmMode.value);
-            pw.println("  mCachedBrightnessInfo.hbmTransitionPoint="
-                    + mCachedBrightnessInfo.hbmTransitionPoint.value);
-            pw.println("  mCachedBrightnessInfo.brightnessMaxReason ="
-                    + mCachedBrightnessInfo.brightnessMaxReason.value);
-        }
-        pw.println("  mDisplayBlanksAfterDozeConfig=" + mDisplayBlanksAfterDozeConfig);
-        pw.println("  mBrightnessBucketsInDozeConfig=" + mBrightnessBucketsInDozeConfig);
-        mHandler.runWithScissors(() -> dumpLocal(pw), 1000);
-    }
-
-    private void dumpLocal(PrintWriter pw) {
-        pw.println();
-        pw.println("Display Power Controller Thread State:");
-        pw.println("  mPowerRequest=" + mPowerRequest);
-        pw.println("  mBrightnessReason=" + mBrightnessReason);
-        pw.println("  mAppliedDimming=" + mAppliedDimming);
-        pw.println("  mAppliedThrottling=" + mAppliedThrottling);
-        pw.println("  mDozing=" + mDozing);
-        pw.println("  mSkipRampState=" + skipRampStateToString(mSkipRampState));
-        pw.println("  mScreenOnBlockStartRealTime=" + mScreenOnBlockStartRealTime);
-        pw.println("  mScreenOffBlockStartRealTime=" + mScreenOffBlockStartRealTime);
-        pw.println("  mPendingScreenOnUnblocker=" + mPendingScreenOnUnblocker);
-        pw.println("  mPendingScreenOffUnblocker=" + mPendingScreenOffUnblocker);
-        pw.println("  mPendingScreenOff=" + mPendingScreenOff);
-        pw.println("  mReportedToPolicy="
-                + reportedToPolicyToString(mReportedScreenStateToPolicy));
-        pw.println("  mIsRbcActive=" + mIsRbcActive);
-        IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "    ");
-        mAutomaticBrightnessStrategy.dump(ipw);
-
-        if (mScreenBrightnessRampAnimator != null) {
-            pw.println("  mScreenBrightnessRampAnimator.isAnimating()="
-                    + mScreenBrightnessRampAnimator.isAnimating());
-        }
-
-        if (mColorFadeOnAnimator != null) {
-            pw.println("  mColorFadeOnAnimator.isStarted()="
-                    + mColorFadeOnAnimator.isStarted());
-        }
-        if (mColorFadeOffAnimator != null) {
-            pw.println("  mColorFadeOffAnimator.isStarted()="
-                    + mColorFadeOffAnimator.isStarted());
-        }
-
-        if (mPowerState != null) {
-            mPowerState.dump(pw);
-        }
-
-        if (mAutomaticBrightnessController != null) {
-            mAutomaticBrightnessController.dump(pw);
-            dumpBrightnessEvents(pw);
-        }
-
-        dumpRbcEvents(pw);
-
-        if (mScreenOffBrightnessSensorController != null) {
-            mScreenOffBrightnessSensorController.dump(pw);
-        }
-
-        if (mBrightnessRangeController != null) {
-            mBrightnessRangeController.dump(pw);
-        }
-
-        if (mBrightnessThrottler != null) {
-            mBrightnessThrottler.dump(pw);
-        }
-
-        pw.println();
-        if (mDisplayWhiteBalanceController != null) {
-            mDisplayWhiteBalanceController.dump(pw);
-            mDisplayWhiteBalanceSettings.dump(pw);
-        }
-
-        pw.println();
-
-        if (mWakelockController != null) {
-            mWakelockController.dumpLocal(pw);
-        }
-
-        pw.println();
-        if (mDisplayBrightnessController != null) {
-            mDisplayBrightnessController.dump(pw);
-        }
-
-        pw.println();
-        if (mDisplayStateController != null) {
-            mDisplayStateController.dumpsys(pw);
-        }
-
-        pw.println();
-        if (mBrightnessClamperController != null) {
-            mBrightnessClamperController.dump(ipw);
-        }
-    }
-
-
-    private static String reportedToPolicyToString(int state) {
-        switch (state) {
-            case REPORTED_TO_POLICY_SCREEN_OFF:
-                return "REPORTED_TO_POLICY_SCREEN_OFF";
-            case REPORTED_TO_POLICY_SCREEN_TURNING_ON:
-                return "REPORTED_TO_POLICY_SCREEN_TURNING_ON";
-            case REPORTED_TO_POLICY_SCREEN_ON:
-                return "REPORTED_TO_POLICY_SCREEN_ON";
-            default:
-                return Integer.toString(state);
-        }
-    }
-
-    private static String skipRampStateToString(int state) {
-        switch (state) {
-            case RAMP_STATE_SKIP_NONE:
-                return "RAMP_STATE_SKIP_NONE";
-            case RAMP_STATE_SKIP_INITIAL:
-                return "RAMP_STATE_SKIP_INITIAL";
-            case RAMP_STATE_SKIP_AUTOBRIGHT:
-                return "RAMP_STATE_SKIP_AUTOBRIGHT";
-            default:
-                return Integer.toString(state);
-        }
-    }
-
-    private void dumpBrightnessEvents(PrintWriter pw) {
-        int size = mBrightnessEventRingBuffer.size();
-        if (size < 1) {
-            pw.println("No Automatic Brightness Adjustments");
-            return;
-        }
-
-        pw.println("Automatic Brightness Adjustments Last " + size + " Events: ");
-        BrightnessEvent[] eventArray = mBrightnessEventRingBuffer.toArray();
-        for (int i = 0; i < mBrightnessEventRingBuffer.size(); i++) {
-            pw.println("  " + eventArray[i].toString());
-        }
-    }
-
-    private void dumpRbcEvents(PrintWriter pw) {
-        int size = mRbcEventRingBuffer.size();
-        if (size < 1) {
-            pw.println("No Reduce Bright Colors Adjustments");
-            return;
-        }
-
-        pw.println("Reduce Bright Colors Adjustments Last " + size + " Events: ");
-        BrightnessEvent[] eventArray = mRbcEventRingBuffer.toArray();
-        for (int i = 0; i < mRbcEventRingBuffer.size(); i++) {
-            pw.println("  " + eventArray[i]);
-        }
-    }
-
-
-    private void noteScreenState(int screenState) {
-        // Log screen state change with display id
-        FrameworkStatsLog.write(FrameworkStatsLog.SCREEN_STATE_CHANGED_V2,
-                screenState, mDisplayStatsId);
-        if (mBatteryStats != null) {
-            try {
-                // TODO(multi-display): make this multi-display
-                mBatteryStats.noteScreenState(screenState);
-            } catch (RemoteException e) {
-                // same process
-            }
-        }
-    }
-
-    @SuppressLint("AndroidFrameworkRequiresPermission")
-    private void noteScreenBrightness(float brightness) {
-        if (mBatteryStats != null) {
-            try {
-                // TODO(brightnessfloat): change BatteryStats to use float
-                int brightnessInt = mFlags.isBrightnessIntRangeUserPerceptionEnabled()
-                        ? BrightnessSynchronizer.brightnessFloatToIntSetting(mContext, brightness)
-                        : BrightnessSynchronizer.brightnessFloatToInt(brightness);
-                mBatteryStats.noteScreenBrightness(brightnessInt);
-            } catch (RemoteException e) {
-                // same process
-            }
-        }
-    }
-
-    private void reportStats(float brightness) {
-        if (mLastStatsBrightness == brightness) {
-            return;
-        }
-
-        float hbmTransitionPoint = PowerManager.BRIGHTNESS_MAX;
-        synchronized (mCachedBrightnessInfo) {
-            if (mCachedBrightnessInfo.hbmTransitionPoint == null) {
-                return;
-            }
-            hbmTransitionPoint = mCachedBrightnessInfo.hbmTransitionPoint.value;
-        }
-
-        final boolean aboveTransition = brightness > hbmTransitionPoint;
-        final boolean oldAboveTransition = mLastStatsBrightness > hbmTransitionPoint;
-
-        if (aboveTransition || oldAboveTransition) {
-            mLastStatsBrightness = brightness;
-            mHandler.removeMessages(MSG_STATSD_HBM_BRIGHTNESS);
-            if (aboveTransition != oldAboveTransition) {
-                // report immediately
-                logHbmBrightnessStats(brightness, mDisplayStatsId);
-            } else {
-                // delay for rate limiting
-                Message msg = mHandler.obtainMessage();
-                msg.what = MSG_STATSD_HBM_BRIGHTNESS;
-                msg.arg1 = Float.floatToIntBits(brightness);
-                msg.arg2 = mDisplayStatsId;
-                mHandler.sendMessageAtTime(msg, mClock.uptimeMillis()
-                        + BRIGHTNESS_CHANGE_STATSD_REPORT_INTERVAL_MS);
-            }
-        }
-    }
-
-    private void logHbmBrightnessStats(float brightness, int displayStatsId) {
-        synchronized (mHandler) {
-            FrameworkStatsLog.write(
-                    FrameworkStatsLog.DISPLAY_HBM_BRIGHTNESS_CHANGED, displayStatsId, brightness);
-        }
-    }
-
-    // Return bucket index of range_[left]_[right] where
-    // left <= nits < right
-    private int nitsToRangeIndex(float nits) {
-        for (int i = 0; i < BRIGHTNESS_RANGE_BOUNDARIES.length; i++) {
-            if (nits < BRIGHTNESS_RANGE_BOUNDARIES[i]) {
-                return BRIGHTNESS_RANGE_INDEX[i];
-            }
-        }
-        return FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_3000_INF;
-    }
-
-    private int convertBrightnessReasonToStatsEnum(int brightnessReason) {
-        switch(brightnessReason) {
-            case BrightnessReason.REASON_UNKNOWN:
-                return FrameworkStatsLog
-                    .DISPLAY_BRIGHTNESS_CHANGED__ENTIRE_REASON__REASON_UNKNOWN;
-            case BrightnessReason.REASON_MANUAL:
-                return FrameworkStatsLog
-                    .DISPLAY_BRIGHTNESS_CHANGED__ENTIRE_REASON__REASON_MANUAL;
-            case BrightnessReason.REASON_DOZE:
-                return FrameworkStatsLog
-                    .DISPLAY_BRIGHTNESS_CHANGED__ENTIRE_REASON__REASON_DOZE;
-            case BrightnessReason.REASON_DOZE_DEFAULT:
-                return FrameworkStatsLog
-                    .DISPLAY_BRIGHTNESS_CHANGED__ENTIRE_REASON__REASON_DOZE_DEFAULT;
-            case BrightnessReason.REASON_AUTOMATIC:
-                return FrameworkStatsLog
-                    .DISPLAY_BRIGHTNESS_CHANGED__ENTIRE_REASON__REASON_AUTOMATIC;
-            case BrightnessReason.REASON_SCREEN_OFF:
-                return FrameworkStatsLog
-                    .DISPLAY_BRIGHTNESS_CHANGED__ENTIRE_REASON__REASON_SCREEN_OFF;
-            case BrightnessReason.REASON_OVERRIDE:
-                return FrameworkStatsLog
-                    .DISPLAY_BRIGHTNESS_CHANGED__ENTIRE_REASON__REASON_OVERRIDE;
-            case BrightnessReason.REASON_TEMPORARY:
-                return FrameworkStatsLog
-                    .DISPLAY_BRIGHTNESS_CHANGED__ENTIRE_REASON__REASON_TEMPORARY;
-            case BrightnessReason.REASON_BOOST:
-                return FrameworkStatsLog
-                    .DISPLAY_BRIGHTNESS_CHANGED__ENTIRE_REASON__REASON_BOOST;
-            case BrightnessReason.REASON_SCREEN_OFF_BRIGHTNESS_SENSOR:
-                return FrameworkStatsLog
-                    .DISPLAY_BRIGHTNESS_CHANGED__ENTIRE_REASON__REASON_SCREEN_OFF_BRIGHTNESS_SENSOR;
-            case BrightnessReason.REASON_FOLLOWER:
-                return FrameworkStatsLog
-                    .DISPLAY_BRIGHTNESS_CHANGED__ENTIRE_REASON__REASON_FOLLOWER;
-        }
-        return FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__ENTIRE_REASON__REASON_UNKNOWN;
-    }
-
-    private void logBrightnessEvent(BrightnessEvent event, float unmodifiedBrightness) {
-        int modifier = event.getReason().getModifier();
-        int flags = event.getFlags();
-        // It's easier to check if the brightness is at maximum level using the brightness
-        // value untouched by any modifiers
-        boolean brightnessIsMax = unmodifiedBrightness == event.getHbmMax();
-        float brightnessInNits =
-                mDisplayBrightnessController.convertToAdjustedNits(event.getBrightness());
-        float appliedLowPowerMode = event.isLowPowerModeSet() ? event.getPowerFactor() : -1f;
-        int appliedRbcStrength  = event.isRbcEnabled() ? event.getRbcStrength() : -1;
-        float appliedHbmMaxNits =
-                event.getHbmMode() == BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF
-                ? -1f : mDisplayBrightnessController.convertToAdjustedNits(event.getHbmMax());
-        // thermalCapNits set to -1 if not currently capping max brightness
-        float appliedThermalCapNits =
-                event.getThermalMax() == PowerManager.BRIGHTNESS_MAX
-                ? -1f : mDisplayBrightnessController.convertToAdjustedNits(event.getThermalMax());
-        if (mIsDisplayInternal) {
-            FrameworkStatsLog.write(FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED,
-                    mDisplayBrightnessController
-                            .convertToAdjustedNits(event.getInitialBrightness()),
-                    brightnessInNits,
-                    event.getLux(),
-                    event.getPhysicalDisplayId(),
-                    event.wasShortTermModelActive(),
-                    appliedLowPowerMode,
-                    appliedRbcStrength,
-                    appliedHbmMaxNits,
-                    appliedThermalCapNits,
-                    event.isAutomaticBrightnessEnabled(),
-                    FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__REASON__REASON_MANUAL,
-                    convertBrightnessReasonToStatsEnum(event.getReason().getReason()),
-                    nitsToRangeIndex(brightnessInNits),
-                    brightnessIsMax,
-                    event.getHbmMode() == BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT,
-                    event.getHbmMode() == BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR,
-                    (modifier & BrightnessReason.MODIFIER_LOW_POWER) > 0,
-                    mBrightnessClamperController.getBrightnessMaxReason(),
-                    (modifier & BrightnessReason.MODIFIER_DIMMED) > 0,
-                    event.isRbcEnabled(),
-                    (flags & BrightnessEvent.FLAG_INVALID_LUX) > 0,
-                    (flags & BrightnessEvent.FLAG_DOZE_SCALE) > 0,
-                    (flags & BrightnessEvent.FLAG_USER_SET) > 0,
-                    (flags & BrightnessEvent.FLAG_IDLE_CURVE) > 0,
-                    (flags & BrightnessEvent.FLAG_LOW_POWER_MODE) > 0);
-        }
-    }
-
-    /**
-     * Indicates whether the display state is ready to update. If this is the default display, we
-     * want to update it right away so that we can draw the boot animation on it. If it is not
-     * the default display, drawing the boot animation on it would look incorrect, so we need
-     * to wait until boot is completed.
-     * @return True if the display state is ready to update
-     */
-    private boolean readyToUpdateDisplayState() {
-        return mDisplayId == Display.DEFAULT_DISPLAY || mBootCompleted;
-    }
-
-    private final class DisplayControllerHandler extends Handler {
-        DisplayControllerHandler(Looper looper) {
-            super(looper, null, true /*async*/);
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case MSG_UPDATE_POWER_STATE:
-                    updatePowerState();
-                    break;
-
-                case MSG_SCREEN_ON_UNBLOCKED:
-                    if (mPendingScreenOnUnblocker == msg.obj) {
-                        unblockScreenOn();
-                        updatePowerState();
-                    }
-                    break;
-                case MSG_SCREEN_OFF_UNBLOCKED:
-                    if (mPendingScreenOffUnblocker == msg.obj) {
-                        unblockScreenOff();
-                        updatePowerState();
-                    }
-                    break;
-                case MSG_OFFLOADING_SCREEN_ON_UNBLOCKED:
-                    if (mDisplayOffloadSession == msg.obj) {
-                        unblockScreenOnByDisplayOffload();
-                        updatePowerState();
-                    }
-                    break;
-                case MSG_CONFIGURE_BRIGHTNESS:
-                    BrightnessConfiguration brightnessConfiguration =
-                            (BrightnessConfiguration) msg.obj;
-                    mAutomaticBrightnessStrategy.setBrightnessConfiguration(brightnessConfiguration,
-                            msg.arg1 == 1);
-                    if (mBrightnessTracker != null) {
-                        mBrightnessTracker
-                                .setShouldCollectColorSample(brightnessConfiguration != null
-                                        && brightnessConfiguration.shouldCollectColorSamples());
-                    }
-                    updatePowerState();
-                    break;
-
-                case MSG_SET_TEMPORARY_BRIGHTNESS:
-                    // TODO: Should we have a a timeout for the temporary brightness?
-                    mDisplayBrightnessController
-                            .setTemporaryBrightness(Float.intBitsToFloat(msg.arg1));
-                    updatePowerState();
-                    break;
-
-                case MSG_SET_TEMPORARY_AUTO_BRIGHTNESS_ADJUSTMENT:
-                    mAutomaticBrightnessStrategy
-                            .setTemporaryAutoBrightnessAdjustment(Float.intBitsToFloat(msg.arg1));
-                    updatePowerState();
-                    break;
-
-                case MSG_STOP:
-                    cleanupHandlerThreadAfterStop();
-                    break;
-
-                case MSG_UPDATE_BRIGHTNESS:
-                    if (mStopped) {
-                        return;
-                    }
-                    handleSettingsChange(false /*userSwitch*/);
-                    break;
-
-                case MSG_UPDATE_RBC:
-                    handleRbcChanged();
-                    break;
-
-                case MSG_BRIGHTNESS_RAMP_DONE:
-                    if (mPowerState != null) {
-                        final float brightness = mPowerState.getScreenBrightness();
-                        reportStats(brightness);
-                    }
-                    break;
-
-                case MSG_STATSD_HBM_BRIGHTNESS:
-                    logHbmBrightnessStats(Float.intBitsToFloat(msg.arg1), msg.arg2);
-                    break;
-
-                case MSG_SWITCH_USER:
-                    handleOnSwitchUser(msg.arg1);
-                    break;
-
-                case MSG_BOOT_COMPLETED:
-                    mBootCompleted = true;
-                    updatePowerState();
-                    break;
-
-                case MSG_SET_DWBC_STRONG_MODE:
-                    setDwbcStrongMode(msg.arg1);
-                    break;
-
-                case MSG_SET_DWBC_COLOR_OVERRIDE:
-                    final float cct = Float.intBitsToFloat(msg.arg1);
-                    setDwbcOverride(cct);
-                    break;
-
-                case MSG_SET_DWBC_LOGGING_ENABLED:
-                    setDwbcLoggingEnabled(msg.arg1);
-                    break;
-                case MSG_SET_BRIGHTNESS_FROM_OFFLOAD:
-                    mDisplayBrightnessController.setBrightnessFromOffload(
-                            Float.intBitsToFloat(msg.arg1));
-                    updatePowerState();
-                    break;
-            }
-        }
-    }
-
-
-    private final class SettingsObserver extends ContentObserver {
-        SettingsObserver(Handler handler) {
-            super(handler);
-        }
-
-        @Override
-        public void onChange(boolean selfChange, Uri uri) {
-            if (uri.equals(Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_MODE))) {
-                handleBrightnessModeChange();
-            } else if (uri.equals(Settings.System.getUriFor(
-                    Settings.System.SCREEN_BRIGHTNESS_FOR_ALS))) {
-                int preset = Settings.System.getIntForUser(mContext.getContentResolver(),
-                        Settings.System.SCREEN_BRIGHTNESS_FOR_ALS,
-                        Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_NORMAL,
-                        UserHandle.USER_CURRENT);
-                Slog.i(mTag, "Setting up auto-brightness for preset "
-                        + autoBrightnessPresetToString(preset));
-                setUpAutoBrightness(mContext, mHandler);
-                sendUpdatePowerState();
-            } else {
-                handleSettingsChange(false /* userSwitch */);
-            }
-        }
-    }
-
-    private final class ScreenOnUnblocker implements WindowManagerPolicy.ScreenOnListener {
-        @Override
-        public void onScreenOn() {
-            Message msg = mHandler.obtainMessage(MSG_SCREEN_ON_UNBLOCKED, this);
-            mHandler.sendMessageAtTime(msg, mClock.uptimeMillis());
-        }
-    }
-
-    private final class ScreenOffUnblocker implements WindowManagerPolicy.ScreenOffListener {
-        @Override
-        public void onScreenOff() {
-            Message msg = mHandler.obtainMessage(MSG_SCREEN_OFF_UNBLOCKED, this);
-            mHandler.sendMessageAtTime(msg, mClock.uptimeMillis());
-        }
-    }
-
-    @Override
-    public void setAutoBrightnessLoggingEnabled(boolean enabled) {
-        if (mAutomaticBrightnessController != null) {
-            mAutomaticBrightnessController.setLoggingEnabled(enabled);
-        }
-    }
-
-    @Override // DisplayWhiteBalanceController.Callbacks
-    public void updateWhiteBalance() {
-        sendUpdatePowerState();
-    }
-
-    @Override
-    public void setDisplayWhiteBalanceLoggingEnabled(boolean enabled) {
-        Message msg = mHandler.obtainMessage();
-        msg.what = MSG_SET_DWBC_LOGGING_ENABLED;
-        msg.arg1 = enabled ? 1 : 0;
-        msg.sendToTarget();
-    }
-
-    @Override
-    public void setAmbientColorTemperatureOverride(float cct) {
-        Message msg = mHandler.obtainMessage();
-        msg.what = MSG_SET_DWBC_COLOR_OVERRIDE;
-        msg.arg1 = Float.floatToIntBits(cct);
-        msg.sendToTarget();
-    }
-
-    /** Functional interface for providing time. */
-    @VisibleForTesting
-    interface Clock {
-        /**
-         * Returns current time in milliseconds since boot, not counting time spent in deep sleep.
-         */
-        long uptimeMillis();
-    }
-
-    @VisibleForTesting
-    static class Injector {
-        Clock getClock() {
-            return SystemClock::uptimeMillis;
-        }
-
-        DisplayPowerState getDisplayPowerState(DisplayBlanker blanker, ColorFade colorFade,
-                int displayId, int displayState) {
-            return new DisplayPowerState(blanker, colorFade, displayId, displayState);
-        }
-
-        DualRampAnimator<DisplayPowerState> getDualRampAnimator(DisplayPowerState dps,
-                FloatProperty<DisplayPowerState> firstProperty,
-                FloatProperty<DisplayPowerState> secondProperty) {
-            return new DualRampAnimator(dps, firstProperty, secondProperty);
-        }
-
-        WakelockController getWakelockController(int displayId,
-                DisplayPowerCallbacks displayPowerCallbacks) {
-            return new WakelockController(displayId, displayPowerCallbacks);
-        }
-
-        DisplayPowerProximityStateController getDisplayPowerProximityStateController(
-                WakelockController wakelockController, DisplayDeviceConfig displayDeviceConfig,
-                Looper looper, Runnable nudgeUpdatePowerState,
-                int displayId, SensorManager sensorManager) {
-            return new DisplayPowerProximityStateController(wakelockController, displayDeviceConfig,
-                    looper, nudgeUpdatePowerState,
-                    displayId, sensorManager, /* injector= */ null);
-        }
-
-        AutomaticBrightnessController getAutomaticBrightnessController(
-                AutomaticBrightnessController.Callbacks callbacks, Looper looper,
-                SensorManager sensorManager, Sensor lightSensor,
-                SparseArray<BrightnessMappingStrategy> brightnessMappingStrategyMap,
-                int lightSensorWarmUpTime, float brightnessMin, float brightnessMax,
-                float dozeScaleFactor, int lightSensorRate, int initialLightSensorRate,
-                long brighteningLightDebounceConfig, long darkeningLightDebounceConfig,
-                long brighteningLightDebounceConfigIdle, long darkeningLightDebounceConfigIdle,
-                boolean resetAmbientLuxAfterWarmUpConfig,
-                HysteresisLevels ambientBrightnessThresholds,
-                HysteresisLevels screenBrightnessThresholds,
-                HysteresisLevels ambientBrightnessThresholdsIdle,
-                HysteresisLevels screenBrightnessThresholdsIdle, Context context,
-                BrightnessRangeController brightnessModeController,
-                BrightnessThrottler brightnessThrottler, int ambientLightHorizonShort,
-                int ambientLightHorizonLong, float userLux, float userNits) {
-            return new AutomaticBrightnessController(callbacks, looper, sensorManager, lightSensor,
-                    brightnessMappingStrategyMap, lightSensorWarmUpTime, brightnessMin,
-                    brightnessMax, dozeScaleFactor, lightSensorRate, initialLightSensorRate,
-                    brighteningLightDebounceConfig, darkeningLightDebounceConfig,
-                    brighteningLightDebounceConfigIdle, darkeningLightDebounceConfigIdle,
-                    resetAmbientLuxAfterWarmUpConfig, ambientBrightnessThresholds,
-                    screenBrightnessThresholds, ambientBrightnessThresholdsIdle,
-                    screenBrightnessThresholdsIdle, context, brightnessModeController,
-                    brightnessThrottler, ambientLightHorizonShort, ambientLightHorizonLong, userLux,
-                    userNits);
-        }
-
-        BrightnessMappingStrategy getDefaultModeBrightnessMapper(Context context,
-                DisplayDeviceConfig displayDeviceConfig,
-                DisplayWhiteBalanceController displayWhiteBalanceController) {
-            return BrightnessMappingStrategy.create(context, displayDeviceConfig,
-                    AUTO_BRIGHTNESS_MODE_DEFAULT, displayWhiteBalanceController);
-        }
-
-        HysteresisLevels getHysteresisLevels(float[] brighteningThresholdsPercentages,
-                float[] darkeningThresholdsPercentages, float[] brighteningThresholdLevels,
-                float[] darkeningThresholdLevels, float minDarkeningThreshold,
-                float minBrighteningThreshold) {
-            return new HysteresisLevels(brighteningThresholdsPercentages,
-                    darkeningThresholdsPercentages, brighteningThresholdLevels,
-                    darkeningThresholdLevels, minDarkeningThreshold, minBrighteningThreshold);
-        }
-
-        HysteresisLevels getHysteresisLevels(float[] brighteningThresholdsPercentages,
-                float[] darkeningThresholdsPercentages, float[] brighteningThresholdLevels,
-                float[] darkeningThresholdLevels, float minDarkeningThreshold,
-                float minBrighteningThreshold, boolean potentialOldBrightnessRange) {
-            return new HysteresisLevels(brighteningThresholdsPercentages,
-                    darkeningThresholdsPercentages, brighteningThresholdLevels,
-                    darkeningThresholdLevels, minDarkeningThreshold, minBrighteningThreshold,
-                    potentialOldBrightnessRange);
-        }
-
-        ScreenOffBrightnessSensorController getScreenOffBrightnessSensorController(
-                SensorManager sensorManager,
-                Sensor lightSensor,
-                Handler handler,
-                ScreenOffBrightnessSensorController.Clock clock,
-                int[] sensorValueToLux,
-                BrightnessMappingStrategy brightnessMapper) {
-            return new ScreenOffBrightnessSensorController(
-                    sensorManager,
-                    lightSensor,
-                    handler,
-                    clock,
-                    sensorValueToLux,
-                    brightnessMapper
-            );
-        }
-
-        HighBrightnessModeController getHighBrightnessModeController(Handler handler, int width,
-                int height, IBinder displayToken, String displayUniqueId, float brightnessMin,
-                float brightnessMax, DisplayDeviceConfig.HighBrightnessModeData hbmData,
-                HighBrightnessModeController.HdrBrightnessDeviceConfig hdrBrightnessCfg,
-                Runnable hbmChangeCallback, HighBrightnessModeMetadata hbmMetadata,
-                Context context) {
-            return new HighBrightnessModeController(handler, width, height, displayToken,
-                    displayUniqueId, brightnessMin, brightnessMax, hbmData, hdrBrightnessCfg,
-                    hbmChangeCallback, hbmMetadata, context);
-        }
-
-        BrightnessRangeController getBrightnessRangeController(
-                HighBrightnessModeController hbmController, Runnable modeChangeCallback,
-                DisplayDeviceConfig displayDeviceConfig, Handler handler,
-                DisplayManagerFlags flags, IBinder displayToken, DisplayDeviceInfo info) {
-            return new BrightnessRangeController(hbmController,
-                    modeChangeCallback, displayDeviceConfig, handler, flags, displayToken, info);
-        }
-
-        BrightnessClamperController getBrightnessClamperController(Handler handler,
-                BrightnessClamperController.ClamperChangeListener clamperChangeListener,
-                BrightnessClamperController.DisplayDeviceData data, Context context,
-                DisplayManagerFlags flags) {
-
-            return new BrightnessClamperController(handler, clamperChangeListener, data, context,
-                    flags);
-        }
-
-        DisplayWhiteBalanceController getDisplayWhiteBalanceController(Handler handler,
-                SensorManager sensorManager, Resources resources) {
-            return DisplayWhiteBalanceFactory.create(handler,
-                    sensorManager, resources);
-        }
-
-        boolean isColorFadeEnabled() {
-            return !ActivityManager.isLowRamDeviceStatic();
-        }
-    }
-
-    static class CachedBrightnessInfo {
-        public MutableFloat brightness = new MutableFloat(PowerManager.BRIGHTNESS_INVALID_FLOAT);
-        public MutableFloat adjustedBrightness =
-                new MutableFloat(PowerManager.BRIGHTNESS_INVALID_FLOAT);
-        public MutableFloat brightnessMin =
-                new MutableFloat(PowerManager.BRIGHTNESS_INVALID_FLOAT);
-        public MutableFloat brightnessMax =
-                new MutableFloat(PowerManager.BRIGHTNESS_INVALID_FLOAT);
-        public MutableInt hbmMode = new MutableInt(BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF);
-        public MutableFloat hbmTransitionPoint =
-                new MutableFloat(HighBrightnessModeController.HBM_TRANSITION_POINT_INVALID);
-        public MutableInt brightnessMaxReason =
-                new MutableInt(BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE);
-
-        public boolean checkAndSetFloat(MutableFloat mf, float f) {
-            if (mf.value != f) {
-                mf.value = f;
-                return true;
-            }
-            return false;
-        }
-
-        public boolean checkAndSetInt(MutableInt mi, int i) {
-            if (mi.value != i) {
-                mi.value = i;
-                return true;
-            }
-            return false;
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java b/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java
index c279184..13acb3f 100644
--- a/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java
+++ b/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java
@@ -237,4 +237,21 @@
      * Indicate that boot has been completed and the screen is ready to update.
      */
     void onBootCompleted();
+
+    /**
+     * Get the brightness levels used to determine automatic brightness based on lux levels.
+     * @param mode The auto-brightness mode
+     * @return The brightness levels for the specified mode. The values are between
+     * {@link PowerManager.BRIGHTNESS_MIN} and {@link PowerManager.BRIGHTNESS_MAX}.
+     */
+    float[] getAutoBrightnessLevels(
+            @AutomaticBrightnessController.AutomaticBrightnessMode int mode);
+
+    /**
+     * Get the lux levels used to determine automatic brightness.
+     * @param mode The auto-brightness mode
+     * @return The lux levels for the specified mode
+     */
+    float[] getAutoBrightnessLuxLevels(
+            @AutomaticBrightnessController.AutomaticBrightnessMode int mode);
 }
diff --git a/services/core/java/com/android/server/display/DisplayPowerState.java b/services/core/java/com/android/server/display/DisplayPowerState.java
index f994c05..90bad12 100644
--- a/services/core/java/com/android/server/display/DisplayPowerState.java
+++ b/services/core/java/com/android/server/display/DisplayPowerState.java
@@ -26,9 +26,12 @@
 import android.view.Choreographer;
 import android.view.Display;
 
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.BackgroundThread;
 import com.android.server.display.utils.DebugUtils;
 
 import java.io.PrintWriter;
+import java.util.concurrent.Executor;
 
 /**
  * Controls the display power state.
@@ -75,10 +78,19 @@
 
     private Runnable mCleanListener;
 
+    private Executor mAsyncDestroyExecutor;
+
     private volatile boolean mStopped;
 
     DisplayPowerState(
             DisplayBlanker blanker, ColorFade colorFade, int displayId, int displayState) {
+        this(blanker, colorFade, displayId, displayState, BackgroundThread.getExecutor());
+    }
+
+    @VisibleForTesting
+    DisplayPowerState(
+            DisplayBlanker blanker, ColorFade colorFade, int displayId, int displayState,
+            Executor asyncDestroyExecutor) {
         mHandler = new Handler(true /*async*/);
         mChoreographer = Choreographer.getInstance();
         mBlanker = blanker;
@@ -86,6 +98,7 @@
         mPhotonicModulator = new PhotonicModulator();
         mPhotonicModulator.start();
         mDisplayId = displayId;
+        mAsyncDestroyExecutor = asyncDestroyExecutor;
 
         // At boot time, we don't know the screen's brightness,
         // so prepare to set it to a known state when the state is next applied.
@@ -320,8 +333,10 @@
     public void stop() {
         mStopped = true;
         mPhotonicModulator.interrupt();
+        mColorFadePrepared = false;
+        mColorFadeReady = true;
         if (mColorFade != null) {
-            mColorFade.destroy();
+            mAsyncDestroyExecutor.execute(mColorFade::destroy);
         }
         mCleanListener = null;
         mHandler.removeCallbacksAndMessages(null);
@@ -406,7 +421,8 @@
         }
     };
 
-    private final Runnable mColorFadeDrawRunnable = new Runnable() {
+    @VisibleForTesting
+    final Runnable mColorFadeDrawRunnable = new Runnable() {
         @Override
         public void run() {
             mColorFadeDrawPending = false;
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 25576ce..3a63330 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -19,6 +19,8 @@
 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
 import static android.view.Display.Mode.INVALID_MODE_ID;
 
+import static com.android.server.display.BrightnessMappingStrategy.INVALID_NITS;
+
 import android.annotation.Nullable;
 import android.app.ActivityThread;
 import android.content.Context;
@@ -956,8 +958,7 @@
 
                     void handleHdrSdrNitsChanged(float displayNits, float sdrNits) {
                         final float newHdrSdrRatio;
-                        if (displayNits != DisplayDeviceConfig.NITS_INVALID
-                                && sdrNits != DisplayDeviceConfig.NITS_INVALID) {
+                        if (displayNits != INVALID_NITS && sdrNits != INVALID_NITS) {
                             // Ensure the ratio stays >= 1.0f as values below that are nonsensical
                             newHdrSdrRatio = Math.max(1.f, displayNits / sdrNits);
                         } else {
diff --git a/services/core/java/com/android/server/display/LogicalDisplayMapper.java b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
index 115111a..2e8de31 100644
--- a/services/core/java/com/android/server/display/LogicalDisplayMapper.java
+++ b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
@@ -205,7 +205,7 @@
             @NonNull Handler handler, DisplayManagerFlags flags) {
         this(context, foldSettingProvider, repo, listener, syncRoot, handler,
                 new DeviceStateToLayoutMap((isDefault) -> isDefault ? DEFAULT_DISPLAY
-                        : sNextNonDefaultDisplayId++), flags);
+                        : sNextNonDefaultDisplayId++, flags), flags);
     }
 
     LogicalDisplayMapper(@NonNull Context context, FoldSettingProvider foldSettingProvider,
@@ -1094,8 +1094,8 @@
             final DisplayAddress address = displayLayout.getAddress();
             final DisplayDevice device = mDisplayDeviceRepo.getByAddressLocked(address);
             if (device == null) {
-                Slog.w(TAG, "The display device (" + address + "), is not available"
-                        + " for the display state " + mDeviceState);
+                Slog.w(TAG, "applyLayoutLocked: The display device (" + address + "), is not "
+                        + "available for the display state " + mDeviceState);
                 continue;
             }
 
diff --git a/services/core/java/com/android/server/display/brightness/BrightnessReason.java b/services/core/java/com/android/server/display/brightness/BrightnessReason.java
index 8fe5f21..bc443a8 100644
--- a/services/core/java/com/android/server/display/brightness/BrightnessReason.java
+++ b/services/core/java/com/android/server/display/brightness/BrightnessReason.java
@@ -46,8 +46,10 @@
     public static final int MODIFIER_LOW_POWER = 0x2;
     public static final int MODIFIER_HDR = 0x4;
     public static final int MODIFIER_THROTTLED = 0x8;
+    public static final int MODIFIER_MIN_LUX = 0x10;
+    public static final int MODIFIER_MIN_USER_SET_LOWER_BOUND = 0x20;
     public static final int MODIFIER_MASK = MODIFIER_DIMMED | MODIFIER_LOW_POWER | MODIFIER_HDR
-            | MODIFIER_THROTTLED;
+            | MODIFIER_THROTTLED | MODIFIER_MIN_LUX | MODIFIER_MIN_USER_SET_LOWER_BOUND;
 
     // ADJUSTMENT_*
     // These things can happen at any point, even if the main brightness reason doesn't
@@ -131,6 +133,12 @@
         if ((mModifier & MODIFIER_THROTTLED) != 0) {
             sb.append(" throttled");
         }
+        if ((mModifier & MODIFIER_MIN_LUX) != 0) {
+            sb.append(" lux_lower_bound");
+        }
+        if ((mModifier & MODIFIER_MIN_USER_SET_LOWER_BOUND) != 0) {
+            sb.append(" user_min_pref");
+        }
         int strlen = sb.length();
         if (sb.charAt(strlen - 1) == '[') {
             sb.setLength(strlen - 2);
diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamper.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamper.java
index 42ebc40..fab769e 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamper.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamper.java
@@ -30,6 +30,7 @@
 abstract class BrightnessClamper<T> {
 
     protected float mBrightnessCap = PowerManager.BRIGHTNESS_MAX;
+
     protected boolean mIsActive = false;
 
     @NonNull
@@ -75,6 +76,5 @@
         THERMAL,
         POWER,
         BEDTIME_MODE,
-        LUX,
     }
 }
diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java
index 01694dd..2c02fc6 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java
@@ -58,13 +58,14 @@
     private final Executor mExecutor;
     private final List<BrightnessClamper<? super DisplayDeviceData>> mClampers;
 
-    private final List<BrightnessModifier> mModifiers;
+    private final List<BrightnessStateModifier> mModifiers;
     private final DeviceConfig.OnPropertiesChangedListener mOnPropertiesChangedListener;
     private float mBrightnessCap = PowerManager.BRIGHTNESS_MAX;
 
     private float mCustomAnimationRate = DisplayBrightnessState.CUSTOM_ANIMATION_RATE_NOT_SET;
     @Nullable
     private Type mClamperType = null;
+
     private boolean mClamperApplied = false;
 
     public BrightnessClamperController(Handler handler,
@@ -92,7 +93,7 @@
 
         mClampers = injector.getClampers(handler, clamperChangeListenerInternal, data, flags,
                 context);
-        mModifiers = injector.getModifiers(context);
+        mModifiers = injector.getModifiers(flags, context, handler, clamperChangeListener);
         mOnPropertiesChangedListener =
                 properties -> mClampers.forEach(BrightnessClamper::onDeviceConfigChanged);
         start();
@@ -165,9 +166,10 @@
      * Used to dump ClampersController state.
      */
     public void dump(PrintWriter writer) {
-        writer.println("BrightnessClampersController:");
+        writer.println("BrightnessClamperController:");
         writer.println("  mBrightnessCap: " + mBrightnessCap);
         writer.println("  mClamperType: " + mClamperType);
+        writer.println("  mClamperApplied: " + mClamperApplied);
         IndentingPrintWriter ipw = new IndentingPrintWriter(writer, "    ");
         mClampers.forEach(clamper -> clamper.dump(ipw));
         mModifiers.forEach(modifier -> modifier.dump(ipw));
@@ -181,6 +183,7 @@
         mDeviceConfigParameterProvider.removeOnPropertiesChangedListener(
                 mOnPropertiesChangedListener);
         mClampers.forEach(BrightnessClamper::stop);
+        mModifiers.forEach(BrightnessStateModifier::stop);
     }
 
 
@@ -201,14 +204,14 @@
             customAnimationRate = minClamper.getCustomAnimationRate();
         }
 
-        if (mBrightnessCap != brightnessCap || mClamperType != clamperType
+        if (mBrightnessCap != brightnessCap
+                || mClamperType != clamperType
                 || mCustomAnimationRate != customAnimationRate) {
             mBrightnessCap = brightnessCap;
             mClamperType = clamperType;
             mCustomAnimationRate = customAnimationRate;
             mClamperChangeListenerExternal.onChanged();
         }
-
     }
 
     private void start() {
@@ -248,16 +251,17 @@
                 clampers.add(new BrightnessWearBedtimeModeClamper(handler, context,
                         clamperChangeListener, data));
             }
-            if (flags.isEvenDimmerEnabled()) {
-                clampers.add(new BrightnessMinClamper(handler, clamperChangeListener, context));
-            }
             return clampers;
         }
 
-        List<BrightnessModifier> getModifiers(Context context) {
-            List<BrightnessModifier> modifiers = new ArrayList<>();
+        List<BrightnessStateModifier> getModifiers(DisplayManagerFlags flags, Context context,
+                Handler handler, ClamperChangeListener listener) {
+            List<BrightnessStateModifier> modifiers = new ArrayList<>();
             modifiers.add(new DisplayDimModifier(context));
             modifiers.add(new BrightnessLowPowerModeModifier());
+            if (flags.isEvenDimmerEnabled()) {
+                modifiers.add(new BrightnessLowLuxModifier(handler, listener, context));
+            }
             return modifiers;
         }
     }
diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessLowLuxModifier.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessLowLuxModifier.java
new file mode 100644
index 0000000..7f1f7a9
--- /dev/null
+++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessLowLuxModifier.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display.brightness.clamper;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.hardware.display.DisplayManagerInternal;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.PowerManager;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.display.BrightnessSynchronizer;
+import com.android.server.display.DisplayBrightnessState;
+import com.android.server.display.brightness.BrightnessReason;
+import com.android.server.display.utils.DebugUtils;
+
+import java.io.PrintWriter;
+
+/**
+ * Class used to prevent the screen brightness dipping below a certain value, based on current
+ * lux conditions and user preferred minimum.
+ */
+public class BrightnessLowLuxModifier implements
+        BrightnessStateModifier {
+
+    // To enable these logs, run:
+    // 'adb shell setprop persist.log.tag.BrightnessLowLuxModifier DEBUG && adb reboot'
+    private static final String TAG = "BrightnessLowLuxModifier";
+    private static final boolean DEBUG = DebugUtils.isDebuggable(TAG);
+    private final SettingsObserver mSettingsObserver;
+    private final ContentResolver mContentResolver;
+    private final Handler mHandler;
+    private final BrightnessClamperController.ClamperChangeListener mChangeListener;
+    protected float mSettingNitsLowerBound = PowerManager.BRIGHTNESS_MIN;
+    private int mReason;
+    private float mBrightnessLowerBound;
+    private boolean mIsActive;
+
+    @VisibleForTesting
+    BrightnessLowLuxModifier(Handler handler,
+            BrightnessClamperController.ClamperChangeListener listener, Context context) {
+        super();
+
+        mChangeListener = listener;
+        mHandler = handler;
+        mContentResolver = context.getContentResolver();
+        mSettingsObserver = new SettingsObserver(mHandler);
+        mHandler.post(() -> {
+            start();
+        });
+    }
+
+    /**
+     * Calculates new lower bound for brightness range, based on whether the setting is active,
+     * the user defined min brightness setting, and current lux environment.
+     */
+    @VisibleForTesting
+    public void recalculateLowerBound() {
+        int userId = UserHandle.USER_CURRENT;
+        float settingNitsLowerBound = Settings.Secure.getFloatForUser(
+                mContentResolver, Settings.Secure.EVEN_DIMMER_MIN_NITS,
+                /* def= */ PowerManager.BRIGHTNESS_MIN, userId);
+
+        boolean isActive = Settings.Secure.getIntForUser(mContentResolver,
+                Settings.Secure.EVEN_DIMMER_ACTIVATED,
+                /* def= */ 0, userId) == 1;
+
+        // TODO: luxBasedNitsLowerBound = mMinNitsToLuxSpline(currentLux);
+        float luxBasedNitsLowerBound = 0.0f;
+
+        // TODO: final float nitsLowerBound = isActive ? Math.max(settingNitsLowerBound,
+                // luxBasedNitsLowerBound) : PowerManager.BRIGHTNESS_MIN;
+
+        final int reason = settingNitsLowerBound > luxBasedNitsLowerBound
+                ? BrightnessReason.MODIFIER_MIN_USER_SET_LOWER_BOUND
+                : BrightnessReason.MODIFIER_MIN_LUX;
+
+        // TODO: brightnessLowerBound = nitsToBrightnessSpline(nitsLowerBound);
+        final float brightnessLowerBound = PowerManager.BRIGHTNESS_MIN;
+
+        if (mBrightnessLowerBound != brightnessLowerBound
+                || mReason != reason
+                || mIsActive != isActive) {
+            mIsActive = isActive;
+            mReason = reason;
+            if (DEBUG) {
+                Slog.i(TAG, "isActive: " + isActive
+                        + ", settingNitsLowerBound: " + settingNitsLowerBound
+                        + ", lowerBound: " + brightnessLowerBound);
+            }
+            mBrightnessLowerBound = brightnessLowerBound;
+            mChangeListener.onChanged();
+        }
+    }
+
+    @VisibleForTesting
+    public boolean isActive() {
+        return mIsActive;
+    }
+
+    @VisibleForTesting
+    public int getBrightnessReason() {
+        return mReason;
+    }
+
+    @VisibleForTesting
+    public float getBrightnessLowerBound() {
+        return mBrightnessLowerBound;
+    }
+
+    void start() {
+        recalculateLowerBound();
+    }
+
+    @Override
+    public void apply(DisplayManagerInternal.DisplayPowerRequest request,
+            DisplayBrightnessState.Builder stateBuilder) {
+        stateBuilder.setMinBrightness(mBrightnessLowerBound);
+        float boundedBrightness = Math.max(mBrightnessLowerBound, stateBuilder.getBrightness());
+        stateBuilder.setBrightness(boundedBrightness);
+
+        if (BrightnessSynchronizer.floatEquals(stateBuilder.getBrightness(),
+                mBrightnessLowerBound)) {
+            stateBuilder.getBrightnessReason().addModifier(mReason);
+        }
+    }
+
+    @Override
+    public void stop() {
+        mContentResolver.unregisterContentObserver(mSettingsObserver);
+    }
+
+    @Override
+    public void dump(PrintWriter pw) {
+        pw.println("BrightnessLowLuxModifier:");
+        pw.println("  mBrightnessLowerBound=" + mBrightnessLowerBound);
+        pw.println("  mIsActive=" + mIsActive);
+        pw.println("  mReason=" + mReason);
+    }
+
+    private final class SettingsObserver extends ContentObserver {
+        SettingsObserver(Handler handler) {
+            super(handler);
+            mContentResolver.registerContentObserver(
+                    Settings.Secure.getUriFor(Settings.Secure.EVEN_DIMMER_MIN_NITS),
+                    false, this);
+            mContentResolver.registerContentObserver(
+                    Settings.Secure.getUriFor(Settings.Secure.EVEN_DIMMER_ACTIVATED),
+                    false, this);
+        }
+
+        @Override
+        public void onChange(boolean selfChange, Uri uri) {
+            recalculateLowerBound();
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessMinClamper.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessMinClamper.java
deleted file mode 100644
index 71efca1..0000000
--- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessMinClamper.java
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.display.brightness.clamper;
-
-import android.content.ContentResolver;
-import android.content.Context;
-import android.database.ContentObserver;
-import android.net.Uri;
-import android.os.Handler;
-import android.os.PowerManager;
-import android.os.UserHandle;
-import android.provider.Settings;
-import android.util.Slog;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.display.utils.DebugUtils;
-
-import java.io.PrintWriter;
-
-/**
- * Class used to prevent the screen brightness dipping below a certain value, based on current
- * lux conditions.
- */
-public class BrightnessMinClamper extends BrightnessClamper {
-
-    // To enable these logs, run:
-    // 'adb shell setprop persist.log.tag.BrightnessMinClamper DEBUG && adb reboot'
-    private static final String TAG = "BrightnessMinClamper";
-    private static final boolean DEBUG = DebugUtils.isDebuggable(TAG);
-
-    private final SettingsObserver mSettingsObserver;
-
-    ContentResolver mContentResolver;
-    private float mNitsLowerBound;
-
-    @VisibleForTesting
-    BrightnessMinClamper(Handler handler,
-            BrightnessClamperController.ClamperChangeListener listener, Context context) {
-        super(handler, listener);
-
-        mContentResolver = context.getContentResolver();
-        mSettingsObserver = new SettingsObserver(mHandler);
-        mHandler.post(() -> {
-            start();
-        });
-    }
-
-    private void recalculateLowerBound() {
-        final int userId = UserHandle.USER_CURRENT;
-        float settingNitsLowerBound = Settings.Secure.getFloatForUser(
-                mContentResolver, Settings.Secure.EVEN_DIMMER_MIN_NITS,
-                /* def= */ PowerManager.BRIGHTNESS_MIN, userId);
-
-        boolean isActive = Settings.Secure.getIntForUser(mContentResolver,
-                Settings.Secure.EVEN_DIMMER_ACTIVATED,
-                /* def= */ 0, userId) == 1;
-
-        // TODO: luxBasedNitsLowerBound = mMinNitsToLuxSpline(currentLux);
-        float luxBasedNitsLowerBound = PowerManager.BRIGHTNESS_MIN;
-        final float nitsLowerBound = Math.max(settingNitsLowerBound, luxBasedNitsLowerBound);
-
-        if (mNitsLowerBound != nitsLowerBound || mIsActive != isActive) {
-            mIsActive = isActive;
-            mNitsLowerBound = nitsLowerBound;
-            if (DEBUG) {
-                Slog.i(TAG, "mIsActive: " + mIsActive);
-            }
-            // TODO: mBrightnessCap = nitsToBrightnessSpline(mNitsLowerBound);
-            mChangeListener.onChanged();
-        }
-    }
-
-    void start() {
-        recalculateLowerBound();
-    }
-
-
-    @Override
-    Type getType() {
-        return Type.LUX;
-    }
-
-    @Override
-    void onDeviceConfigChanged() {
-        // TODO
-    }
-
-    @Override
-    void onDisplayChanged(Object displayData) {
-
-    }
-
-    @Override
-    void stop() {
-        mContentResolver.unregisterContentObserver(mSettingsObserver);
-    }
-
-    @Override
-    void dump(PrintWriter pw) {
-        pw.println("BrightnessMinClamper:");
-        pw.println("  mBrightnessCap=" + mBrightnessCap);
-        pw.println("  mIsActive=" + mIsActive);
-        pw.println("  mNitsLowerBound=" + mNitsLowerBound);
-        super.dump(pw);
-    }
-
-    private final class SettingsObserver extends ContentObserver {
-        SettingsObserver(Handler handler) {
-            super(handler);
-            mContentResolver.registerContentObserver(
-                    Settings.Secure.getUriFor(Settings.Secure.EVEN_DIMMER_MIN_NITS),
-                    false, this);
-            mContentResolver.registerContentObserver(
-                    Settings.Secure.getUriFor(Settings.Secure.EVEN_DIMMER_ACTIVATED),
-                    false, this);
-        }
-
-        @Override
-        public void onChange(boolean selfChange, Uri uri) {
-            recalculateLowerBound();
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessModifier.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessModifier.java
index 112e63d..be8fa5a 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessModifier.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessModifier.java
@@ -26,7 +26,7 @@
 /**
  * Modifies current brightness based on request
  */
-abstract class BrightnessModifier {
+abstract class BrightnessModifier implements BrightnessStateModifier {
 
     private boolean mApplied = false;
 
@@ -37,7 +37,8 @@
 
     abstract int getModifier();
 
-    void apply(DisplayManagerInternal.DisplayPowerRequest request,
+    @Override
+    public void apply(DisplayManagerInternal.DisplayPowerRequest request,
             DisplayBrightnessState.Builder stateBuilder) {
         // If low power mode is enabled, scale brightness by screenLowPowerBrightnessFactor
         // as long as it is above the minimum threshold.
@@ -57,8 +58,14 @@
         }
     }
 
-    void dump(PrintWriter pw) {
+    @Override
+    public void dump(PrintWriter pw) {
         pw.println("BrightnessModifier:");
         pw.println("  mApplied=" + mApplied);
     }
+
+    @Override
+    public void stop() {
+        // do nothing
+    }
 }
diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessStateModifier.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessStateModifier.java
new file mode 100644
index 0000000..441ba8f
--- /dev/null
+++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessStateModifier.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display.brightness.clamper;
+
+import android.hardware.display.DisplayManagerInternal;
+
+import com.android.server.display.DisplayBrightnessState;
+
+import java.io.PrintWriter;
+
+public interface BrightnessStateModifier {
+    /**
+     * Applies the changes to brightness state, by modifying properties of the brightness
+     * state builder.
+     * @param request
+     * @param stateBuilder
+     */
+    void apply(DisplayManagerInternal.DisplayPowerRequest request,
+            DisplayBrightnessState.Builder stateBuilder);
+
+    /**
+     * Prints contents of this brightness state modifier
+     * @param printWriter
+     */
+    void dump(PrintWriter printWriter);
+
+    /**
+     * Called when stopped. Listeners can be unregistered here.
+     */
+    void stop();
+}
diff --git a/services/core/java/com/android/server/display/brightness/clamper/HdrClamper.java b/services/core/java/com/android/server/display/brightness/clamper/HdrClamper.java
index 200d88a..01a8d360a 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/HdrClamper.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/HdrClamper.java
@@ -104,8 +104,7 @@
     public void resetHdrConfig(HdrBrightnessData data, int width, int height,
             float minimumHdrPercentOfScreen, IBinder displayToken) {
         mHdrBrightnessData = data;
-        mHdrListener.mHdrMinPixels = minimumHdrPercentOfScreen <= 0 ? -1
-                : (float) (width * height) * minimumHdrPercentOfScreen;
+        mHdrListener.mHdrMinPixels = (float) (width * height) * minimumHdrPercentOfScreen;
         if (displayToken != mRegisteredDisplayToken) { // token changed, resubscribe
             if (mRegisteredDisplayToken != null) { // previous token not null, unsubscribe
                 mHdrListener.unregister(mRegisteredDisplayToken);
@@ -115,7 +114,7 @@
             // new token not null and hdr min % of the screen is set, subscribe.
             // e.g. for virtual display, HBM data will be missing and HdrListener
             // should not be registered
-            if (displayToken != null && mHdrListener.mHdrMinPixels > 0) {
+            if (displayToken != null && mHdrListener.mHdrMinPixels >= 0) {
                 mHdrListener.register(displayToken);
                 mRegisteredDisplayToken = displayToken;
             }
@@ -140,8 +139,11 @@
         pw.println("  mDesiredMaxBrightness=" + mDesiredMaxBrightness);
         pw.println("  mTransitionRate=" + mTransitionRate);
         pw.println("  mDesiredTransitionRate=" + mDesiredTransitionRate);
+        pw.println("  mHdrVisible=" + mHdrVisible);
+        pw.println("  mHdrListener.mHdrMinPixels=" + mHdrListener.mHdrMinPixels);
         pw.println("  mHdrBrightnessData=" + (mHdrBrightnessData == null ? "null"
                 : mHdrBrightnessData.toString()));
+        pw.println("  mHdrListener registered=" + (mRegisteredDisplayToken != null));
         pw.println("  mAmbientLux=" + mAmbientLux);
     }
 
diff --git a/services/core/java/com/android/server/display/config/DisplayBrightnessMappingConfig.java b/services/core/java/com/android/server/display/config/DisplayBrightnessMappingConfig.java
index 6978686..544f490 100644
--- a/services/core/java/com/android/server/display/config/DisplayBrightnessMappingConfig.java
+++ b/services/core/java/com/android/server/display/config/DisplayBrightnessMappingConfig.java
@@ -118,8 +118,13 @@
                             "The first lux value in the display brightness mapping must be 0");
                 }
 
-                String key = (mapping.getMode() == null ? "default" : mapping.getMode()) + "_"
-                        + (mapping.getSetting() == null ? "normal" : mapping.getSetting());
+                String key = (mapping.getMode() == null
+                        ? AutoBrightnessModeName._default.getRawName()
+                        : mapping.getMode().getRawName())
+                        + "_"
+                        + (mapping.getSetting() == null
+                        ? AutoBrightnessSettingName.normal.getRawName()
+                        : mapping.getSetting().getRawName());
                 if (mBrightnessLevelsMap.containsKey(key)
                         || mBrightnessLevelsLuxMap.containsKey(key)) {
                     throw new IllegalArgumentException(
diff --git a/services/core/java/com/android/server/display/feature/DeviceConfigParameterProvider.java b/services/core/java/com/android/server/display/feature/DeviceConfigParameterProvider.java
index 465584c..403dfbe 100644
--- a/services/core/java/com/android/server/display/feature/DeviceConfigParameterProvider.java
+++ b/services/core/java/com/android/server/display/feature/DeviceConfigParameterProvider.java
@@ -41,13 +41,6 @@
         mDeviceConfig = deviceConfig;
     }
 
-    // feature: revamping_display_power_controller_feature
-    // parameter: use_newly_structured_display_power_controller
-    public boolean isNewPowerControllerFeatureEnabled() {
-        return mDeviceConfig.getBoolean(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
-                DisplayManager.DeviceConfig.KEY_NEW_POWER_CONTROLLER, true);
-    }
-
     // feature: hdr_output_control
     // parameter: enable_hdr_output_control
     public boolean isHdrOutputControlFeatureEnabled() {
diff --git a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
index aa7f07d..be48eb4 100644
--- a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
+++ b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
@@ -37,6 +37,9 @@
     // 'adb shell setprop persist.log.tag.DisplayManagerFlags DEBUG && adb reboot'
     private static final boolean DEBUG = DebugUtils.isDebuggable(TAG);
 
+    private final FlagState mPortInDisplayLayoutFlagState = new FlagState(
+            Flags.FLAG_ENABLE_PORT_IN_DISPLAY_LAYOUT,
+            Flags::enablePortInDisplayLayout);
 
     private final FlagState mConnectedDisplayManagementFlagState = new FlagState(
             Flags.FLAG_ENABLE_CONNECTED_DISPLAY_MANAGEMENT,
@@ -109,6 +112,18 @@
             Flags.FLAG_FAST_HDR_TRANSITIONS,
             Flags::fastHdrTransitions);
 
+    private final FlagState mRefreshRateVotingTelemetry = new FlagState(
+            Flags.FLAG_REFRESH_RATE_VOTING_TELEMETRY,
+            Flags::refreshRateVotingTelemetry
+    );
+
+    /**
+     * @return {@code true} if 'port' is allowed in display layout configuration file.
+     */
+    public boolean isPortInDisplayLayoutEnabled() {
+        return mPortInDisplayLayoutFlagState.isEnabled();
+    }
+
     /** Returns whether connected display management is enabled or not. */
     public boolean isConnectedDisplayManagementEnabled() {
         return mConnectedDisplayManagementFlagState.isEnabled();
@@ -220,6 +235,10 @@
         return mFastHdrTransitions.isEnabled();
     }
 
+    public boolean isRefreshRateVotingTelemetryEnabled() {
+        return mRefreshRateVotingTelemetry.isEnabled();
+    }
+
     /**
      * dumps all flagstates
      * @param pw printWriter
@@ -242,6 +261,7 @@
         pw.println(" " + mBrightnessWearBedtimeModeClamperFlagState);
         pw.println(" " + mAutoBrightnessModesFlagState);
         pw.println(" " + mFastHdrTransitions);
+        pw.println(" " + mRefreshRateVotingTelemetry);
     }
 
     private static class FlagState {
diff --git a/services/core/java/com/android/server/display/feature/display_flags.aconfig b/services/core/java/com/android/server/display/feature/display_flags.aconfig
index e04efb7..a2319a8 100644
--- a/services/core/java/com/android/server/display/feature/display_flags.aconfig
+++ b/services/core/java/com/android/server/display/feature/display_flags.aconfig
@@ -3,6 +3,14 @@
 # Important: Flags must be accessed through DisplayManagerFlags.
 
 flag {
+    name: "enable_port_in_display_layout"
+    namespace: "display_manager"
+    description: "Allows refering to displays by port in display layout"
+    bug: "303058435"
+    is_fixed_read_only: true
+}
+
+flag {
     name: "enable_connected_display_management"
     namespace: "display_manager"
     description: "Feature flag for Connected Display management"
@@ -161,3 +169,10 @@
     is_fixed_read_only: true
 }
 
+flag {
+    name: "refresh_rate_voting_telemetry"
+    namespace: "display_manager"
+    description: "Feature flag for enabling telemetry for refresh rate voting in DisplayManager"
+    bug: "310029108"
+    is_fixed_read_only: true
+}
diff --git a/services/core/java/com/android/server/display/layout/Layout.java b/services/core/java/com/android/server/display/layout/Layout.java
index 40cb3303..8a362f7 100644
--- a/services/core/java/com/android/server/display/layout/Layout.java
+++ b/services/core/java/com/android/server/display/layout/Layout.java
@@ -200,13 +200,7 @@
      * @return True if the specified address is used in this layout.
      */
     public boolean contains(@NonNull DisplayAddress address) {
-        final int size = mDisplays.size();
-        for (int i = 0; i < size; i++) {
-            if (address.equals(mDisplays.get(i).getAddress())) {
-                return true;
-            }
-        }
-        return false;
+        return getByAddress(address) != null;
     }
 
     /**
@@ -237,6 +231,9 @@
             if (address.equals(display.getAddress())) {
                 return display;
             }
+            if (DisplayAddress.Physical.isPortMatch(address, display.getAddress())) {
+                return display;
+            }
         }
         return null;
     }
diff --git a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
index 6e503cb..50e9533 100644
--- a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
@@ -154,6 +154,9 @@
 
     private final VotesStorage mVotesStorage;
 
+    @Nullable
+    private final VotesStatsReporter mVotesStatsReporter;
+
     /**
      * The allowed refresh rate switching type. This is used by SurfaceFlinger.
      */
@@ -204,6 +207,8 @@
         mContext = context;
         mHandler = new DisplayModeDirectorHandler(handler.getLooper());
         mInjector = injector;
+        mVotesStatsReporter = injector.getVotesStatsReporter(
+                displayManagerFlags.isRefreshRateVotingTelemetryEnabled());
         mSupportedModesByDisplay = new SparseArray<>();
         mDefaultModeByDisplay = new SparseArray<>();
         mAppRequestObserver = new AppRequestObserver();
@@ -214,7 +219,8 @@
         mBrightnessObserver = new BrightnessObserver(context, handler, injector);
         mDefaultDisplayDeviceConfig = null;
         mUdfpsObserver = new UdfpsObserver();
-        mVotesStorage = new VotesStorage(this::notifyDesiredDisplayModeSpecsChangedLocked);
+        mVotesStorage = new VotesStorage(this::notifyDesiredDisplayModeSpecsChangedLocked,
+                mVotesStatsReporter);
         mDisplayObserver = new DisplayObserver(context, handler, mVotesStorage);
         mSensorObserver = new SensorObserver(context, mVotesStorage, injector);
         mSkinThermalStatusObserver = new SkinThermalStatusObserver(injector, mVotesStorage);
@@ -341,6 +347,11 @@
             appRequestSummary.limitRefreshRanges(primarySummary);
 
             Display.Mode baseMode = primarySummary.selectBaseMode(availableModes, defaultMode);
+            if (mVotesStatsReporter != null) {
+                mVotesStatsReporter.reportVotesActivated(displayId, lowestConsideredPriority,
+                        baseMode, votes);
+            }
+
             if (baseMode == null) {
                 Slog.w(TAG, "Can't find a set of allowed modes which satisfies the votes. Falling"
                         + " back to the default mode. Display = " + displayId + ", votes = " + votes
@@ -970,9 +981,14 @@
 
                 if (!mIsBackUpSmoothDisplayAndForcePeakRefreshRateEnabled) {
                     // The flag had been turned off, we need to restore the original value
-                    Settings.System.putFloatForUser(cr,
-                            Settings.System.MIN_REFRESH_RATE, minRefreshRate, cr.getUserId());
+                    Settings.System.putFloatForUser(cr, Settings.System.MIN_REFRESH_RATE,
+                            highestRefreshRate, cr.getUserId());
                 }
+            } else if (mIsBackUpSmoothDisplayAndForcePeakRefreshRateEnabled
+                    && Math.round(minRefreshRate) == Math.round(highestRefreshRate)) {
+                // The flag has been turned on, we need to upgrade the setting
+                Settings.System.putFloatForUser(cr, Settings.System.MIN_REFRESH_RATE,
+                        Float.POSITIVE_INFINITY, cr.getUserId());
             }
 
             float peakRefreshRate = Settings.System.getFloatForUser(cr,
@@ -983,9 +999,14 @@
 
                 if (!mIsBackUpSmoothDisplayAndForcePeakRefreshRateEnabled) {
                     // The flag had been turned off, we need to restore the original value
-                    Settings.System.putFloatForUser(cr,
-                            Settings.System.PEAK_REFRESH_RATE, peakRefreshRate, cr.getUserId());
+                    Settings.System.putFloatForUser(cr, Settings.System.PEAK_REFRESH_RATE,
+                            highestRefreshRate, cr.getUserId());
                 }
+            } else if (mIsBackUpSmoothDisplayAndForcePeakRefreshRateEnabled
+                    && Math.round(peakRefreshRate) == Math.round(highestRefreshRate)) {
+                // The flag has been turned on, we need to upgrade the setting
+                Settings.System.putFloatForUser(cr, Settings.System.PEAK_REFRESH_RATE,
+                        Float.POSITIVE_INFINITY, cr.getUserId());
             }
 
             updateRefreshRateSettingLocked(minRefreshRate, peakRefreshRate, mDefaultRefreshRate);
@@ -2811,6 +2832,9 @@
         StatusBarManagerInternal getStatusBarManagerInternal();
 
         SensorManagerInternal getSensorManagerInternal();
+
+        @Nullable
+        VotesStatsReporter getVotesStatsReporter(boolean refreshRateVotingTelemetryEnabled);
     }
 
     @VisibleForTesting
@@ -2943,6 +2967,13 @@
             return LocalServices.getService(SensorManagerInternal.class);
         }
 
+        @Override
+        public VotesStatsReporter getVotesStatsReporter(boolean refreshRateVotingTelemetryEnabled) {
+            // if frame rate override supported, renderRates will be ignored in mode selection
+            return new VotesStatsReporter(supportsFrameRateOverride(),
+                    refreshRateVotingTelemetryEnabled);
+        }
+
         private DisplayManager getDisplayManager() {
             if (mDisplayManager == null) {
                 mDisplayManager = mContext.getSystemService(DisplayManager.class);
diff --git a/services/core/java/com/android/server/display/mode/VotesStatsReporter.java b/services/core/java/com/android/server/display/mode/VotesStatsReporter.java
new file mode 100644
index 0000000..a30c4d2
--- /dev/null
+++ b/services/core/java/com/android/server/display/mode/VotesStatsReporter.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display.mode;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Trace;
+import android.util.SparseArray;
+import android.view.Display;
+
+/**
+ * The VotesStatsReporter is responsible for collecting and sending Vote related statistics 
+ */
+class VotesStatsReporter {
+    private static final String TAG = "VotesStatsReporter";
+    private static final int REFRESH_RATE_NOT_LIMITED = 1000;
+    private final boolean mIgnoredRenderRate;
+    private final boolean mFrameworkStatsLogReportingEnabled;
+
+    public VotesStatsReporter(boolean ignoreRenderRate, boolean refreshRateVotingTelemetryEnabled) {
+        mIgnoredRenderRate = ignoreRenderRate;
+        mFrameworkStatsLogReportingEnabled = refreshRateVotingTelemetryEnabled;
+    }
+
+    void reportVoteAdded(int displayId, int priority,  @NonNull Vote vote) {
+        int maxRefreshRate = getMaxRefreshRate(vote, mIgnoredRenderRate);
+        Trace.traceCounter(Trace.TRACE_TAG_POWER,
+                TAG + "." + displayId + ":" + Vote.priorityToString(priority), maxRefreshRate);
+        // if ( mFrameworkStatsLogReportingEnabled) {
+        // FrameworkStatsLog.write(VOTE_CHANGED, displayID, priority, ADDED, maxRefreshRate, -1);
+        // }
+    }
+
+    void reportVoteRemoved(int displayId, int priority) {
+        Trace.traceCounter(Trace.TRACE_TAG_POWER,
+                TAG + "." + displayId + ":" + Vote.priorityToString(priority), -1);
+        // if ( mFrameworkStatsLogReportingEnabled) {
+        // FrameworkStatsLog.write(VOTE_CHANGED, displayID, priority, REMOVED, -1, -1);
+        // }
+    }
+
+    void reportVotesActivated(int displayId, int minPriority, @Nullable Display.Mode baseMode,
+            SparseArray<Vote> votes) {
+//        if (!mFrameworkStatsLogReportingEnabled) {
+//            return;
+//        }
+//        int selectedRefreshRate = baseMode != null ? (int) baseMode.getRefreshRate() : -1;
+//        for (int priority = minPriority; priority <= Vote.MAX_PRIORITY; priority ++) {
+//            Vote vote = votes.get(priority);
+//            if (vote != null) {
+//                int maxRefreshRate = getMaxRefreshRate(vote, mIgnoredRenderRate);
+//                FrameworkStatsLog.write(VOTE_CHANGED, displayId, priority,
+//                        ACTIVE, maxRefreshRate, selectedRefreshRate);
+//            }
+//        }
+    }
+
+    private static int getMaxRefreshRate(@NonNull Vote vote, boolean ignoreRenderRate) {
+        int maxRefreshRate = REFRESH_RATE_NOT_LIMITED;
+        if (vote instanceof RefreshRateVote.PhysicalVote physicalVote) {
+            maxRefreshRate = (int) physicalVote.mMaxRefreshRate;
+        } else if (!ignoreRenderRate && (vote instanceof RefreshRateVote.RenderVote renderVote)) {
+            maxRefreshRate = (int)  renderVote.mMaxRefreshRate;
+        } else if (vote instanceof SupportedModesVote supportedModesVote) {
+            // SupportedModesVote limits mode by specific refreshRates, so highest rr is allowed
+            maxRefreshRate = 0;
+            for (SupportedModesVote.SupportedMode mode : supportedModesVote.mSupportedModes) {
+                maxRefreshRate = Math.max(maxRefreshRate, (int) mode.mPeakRefreshRate);
+            }
+        } else if (vote instanceof CombinedVote combinedVote) {
+            for (Vote subVote: combinedVote.mVotes) {
+                // CombinedVote should not have CombinedVote in mVotes, so recursion depth will be 1
+                maxRefreshRate = Math.min(maxRefreshRate,
+                        getMaxRefreshRate(subVote, ignoreRenderRate));
+            }
+        }
+        return maxRefreshRate;
+    }
+}
diff --git a/services/core/java/com/android/server/display/mode/VotesStorage.java b/services/core/java/com/android/server/display/mode/VotesStorage.java
index 95fb8fc..7a1f7e9 100644
--- a/services/core/java/com/android/server/display/mode/VotesStorage.java
+++ b/services/core/java/com/android/server/display/mode/VotesStorage.java
@@ -18,7 +18,6 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.os.Trace;
 import android.util.Slog;
 import android.util.SparseArray;
 
@@ -38,6 +37,9 @@
 
     private final Listener mListener;
 
+    @Nullable
+    private final VotesStatsReporter mVotesStatsReporter;
+
     private final Object mStorageLock = new Object();
     // A map from the display ID to the collection of votes and their priority. The latter takes
     // the form of another map from the priority to the vote itself so that each priority is
@@ -45,8 +47,9 @@
     @GuardedBy("mStorageLock")
     private final SparseArray<SparseArray<Vote>> mVotesByDisplay = new SparseArray<>();
 
-    VotesStorage(@NonNull Listener listener) {
+    VotesStorage(@NonNull Listener listener, @Nullable VotesStatsReporter votesStatsReporter) {
         mListener = listener;
+        mVotesStatsReporter = votesStatsReporter;
     }
     /** sets logging enabled/disabled for this class */
     void setLoggingEnabled(boolean loggingEnabled) {
@@ -110,17 +113,26 @@
                 changed = true;
             }
         }
-        Trace.traceCounter(Trace.TRACE_TAG_POWER,
-                TAG + "." + displayId + ":" + Vote.priorityToString(priority),
-                getMaxPhysicalRefreshRate(vote));
         if (mLoggingEnabled) {
             Slog.i(TAG, "Updated votes for display=" + displayId + " votes=" + votes);
         }
         if (changed) {
+            reportVoteStats(displayId, priority, vote);
             mListener.onChanged();
         }
     }
 
+    private void reportVoteStats(int displayId, int priority, @Nullable Vote vote) {
+        if (mVotesStatsReporter == null) {
+            return;
+        }
+        if (vote == null) {
+            mVotesStatsReporter.reportVoteRemoved(displayId, priority);
+        } else {
+            mVotesStatsReporter.reportVoteAdded(displayId, priority, vote);
+        }
+    }
+
     /** dump class values, for debugging */
     void dump(@NonNull PrintWriter pw) {
         SparseArray<SparseArray<Vote>> votesByDisplayLocal = new SparseArray<>();
@@ -157,21 +169,6 @@
         }
     }
 
-    private static int getMaxPhysicalRefreshRate(@Nullable Vote vote) {
-        if (vote == null) {
-            return -1;
-        } else if (vote instanceof RefreshRateVote.PhysicalVote physicalVote) {
-            return (int) physicalVote.mMaxRefreshRate;
-        } else if (vote instanceof CombinedVote combinedVote) {
-            return combinedVote.mVotes.stream()
-                    .filter(v -> v instanceof RefreshRateVote.PhysicalVote)
-                    .map(pv -> (int) (((RefreshRateVote.PhysicalVote) pv).mMaxRefreshRate))
-                    .min(Integer::compare)
-                    .orElse(1000); // for visualisation
-        }
-        return 1000; // for visualisation, otherwise e.g. -1 -> 60 will be unnoticeable
-    }
-
     interface Listener {
         void onChanged();
     }
diff --git a/services/core/java/com/android/server/input/InputManagerInternal.java b/services/core/java/com/android/server/input/InputManagerInternal.java
index 7e990c6..380106b 100644
--- a/services/core/java/com/android/server/input/InputManagerInternal.java
+++ b/services/core/java/com/android/server/input/InputManagerInternal.java
@@ -104,10 +104,13 @@
     public abstract PointF getCursorPosition();
 
     /**
-     * Sets the pointer acceleration.
-     * See {@code frameworks/native/include/input/VelocityControl.h#VelocityControlParameters}.
+     * Enables or disables pointer acceleration for mouse movements.
+     *
+     * Note that this only affects pointer movements from mice (that is, pointing devices which send
+     * relative motions, including trackballs and pointing sticks), not from other pointer devices
+     * such as touchpads and styluses.
      */
-    public abstract void setPointerAcceleration(float acceleration, int displayId);
+    public abstract void setMousePointerAccelerationEnabled(boolean enabled, int displayId);
 
     /**
      * Sets the eligibility of windows on a given display for pointer capture. If a display is
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 24e23003..bc7205a 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -48,6 +48,7 @@
 import android.hardware.input.IInputManager;
 import android.hardware.input.IInputSensorEventListener;
 import android.hardware.input.IKeyboardBacklightListener;
+import android.hardware.input.IStickyModifierStateListener;
 import android.hardware.input.ITabletModeChangedListener;
 import android.hardware.input.InputDeviceIdentifier;
 import android.hardware.input.InputManager;
@@ -63,7 +64,6 @@
 import android.os.Environment;
 import android.os.Handler;
 import android.os.IBinder;
-import android.os.IInputConstants;
 import android.os.IVibratorStateListener;
 import android.os.InputEventInjectionResult;
 import android.os.InputEventInjectionSync;
@@ -319,6 +319,9 @@
     // Manages Keyboard backlight
     private final KeyboardBacklightControllerInterface mKeyboardBacklightController;
 
+    // Manages Sticky modifier state
+    private final StickyModifierStateController mStickyModifierStateController;
+
     // Manages Keyboard modifier keys remapping
     private final KeyRemapper mKeyRemapper;
 
@@ -464,6 +467,7 @@
                 ? new KeyboardBacklightController(mContext, mNative, mDataStore,
                         injector.getLooper(), injector.getUEventManager())
                 : new KeyboardBacklightControllerInterface() {};
+        mStickyModifierStateController = new StickyModifierStateController();
         mKeyRemapper = new KeyRemapper(mContext, mNative, mDataStore, injector.getLooper());
 
         mUseDevInputEventForAudioJack =
@@ -1367,9 +1371,9 @@
         mNative.setPointerSpeed(speed);
     }
 
-    private void setPointerAcceleration(float acceleration, int displayId) {
+    private void setMousePointerAccelerationEnabled(boolean enabled, int displayId) {
         updateAdditionalDisplayInputProperties(displayId,
-                properties -> properties.pointerAcceleration = acceleration);
+                properties -> properties.mousePointerAccelerationEnabled = enabled);
     }
 
     private void setPointerIconVisible(boolean visible, int displayId) {
@@ -2256,7 +2260,8 @@
                             + mAdditionalDisplayInputProperties.keyAt(i));
                     final AdditionalDisplayInputProperties properties =
                             mAdditionalDisplayInputProperties.valueAt(i);
-                    pw.println("pointerAcceleration: " + properties.pointerAcceleration);
+                    pw.println("mousePointerAccelerationEnabled: "
+                            + properties.mousePointerAccelerationEnabled);
                     pw.println("pointerIconVisible: " + properties.pointerIconVisible);
                 }
                 pw.decreaseIndent();
@@ -2523,9 +2528,9 @@
     // Native callback.
     @SuppressWarnings("unused")
     private int interceptMotionBeforeQueueingNonInteractive(int displayId,
-            long whenNanos, int policyFlags) {
+            int source, int action, long whenNanos, int policyFlags) {
         return mWindowManagerCallbacks.interceptMotionBeforeQueueingNonInteractive(
-                displayId, whenNanos, policyFlags);
+                displayId, source, action, whenNanos, policyFlags);
     }
 
     // Native callback.
@@ -2827,6 +2832,33 @@
                         yPosition)).sendToTarget();
     }
 
+    @Override
+    @EnforcePermission(Manifest.permission.MONITOR_STICKY_MODIFIER_STATE)
+    public void registerStickyModifierStateListener(
+            @NonNull IStickyModifierStateListener listener) {
+        super.registerStickyModifierStateListener_enforcePermission();
+        Objects.requireNonNull(listener);
+        mStickyModifierStateController.registerStickyModifierStateListener(listener,
+                Binder.getCallingPid());
+    }
+
+    @Override
+    @EnforcePermission(Manifest.permission.MONITOR_STICKY_MODIFIER_STATE)
+    public void unregisterStickyModifierStateListener(
+            @NonNull IStickyModifierStateListener listener) {
+        super.unregisterStickyModifierStateListener_enforcePermission();
+        Objects.requireNonNull(listener);
+        mStickyModifierStateController.unregisterStickyModifierStateListener(listener,
+                Binder.getCallingPid());
+    }
+
+    // Native callback
+    @SuppressWarnings("unused")
+    void notifyStickyModifierStateChanged(int modifierState, int lockedModifierState) {
+        mStickyModifierStateController.notifyStickyModifierStateChanged(modifierState,
+                lockedModifierState);
+    }
+
     // Native callback.
     @SuppressWarnings("unused")
     boolean isInputMethodConnectionActive() {
@@ -2901,8 +2933,8 @@
          * processing when the device is in a non-interactive state since these events are normally
          * dropped.
          */
-        int interceptMotionBeforeQueueingNonInteractive(int displayId, long whenNanos,
-                int policyFlags);
+        int interceptMotionBeforeQueueingNonInteractive(int displayId, int source, int action,
+                long whenNanos, int policyFlags);
 
         /**
          * This callback is invoked just before the key is about to be sent to an application.
@@ -3257,8 +3289,8 @@
         }
 
         @Override
-        public void setPointerAcceleration(float acceleration, int displayId) {
-            InputManagerService.this.setPointerAcceleration(acceleration, displayId);
+        public void setMousePointerAccelerationEnabled(boolean enabled, int displayId) {
+            InputManagerService.this.setMousePointerAccelerationEnabled(enabled, displayId);
         }
 
         @Override
@@ -3350,11 +3382,15 @@
     private static class AdditionalDisplayInputProperties {
 
         static final boolean DEFAULT_POINTER_ICON_VISIBLE = true;
-        static final float DEFAULT_POINTER_ACCELERATION =
-                (float) IInputConstants.DEFAULT_POINTER_ACCELERATION;
+        static final boolean DEFAULT_MOUSE_POINTER_ACCELERATION_ENABLED = true;
 
-        // The pointer acceleration for this display.
-        public float pointerAcceleration;
+        /**
+         * Whether to enable mouse pointer acceleration on this display. Note that this only affects
+         * pointer movements from mice (that is, pointing devices which send relative motions,
+         * including trackballs and pointing sticks), not from other pointer devices such as
+         * touchpads and styluses.
+         */
+        public boolean mousePointerAccelerationEnabled;
 
         // Whether the pointer icon should be visible or hidden on this display.
         public boolean pointerIconVisible;
@@ -3364,12 +3400,12 @@
         }
 
         public boolean allDefaults() {
-            return Float.compare(pointerAcceleration, DEFAULT_POINTER_ACCELERATION) == 0
+            return mousePointerAccelerationEnabled == DEFAULT_MOUSE_POINTER_ACCELERATION_ENABLED
                     && pointerIconVisible == DEFAULT_POINTER_ICON_VISIBLE;
         }
 
         public void reset() {
-            pointerAcceleration = DEFAULT_POINTER_ACCELERATION;
+            mousePointerAccelerationEnabled = DEFAULT_MOUSE_POINTER_ACCELERATION_ENABLED;
             pointerIconVisible = DEFAULT_POINTER_ICON_VISIBLE;
         }
     }
@@ -3401,9 +3437,11 @@
             }
         }
 
-        if (properties.pointerAcceleration != mCurrentDisplayProperties.pointerAcceleration) {
-            mCurrentDisplayProperties.pointerAcceleration = properties.pointerAcceleration;
-            mNative.setPointerAcceleration(properties.pointerAcceleration);
+        if (properties.mousePointerAccelerationEnabled
+                != mCurrentDisplayProperties.mousePointerAccelerationEnabled) {
+            mCurrentDisplayProperties.mousePointerAccelerationEnabled =
+                    properties.mousePointerAccelerationEnabled;
+            mNative.setMousePointerAccelerationEnabled(properties.mousePointerAccelerationEnabled);
         }
     }
 
diff --git a/services/core/java/com/android/server/input/InputSettingsObserver.java b/services/core/java/com/android/server/input/InputSettingsObserver.java
index c9668a2..be8d2a4 100644
--- a/services/core/java/com/android/server/input/InputSettingsObserver.java
+++ b/services/core/java/com/android/server/input/InputSettingsObserver.java
@@ -86,9 +86,9 @@
                         (reason) -> updateKeyRepeatInfo()),
                 Map.entry(Settings.System.getUriFor(Settings.System.SHOW_ROTARY_INPUT),
                         (reason) -> updateShowRotaryInput()),
-                Map.entry(Settings.System.getUriFor(Settings.Secure.ACCESSIBILITY_BOUNCE_KEYS),
+                Map.entry(Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_BOUNCE_KEYS),
                         (reason) -> updateAccessibilityBounceKeys()),
-                Map.entry(Settings.System.getUriFor(Settings.Secure.ACCESSIBILITY_STICKY_KEYS),
+                Map.entry(Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_STICKY_KEYS),
                         (reason) -> updateAccessibilityStickyKeys()));
     }
 
diff --git a/services/core/java/com/android/server/input/NativeInputManagerService.java b/services/core/java/com/android/server/input/NativeInputManagerService.java
index 829b660..dd9204c 100644
--- a/services/core/java/com/android/server/input/NativeInputManagerService.java
+++ b/services/core/java/com/android/server/input/NativeInputManagerService.java
@@ -118,7 +118,7 @@
 
     void setPointerSpeed(int speed);
 
-    void setPointerAcceleration(float acceleration);
+    void setMousePointerAccelerationEnabled(boolean enabled);
 
     void setTouchpadPointerSpeed(int speed);
 
@@ -351,7 +351,7 @@
         public native void setPointerSpeed(int speed);
 
         @Override
-        public native void setPointerAcceleration(float acceleration);
+        public native void setMousePointerAccelerationEnabled(boolean enabled);
 
         @Override
         public native void setTouchpadPointerSpeed(int speed);
diff --git a/services/core/java/com/android/server/input/StickyModifierStateController.java b/services/core/java/com/android/server/input/StickyModifierStateController.java
new file mode 100644
index 0000000..5a22c10
--- /dev/null
+++ b/services/core/java/com/android/server/input/StickyModifierStateController.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.input;
+
+import android.annotation.BinderThread;
+import android.hardware.input.IStickyModifierStateListener;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+import android.util.Slog;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+
+/**
+ * A thread-safe component of {@link InputManagerService} responsible for managing the sticky
+ * modifier state for A11y Sticky keys feature.
+ */
+final class StickyModifierStateController {
+
+    private static final String TAG = "ModifierStateController";
+
+    // To enable these logs, run:
+    // 'adb shell setprop log.tag.ModifierStateController DEBUG' (requires restart)
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+    // List of currently registered sticky modifier state listeners
+    @GuardedBy("mStickyModifierStateListenerRecords")
+    private final SparseArray<StickyModifierStateListenerRecord>
+            mStickyModifierStateListenerRecords = new SparseArray<>();
+
+    public void notifyStickyModifierStateChanged(int modifierState, int lockedModifierState) {
+        if (DEBUG) {
+            Slog.d(TAG, "Sticky modifier state changed, modifierState = " + modifierState
+                    + ", lockedModifierState = " + lockedModifierState);
+        }
+
+        synchronized (mStickyModifierStateListenerRecords) {
+            for (int i = 0; i < mStickyModifierStateListenerRecords.size(); i++) {
+                mStickyModifierStateListenerRecords.valueAt(i).notifyStickyModifierStateChanged(
+                        modifierState, lockedModifierState);
+            }
+        }
+    }
+
+    /** Register the sticky modifier state listener for a process. */
+    @BinderThread
+    public void registerStickyModifierStateListener(IStickyModifierStateListener listener,
+            int pid) {
+        synchronized (mStickyModifierStateListenerRecords) {
+            if (mStickyModifierStateListenerRecords.get(pid) != null) {
+                throw new IllegalStateException("The calling process has already registered "
+                        + "a StickyModifierStateListener.");
+            }
+            StickyModifierStateListenerRecord record = new StickyModifierStateListenerRecord(pid,
+                    listener);
+            try {
+                listener.asBinder().linkToDeath(record, 0);
+            } catch (RemoteException ex) {
+                throw new RuntimeException(ex);
+            }
+            mStickyModifierStateListenerRecords.put(pid, record);
+        }
+    }
+
+    /** Unregister the sticky modifier state listener for a process. */
+    @BinderThread
+    public void unregisterStickyModifierStateListener(IStickyModifierStateListener listener,
+            int pid) {
+        synchronized (mStickyModifierStateListenerRecords) {
+            StickyModifierStateListenerRecord record = mStickyModifierStateListenerRecords.get(pid);
+            if (record == null) {
+                throw new IllegalStateException("The calling process has no registered "
+                        + "StickyModifierStateListener.");
+            }
+            if (record.mListener.asBinder() != listener.asBinder()) {
+                throw new IllegalStateException("The calling process has a different registered "
+                        + "StickyModifierStateListener.");
+            }
+            record.mListener.asBinder().unlinkToDeath(record, 0);
+            mStickyModifierStateListenerRecords.remove(pid);
+        }
+    }
+
+    private void onStickyModifierStateListenerDied(int pid) {
+        synchronized (mStickyModifierStateListenerRecords) {
+            mStickyModifierStateListenerRecords.remove(pid);
+        }
+    }
+
+    // A record of a registered sticky modifier state listener from one process.
+    private class StickyModifierStateListenerRecord implements IBinder.DeathRecipient {
+        public final int mPid;
+        public final IStickyModifierStateListener mListener;
+
+        StickyModifierStateListenerRecord(int pid, IStickyModifierStateListener listener) {
+            mPid = pid;
+            mListener = listener;
+        }
+
+        @Override
+        public void binderDied() {
+            if (DEBUG) {
+                Slog.d(TAG, "Sticky modifier state listener for pid " + mPid + " died.");
+            }
+            onStickyModifierStateListenerDied(mPid);
+        }
+
+        public void notifyStickyModifierStateChanged(int modifierState, int lockedModifierState) {
+            try {
+                mListener.onStickyModifierStateChanged(modifierState, lockedModifierState);
+            } catch (RemoteException ex) {
+                Slog.w(TAG, "Failed to notify process " + mPid
+                        + " that sticky modifier state changed, assuming it died.", ex);
+                binderDied();
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/inputmethod/ClientController.java b/services/core/java/com/android/server/inputmethod/ClientController.java
new file mode 100644
index 0000000..2934640
--- /dev/null
+++ b/services/core/java/com/android/server/inputmethod/ClientController.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.inputmethod;
+
+import android.annotation.NonNull;
+import android.content.pm.PackageManagerInternal;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.ArrayMap;
+import android.util.SparseArray;
+import android.view.inputmethod.InputBinding;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.inputmethod.IInputMethodClient;
+import com.android.internal.inputmethod.IRemoteInputConnection;
+
+/**
+ * Store and manage {@link InputMethodManagerService} clients. This class was designed to be a
+ * singleton in {@link InputMethodManagerService} since it stores information about all clients,
+ * still the current client will be defined per display.
+ *
+ * <p>
+ * As part of the re-architecture plan (described in go/imms-rearchitecture-plan), the following
+ * fields and methods will be moved out from IMMS and placed here:
+ * <ul>
+ * <li>mCurClient (ClientState)</li>
+ * <li>mClients (ArrayMap of ClientState indexed by IBinder)</li>
+ * <li>mLastSwitchUserId</li>
+ * </ul>
+ * <p>
+ * Nested Classes (to move from IMMS):
+ * <ul>
+ * <li>ClientDeathRecipient</li>
+ * <li>ClientState<</li>
+ * </ul>
+ * <p>
+ * Methods to rewrite and/or extract from IMMS and move here:
+ * <ul>
+ * <li>addClient</li>
+ * <li>removeClient</li>
+ * <li>verifyClientAndPackageMatch</li>
+ * <li>setImeTraceEnabledForAllClients (make it reactive)</li>
+ * <li>unbindCurrentClient</li>
+ * </ul>
+ */
+// TODO(b/314150112): Update the Javadoc above, by removing the re-architecture steps, once this
+//  class is finalized
+final class ClientController {
+
+    // TODO(b/314150112): Make this field private when breaking the cycle with IMMS.
+    @GuardedBy("ImfLock.class")
+    final ArrayMap<IBinder, ClientState> mClients = new ArrayMap<>();
+
+    private final PackageManagerInternal mPackageManagerInternal;
+
+    ClientController(PackageManagerInternal packageManagerInternal) {
+        mPackageManagerInternal = packageManagerInternal;
+    }
+
+    @GuardedBy("ImfLock.class")
+    void addClient(IInputMethodClientInvoker clientInvoker,
+            IRemoteInputConnection inputConnection,
+            int selfReportedDisplayId, IBinder.DeathRecipient deathRecipient, int callerUid,
+            int callerPid) {
+        // TODO: Optimize this linear search.
+        final int numClients = mClients.size();
+        for (int i = 0; i < numClients; ++i) {
+            final ClientState state = mClients.valueAt(i);
+            if (state.mUid == callerUid && state.mPid == callerPid
+                    && state.mSelfReportedDisplayId == selfReportedDisplayId) {
+                throw new SecurityException("uid=" + callerUid + "/pid=" + callerPid
+                        + "/displayId=" + selfReportedDisplayId + " is already registered");
+            }
+        }
+        try {
+            clientInvoker.asBinder().linkToDeath(deathRecipient, 0 /* flags */);
+        } catch (RemoteException e) {
+            throw new IllegalStateException(e);
+        }
+        // We cannot fully avoid race conditions where the client UID already lost the access to
+        // the given self-reported display ID, even if the client is not maliciously reporting
+        // a fake display ID. Unconditionally returning SecurityException just because the
+        // client doesn't pass display ID verification can cause many test failures hence not an
+        // option right now.  At the same time
+        //    context.getSystemService(InputMethodManager.class)
+        // is expected to return a valid non-null instance at any time if we do not choose to
+        // have the client crash.  Thus we do not verify the display ID at all here.  Instead we
+        // later check the display ID every time the client needs to interact with the specified
+        // display.
+        mClients.put(clientInvoker.asBinder(), new ClientState(clientInvoker, inputConnection,
+                callerUid, callerPid, selfReportedDisplayId, deathRecipient));
+    }
+
+    @GuardedBy("ImfLock.class")
+    boolean verifyClientAndPackageMatch(
+            @NonNull IInputMethodClient client, @NonNull String packageName) {
+        ClientState cs = mClients.get(client.asBinder());
+        if (cs == null) {
+            throw new IllegalArgumentException("unknown client " + client.asBinder());
+        }
+        return InputMethodUtils.checkIfPackageBelongsToUid(
+                mPackageManagerInternal, cs.mUid, packageName);
+    }
+
+    static final class ClientState {
+        final IInputMethodClientInvoker mClient;
+        final IRemoteInputConnection mFallbackInputConnection;
+        final int mUid;
+        final int mPid;
+        final int mSelfReportedDisplayId;
+        final InputBinding mBinding;
+        final IBinder.DeathRecipient mClientDeathRecipient;
+
+        @GuardedBy("ImfLock.class")
+        boolean mSessionRequested;
+
+        @GuardedBy("ImfLock.class")
+        boolean mSessionRequestedForAccessibility;
+
+        @GuardedBy("ImfLock.class")
+        InputMethodManagerService.SessionState mCurSession;
+
+        @GuardedBy("ImfLock.class")
+        SparseArray<InputMethodManagerService.AccessibilitySessionState> mAccessibilitySessions =
+                new SparseArray<>();
+
+        @Override
+        public String toString() {
+            return "ClientState{" + Integer.toHexString(
+                    System.identityHashCode(this)) + " mUid=" + mUid
+                    + " mPid=" + mPid + " mSelfReportedDisplayId=" + mSelfReportedDisplayId + "}";
+        }
+
+        ClientState(IInputMethodClientInvoker client,
+                IRemoteInputConnection fallbackInputConnection,
+                int uid, int pid, int selfReportedDisplayId,
+                IBinder.DeathRecipient clientDeathRecipient) {
+            mClient = client;
+            mFallbackInputConnection = fallbackInputConnection;
+            mUid = uid;
+            mPid = pid;
+            mSelfReportedDisplayId = selfReportedDisplayId;
+            mBinding = new InputBinding(null /*conn*/, mFallbackInputConnection.asBinder(), mUid,
+                    mPid);
+            mClientDeathRecipient = clientDeathRecipient;
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/inputmethod/HandwritingModeController.java b/services/core/java/com/android/server/inputmethod/HandwritingModeController.java
index 66807ae..f96bb8fb 100644
--- a/services/core/java/com/android/server/inputmethod/HandwritingModeController.java
+++ b/services/core/java/com/android/server/inputmethod/HandwritingModeController.java
@@ -52,6 +52,7 @@
 import java.util.List;
 import java.util.Objects;
 import java.util.OptionalInt;
+import java.util.function.IntConsumer;
 
 // TODO(b/210039666): See if we can make this class thread-safe.
 final class HandwritingModeController {
@@ -84,14 +85,14 @@
     private boolean mDelegatorFromDefaultHomePackage;
     private Runnable mDelegationIdleTimeoutRunnable;
     private Handler mDelegationIdleTimeoutHandler;
-
+    private IntConsumer mPointerToolTypeConsumer;
     private HandwritingEventReceiverSurface mHandwritingSurface;
 
     private int mCurrentRequestId;
 
     @AnyThread
     HandwritingModeController(Context context, Looper uiThreadLooper,
-            Runnable inkWindowInitRunnable) {
+            Runnable inkWindowInitRunnable, IntConsumer toolTypeConsumer) {
         mContext = context;
         mLooper = uiThreadLooper;
         mCurrentDisplayId = Display.INVALID_DISPLAY;
@@ -100,6 +101,7 @@
         mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
         mCurrentRequestId = 0;
         mInkWindowInitRunnable = inkWindowInitRunnable;
+        mPointerToolTypeConsumer = toolTypeConsumer;
     }
 
     /**
@@ -355,6 +357,11 @@
             return false;
         }
         final MotionEvent event = (MotionEvent) ev;
+        if (mPointerToolTypeConsumer != null && event.getAction() == MotionEvent.ACTION_DOWN) {
+            int toolType = event.getToolType(event.getActionIndex());
+            // notify IME of change in tool type.
+            mPointerToolTypeConsumer.accept(toolType);
+        }
         if (!event.isStylusPointer()) {
             return false;
         }
diff --git a/services/core/java/com/android/server/inputmethod/HardwareKeyboardShortcutController.java b/services/core/java/com/android/server/inputmethod/HardwareKeyboardShortcutController.java
index f0e4b0f5..898d5a5 100644
--- a/services/core/java/com/android/server/inputmethod/HardwareKeyboardShortcutController.java
+++ b/services/core/java/com/android/server/inputmethod/HardwareKeyboardShortcutController.java
@@ -19,6 +19,8 @@
 import android.annotation.AnyThread;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.util.ArrayMap;
 import android.view.inputmethod.InputMethodInfo;
 import android.view.inputmethod.InputMethodSubtype;
 
@@ -33,9 +35,26 @@
     @GuardedBy("ImfLock.class")
     private final ArrayList<InputMethodSubtypeHandle> mSubtypeHandles = new ArrayList<>();
 
+    @UserIdInt
+    private final int mUserId;
+
+    @AnyThread
+    @UserIdInt
+    int getUserId() {
+        return mUserId;
+    }
+
+    HardwareKeyboardShortcutController(
+            @NonNull ArrayMap<String, InputMethodInfo> methodMap, @UserIdInt int userId) {
+        mUserId = userId;
+        reset(methodMap);
+    }
+
     @GuardedBy("ImfLock.class")
-    void reset(@NonNull InputMethodUtils.InputMethodSettings settings) {
+    void reset(@NonNull ArrayMap<String, InputMethodInfo> methodMap) {
         mSubtypeHandles.clear();
+        final InputMethodUtils.InputMethodSettings settings =
+                new InputMethodUtils.InputMethodSettings(methodMap, mUserId);
         for (final InputMethodInfo imi : settings.getEnabledInputMethodListLocked()) {
             if (!imi.shouldShowInInputMethodPicker()) {
                 continue;
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 0d29b7d..25ec683 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -16,6 +16,7 @@
 
 package com.android.server.inputmethod;
 
+import static android.content.Context.DEVICE_ID_DEFAULT;
 import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_CRITICAL;
 import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL;
 import static android.os.IServiceManager.DUMP_FLAG_PROTO;
@@ -47,6 +48,7 @@
 import static android.view.WindowManager.DISPLAY_IME_POLICY_HIDE;
 import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL;
 
+import static com.android.server.inputmethod.ClientController.ClientState;
 import static com.android.server.inputmethod.ImeVisibilityStateComputer.ImeTargetWindowState;
 import static com.android.server.inputmethod.ImeVisibilityStateComputer.ImeVisibilityResult;
 import static com.android.server.inputmethod.ImeVisibilityStateComputer.STATE_HIDE_IME;
@@ -124,8 +126,8 @@
 import android.view.WindowManager.LayoutParams;
 import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
 import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.Flags;
 import android.view.inputmethod.ImeTracker;
-import android.view.inputmethod.InputBinding;
 import android.view.inputmethod.InputConnection;
 import android.view.inputmethod.InputMethod;
 import android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceFileProto;
@@ -206,6 +208,7 @@
 import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.concurrent.Future;
 import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.IntConsumer;
 
 /**
  * This class provides a system service that manages input methods.
@@ -270,13 +273,15 @@
     @NonNull
     private final String[] mNonPreemptibleInputMethods;
 
+    // TODO(b/314150112): Move this to ClientController.
     @UserIdInt
     private int mLastSwitchUserId;
 
     final Context mContext;
     final Resources mRes;
     private final Handler mHandler;
-    final InputMethodSettings mSettings;
+    @NonNull
+    private InputMethodSettings mSettings;
     final SettingsObserver mSettingsObserver;
     private final SparseBooleanArray mLoggedDeniedGetInputMethodWindowVisibleHeightForUid =
             new SparseBooleanArray(0);
@@ -313,12 +318,17 @@
     // All known input methods.
     final ArrayList<InputMethodInfo> mMethodList = new ArrayList<>();
     private final ArrayMap<String, InputMethodInfo> mMethodMap = new ArrayMap<>();
+
     // Mapping from deviceId to the device-specific imeId for that device.
+    @GuardedBy("ImfLock.class")
     private final SparseArray<String> mVirtualDeviceMethodMap = new SparseArray<>();
 
-    final InputMethodSubtypeSwitchingController mSwitchingController;
-    final HardwareKeyboardShortcutController mHardwareKeyboardShortcutController =
-            new HardwareKeyboardShortcutController();
+    // TODO: Instantiate mSwitchingController for each user.
+    @NonNull
+    private InputMethodSubtypeSwitchingController mSwitchingController;
+    // TODO: Instantiate mHardwareKeyboardShortcutController for each user.
+    @NonNull
+    private HardwareKeyboardShortcutController mHardwareKeyboardShortcutController;
 
     /**
      * Tracks how many times {@link #mMethodMap} was updated.
@@ -337,6 +347,9 @@
     @GuardedBy("ImfLock.class")
     private int mDisplayIdToShowIme = INVALID_DISPLAY;
 
+    @GuardedBy("ImfLock.class")
+    private int mDeviceIdToShowIme = DEVICE_ID_DEFAULT;
+
     @Nullable private StatusBarManagerInternal mStatusBarManagerInternal;
     private boolean mShowOngoingImeSwitcherForPhones;
     @GuardedBy("ImfLock.class")
@@ -381,7 +394,7 @@
     /**
      * Record session state for an accessibility service.
      */
-    private static class AccessibilitySessionState {
+    static class AccessibilitySessionState {
         final ClientState mClient;
         // Id of the accessibility service.
         final int mId;
@@ -405,58 +418,10 @@
         }
     }
 
-    private static final class ClientDeathRecipient implements IBinder.DeathRecipient {
-        private final InputMethodManagerService mImms;
-        private final IInputMethodClient mClient;
-
-        ClientDeathRecipient(InputMethodManagerService imms, IInputMethodClient client) {
-            mImms = imms;
-            mClient = client;
-        }
-
-        @Override
-        public void binderDied() {
-            mImms.removeClient(mClient);
-        }
-    }
-
-    static final class ClientState {
-        final IInputMethodClientInvoker mClient;
-        final IRemoteInputConnection mFallbackInputConnection;
-        final int mUid;
-        final int mPid;
-        final int mSelfReportedDisplayId;
-        final InputBinding mBinding;
-        final ClientDeathRecipient mClientDeathRecipient;
-
-        boolean mSessionRequested;
-        boolean mSessionRequestedForAccessibility;
-        SessionState mCurSession;
-        SparseArray<AccessibilitySessionState> mAccessibilitySessions = new SparseArray<>();
-
-        @Override
-        public String toString() {
-            return "ClientState{" + Integer.toHexString(
-                    System.identityHashCode(this)) + " mUid=" + mUid
-                    + " mPid=" + mPid + " mSelfReportedDisplayId=" + mSelfReportedDisplayId + "}";
-        }
-
-        ClientState(IInputMethodClientInvoker client,
-                IRemoteInputConnection fallbackInputConnection,
-                int uid, int pid, int selfReportedDisplayId,
-                ClientDeathRecipient clientDeathRecipient) {
-            mClient = client;
-            mFallbackInputConnection = fallbackInputConnection;
-            mUid = uid;
-            mPid = pid;
-            mSelfReportedDisplayId = selfReportedDisplayId;
-            mBinding = new InputBinding(null, mFallbackInputConnection.asBinder(), mUid, mPid);
-            mClientDeathRecipient = clientDeathRecipient;
-        }
-    }
-
-    @GuardedBy("ImfLock.class")
-    final ArrayMap<IBinder, ClientState> mClients = new ArrayMap<>();
+    /**
+     * Manages the IME clients.
+     */
+    private final ClientController mClientController;
 
     /**
      * Set once the system is ready to run third party code.
@@ -514,6 +479,7 @@
     /**
      * The client that is currently bound to an input method.
      */
+    // TODO(b/314150112): Move this to ClientController.
     @Nullable
     private ClientState mCurClient;
 
@@ -854,8 +820,9 @@
             @Nullable
             final String mImeSurfaceParentName;
 
-            Entry(ClientState client, EditorInfo editorInfo, String focusedWindowName,
-                    @SoftInputModeFlags int softInputMode, @SoftInputShowHideReason int reason,
+            Entry(ClientState client, EditorInfo editorInfo,
+                    String focusedWindowName, @SoftInputModeFlags int softInputMode,
+                    @SoftInputShowHideReason int reason,
                     boolean inFullscreenMode, String requestWindowName,
                     @Nullable String imeControlTargetName, @Nullable String imeTargetName,
                     @Nullable String imeSurfaceParentName) {
@@ -1619,7 +1586,7 @@
             if (userId != currentUserId) {
                 return;
             }
-            mSettings.switchCurrentUser(currentUserId, !mSystemReady);
+            mSettings = new InputMethodSettings(mMethodMap, userId);
             if (mSystemReady) {
                 // We need to rebuild IMEs.
                 buildInputMethodListLocked(false /* resetDefaultEnabledIme */);
@@ -1693,12 +1660,14 @@
         mLastSwitchUserId = userId;
 
         // mSettings should be created before buildInputMethodListLocked
-        mSettings = new InputMethodSettings(mMethodMap, userId, !mSystemReady);
+        mSettings = new InputMethodSettings(mMethodMap, userId);
 
         AdditionalSubtypeUtils.load(mAdditionalSubtypeMap, userId);
         mSwitchingController =
-                InputMethodSubtypeSwitchingController.createInstanceLocked(mSettings, context);
-        mHardwareKeyboardShortcutController.reset(mSettings);
+                InputMethodSubtypeSwitchingController.createInstanceLocked(context, mMethodMap,
+                        userId);
+        mHardwareKeyboardShortcutController =
+                new HardwareKeyboardShortcutController(mMethodMap, userId);
         mMenuController = new InputMethodMenuController(this);
         mBindingController =
                 bindingControllerForTesting != null
@@ -1708,13 +1677,17 @@
 
         mVisibilityStateComputer = new ImeVisibilityStateComputer(this);
         mVisibilityApplier = new DefaultImeVisibilityApplier(this);
+        mClientController = new ClientController(mPackageManagerInternal);
 
         mPreventImeStartupUnlessTextEditor = mRes.getBoolean(
                 com.android.internal.R.bool.config_preventImeStartupUnlessTextEditor);
         mNonPreemptibleInputMethods = mRes.getStringArray(
                 com.android.internal.R.array.config_nonPreemptibleInputMethods);
+        IntConsumer toolTypeConsumer =
+                Flags.useHandwritingListenerForTooltype()
+                        ? toolType -> onUpdateEditorToolType(toolType) : null;
         mHwController = new HandwritingModeController(mContext, thread.getLooper(),
-                new InkWindowInitializer());
+                new InkWindowInitializer(), toolTypeConsumer);
         registerDeviceListenerAndCheckStylusSupport();
     }
 
@@ -1735,6 +1708,15 @@
         }
     }
 
+    private void onUpdateEditorToolType(int toolType) {
+        synchronized (ImfLock.class) {
+            IInputMethodInvoker curMethod = getCurMethodLocked();
+            if (curMethod != null) {
+                curMethod.updateEditorToolType(toolType);
+            }
+        }
+    }
+
     @GuardedBy("ImfLock.class")
     private void resetDefaultImeLocked(Context context) {
         // Do not reset the default (current) IME when it is a 3rd-party IME
@@ -1807,11 +1789,7 @@
         // ContentObserver should be registered again when the user is changed
         mSettingsObserver.registerContentObserverLocked(newUserId);
 
-        // If the system is not ready or the device is not yed unlocked by the user, then we use
-        // copy-on-write settings.
-        final boolean useCopyOnWriteSettings =
-                !mSystemReady || !mUserManagerInternal.isUserUnlockingOrUnlocked(newUserId);
-        mSettings.switchCurrentUser(newUserId, useCopyOnWriteSettings);
+        mSettings = new InputMethodSettings(mMethodMap, newUserId);
         // Additional subtypes should be reset when the user is changed
         AdditionalSubtypeUtils.load(mAdditionalSubtypeMap, newUserId);
         final String defaultImiId = mSettings.getSelectedInputMethod();
@@ -1853,7 +1831,8 @@
         mLastSwitchUserId = newUserId;
 
         if (mIsInteractive && clientToBeReset != null) {
-            final ClientState cs = mClients.get(clientToBeReset.asBinder());
+            final ClientState cs =
+                    mClientController.mClients.get(clientToBeReset.asBinder());
             if (cs == null) {
                 // The client is already gone.
                 return;
@@ -1873,8 +1852,6 @@
             if (!mSystemReady) {
                 mSystemReady = true;
                 final int currentUserId = mSettings.getCurrentUserId();
-                mSettings.switchCurrentUser(currentUserId,
-                        !mUserManagerInternal.isUserUnlockingOrUnlocked(currentUserId));
                 mStatusBarManagerInternal =
                         LocalServices.getService(StatusBarManagerInternal.class);
                 hideStatusBarIconLocked();
@@ -2023,7 +2000,7 @@
             //TODO(b/197848765): This can be optimized by caching multi-user methodMaps/methodList.
             //TODO(b/210039666): use cache.
             final ArrayMap<String, InputMethodInfo> methodMap = queryMethodMapForUser(userId);
-            final InputMethodSettings settings = new InputMethodSettings(methodMap, userId, true);
+            final InputMethodSettings settings = new InputMethodSettings(methodMap, userId);
             final InputMethodInfo imi = methodMap.get(settings.getSelectedInputMethod());
             return imi != null && imi.supportsStylusHandwriting();
         }
@@ -2059,7 +2036,7 @@
             AdditionalSubtypeUtils.load(additionalSubtypeMap, userId);
             queryInputMethodServicesInternal(mContext, userId, additionalSubtypeMap, methodMap,
                     methodList, directBootAwareness);
-            settings = new InputMethodSettings(methodMap, userId, true /* copyOnWrite */);
+            settings = new InputMethodSettings(methodMap, userId);
         }
         // filter caller's access to input methods
         methodList.removeIf(imi ->
@@ -2077,7 +2054,7 @@
             settings = mSettings;
         } else {
             final ArrayMap<String, InputMethodInfo> methodMap = queryMethodMapForUser(userId);
-            settings = new InputMethodSettings(methodMap, userId, true /* copyOnWrite */);
+            settings = new InputMethodSettings(methodMap, userId);
             methodList = settings.getEnabledInputMethodListLocked();
         }
         // filter caller's access to input methods
@@ -2157,7 +2134,7 @@
         if (imi == null) {
             return Collections.emptyList();
         }
-        final InputMethodSettings settings = new InputMethodSettings(methodMap, userId, true);
+        final InputMethodSettings settings = new InputMethodSettings(methodMap, userId);
         if (!canCallerAccessInputMethod(imi.getPackageName(), callingUid, userId, settings)) {
             return Collections.emptyList();
         }
@@ -2192,43 +2169,22 @@
         // actually running.
         final int callerUid = Binder.getCallingUid();
         final int callerPid = Binder.getCallingPid();
+
+        // TODO(b/314150112): Move the death recipient logic to ClientController when moving
+        //     removeClient method.
+        final IBinder.DeathRecipient deathRecipient = () -> removeClient(client);
+        final IInputMethodClientInvoker clientInvoker =
+                IInputMethodClientInvoker.create(client, mHandler);
         synchronized (ImfLock.class) {
-            // TODO: Optimize this linear search.
-            final int numClients = mClients.size();
-            for (int i = 0; i < numClients; ++i) {
-                final ClientState state = mClients.valueAt(i);
-                if (state.mUid == callerUid && state.mPid == callerPid
-                        && state.mSelfReportedDisplayId == selfReportedDisplayId) {
-                    throw new SecurityException("uid=" + callerUid + "/pid=" + callerPid
-                            + "/displayId=" + selfReportedDisplayId + " is already registered.");
-                }
-            }
-            final ClientDeathRecipient deathRecipient = new ClientDeathRecipient(this, client);
-            try {
-                client.asBinder().linkToDeath(deathRecipient, 0 /* flags */);
-            } catch (RemoteException e) {
-                throw new IllegalStateException(e);
-            }
-            // We cannot fully avoid race conditions where the client UID already lost the access to
-            // the given self-reported display ID, even if the client is not maliciously reporting
-            // a fake display ID. Unconditionally returning SecurityException just because the
-            // client doesn't pass display ID verification can cause many test failures hence not an
-            // option right now.  At the same time
-            //    context.getSystemService(InputMethodManager.class)
-            // is expected to return a valid non-null instance at any time if we do not choose to
-            // have the client crash.  Thus we do not verify the display ID at all here.  Instead we
-            // later check the display ID every time the client needs to interact with the specified
-            // display.
-            final IInputMethodClientInvoker clientInvoker =
-                    IInputMethodClientInvoker.create(client, mHandler);
-            mClients.put(client.asBinder(), new ClientState(clientInvoker, inputConnection,
-                    callerUid, callerPid, selfReportedDisplayId, deathRecipient));
+            mClientController.addClient(clientInvoker, inputConnection, selfReportedDisplayId,
+                    deathRecipient, callerUid, callerPid);
         }
     }
 
+    // TODO(b/314150112): Move this to ClientController.
     void removeClient(IInputMethodClient client) {
         synchronized (ImfLock.class) {
-            ClientState cs = mClients.remove(client.asBinder());
+            ClientState cs = mClientController.mClients.remove(client.asBinder());
             if (cs != null) {
                 client.asBinder().unlinkToDeath(cs.mClientDeathRecipient, 0 /* flags */);
                 clearClientSessionLocked(cs);
@@ -2258,6 +2214,7 @@
         }
     }
 
+    // TODO(b/314150112): Move this to ClientController.
     @GuardedBy("ImfLock.class")
     void unbindCurrentClientLocked(@UnbindReason int unbindClientReason) {
         if (mCurClient != null) {
@@ -2310,7 +2267,10 @@
         }
     }
 
-    /** {@code true} when a {@link ClientState} has attached from starting the input connection. */
+    /**
+     * {@code true} when a {@link ClientState} has attached from starting the
+     * input connection.
+     */
     @GuardedBy("ImfLock.class")
     boolean hasAttachedClient() {
         return mCurClient != null;
@@ -2451,11 +2411,7 @@
             @StartInputReason int startInputReason,
             int unverifiedTargetSdkVersion,
             @NonNull ImeOnBackInvokedDispatcher imeDispatcher) {
-        // If no method is currently selected, do nothing.
-        final String selectedMethodId = getSelectedMethodIdLocked();
-        if (selectedMethodId == null) {
-            return InputBindResult.NO_IME;
-        }
+        String selectedMethodId = getSelectedMethodIdLocked();
 
         if (!mSystemReady) {
             // If the system is not yet ready, we shouldn't be running third
@@ -2480,8 +2436,21 @@
             return InputBindResult.NOT_IME_TARGET_WINDOW;
         }
         final int csDisplayId = cs.mSelfReportedDisplayId;
+        final int oldDisplayIdToShowIme = mDisplayIdToShowIme;
         mDisplayIdToShowIme = mVisibilityStateComputer.computeImeDisplayId(winState, csDisplayId);
 
+        // Potentially override the selected input method if the new display belongs to a virtual
+        // device with a custom IME.
+        if (oldDisplayIdToShowIme != mDisplayIdToShowIme) {
+            final String deviceMethodId = computeCurrentDeviceMethodIdLocked(selectedMethodId);
+            if (deviceMethodId == null) {
+                mVisibilityStateComputer.getImePolicy().setImeHiddenByDisplayPolicy(true);
+            } else if (!Objects.equals(deviceMethodId, selectedMethodId)) {
+                setInputMethodLocked(deviceMethodId, NOT_A_SUBTYPE_ID, mDeviceIdToShowIme);
+                selectedMethodId = deviceMethodId;
+            }
+        }
+
         if (mVisibilityStateComputer.getImePolicy().isImeHiddenByDisplayPolicy()) {
             hideCurrentInputLocked(mCurFocusedWindow, null /* statsToken */, 0 /* flags */,
                     null /* resultReceiver */,
@@ -2489,6 +2458,11 @@
             return InputBindResult.NO_IME;
         }
 
+        // If no method is currently selected, do nothing.
+        if (selectedMethodId == null) {
+            return InputBindResult.NO_IME;
+        }
+
         if (mCurClient != cs) {
             prepareClientSwitchLocked(cs);
         }
@@ -2555,6 +2529,62 @@
         return mBindingController.bindCurrentMethod();
     }
 
+    /**
+     * Update the current deviceId and return the relevant imeId for this device.
+     *   1. If the device changes to virtual and its custom IME is not available, then disable IME.
+     *   2. If the device changes to virtual with valid custom IME, then return the custom IME. If
+     *      the old device was default, then store the current imeId so it can be restored.
+     *   3. If the device changes to default, restore the default device IME.
+     *   4. Otherwise keep the current imeId.
+     */
+    @GuardedBy("ImfLock.class")
+    private String computeCurrentDeviceMethodIdLocked(String currentMethodId) {
+        if (mVdmInternal == null) {
+            mVdmInternal = LocalServices.getService(VirtualDeviceManagerInternal.class);
+        }
+        if (mVdmInternal == null || !android.companion.virtual.flags.Flags.vdmCustomIme()) {
+            return currentMethodId;
+        }
+
+        final int oldDeviceId = mDeviceIdToShowIme;
+        mDeviceIdToShowIme = mVdmInternal.getDeviceIdForDisplayId(mDisplayIdToShowIme);
+        if (mDeviceIdToShowIme == oldDeviceId) {
+            return currentMethodId;
+        }
+        if (mDeviceIdToShowIme == DEVICE_ID_DEFAULT) {
+            final String defaultDeviceMethodId = mSettings.getSelectedDefaultDeviceInputMethod();
+            if (DEBUG) {
+                Slog.v(TAG, "Restoring default device input method: " + defaultDeviceMethodId);
+            }
+            return defaultDeviceMethodId;
+        }
+
+        final String deviceMethodId =
+                mVirtualDeviceMethodMap.get(mDeviceIdToShowIme, currentMethodId);
+        if (Objects.equals(deviceMethodId, currentMethodId)) {
+            return currentMethodId;
+        } else if (!mMethodMap.containsKey(deviceMethodId)) {
+            if (DEBUG) {
+                Slog.v(TAG, "Disabling IME on virtual device with id " + mDeviceIdToShowIme
+                        + " because its custom input method is not available: " + deviceMethodId);
+            }
+            return null;
+        }
+
+        if (oldDeviceId == DEVICE_ID_DEFAULT) {
+            if (DEBUG) {
+                Slog.v(TAG, "Storing default device input method " + currentMethodId);
+            }
+            mSettings.putSelectedDefaultDeviceInputMethod(currentMethodId);
+        }
+        if (DEBUG) {
+            Slog.v(TAG, "Switching current input method from " + currentMethodId
+                    + " to device-specific one " + deviceMethodId + " because the current display "
+                    + mDisplayIdToShowIme + " belongs to device with id " + mDeviceIdToShowIme);
+        }
+        return deviceMethodId;
+    }
+
     @GuardedBy("ImfLock.class")
     void invalidateAutofillSessionLocked() {
         mAutofillController.invalidateAutofillSession();
@@ -2884,10 +2914,10 @@
     @GuardedBy("ImfLock.class")
     void clearClientSessionsLocked() {
         if (getCurMethodLocked() != null) {
-            final int numClients = mClients.size();
+            final int numClients = mClientController.mClients.size();
             for (int i = 0; i < numClients; ++i) {
-                clearClientSessionLocked(mClients.valueAt(i));
-                clearClientSessionForAccessibilityLocked(mClients.valueAt(i));
+                clearClientSessionLocked(mClientController.mClients.valueAt(i));
+                clearClientSessionForAccessibilityLocked(mClientController.mClients.valueAt(i));
             }
 
             finishSessionLocked(mEnabledSession);
@@ -3205,13 +3235,21 @@
             // There is no longer an input method set, so stop any current one.
             resetCurrentMethodAndClientLocked(UnbindReason.NO_IME);
         }
-        // Here is not the perfect place to reset the switching controller. Ideally
-        // mSwitchingController and mSettings should be able to share the same state.
-        // TODO: Make sure that mSwitchingController and mSettings are sharing the
-        // the same enabled IMEs list.
-        mSwitchingController.resetCircularListLocked(mContext);
-        mHardwareKeyboardShortcutController.reset(mSettings);
 
+        // TODO: Instantiate mSwitchingController for each user.
+        if (mSettings.getCurrentUserId() == mSwitchingController.getUserId()) {
+            mSwitchingController.resetCircularListLocked(mMethodMap);
+        } else {
+            mSwitchingController = InputMethodSubtypeSwitchingController.createInstanceLocked(
+                    mContext, mMethodMap, mSettings.getCurrentUserId());
+        }
+        // TODO: Instantiate mHardwareKeyboardShortcutController for each user.
+        if (mSettings.getCurrentUserId() == mHardwareKeyboardShortcutController.getUserId()) {
+            mHardwareKeyboardShortcutController.reset(mMethodMap);
+        } else {
+            mHardwareKeyboardShortcutController = new HardwareKeyboardShortcutController(
+                    mMethodMap, mSettings.getCurrentUserId());
+        }
         sendOnNavButtonFlagsChangedLocked();
     }
 
@@ -3229,6 +3267,11 @@
 
     @GuardedBy("ImfLock.class")
     void setInputMethodLocked(String id, int subtypeId) {
+        setInputMethodLocked(id, subtypeId, DEVICE_ID_DEFAULT);
+    }
+
+    @GuardedBy("ImfLock.class")
+    void setInputMethodLocked(String id, int subtypeId, int deviceId) {
         InputMethodInfo info = mMethodMap.get(id);
         if (info == null) {
             throw getExceptionForUnknownImeId(id);
@@ -3272,6 +3315,14 @@
         }
 
         // Changing to a different IME.
+        if (mDeviceIdToShowIme != DEVICE_ID_DEFAULT && deviceId == DEVICE_ID_DEFAULT) {
+            // This change should only be applicable to the default device but the current input
+            // method is a custom one specific to a virtual device. So only update the settings
+            // entry used to restore the default device input method once we want to show the IME
+            // back on the default device.
+            mSettings.putSelectedDefaultDeviceInputMethod(id);
+            return;
+        }
         IInputMethodInvoker curMethod = getCurMethodLocked();
         if (curMethod != null) {
             curMethod.removeStylusHandwritingWindow();
@@ -3401,9 +3452,12 @@
                     + " pref is disabled for user: " + userId);
             return;
         }
-        if (!verifyClientAndPackageMatch(client, delegatorPackageName)) {
-            Slog.w(TAG, "prepareStylusHandwritingDelegation() fail");
-            throw new IllegalArgumentException("Delegator doesn't match Uid");
+        synchronized (ImfLock.class) {
+            if (!mClientController.verifyClientAndPackageMatch(client,
+                    delegatorPackageName)) {
+                Slog.w(TAG, "prepareStylusHandwritingDelegation() fail");
+                throw new IllegalArgumentException("Delegator doesn't match Uid");
+            }
         }
         schedulePrepareStylusHandwritingDelegation(
                 userId, delegatePackageName, delegatorPackageName);
@@ -3429,30 +3483,17 @@
         return true;
     }
 
-    private boolean verifyClientAndPackageMatch(
-            @NonNull IInputMethodClient client, @NonNull String packageName) {
-        ClientState cs;
-        synchronized (ImfLock.class) {
-            cs = mClients.get(client.asBinder());
-        }
-        if (cs == null) {
-            throw new IllegalArgumentException("unknown client " + client.asBinder());
-        }
-        return InputMethodUtils.checkIfPackageBelongsToUid(
-                mPackageManagerInternal, cs.mUid, packageName);
-    }
-
     private boolean verifyDelegator(
             @NonNull IInputMethodClient client,
             @NonNull String delegatePackageName,
             @NonNull String delegatorPackageName,
             @InputMethodManager.HandwritingDelegateFlags int flags) {
-        if (!verifyClientAndPackageMatch(client, delegatePackageName)) {
-            Slog.w(TAG, "Delegate package does not belong to the same user. Ignoring"
-                    + " startStylusHandwriting");
-            return false;
-        }
         synchronized (ImfLock.class) {
+            if (!mClientController.verifyClientAndPackageMatch(client, delegatePackageName)) {
+                Slog.w(TAG, "Delegate package does not belong to the same user. Ignoring"
+                        + " startStylusHandwriting");
+                return false;
+            }
             boolean homeDelegatorAllowed =
                     (flags & InputMethodManager.HANDWRITING_DELEGATE_FLAG_HOME_DELEGATOR_ALLOWED)
                             != 0;
@@ -3526,7 +3567,8 @@
             ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_SERVER_HAS_IME);
             mCurStatsToken = null;
 
-            if (lastClickToolType != MotionEvent.TOOL_TYPE_UNKNOWN) {
+            if (!Flags.useHandwritingListenerForTooltype()
+                    && lastClickToolType != MotionEvent.TOOL_TYPE_UNKNOWN) {
                 curMethod.updateEditorToolType(lastClickToolType);
             }
             mVisibilityApplier.performShowIme(windowToken, statsToken,
@@ -3714,7 +3756,7 @@
             return InputBindResult.INVALID_USER;
         }
 
-        final ClientState cs = mClients.get(client.asBinder());
+        final ClientState cs = mClientController.mClients.get(client.asBinder());
         if (cs == null) {
             throw new IllegalArgumentException("unknown client " + client.asBinder());
         }
@@ -3888,7 +3930,8 @@
             // We need to check if this is the current client with
             // focus in the window manager, to allow this call to
             // be made before input is started in it.
-            final ClientState cs = mClients.get(client.asBinder());
+            final ClientState cs =
+                    mClientController.mClients.get(client.asBinder());
             if (cs == null) {
                 ImeTracker.forLogging().onFailed(statsToken, ImeTracker.PHASE_SERVER_CLIENT_KNOWN);
                 throw new IllegalArgumentException("unknown client " + client.asBinder());
@@ -4134,7 +4177,7 @@
             }
 
             final ArrayMap<String, InputMethodInfo> methodMap = queryMethodMapForUser(userId);
-            final InputMethodSettings settings = new InputMethodSettings(methodMap, userId, false);
+            final InputMethodSettings settings = new InputMethodSettings(methodMap, userId);
             return settings.getLastInputMethodSubtypeLocked();
         }
     }
@@ -4186,7 +4229,7 @@
             AdditionalSubtypeUtils.load(additionalSubtypeMap, userId);
             queryInputMethodServicesInternal(mContext, userId, additionalSubtypeMap, methodMap,
                     methodList, DirectBootAwareness.AUTO);
-            final InputMethodSettings settings = new InputMethodSettings(methodMap, userId, false);
+            final InputMethodSettings settings = new InputMethodSettings(methodMap, userId);
             settings.setAdditionalInputMethodSubtypes(imiId, toBeAdded, additionalSubtypeMap,
                     mPackageManagerInternal, callingUid);
         }
@@ -4215,8 +4258,7 @@
                 final boolean currentUser = (mSettings.getCurrentUserId() == userId);
                 final InputMethodSettings settings = currentUser
                         ? mSettings
-                        : new InputMethodSettings(queryMethodMapForUser(userId), userId,
-                                !mUserManagerInternal.isUserUnlocked(userId));
+                        : new InputMethodSettings(queryMethodMapForUser(userId), userId);
                 if (!settings.setEnabledInputMethodSubtypes(imeId, subtypeHashCodes)) {
                     return;
                 }
@@ -4513,7 +4555,7 @@
         ImeTracing.getInstance().startTrace(null /* printwriter */);
         ArrayMap<IBinder, ClientState> clients;
         synchronized (ImfLock.class) {
-            clients = new ArrayMap<>(mClients);
+            clients = new ArrayMap<>(mClientController.mClients);
         }
         for (ClientState state : clients.values()) {
             if (state != null) {
@@ -4531,7 +4573,7 @@
         ImeTracing.getInstance().stopTrace(null /* printwriter */);
         ArrayMap<IBinder, ClientState> clients;
         synchronized (ImfLock.class) {
-            clients = new ArrayMap<>(mClients);
+            clients = new ArrayMap<>(mClientController.mClients);
         }
         for (ClientState state : clients.values()) {
             if (state != null) {
@@ -4585,6 +4627,9 @@
                 }
                 return;
             }
+            if (mSettings.getCurrentUserId() != mSwitchingController.getUserId()) {
+                return;
+            }
             final InputMethodInfo imi = mMethodMap.get(getSelectedMethodIdLocked());
             if (imi != null) {
                 mSwitchingController.onUserActionLocked(imi, mCurrentSubtype);
@@ -4814,7 +4859,21 @@
                         Slog.e(TAG, "Unknown subtype picker mode = " + msg.arg1);
                         return false;
                 }
-                mMenuController.showInputMethodMenu(showAuxSubtypes, displayId);
+                synchronized (ImfLock.class) {
+                    final boolean isScreenLocked = mWindowManagerInternal.isKeyguardLocked()
+                            && mWindowManagerInternal.isKeyguardSecure(
+                                    mSettings.getCurrentUserId());
+                    final String lastInputMethodId = mSettings.getSelectedInputMethod();
+                    int lastInputMethodSubtypeId =
+                            mSettings.getSelectedInputMethodSubtypeId(lastInputMethodId);
+
+                    final List<ImeSubtypeListItem> imList = InputMethodSubtypeSwitchingController
+                            .getSortedInputMethodAndSubtypeList(
+                                    showAuxSubtypes, isScreenLocked, false, mContext,
+                                    mMethodMap, mSettings.getCurrentUserId());
+                    mMenuController.showInputMethodMenuLocked(showAuxSubtypes, displayId,
+                            lastInputMethodId, lastInputMethodSubtypeId, imList);
+                }
                 return true;
 
             // ---------------------------------------------------------
@@ -5196,12 +5255,20 @@
 
         updateDefaultVoiceImeIfNeededLocked();
 
-        // Here is not the perfect place to reset the switching controller. Ideally
-        // mSwitchingController and mSettings should be able to share the same state.
-        // TODO: Make sure that mSwitchingController and mSettings are sharing the
-        // the same enabled IMEs list.
-        mSwitchingController.resetCircularListLocked(mContext);
-        mHardwareKeyboardShortcutController.reset(mSettings);
+        // TODO: Instantiate mSwitchingController for each user.
+        if (mSettings.getCurrentUserId() == mSwitchingController.getUserId()) {
+            mSwitchingController.resetCircularListLocked(mMethodMap);
+        } else {
+            mSwitchingController = InputMethodSubtypeSwitchingController.createInstanceLocked(
+                    mContext, mMethodMap, mSettings.getCurrentUserId());
+        }
+        // TODO: Instantiate mHardwareKeyboardShortcutController for each user.
+        if (mSettings.getCurrentUserId() == mHardwareKeyboardShortcutController.getUserId()) {
+            mHardwareKeyboardShortcutController.reset(mMethodMap);
+        } else {
+            mHardwareKeyboardShortcutController = new HardwareKeyboardShortcutController(
+                    mMethodMap, mSettings.getCurrentUserId());
+        }
 
         sendOnNavButtonFlagsChangedLocked();
 
@@ -5264,29 +5331,39 @@
      */
     @GuardedBy("ImfLock.class")
     private boolean setInputMethodEnabledLocked(String id, boolean enabled) {
-        List<Pair<String, ArrayList<String>>> enabledInputMethodsList = mSettings
-                .getEnabledInputMethodsAndSubtypeListLocked();
-
         if (enabled) {
-            for (Pair<String, ArrayList<String>> pair: enabledInputMethodsList) {
-                if (pair.first.equals(id)) {
-                    // We are enabling this input method, but it is already enabled.
-                    // Nothing to do. The previous state was enabled.
-                    return true;
-                }
+            final String enabledImeIdsStr = mSettings.getEnabledInputMethodsStr();
+            final String newEnabledImeIdsStr = InputMethodUtils.concatEnabledImeIds(
+                    enabledImeIdsStr, id);
+            if (TextUtils.equals(enabledImeIdsStr, newEnabledImeIdsStr)) {
+                // We are enabling this input method, but it is already enabled.
+                // Nothing to do. The previous state was enabled.
+                return true;
             }
-            mSettings.appendAndPutEnabledInputMethodLocked(id);
+            mSettings.putEnabledInputMethodsStr(newEnabledImeIdsStr);
             // Previous state was disabled.
             return false;
         } else {
+            final List<Pair<String, ArrayList<String>>> enabledInputMethodsList = mSettings
+                    .getEnabledInputMethodsAndSubtypeListLocked();
             StringBuilder builder = new StringBuilder();
             if (mSettings.buildAndPutEnabledInputMethodsStrRemovingIdLocked(
                     builder, enabledInputMethodsList, id)) {
-                // Disabled input method is currently selected, switch to another one.
-                final String selId = mSettings.getSelectedInputMethod();
-                if (id.equals(selId) && !chooseNewDefaultIMELocked()) {
-                    Slog.i(TAG, "Can't find new IME, unsetting the current input method.");
-                    resetSelectedInputMethodAndSubtypeLocked("");
+                if (mDeviceIdToShowIme == DEVICE_ID_DEFAULT) {
+                    // Disabled input method is currently selected, switch to another one.
+                    final String selId = mSettings.getSelectedInputMethod();
+                    if (id.equals(selId) && !chooseNewDefaultIMELocked()) {
+                        Slog.i(TAG, "Can't find new IME, unsetting the current input method.");
+                        resetSelectedInputMethodAndSubtypeLocked("");
+                    }
+                } else if (id.equals(mSettings.getSelectedDefaultDeviceInputMethod())) {
+                    // Disabled default device IME while using a virtual device one, choose a
+                    // new default one but only update the settings.
+                    InputMethodInfo newDefaultIme =
+                            InputMethodInfoUtils.getMostApplicableDefaultIME(
+                                        mSettings.getEnabledInputMethodListLocked());
+                    mSettings.putSelectedDefaultDeviceInputMethod(
+                            newDefaultIme == null ? "" : newDefaultIme.getId());
                 }
                 // Previous state was enabled.
                 return true;
@@ -5365,7 +5442,7 @@
             }
 
             final ArrayMap<String, InputMethodInfo> methodMap = queryMethodMapForUser(userId);
-            final InputMethodSettings settings = new InputMethodSettings(methodMap, userId, false);
+            final InputMethodSettings settings = new InputMethodSettings(methodMap, userId);
             return settings.getCurrentInputMethodSubtypeForNonCurrentUsers();
         }
     }
@@ -5438,7 +5515,7 @@
         AdditionalSubtypeUtils.load(additionalSubtypeMap, userId);
         queryInputMethodServicesInternal(mContext, userId, additionalSubtypeMap, methodMap,
                 methodList, DirectBootAwareness.AUTO);
-        InputMethodSettings settings = new InputMethodSettings(methodMap, userId, true);
+        InputMethodSettings settings = new InputMethodSettings(methodMap, userId);
         return methodMap.get(settings.getSelectedInputMethod());
     }
 
@@ -5465,7 +5542,7 @@
             return true;
         }
         final ArrayMap<String, InputMethodInfo> methodMap = queryMethodMapForUser(userId);
-        final InputMethodSettings settings = new InputMethodSettings(methodMap, userId, false);
+        final InputMethodSettings settings = new InputMethodSettings(methodMap, userId);
         if (!methodMap.containsKey(imeId)
                 || !settings.getEnabledInputMethodListLocked().contains(methodMap.get(imeId))) {
             return false; // IME is not found or not enabled.
@@ -5604,15 +5681,16 @@
                     return true;
                 }
                 final ArrayMap<String, InputMethodInfo> methodMap = queryMethodMapForUser(userId);
-                final InputMethodSettings settings = new InputMethodSettings(methodMap,
-                        userId, false);
+                final InputMethodSettings settings = new InputMethodSettings(methodMap, userId);
                 if (!methodMap.containsKey(imeId)) {
                     return false; // IME is not found.
                 }
                 if (enabled) {
-                    if (!settings.getEnabledInputMethodListLocked().contains(
-                            methodMap.get(imeId))) {
-                        settings.appendAndPutEnabledInputMethodLocked(imeId);
+                    final String enabledImeIdsStr = settings.getEnabledInputMethodsStr();
+                    final String newEnabledImeIdsStr = InputMethodUtils.concatEnabledImeIds(
+                            enabledImeIdsStr, imeId);
+                    if (!TextUtils.equals(enabledImeIdsStr, newEnabledImeIdsStr)) {
+                        settings.putEnabledInputMethodsStr(newEnabledImeIdsStr);
                     }
                 } else {
                     settings.buildAndPutEnabledInputMethodsStrRemovingIdLocked(
@@ -5625,9 +5703,8 @@
 
         @Override
         public void setVirtualDeviceInputMethodForAllUsers(int deviceId, @Nullable String imeId) {
-            // TODO(b/287269288): validate that id belongs to a valid virtual device instead.
-            Preconditions.checkArgument(deviceId != Context.DEVICE_ID_DEFAULT,
-                    "DeviceId " + deviceId + " does not belong to a virtual device.");
+            Preconditions.checkArgument(deviceId != DEVICE_ID_DEFAULT,
+                    TextUtils.formatSimple("DeviceId %d is not a virtual device id.", deviceId));
             synchronized (ImfLock.class) {
                 if (imeId == null) {
                     mVirtualDeviceMethodMap.remove(deviceId);
@@ -5741,10 +5818,10 @@
                 // We only have sessions when we bound to an input method. Remove this session
                 // from all clients.
                 if (getCurMethodLocked() != null) {
-                    final int numClients = mClients.size();
+                    final int numClients = mClientController.mClients.size();
                     for (int i = 0; i < numClients; ++i) {
-                        clearClientSessionForAccessibilityLocked(mClients.valueAt(i),
-                                accessibilityConnectionId);
+                        clearClientSessionForAccessibilityLocked(
+                                mClientController.mClients.valueAt(i), accessibilityConnectionId);
                     }
                     AccessibilitySessionState session = mEnabledAccessibilitySessions.get(
                             accessibilityConnectionId);
@@ -5929,9 +6006,10 @@
                 info.dump(p, "    ");
             }
             p.println("  ClientStates:");
-            final int numClients = mClients.size();
+            // TODO(b/314150112): move client related dump info to ClientController#dump
+            final int numClients = mClientController.mClients.size();
             for (int i = 0; i < numClients; ++i) {
-                final ClientState ci = mClients.valueAt(i);
+                final ClientState ci = mClientController.mClients.valueAt(i);
                 p.println("  " + ci + ":");
                 p.println("    client=" + ci.mClient);
                 p.println("    fallbackInputConnection=" + ci.mFallbackInputConnection);
@@ -6346,19 +6424,17 @@
             }
         } else {
             final ArrayMap<String, InputMethodInfo> methodMap = queryMethodMapForUser(userId);
-            final InputMethodSettings settings = new InputMethodSettings(methodMap, userId, false);
+            final InputMethodSettings settings = new InputMethodSettings(methodMap, userId);
             if (enabled) {
                 if (!methodMap.containsKey(imeId)) {
                     failedToEnableUnknownIme = true;
                 } else {
-                    for (InputMethodInfo imi : settings.getEnabledInputMethodListLocked()) {
-                        if (TextUtils.equals(imi.getId(), imeId)) {
-                            previouslyEnabled = true;
-                            break;
-                        }
-                    }
+                    final String enabledImeIdsStr = settings.getEnabledInputMethodsStr();
+                    final String newEnabledImeIdsStr = InputMethodUtils.concatEnabledImeIds(
+                            enabledImeIdsStr, imeId);
+                    previouslyEnabled = TextUtils.equals(enabledImeIdsStr, newEnabledImeIdsStr);
                     if (!previouslyEnabled) {
-                        settings.appendAndPutEnabledInputMethodLocked(imeId);
+                        settings.putEnabledInputMethodsStr(newEnabledImeIdsStr);
                     }
                 }
             } else {
@@ -6491,7 +6567,7 @@
                         queryInputMethodServicesInternal(mContext, userId, additionalSubtypeMap,
                                 methodMap, methodList, DirectBootAwareness.AUTO);
                         final InputMethodSettings settings = new InputMethodSettings(
-                                methodMap, userId, false);
+                                methodMap, userId);
 
                         nextEnabledImes = InputMethodInfoUtils.getDefaultEnabledImes(mContext,
                                 methodList);
@@ -6552,7 +6628,7 @@
         boolean isImeTraceEnabled = ImeTracing.getInstance().isEnabled();
         ArrayMap<IBinder, ClientState> clients;
         synchronized (ImfLock.class) {
-            clients = new ArrayMap<>(mClients);
+            clients = new ArrayMap<>(mClientController.mClients);
         }
         for (ClientState state : clients.values()) {
             if (state != null) {
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java b/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java
index efa1e0d..6ed4848 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java
@@ -19,12 +19,14 @@
 import static com.android.server.inputmethod.InputMethodManagerService.DEBUG;
 import static com.android.server.inputmethod.InputMethodUtils.NOT_A_SUBTYPE_ID;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.AlertDialog;
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.res.TypedArray;
 import android.graphics.drawable.Drawable;
+import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.Slog;
 import android.view.LayoutInflater;
@@ -51,8 +53,6 @@
     private static final String TAG = InputMethodMenuController.class.getSimpleName();
 
     private final InputMethodManagerService mService;
-    private final InputMethodUtils.InputMethodSettings mSettings;
-    private final InputMethodSubtypeSwitchingController mSwitchingController;
     private final WindowManagerInternal mWindowManagerInternal;
 
     private AlertDialog.Builder mDialogBuilder;
@@ -69,145 +69,141 @@
 
     InputMethodMenuController(InputMethodManagerService service) {
         mService = service;
-        mSettings = mService.mSettings;
-        mSwitchingController = mService.mSwitchingController;
         mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
     }
 
-    void showInputMethodMenu(boolean showAuxSubtypes, int displayId) {
+    @GuardedBy("ImfLock.class")
+    void showInputMethodMenuLocked(boolean showAuxSubtypes, int displayId,
+            String preferredInputMethodId, int preferredInputMethodSubtypeId,
+            @NonNull List<ImeSubtypeListItem> imList) {
         if (DEBUG) Slog.v(TAG, "Show switching menu. showAuxSubtypes=" + showAuxSubtypes);
 
-        synchronized (ImfLock.class) {
-            final boolean isScreenLocked = mWindowManagerInternal.isKeyguardLocked()
-                    && mWindowManagerInternal.isKeyguardSecure(
-                            mService.getCurrentImeUserIdLocked());
-            final String lastInputMethodId = mSettings.getSelectedInputMethod();
-            int lastInputMethodSubtypeId =
-                    mSettings.getSelectedInputMethodSubtypeId(lastInputMethodId);
-            if (DEBUG) Slog.v(TAG, "Current IME: " + lastInputMethodId);
+        final int userId = mService.getCurrentImeUserIdLocked();
 
-            final List<ImeSubtypeListItem> imList = mSwitchingController
-                    .getSortedInputMethodAndSubtypeListForImeMenuLocked(
-                            showAuxSubtypes, isScreenLocked);
-            if (imList.isEmpty()) {
-                return;
-            }
-
-            hideInputMethodMenuLocked();
-
-            if (lastInputMethodSubtypeId == NOT_A_SUBTYPE_ID) {
-                final InputMethodSubtype currentSubtype =
-                        mService.getCurrentInputMethodSubtypeLocked();
-                if (currentSubtype != null) {
-                    final String curMethodId = mService.getSelectedMethodIdLocked();
-                    final InputMethodInfo currentImi =
-                            mService.queryInputMethodForCurrentUserLocked(curMethodId);
-                    lastInputMethodSubtypeId = SubtypeUtils.getSubtypeIdFromHashCode(
-                            currentImi, currentSubtype.hashCode());
-                }
-            }
-
-            final int size = imList.size();
-            mIms = new InputMethodInfo[size];
-            mSubtypeIds = new int[size];
-            int checkedItem = 0;
-            for (int i = 0; i < size; ++i) {
-                final ImeSubtypeListItem item = imList.get(i);
-                mIms[i] = item.mImi;
-                mSubtypeIds[i] = item.mSubtypeId;
-                if (mIms[i].getId().equals(lastInputMethodId)) {
-                    int subtypeId = mSubtypeIds[i];
-                    if ((subtypeId == NOT_A_SUBTYPE_ID)
-                            || (lastInputMethodSubtypeId == NOT_A_SUBTYPE_ID && subtypeId == 0)
-                            || (subtypeId == lastInputMethodSubtypeId)) {
-                        checkedItem = i;
-                    }
-                }
-            }
-
-            if (mDialogWindowContext == null) {
-                mDialogWindowContext = new InputMethodDialogWindowContext();
-            }
-            final Context dialogWindowContext = mDialogWindowContext.get(displayId);
-            mDialogBuilder = new AlertDialog.Builder(dialogWindowContext);
-            mDialogBuilder.setOnCancelListener(dialog -> hideInputMethodMenu());
-
-            final Context dialogContext = mDialogBuilder.getContext();
-            final TypedArray a = dialogContext.obtainStyledAttributes(null,
-                    com.android.internal.R.styleable.DialogPreference,
-                    com.android.internal.R.attr.alertDialogStyle, 0);
-            final Drawable dialogIcon = a.getDrawable(
-                    com.android.internal.R.styleable.DialogPreference_dialogIcon);
-            a.recycle();
-
-            mDialogBuilder.setIcon(dialogIcon);
-
-            final LayoutInflater inflater = dialogContext.getSystemService(LayoutInflater.class);
-            final View tv = inflater.inflate(
-                    com.android.internal.R.layout.input_method_switch_dialog_title, null);
-            mDialogBuilder.setCustomTitle(tv);
-
-            // Setup layout for a toggle switch of the hardware keyboard
-            mSwitchingDialogTitleView = tv;
-            mSwitchingDialogTitleView
-                    .findViewById(com.android.internal.R.id.hard_keyboard_section)
-                    .setVisibility(mWindowManagerInternal.isHardKeyboardAvailable()
-                            ? View.VISIBLE : View.GONE);
-            final Switch hardKeySwitch = mSwitchingDialogTitleView.findViewById(
-                    com.android.internal.R.id.hard_keyboard_switch);
-            hardKeySwitch.setChecked(mShowImeWithHardKeyboard);
-            hardKeySwitch.setOnCheckedChangeListener((buttonView, isChecked) -> {
-                mSettings.setShowImeWithHardKeyboard(isChecked);
-                // Ensure that the input method dialog is dismissed when changing
-                // the hardware keyboard state.
-                hideInputMethodMenu();
-            });
-
-            final ImeSubtypeListAdapter adapter = new ImeSubtypeListAdapter(dialogContext,
-                    com.android.internal.R.layout.input_method_switch_item, imList, checkedItem);
-            final DialogInterface.OnClickListener choiceListener = (dialog, which) -> {
-                synchronized (ImfLock.class) {
-                    if (mIms == null || mIms.length <= which || mSubtypeIds == null
-                            || mSubtypeIds.length <= which) {
-                        return;
-                    }
-                    final InputMethodInfo im = mIms[which];
-                    int subtypeId = mSubtypeIds[which];
-                    adapter.mCheckedItem = which;
-                    adapter.notifyDataSetChanged();
-                    if (im != null) {
-                        if (subtypeId < 0 || subtypeId >= im.getSubtypeCount()) {
-                            subtypeId = NOT_A_SUBTYPE_ID;
-                        }
-                        mService.setInputMethodLocked(im.getId(), subtypeId);
-                    }
-                    hideInputMethodMenuLocked();
-                }
-            };
-            mDialogBuilder.setSingleChoiceItems(adapter, checkedItem, choiceListener);
-
-            mSwitchingDialog = mDialogBuilder.create();
-            mSwitchingDialog.setCanceledOnTouchOutside(true);
-            final Window w = mSwitchingDialog.getWindow();
-            final WindowManager.LayoutParams attrs = w.getAttributes();
-            w.setType(WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG);
-            w.setHideOverlayWindows(true);
-            // Use an alternate token for the dialog for that window manager can group the token
-            // with other IME windows based on type vs. grouping based on whichever token happens
-            // to get selected by the system later on.
-            attrs.token = dialogWindowContext.getWindowContextToken();
-            attrs.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
-            attrs.setTitle("Select input method");
-            w.setAttributes(attrs);
-            mService.updateSystemUiLocked();
-            mService.sendOnNavButtonFlagsChangedLocked();
-            mSwitchingDialog.show();
-
+        if (imList.isEmpty()) {
+            return;
         }
+
+        hideInputMethodMenuLocked();
+
+        if (preferredInputMethodSubtypeId == NOT_A_SUBTYPE_ID) {
+            final InputMethodSubtype currentSubtype =
+                    mService.getCurrentInputMethodSubtypeLocked();
+            if (currentSubtype != null) {
+                final String curMethodId = mService.getSelectedMethodIdLocked();
+                final InputMethodInfo currentImi =
+                        mService.queryInputMethodForCurrentUserLocked(curMethodId);
+                preferredInputMethodSubtypeId = SubtypeUtils.getSubtypeIdFromHashCode(
+                        currentImi, currentSubtype.hashCode());
+            }
+        }
+
+        // Find out which item should be checked by default.
+        final int size = imList.size();
+        mIms = new InputMethodInfo[size];
+        mSubtypeIds = new int[size];
+        int checkedItem = 0;
+        for (int i = 0; i < size; ++i) {
+            final ImeSubtypeListItem item = imList.get(i);
+            mIms[i] = item.mImi;
+            mSubtypeIds[i] = item.mSubtypeId;
+            if (mIms[i].getId().equals(preferredInputMethodId)) {
+                int subtypeId = mSubtypeIds[i];
+                if ((subtypeId == NOT_A_SUBTYPE_ID)
+                        || (preferredInputMethodSubtypeId == NOT_A_SUBTYPE_ID && subtypeId == 0)
+                        || (subtypeId == preferredInputMethodSubtypeId)) {
+                    checkedItem = i;
+                }
+            }
+        }
+
+        if (mDialogWindowContext == null) {
+            mDialogWindowContext = new InputMethodDialogWindowContext();
+        }
+        final Context dialogWindowContext = mDialogWindowContext.get(displayId);
+        mDialogBuilder = new AlertDialog.Builder(dialogWindowContext);
+        mDialogBuilder.setOnCancelListener(dialog -> hideInputMethodMenu());
+
+        final Context dialogContext = mDialogBuilder.getContext();
+        final TypedArray a = dialogContext.obtainStyledAttributes(null,
+                com.android.internal.R.styleable.DialogPreference,
+                com.android.internal.R.attr.alertDialogStyle, 0);
+        final Drawable dialogIcon = a.getDrawable(
+                com.android.internal.R.styleable.DialogPreference_dialogIcon);
+        a.recycle();
+
+        mDialogBuilder.setIcon(dialogIcon);
+
+        final LayoutInflater inflater = dialogContext.getSystemService(LayoutInflater.class);
+        final View tv = inflater.inflate(
+                com.android.internal.R.layout.input_method_switch_dialog_title, null);
+        mDialogBuilder.setCustomTitle(tv);
+
+        // Setup layout for a toggle switch of the hardware keyboard
+        mSwitchingDialogTitleView = tv;
+        mSwitchingDialogTitleView
+                .findViewById(com.android.internal.R.id.hard_keyboard_section)
+                .setVisibility(mWindowManagerInternal.isHardKeyboardAvailable()
+                        ? View.VISIBLE : View.GONE);
+        final Switch hardKeySwitch = mSwitchingDialogTitleView.findViewById(
+                com.android.internal.R.id.hard_keyboard_switch);
+        hardKeySwitch.setChecked(mShowImeWithHardKeyboard);
+        hardKeySwitch.setOnCheckedChangeListener((buttonView, isChecked) -> {
+            SecureSettingsWrapper.putBoolean(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD,
+                    isChecked, userId);
+            // Ensure that the input method dialog is dismissed when changing
+            // the hardware keyboard state.
+            hideInputMethodMenu();
+        });
+
+        // Fill the list items with onClick listener, which takes care of IME (and subtype)
+        // switching when clicked.
+        final ImeSubtypeListAdapter adapter = new ImeSubtypeListAdapter(dialogContext,
+                com.android.internal.R.layout.input_method_switch_item, imList, checkedItem);
+        final DialogInterface.OnClickListener choiceListener = (dialog, which) -> {
+            synchronized (ImfLock.class) {
+                if (mIms == null || mIms.length <= which || mSubtypeIds == null
+                        || mSubtypeIds.length <= which) {
+                    return;
+                }
+                final InputMethodInfo im = mIms[which];
+                int subtypeId = mSubtypeIds[which];
+                adapter.mCheckedItem = which;
+                adapter.notifyDataSetChanged();
+                if (im != null) {
+                    if (subtypeId < 0 || subtypeId >= im.getSubtypeCount()) {
+                        subtypeId = NOT_A_SUBTYPE_ID;
+                    }
+                    mService.setInputMethodLocked(im.getId(), subtypeId);
+                }
+                hideInputMethodMenuLocked();
+            }
+        };
+        mDialogBuilder.setSingleChoiceItems(adapter, checkedItem, choiceListener);
+
+        // Final steps to instantiate a dialog to show it up.
+        mSwitchingDialog = mDialogBuilder.create();
+        mSwitchingDialog.setCanceledOnTouchOutside(true);
+        final Window w = mSwitchingDialog.getWindow();
+        final WindowManager.LayoutParams attrs = w.getAttributes();
+        w.setType(WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG);
+        w.setHideOverlayWindows(true);
+        // Use an alternate token for the dialog for that window manager can group the token
+        // with other IME windows based on type vs. grouping based on whichever token happens
+        // to get selected by the system later on.
+        attrs.token = dialogWindowContext.getWindowContextToken();
+        attrs.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
+        attrs.setTitle("Select input method");
+        w.setAttributes(attrs);
+        mService.updateSystemUiLocked();
+        mService.sendOnNavButtonFlagsChangedLocked();
+        mSwitchingDialog.show();
     }
 
     void updateKeyboardFromSettingsLocked() {
-        mShowImeWithHardKeyboard = mSettings.isShowImeWithHardKeyboardEnabled();
+        mShowImeWithHardKeyboard =
+                SecureSettingsWrapper.getBoolean(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD,
+                        false, mService.getCurrentImeUserIdLocked());
         if (mSwitchingDialog != null && mSwitchingDialogTitleView != null
                 && mSwitchingDialog.isShowing()) {
             final Switch hardKeySwitch = mSwitchingDialogTitleView.findViewById(
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java b/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java
index 431aabd..4439b06 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java
@@ -16,10 +16,14 @@
 
 package com.android.server.inputmethod;
 
+import android.annotation.AnyThread;
+import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UserIdInt;
 import android.content.Context;
-import android.content.pm.PackageManager;
+import android.os.UserHandle;
 import android.text.TextUtils;
+import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Printer;
 import android.util.Slog;
@@ -32,7 +36,6 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
-import java.util.Locale;
 import java.util.Objects;
 
 /**
@@ -154,79 +157,71 @@
         }
     }
 
-    private static class InputMethodAndSubtypeList {
-        private final Context mContext;
-        // Used to load label
-        private final PackageManager mPm;
-        private final String mSystemLocaleStr;
-        private final InputMethodSettings mSettings;
+    static List<ImeSubtypeListItem> getSortedInputMethodAndSubtypeList(
+            boolean includeAuxiliarySubtypes, boolean isScreenLocked, boolean forImeMenu,
+            @NonNull Context context, @NonNull ArrayMap<String, InputMethodInfo> methodMap,
+            @UserIdInt int userId) {
+        final Context userAwareContext = context.getUserId() == userId
+                ? context
+                : context.createContextAsUser(UserHandle.of(userId), 0 /* flags */);
+        final String mSystemLocaleStr = SystemLocaleWrapper.get(userId).get(0).toLanguageTag();
+        final InputMethodSettings settings = new InputMethodSettings(methodMap, userId);
 
-        InputMethodAndSubtypeList(Context context, InputMethodSettings settings) {
-            mContext = context;
-            mSettings = settings;
-            mPm = context.getPackageManager();
-            final Locale locale = context.getResources().getConfiguration().locale;
-            mSystemLocaleStr = locale != null ? locale.toString() : "";
+        final ArrayList<InputMethodInfo> imis = settings.getEnabledInputMethodListLocked();
+        if (imis.isEmpty()) {
+            return Collections.emptyList();
         }
-
-        public List<ImeSubtypeListItem> getSortedInputMethodAndSubtypeList(
-                boolean includeAuxiliarySubtypes, boolean isScreenLocked, boolean forImeMenu) {
-            final ArrayList<InputMethodInfo> imis = mSettings.getEnabledInputMethodListLocked();
-            if (imis.isEmpty()) {
-                return Collections.emptyList();
+        if (isScreenLocked && includeAuxiliarySubtypes) {
+            if (DEBUG) {
+                Slog.w(TAG, "Auxiliary subtypes are not allowed to be shown in lock screen.");
             }
-            if (isScreenLocked && includeAuxiliarySubtypes) {
+            includeAuxiliarySubtypes = false;
+        }
+        final ArrayList<ImeSubtypeListItem> imList = new ArrayList<>();
+        final int numImes = imis.size();
+        for (int i = 0; i < numImes; ++i) {
+            final InputMethodInfo imi = imis.get(i);
+            if (forImeMenu && !imi.shouldShowInInputMethodPicker()) {
+                continue;
+            }
+            final List<InputMethodSubtype> explicitlyOrImplicitlyEnabledSubtypeList =
+                    settings.getEnabledInputMethodSubtypeListLocked(imi, true);
+            final ArraySet<String> enabledSubtypeSet = new ArraySet<>();
+            for (InputMethodSubtype subtype : explicitlyOrImplicitlyEnabledSubtypeList) {
+                enabledSubtypeSet.add(String.valueOf(subtype.hashCode()));
+            }
+            final CharSequence imeLabel = imi.loadLabel(userAwareContext.getPackageManager());
+            if (enabledSubtypeSet.size() > 0) {
+                final int subtypeCount = imi.getSubtypeCount();
                 if (DEBUG) {
-                    Slog.w(TAG, "Auxiliary subtypes are not allowed to be shown in lock screen.");
+                    Slog.v(TAG, "Add subtypes: " + subtypeCount + ", " + imi.getId());
                 }
-                includeAuxiliarySubtypes = false;
-            }
-            final ArrayList<ImeSubtypeListItem> imList = new ArrayList<>();
-            final int numImes = imis.size();
-            for (int i = 0; i < numImes; ++i) {
-                final InputMethodInfo imi = imis.get(i);
-                if (forImeMenu && !imi.shouldShowInInputMethodPicker()) {
-                    continue;
-                }
-                final List<InputMethodSubtype> explicitlyOrImplicitlyEnabledSubtypeList =
-                        mSettings.getEnabledInputMethodSubtypeListLocked(imi, true);
-                final ArraySet<String> enabledSubtypeSet = new ArraySet<>();
-                for (InputMethodSubtype subtype : explicitlyOrImplicitlyEnabledSubtypeList) {
-                    enabledSubtypeSet.add(String.valueOf(subtype.hashCode()));
-                }
-                final CharSequence imeLabel = imi.loadLabel(mPm);
-                if (enabledSubtypeSet.size() > 0) {
-                    final int subtypeCount = imi.getSubtypeCount();
-                    if (DEBUG) {
-                        Slog.v(TAG, "Add subtypes: " + subtypeCount + ", " + imi.getId());
-                    }
-                    for (int j = 0; j < subtypeCount; ++j) {
-                        final InputMethodSubtype subtype = imi.getSubtypeAt(j);
-                        final String subtypeHashCode = String.valueOf(subtype.hashCode());
-                        // We show all enabled IMEs and subtypes when an IME is shown.
-                        if (enabledSubtypeSet.contains(subtypeHashCode)
-                                && (includeAuxiliarySubtypes || !subtype.isAuxiliary())) {
-                            final CharSequence subtypeLabel =
-                                    subtype.overridesImplicitlyEnabledSubtype() ? null : subtype
-                                            .getDisplayName(mContext, imi.getPackageName(),
-                                                    imi.getServiceInfo().applicationInfo);
-                            imList.add(new ImeSubtypeListItem(imeLabel,
-                                    subtypeLabel, imi, j, subtype.getLocale(), mSystemLocaleStr));
+                for (int j = 0; j < subtypeCount; ++j) {
+                    final InputMethodSubtype subtype = imi.getSubtypeAt(j);
+                    final String subtypeHashCode = String.valueOf(subtype.hashCode());
+                    // We show all enabled IMEs and subtypes when an IME is shown.
+                    if (enabledSubtypeSet.contains(subtypeHashCode)
+                            && (includeAuxiliarySubtypes || !subtype.isAuxiliary())) {
+                        final CharSequence subtypeLabel =
+                                subtype.overridesImplicitlyEnabledSubtype() ? null : subtype
+                                        .getDisplayName(userAwareContext, imi.getPackageName(),
+                                                imi.getServiceInfo().applicationInfo);
+                        imList.add(new ImeSubtypeListItem(imeLabel,
+                                subtypeLabel, imi, j, subtype.getLocale(), mSystemLocaleStr));
 
-                            // Removing this subtype from enabledSubtypeSet because we no
-                            // longer need to add an entry of this subtype to imList to avoid
-                            // duplicated entries.
-                            enabledSubtypeSet.remove(subtypeHashCode);
-                        }
+                        // Removing this subtype from enabledSubtypeSet because we no
+                        // longer need to add an entry of this subtype to imList to avoid
+                        // duplicated entries.
+                        enabledSubtypeSet.remove(subtypeHashCode);
                     }
-                } else {
-                    imList.add(new ImeSubtypeListItem(imeLabel, null, imi, NOT_A_SUBTYPE_ID, null,
-                            mSystemLocaleStr));
                 }
+            } else {
+                imList.add(new ImeSubtypeListItem(imeLabel, null, imi, NOT_A_SUBTYPE_ID, null,
+                        mSystemLocaleStr));
             }
-            Collections.sort(imList);
-            return imList;
         }
+        Collections.sort(imList);
+        return imList;
     }
 
     private static int calculateSubtypeId(InputMethodInfo imi, InputMethodSubtype subtype) {
@@ -479,18 +474,32 @@
         }
     }
 
-    private final InputMethodSettings mSettings;
-    private InputMethodAndSubtypeList mSubtypeList;
+    private final Context mContext;
+    @UserIdInt
+    private final int mUserId;
     private ControllerImpl mController;
 
-    private InputMethodSubtypeSwitchingController(InputMethodSettings settings, Context context) {
-        mSettings = settings;
-        resetCircularListLocked(context);
+    private InputMethodSubtypeSwitchingController(@NonNull Context context,
+            @NonNull ArrayMap<String, InputMethodInfo> methodMap, @UserIdInt int userId) {
+        mContext = context;
+        mUserId = userId;
+        mController = ControllerImpl.createFrom(null,
+                getSortedInputMethodAndSubtypeList(
+                        false /* includeAuxiliarySubtypes */, false /* isScreenLocked */,
+                        false /* forImeMenu */, context, methodMap, userId));
     }
 
+    @NonNull
     public static InputMethodSubtypeSwitchingController createInstanceLocked(
-            InputMethodSettings settings, Context context) {
-        return new InputMethodSubtypeSwitchingController(settings, context);
+            @NonNull Context context,
+            @NonNull ArrayMap<String, InputMethodInfo> methodMap, @UserIdInt int userId) {
+        return new InputMethodSubtypeSwitchingController(context, methodMap, userId);
+    }
+
+    @AnyThread
+    @UserIdInt
+    int getUserId() {
+        return mUserId;
     }
 
     public void onUserActionLocked(InputMethodInfo imi, InputMethodSubtype subtype) {
@@ -503,12 +512,12 @@
         mController.onUserActionLocked(imi, subtype);
     }
 
-    public void resetCircularListLocked(Context context) {
-        mSubtypeList = new InputMethodAndSubtypeList(context, mSettings);
+    public void resetCircularListLocked(
+            @NonNull ArrayMap<String, InputMethodInfo> methodMap) {
         mController = ControllerImpl.createFrom(mController,
-                mSubtypeList.getSortedInputMethodAndSubtypeList(
+                getSortedInputMethodAndSubtypeList(
                         false /* includeAuxiliarySubtypes */, false /* isScreenLocked */,
-                        false /* forImeMenu */));
+                        false /* forImeMenu */, mContext, methodMap, mUserId));
     }
 
     public ImeSubtypeListItem getNextInputMethodLocked(boolean onlyCurrentIme, InputMethodInfo imi,
@@ -522,12 +531,6 @@
         return mController.getNextInputMethod(onlyCurrentIme, imi, subtype);
     }
 
-    public List<ImeSubtypeListItem> getSortedInputMethodAndSubtypeListForImeMenuLocked(
-            boolean includingAuxiliarySubtypes, boolean isScreenLocked) {
-        return mSubtypeList.getSortedInputMethodAndSubtypeList(
-                includingAuxiliarySubtypes, isScreenLocked, true /* forImeMenu */);
-    }
-
     public void dump(final Printer pw) {
         if (mController != null) {
             mController.dump(pw);
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
index a88d85e..fb57c09 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
@@ -51,6 +51,7 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Objects;
 import java.util.StringJoiner;
 import java.util.function.Consumer;
 import java.util.function.Predicate;
@@ -213,11 +214,8 @@
     public static class InputMethodSettings {
         private final ArrayMap<String, InputMethodInfo> mMethodMap;
 
-        private boolean mCopyOnWrite = false;
-        @NonNull
-        private String mEnabledInputMethodsStrCache = "";
         @UserIdInt
-        private int mCurrentUserId;
+        private final int mCurrentUserId;
 
         private static void buildEnabledInputMethodsSettingString(
                 StringBuilder builder, Pair<String, ArrayList<String>> ime) {
@@ -229,29 +227,15 @@
             }
         }
 
-        InputMethodSettings(ArrayMap<String, InputMethodInfo> methodMap, @UserIdInt int userId,
-                boolean copyOnWrite) {
+        InputMethodSettings(ArrayMap<String, InputMethodInfo> methodMap, @UserIdInt int userId) {
             mMethodMap = methodMap;
-            switchCurrentUser(userId, copyOnWrite);
-        }
-
-        /**
-         * Must be called when the current user is changed.
-         *
-         * @param userId The user ID.
-         * @param copyOnWrite If {@code true}, for each settings key
-         * (e.g. {@link Settings.Secure#ACTION_INPUT_METHOD_SUBTYPE_SETTINGS}) we use the actual
-         * settings on the {@link Settings.Secure} until we do the first write operation.
-         */
-        void switchCurrentUser(@UserIdInt int userId, boolean copyOnWrite) {
-            if (DEBUG) {
-                Slog.d(TAG, "--- Switch the current user from " + mCurrentUserId + " to " + userId);
-            }
-            if (mCurrentUserId != userId || mCopyOnWrite != copyOnWrite) {
-                mEnabledInputMethodsStrCache = "";
-            }
             mCurrentUserId = userId;
-            mCopyOnWrite = copyOnWrite;
+            String ime = getSelectedInputMethod();
+            String defaultDeviceIme = getSelectedDefaultDeviceInputMethod();
+            if (defaultDeviceIme != null && !Objects.equals(ime, defaultDeviceIme)) {
+                putSelectedInputMethod(defaultDeviceIme);
+                putSelectedDefaultDeviceInputMethod(null);
+            }
         }
 
         private void putString(@NonNull String key, @Nullable String str) {
@@ -352,16 +336,6 @@
             return imsList;
         }
 
-        void appendAndPutEnabledInputMethodLocked(String id) {
-            if (TextUtils.isEmpty(mEnabledInputMethodsStrCache)) {
-                // Add in the newly enabled input method.
-                putEnabledInputMethodsStr(id);
-            } else {
-                putEnabledInputMethodsStr(
-                        mEnabledInputMethodsStrCache + INPUT_METHOD_SEPARATOR + id);
-            }
-        }
-
         /**
          * Build and put a string of EnabledInputMethods with removing specified Id.
          * @return the specified id was removed or not.
@@ -418,18 +392,11 @@
             } else {
                 putString(Settings.Secure.ENABLED_INPUT_METHODS, str);
             }
-            // TODO: Update callers of putEnabledInputMethodsStr to make str @NonNull.
-            mEnabledInputMethodsStrCache = (str != null ? str : "");
         }
 
         @NonNull
         String getEnabledInputMethodsStr() {
-            mEnabledInputMethodsStrCache = getString(Settings.Secure.ENABLED_INPUT_METHODS, "");
-            if (DEBUG) {
-                Slog.d(TAG, "getEnabledInputMethodsStr: " + mEnabledInputMethodsStrCache
-                        + ", " + mCurrentUserId);
-            }
-            return mEnabledInputMethodsStrCache;
+            return getString(Settings.Secure.ENABLED_INPUT_METHODS, "");
         }
 
         private void saveSubtypeHistory(
@@ -664,6 +631,24 @@
             return imi;
         }
 
+        @Nullable
+        String getSelectedDefaultDeviceInputMethod() {
+            final String imi = getString(Settings.Secure.DEFAULT_DEVICE_INPUT_METHOD, null);
+            if (DEBUG) {
+                Slog.d(TAG, "getSelectedDefaultDeviceInputMethodStr: " + imi + ", "
+                        + mCurrentUserId);
+            }
+            return imi;
+        }
+
+        void putSelectedDefaultDeviceInputMethod(String imeId) {
+            if (DEBUG) {
+                Slog.d(TAG, "putSelectedDefaultDeviceInputMethodStr: " + imeId + ", "
+                        + mCurrentUserId);
+            }
+            putString(Settings.Secure.DEFAULT_DEVICE_INPUT_METHOD, imeId);
+        }
+
         void putDefaultVoiceInputMethod(String imeId) {
             if (DEBUG) {
                 Slog.d(TAG, "putDefaultVoiceInputMethodStr: " + imeId + ", " + mCurrentUserId);
@@ -688,14 +673,6 @@
             return getInt(Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE, NOT_A_SUBTYPE_ID);
         }
 
-        boolean isShowImeWithHardKeyboardEnabled() {
-            return getBoolean(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, false);
-        }
-
-        void setShowImeWithHardKeyboard(boolean show) {
-            putBoolean(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, show);
-        }
-
         @UserIdInt
         public int getCurrentUserId() {
             return mCurrentUserId;
@@ -862,8 +839,6 @@
 
         public void dumpLocked(final Printer pw, final String prefix) {
             pw.println(prefix + "mCurrentUserId=" + mCurrentUserId);
-            pw.println(prefix + "mCopyOnWrite=" + mCopyOnWrite);
-            pw.println(prefix + "mEnabledInputMethodsStrCache=" + mEnabledInputMethodsStrCache);
         }
     }
 
diff --git a/services/core/java/com/android/server/inputmethod/OWNERS b/services/core/java/com/android/server/inputmethod/OWNERS
index aa638aa..e507c6b 100644
--- a/services/core/java/com/android/server/inputmethod/OWNERS
+++ b/services/core/java/com/android/server/inputmethod/OWNERS
@@ -6,5 +6,8 @@
 fstern@google.com
 cosminbaies@google.com
 
+# Automotive
+kanant@google.com
+
 ogunwale@google.com #{LAST_RESORT_SUGGESTION}
 jjaggi@google.com #{LAST_RESORT_SUGGESTION}
diff --git a/services/core/java/com/android/server/location/gnss/GnssConfiguration.java b/services/core/java/com/android/server/location/gnss/GnssConfiguration.java
index 5ef89ad..a5939e9 100644
--- a/services/core/java/com/android/server/location/gnss/GnssConfiguration.java
+++ b/services/core/java/com/android/server/location/gnss/GnssConfiguration.java
@@ -17,6 +17,7 @@
 package com.android.server.location.gnss;
 
 import android.content.Context;
+import android.location.flags.Flags;
 import android.os.PersistableBundle;
 import android.os.SystemProperties;
 import android.telephony.CarrierConfigManager;
@@ -36,6 +37,7 @@
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
+import java.util.Locale;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Properties;
@@ -275,6 +277,11 @@
         }
         loadPropertiesFromCarrierConfig(inEmergency, activeSubId);
 
+        if (Flags.gnssConfigurationFromResource()) {
+            // Overlay carrier properties from resources.
+            loadPropertiesFromResource(mContext, mProperties);
+        }
+
         if (isSimAbsent(mContext)) {
             // Use the default SIM's LPP profile when SIM is absent.
             String lpp_prof = SystemProperties.get(LPP_PROFILE);
@@ -382,7 +389,7 @@
             if (configKey.startsWith(CarrierConfigManager.Gps.KEY_PREFIX)) {
                 String key = configKey
                         .substring(CarrierConfigManager.Gps.KEY_PREFIX.length())
-                        .toUpperCase();
+                        .toUpperCase(Locale.ROOT);
                 Object value = configs.get(configKey);
                 if (DEBUG) Log.d(TAG, "Gps config: " + key + " = " + value);
                 if (value instanceof String) {
@@ -410,6 +417,24 @@
         }
     }
 
+    private void loadPropertiesFromResource(Context context,
+            Properties properties) {
+        String[] configValues = context.getResources().getStringArray(
+                com.android.internal.R.array.config_gnssParameters);
+        for (String item : configValues) {
+            if (DEBUG) Log.d(TAG, "GnssParamsResource: " + item);
+            // We need to support "KEY =", but not "=VALUE".
+            int index = item.indexOf("=");
+            if (index > 0 && index + 1 < item.length()) {
+                String key = item.substring(0, index);
+                String value = item.substring(index + 1);
+                properties.setProperty(key.trim().toUpperCase(Locale.ROOT), value);
+            } else {
+                Log.w(TAG, "malformed contents: " + item);
+            }
+        }
+    }
+
     private int getRangeCheckedConfigEsExtensionSec() {
         int emergencyExtensionSeconds = getIntConfig(CONFIG_ES_EXTENSION_SEC, 0);
         if (emergencyExtensionSeconds > MAX_EMERGENCY_MODE_EXTENSION_SECONDS) {
diff --git a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
index 27b01a5..9c4225d 100644
--- a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
@@ -67,6 +67,7 @@
 import android.location.LocationManager;
 import android.location.LocationRequest;
 import android.location.LocationResult;
+import android.location.LocationResult.BadLocationException;
 import android.location.flags.Flags;
 import android.location.provider.ProviderProperties;
 import android.location.provider.ProviderRequest;
@@ -1380,7 +1381,11 @@
 
         location.setExtras(mLocationExtras.getBundle());
 
-        reportLocation(LocationResult.wrap(location).validate());
+        try {
+            reportLocation(LocationResult.wrap(location).validate());
+        } catch (BadLocationException e) {
+            throw new IllegalArgumentException(e);
+        }
 
         if (mStarted) {
             mGnssMetrics.logReceivedLocationStatus(hasLatLong);
@@ -1751,7 +1756,11 @@
                 }
             }
 
-            reportLocation(LocationResult.wrap(locations).validate());
+            try {
+                reportLocation(LocationResult.wrap(locations).validate());
+            } catch (BadLocationException e) {
+                throw new IllegalArgumentException(e);
+            }
         }
 
         Runnable[] listeners;
diff --git a/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java b/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java
index d02b6f4..171fbb6 100644
--- a/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java
@@ -25,6 +25,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.AppOpsManager;
+import android.location.GnssMeasurement;
 import android.location.GnssMeasurementRequest;
 import android.location.GnssMeasurementsEvent;
 import android.location.IGnssMeasurementsListener;
@@ -33,6 +34,7 @@
 import android.stats.location.LocationStatsEnums;
 import android.util.Log;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.server.location.gnss.GnssConfiguration.HalInterfaceVersion;
 import com.android.server.location.gnss.hal.GnssNative;
 import com.android.server.location.injector.AppOpsHelper;
@@ -40,6 +42,8 @@
 import com.android.server.location.injector.LocationUsageLogger;
 import com.android.server.location.injector.SettingsHelper;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
 import java.util.Collection;
 
 /**
@@ -91,6 +95,9 @@
     private final LocationUsageLogger mLogger;
     private final GnssNative mGnssNative;
 
+    @GuardedBy("mMultiplexerLock")
+    private GnssMeasurementsEvent mLastGnssMeasurementsEvent;
+
     public GnssMeasurementsProvider(Injector injector, GnssNative gnssNative) {
         super(injector);
         mAppOpsHelper = injector.getAppOpsHelper();
@@ -264,5 +271,46 @@
                 return null;
             }
         });
+        synchronized (mMultiplexerLock) {
+            mLastGnssMeasurementsEvent = event;
+        }
+    }
+
+    @Override
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        super.dump(fd, pw, args);
+        pw.print("last measurements=");
+        pw.println(getLastMeasurementEventSummary());
+    }
+
+    /**
+     * Returns a string of GnssMeasurementsEvent summary including received time, satellite count
+     * and average baseband C/No.
+     */
+    private String getLastMeasurementEventSummary() {
+        synchronized (mMultiplexerLock) {
+            if (mLastGnssMeasurementsEvent == null) {
+                return null;
+            }
+            StringBuilder builder = new StringBuilder("[");
+            builder.append("elapsedRealtimeNs=").append(
+                    mLastGnssMeasurementsEvent.getClock().getElapsedRealtimeNanos());
+            builder.append(" measurementCount=").append(
+                    mLastGnssMeasurementsEvent.getMeasurements().size());
+
+            float sumBasebandCn0 = 0;
+            int countBasebandCn0 = 0;
+            for (GnssMeasurement measurement : mLastGnssMeasurementsEvent.getMeasurements()) {
+                if (measurement.hasBasebandCn0DbHz()) {
+                    sumBasebandCn0 += measurement.getBasebandCn0DbHz();
+                    countBasebandCn0++;
+                }
+            }
+            if (countBasebandCn0 > 0) {
+                builder.append(" avgBasebandCn0=").append(sumBasebandCn0 / countBasebandCn0);
+            }
+            builder.append("]");
+            return builder.toString();
+        }
     }
 }
diff --git a/services/core/java/com/android/server/location/injector/SystemEmergencyHelper.java b/services/core/java/com/android/server/location/injector/SystemEmergencyHelper.java
index c772e08..5df0de8 100644
--- a/services/core/java/com/android/server/location/injector/SystemEmergencyHelper.java
+++ b/services/core/java/com/android/server/location/injector/SystemEmergencyHelper.java
@@ -22,12 +22,14 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.PackageManager;
 import android.os.SystemClock;
 import android.telephony.TelephonyCallback;
 import android.telephony.TelephonyManager;
 import android.util.Log;
 
 import com.android.internal.telephony.TelephonyIntents;
+import com.android.internal.telephony.flags.Flags;
 import com.android.server.FgThread;
 
 import java.util.Objects;
@@ -104,10 +106,26 @@
         boolean isInExtensionTime = mEmergencyCallEndRealtimeMs != Long.MIN_VALUE
                 && (SystemClock.elapsedRealtime() - mEmergencyCallEndRealtimeMs) < extensionTimeMs;
 
-        return mIsInEmergencyCall
-                || isInExtensionTime
-                || mTelephonyManager.getEmergencyCallbackMode()
-                || mTelephonyManager.isInEmergencySmsMode();
+        if (!Flags.enforceTelephonyFeatureMapping()) {
+            return mIsInEmergencyCall
+                    || isInExtensionTime
+                    || mTelephonyManager.getEmergencyCallbackMode()
+                    || mTelephonyManager.isInEmergencySmsMode();
+        } else {
+            boolean emergencyCallbackMode = false;
+            boolean emergencySmsMode = false;
+            PackageManager pm = mContext.getPackageManager();
+            if (pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY_CALLING)) {
+                emergencyCallbackMode = mTelephonyManager.getEmergencyCallbackMode();
+            }
+            if (pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)) {
+                emergencySmsMode = mTelephonyManager.isInEmergencySmsMode();
+            }
+            return mIsInEmergencyCall
+                    || isInExtensionTime
+                    || emergencyCallbackMode
+                    || emergencySmsMode;
+        }
     }
 
     private class EmergencyCallTelephonyCallback extends TelephonyCallback implements
diff --git a/services/core/java/com/android/server/location/provider/LocationProviderManager.java b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
index 91e6a80..7d44aec 100644
--- a/services/core/java/com/android/server/location/provider/LocationProviderManager.java
+++ b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
@@ -64,6 +64,7 @@
 import android.location.LocationManagerInternal.ProviderEnabledListener;
 import android.location.LocationRequest;
 import android.location.LocationResult;
+import android.location.LocationResult.BadLocationException;
 import android.location.altitude.AltitudeConverter;
 import android.location.provider.IProviderRequestListener;
 import android.location.provider.ProviderProperties;
@@ -910,7 +911,8 @@
                                         < getRequest().getMinUpdateIntervalMillis() - maxJitterMs) {
                                     if (D) {
                                         Log.v(TAG, mName + " provider registration " + getIdentity()
-                                                + " dropped delivery - too fast");
+                                                + " dropped delivery - too fast (deltaMs="
+                                                + deltaMs + ").");
                                     }
                                     return false;
                                 }
@@ -2574,29 +2576,17 @@
     @GuardedBy("mMultiplexerLock")
     @Nullable
     private LocationResult processReportedLocation(LocationResult locationResult) {
-        LocationResult processed = locationResult.filter(location -> {
-            if (!location.isMock()) {
-                if (location.getLatitude() == 0 && location.getLongitude() == 0) {
-                    Log.e(TAG, "blocking 0,0 location from " + mName + " provider");
-                    return false;
-                }
-            }
-
-            if (!location.isComplete()) {
-                Log.e(TAG, "blocking incomplete location from " + mName + " provider");
-                return false;
-            }
-
-            return true;
-        });
-        if (processed == null) {
+        try {
+            locationResult.validate();
+        } catch (BadLocationException e) {
+            Log.e(TAG, "Dropping invalid locations: " + e);
             return null;
         }
 
         // Attempt to add a missing MSL altitude on behalf of the provider.
         if (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_LOCATION,
                 "enable_location_provider_manager_msl", true)) {
-            return processed.map(location -> {
+            return locationResult.map(location -> {
                 if (!location.hasMslAltitude() && location.hasAltitude()) {
                     try {
                         Location locationCopy = new Location(location);
@@ -2626,7 +2616,7 @@
                 return location;
             });
         }
-        return processed;
+        return locationResult;
     }
 
     @GuardedBy("mMultiplexerLock")
diff --git a/services/core/java/com/android/server/location/provider/MockLocationProvider.java b/services/core/java/com/android/server/location/provider/MockLocationProvider.java
index 52b04d4..4efacd7 100644
--- a/services/core/java/com/android/server/location/provider/MockLocationProvider.java
+++ b/services/core/java/com/android/server/location/provider/MockLocationProvider.java
@@ -21,6 +21,7 @@
 import android.annotation.Nullable;
 import android.location.Location;
 import android.location.LocationResult;
+import android.location.LocationResult.BadLocationException;
 import android.location.provider.ProviderProperties;
 import android.location.provider.ProviderRequest;
 import android.location.util.identity.CallerIdentity;
@@ -55,7 +56,11 @@
         Location location = new Location(l);
         location.setIsFromMockProvider(true);
         mLocation = location;
-        reportLocation(LocationResult.wrap(location).validate());
+        try {
+            reportLocation(LocationResult.wrap(location).validate());
+        } catch (BadLocationException e) {
+            throw new IllegalArgumentException(e);
+        }
     }
 
     @Override
diff --git a/services/core/java/com/android/server/location/provider/proxy/ProxyLocationProvider.java b/services/core/java/com/android/server/location/provider/proxy/ProxyLocationProvider.java
index 05966da..a597edd 100644
--- a/services/core/java/com/android/server/location/provider/proxy/ProxyLocationProvider.java
+++ b/services/core/java/com/android/server/location/provider/proxy/ProxyLocationProvider.java
@@ -305,7 +305,7 @@
                     return;
                 }
 
-                reportLocation(LocationResult.wrap(location).validate());
+                reportLocation(LocationResult.wrap(location));
             }
         }
 
@@ -316,8 +316,7 @@
                 if (mProxy != this) {
                     return;
                 }
-
-                reportLocation(LocationResult.wrap(locations).validate());
+                reportLocation(LocationResult.wrap(locations));
             }
         }
 
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 0c2eee5..542b3b0 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -52,6 +52,7 @@
 import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
 import android.annotation.UserIdInt;
 import android.app.ActivityManager;
 import android.app.IActivityManager;
@@ -74,6 +75,7 @@
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
 import android.content.pm.UserInfo;
+import android.content.pm.UserProperties;
 import android.content.res.Resources;
 import android.database.ContentObserver;
 import android.database.sqlite.SQLiteDatabase;
@@ -303,7 +305,7 @@
     private boolean mThirdPartyAppsStarted;
 
     // Current password metrics for all secured users on the device. Updated when user unlocks the
-    // device or changes password. Removed when user is stopped.
+    // device or changes password. Removed if user is stopped with its CE key evicted.
     @GuardedBy("this")
     private final SparseArray<PasswordMetrics> mUserPasswordMetrics = new SparseArray<>();
     @VisibleForTesting
@@ -793,13 +795,33 @@
     }
 
     @VisibleForTesting
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.MANAGE_USERS,
+            android.Manifest.permission.QUERY_USERS,
+            android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true)
     void onUserStopped(int userId) {
         hideEncryptionNotification(new UserHandle(userId));
-        // User is stopped with its CE key evicted. Restore strong auth requirement to the default
-        // flags after boot since stopping and restarting a user later is equivalent to rebooting
-        // the device.
+
+        // Normally, CE storage is locked when a user is stopped, and restarting the user requires
+        // strong auth.  Therefore, reset the user's strong auth flags.  The exception is users that
+        // allow delayed locking; under some circumstances, biometric authentication is allowed to
+        // restart such users.  Don't reset the strong auth flags for such users.
+        //
+        // TODO(b/319142556): It might make more sense to reset the strong auth flags when CE
+        // storage is locked, instead of when the user is stopped.  This would ensure the flags get
+        // reset if CE storage is locked later for a user that allows delayed locking.
+        if (android.os.Flags.allowPrivateProfile()
+                && android.multiuser.Flags.enableBiometricsToUnlockPrivateSpace()) {
+            UserProperties userProperties = mUserManager.getUserProperties(UserHandle.of(userId));
+            if (userProperties != null && userProperties.getAllowStoppingUserWithDelayedLocking()) {
+                return;
+            }
+        }
         int strongAuthRequired = LockPatternUtils.StrongAuthTracker.getDefaultFlags(mContext);
         requireStrongAuth(strongAuthRequired, userId);
+
+        // Don't keep the password metrics in memory for a stopped user that will require strong
+        // auth to start again, since strong auth will make the password metrics available again.
         synchronized (this) {
             mUserPasswordMetrics.remove(userId);
         }
@@ -2118,11 +2140,10 @@
             Slogf.d(TAG, "CE storage for user %d is already unlocked", userId);
             return;
         }
-        final UserInfo userInfo = mUserManager.getUserInfo(userId);
         final String userType = isUserSecure(userId) ? "secured" : "unsecured";
         final byte[] secret = sp.deriveFileBasedEncryptionKey();
         try {
-            mStorageManager.unlockCeStorage(userId, userInfo.serialNumber, secret);
+            mStorageManager.unlockCeStorage(userId, secret);
             Slogf.i(TAG, "Unlocked CE storage for %s user %d", userType, userId);
         } catch (RemoteException e) {
             Slogf.wtf(TAG, e, "Failed to unlock CE storage for %s user %d", userType, userId);
diff --git a/services/core/java/com/android/server/media/AudioPoliciesDeviceRouteController.java b/services/core/java/com/android/server/media/AudioManagerRouteController.java
similarity index 97%
rename from services/core/java/com/android/server/media/AudioPoliciesDeviceRouteController.java
rename to services/core/java/com/android/server/media/AudioManagerRouteController.java
index 0eb9166..087f5a6 100644
--- a/services/core/java/com/android/server/media/AudioPoliciesDeviceRouteController.java
+++ b/services/core/java/com/android/server/media/AudioManagerRouteController.java
@@ -57,12 +57,10 @@
  *
  * <p>This implementation obtains and manages all routes via {@link AudioManager}, with the
  * exception of {@link AudioManager#handleBluetoothActiveDeviceChanged inactive bluetooth} routes
- * which are managed by {@link AudioPoliciesBluetoothRouteController}, which depends on the
- * bluetooth stack (for example {@link BluetoothAdapter}.
+ * which are managed by {@link BluetoothDeviceRoutesManager}, which depends on the
+ * bluetooth stack ({@link BluetoothAdapter} and related classes).
  */
-// TODO: b/305199571 - Rename this class to avoid the AudioPolicies prefix, which has been flagged
-// by the audio team as a confusing name.
-/* package */ final class AudioPoliciesDeviceRouteController implements DeviceRouteController {
+/* package */ final class AudioManagerRouteController implements DeviceRouteController {
     private static final String TAG = SystemMediaRoute2Provider.TAG;
 
     @NonNull
@@ -77,7 +75,7 @@
     @NonNull private final AudioManager mAudioManager;
     @NonNull private final Handler mHandler;
     @NonNull private final OnDeviceRouteChangedListener mOnDeviceRouteChangedListener;
-    @NonNull private final AudioPoliciesBluetoothRouteController mBluetoothRouteController;
+    @NonNull private final BluetoothDeviceRoutesManager mBluetoothRouteController;
 
     @NonNull
     private final Map<String, MediaRoute2InfoHolder> mRouteIdToAvailableDeviceRoutes =
@@ -103,7 +101,7 @@
                 Manifest.permission.MODIFY_AUDIO_ROUTING,
                 Manifest.permission.QUERY_AUDIO_STATE
             })
-    /* package */ AudioPoliciesDeviceRouteController(
+    /* package */ AudioManagerRouteController(
             @NonNull Context context,
             @NonNull AudioManager audioManager,
             @NonNull Looper looper,
@@ -120,7 +118,7 @@
                 DeviceRouteController.getBuiltInSpeakerSuitabilityStatus(mContext);
 
         mBluetoothRouteController =
-                new AudioPoliciesBluetoothRouteController(
+                new BluetoothDeviceRoutesManager(
                         mContext, btAdapter, this::rebuildAvailableRoutesAndNotify);
         // Just build routes but don't notify. The caller may not expect the listener to be invoked
         // before this constructor has finished executing.
diff --git a/services/core/java/com/android/server/media/AudioPoliciesBluetoothRouteController.java b/services/core/java/com/android/server/media/BluetoothDeviceRoutesManager.java
similarity index 95%
rename from services/core/java/com/android/server/media/AudioPoliciesBluetoothRouteController.java
rename to services/core/java/com/android/server/media/BluetoothDeviceRoutesManager.java
index 7cf3983..8119628 100644
--- a/services/core/java/com/android/server/media/AudioPoliciesBluetoothRouteController.java
+++ b/services/core/java/com/android/server/media/BluetoothDeviceRoutesManager.java
@@ -58,9 +58,7 @@
  * <p>This class also serves as ground truth for assigning {@link MediaRoute2Info#getId() route ids}
  * for bluetooth routes via {@link #getRouteIdForBluetoothAddress}.
  */
-// TODO: b/305199571 - Rename this class to remove the RouteController suffix, which causes
-// confusion with the BluetoothRouteController interface.
-/* package */ class AudioPoliciesBluetoothRouteController {
+/* package */ class BluetoothDeviceRoutesManager {
     private static final String TAG = SystemMediaRoute2Provider.TAG;
 
     private static final String HEARING_AID_ROUTE_ID_PREFIX = "HEARING_AID_";
@@ -86,7 +84,7 @@
     @NonNull
     private final BluetoothProfileMonitor mBluetoothProfileMonitor;
 
-    AudioPoliciesBluetoothRouteController(@NonNull Context context,
+    BluetoothDeviceRoutesManager(@NonNull Context context,
             @NonNull BluetoothAdapter bluetoothAdapter,
             @NonNull BluetoothRouteController.BluetoothRoutesUpdatedListener listener) {
         this(context, bluetoothAdapter,
@@ -94,7 +92,7 @@
     }
 
     @VisibleForTesting
-    AudioPoliciesBluetoothRouteController(@NonNull Context context,
+    BluetoothDeviceRoutesManager(@NonNull Context context,
             @NonNull BluetoothAdapter bluetoothAdapter,
             @NonNull BluetoothProfileMonitor bluetoothProfileMonitor,
             @NonNull BluetoothRouteController.BluetoothRoutesUpdatedListener listener) {
@@ -276,7 +274,7 @@
             int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1);
             if (state == BluetoothAdapter.STATE_OFF
                     || state == BluetoothAdapter.STATE_TURNING_OFF) {
-                synchronized (AudioPoliciesBluetoothRouteController.this) {
+                synchronized (BluetoothDeviceRoutesManager.this) {
                     mBluetoothRoutes.clear();
                 }
                 notifyBluetoothRoutesUpdated();
@@ -284,7 +282,7 @@
                 updateBluetoothRoutes();
 
                 boolean shouldCallListener;
-                synchronized (AudioPoliciesBluetoothRouteController.this) {
+                synchronized (BluetoothDeviceRoutesManager.this) {
                     shouldCallListener = !mBluetoothRoutes.isEmpty();
                 }
 
diff --git a/services/core/java/com/android/server/media/DeviceRouteController.java b/services/core/java/com/android/server/media/DeviceRouteController.java
index 8b62cc9..dff0adf 100644
--- a/services/core/java/com/android/server/media/DeviceRouteController.java
+++ b/services/core/java/com/android/server/media/DeviceRouteController.java
@@ -65,7 +65,7 @@
         if (strategyForMedia != null
                 && btAdapter != null
                 && Flags.enableAudioPoliciesDeviceAndBluetoothController()) {
-            return new AudioPoliciesDeviceRouteController(
+            return new AudioManagerRouteController(
                     context,
                     audioManager,
                     looper,
diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
index ae889d8..21e7bef 100644
--- a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
+++ b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
@@ -231,18 +231,21 @@
     }
 
     private boolean shouldBind() {
-        if (mRunning) {
-            boolean shouldBind =
-                    mLastDiscoveryPreference != null
-                            && !mLastDiscoveryPreference.getPreferredFeatures().isEmpty();
-            if (mIsSelfScanOnlyProvider) {
-                shouldBind &= mLastDiscoveryPreferenceIncludesThisPackage;
-            }
-            shouldBind |= mIsManagerScanning;
-            shouldBind |= !getSessionInfos().isEmpty();
-            return shouldBind;
+        if (!mRunning) {
+            return false;
         }
-        return false;
+        if (!getSessionInfos().isEmpty() || mIsManagerScanning) {
+            // We bind if any manager is scanning (regardless of whether an app is scanning) to give
+            // the opportunity for providers to publish routing sessions that were established
+            // directly between the app and the provider (typically via AndroidX MediaRouter). See
+            // b/176774510#comment20 for more information.
+            return true;
+        }
+        boolean anAppIsScanning =
+                mLastDiscoveryPreference != null
+                        && !mLastDiscoveryPreference.getPreferredFeatures().isEmpty();
+        return anAppIsScanning
+                && (mLastDiscoveryPreferenceIncludesThisPackage || !mIsSelfScanOnlyProvider);
     }
 
     private void bind() {
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index 38f0df4..e048522 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -774,7 +774,7 @@
                                 .generateDeviceRouteSelectedSessionInfo(packageName);
                     } else {
                         sessionInfos = userRecord.mHandler.mSystemProvider.getSessionInfos();
-                        if (sessionInfos != null && !sessionInfos.isEmpty()) {
+                        if (!sessionInfos.isEmpty()) {
                             // Return a copy of the current system session with no modification,
                             // except setting the client package name.
                             return new RoutingSessionInfo.Builder(sessionInfos.get(0))
@@ -1158,14 +1158,7 @@
         } else {
             if (route.isSystemRoute()
                     && !routerRecord.hasSystemRoutingPermission()
-                    && !TextUtils.equals(
-                            route.getId(),
-                            routerRecord
-                                    .mUserRecord
-                                    .mHandler
-                                    .mSystemProvider
-                                    .getDefaultRoute()
-                                    .getId())) {
+                    && !TextUtils.equals(route.getId(), MediaRoute2Info.ROUTE_ID_DEFAULT)) {
                 Slog.w(TAG, "MODIFY_AUDIO_ROUTING permission is required to transfer to"
                         + route);
                 routerRecord.mUserRecord.mHandler.notifySessionCreationFailedToRouter(
@@ -1252,11 +1245,9 @@
                         "transferToRouteWithRouter2 | router: %s(id: %d), route: %s",
                         routerRecord.mPackageName, routerRecord.mRouterId, route.getId()));
 
-        String defaultRouteId =
-                routerRecord.mUserRecord.mHandler.mSystemProvider.getDefaultRoute().getId();
         if (route.isSystemRoute()
                 && !routerRecord.hasSystemRoutingPermission()
-                && !TextUtils.equals(route.getId(), defaultRouteId)) {
+                && !TextUtils.equals(route.getId(), MediaRoute2Info.ROUTE_ID_DEFAULT)) {
             routerRecord.mUserRecord.mHandler.sendMessage(
                     obtainMessage(UserHandler::notifySessionCreationFailedToRouter,
                             routerRecord.mUserRecord.mHandler,
@@ -2051,6 +2042,7 @@
             String indent = prefix + "  ";
 
             pw.println(indent + "mOwnerPackageName=" + mOwnerPackageName);
+            pw.println(indent + "mTargetPackageName=" + mTargetPackageName);
             pw.println(indent + "mManagerId=" + mManagerId);
             pw.println(indent + "mOwnerUid=" + mOwnerUid);
             pw.println(indent + "mOwnerPid=" + mOwnerPid);
@@ -2264,7 +2256,6 @@
                     indexOfRouteProviderInfoByUniqueId(provider.getUniqueId(), mLastProviderInfos);
             MediaRoute2ProviderInfo oldInfo =
                     providerInfoIndex == -1 ? null : mLastProviderInfos.get(providerInfoIndex);
-            MediaRouter2ServiceImpl mediaRouter2Service = mServiceRef.get();
 
             if (oldInfo == newInfo) {
                 // Nothing to do.
@@ -2700,11 +2691,8 @@
                 // session info from them.
                 sessionInfo = mSystemProvider.getDefaultSessionInfo();
             }
-            // TODO: b/279555229 - replace with matchingRequest.mRouterRecord.notifySessionCreated.
-            notifySessionCreatedToRouter(
-                    matchingRequest.mRouterRecord,
-                    toOriginalRequestId(uniqueRequestId),
-                    sessionInfo);
+            matchingRequest.mRouterRecord.notifySessionCreated(
+                    toOriginalRequestId(uniqueRequestId), sessionInfo);
         }
 
         private void onSessionInfoChangedOnHandler(@NonNull MediaRoute2Provider provider,
@@ -2764,11 +2752,10 @@
             if (manager != null) {
                 notifyRequestFailedToManager(
                         manager.mManager, toOriginalRequestId(uniqueRequestId), reason);
-                return;
             }
 
-            // Currently, only the manager can get notified of failures.
-            // TODO: Notify router too when the related callback is introduced.
+            // Currently, only manager records can get notified of failures.
+            // TODO(b/282936553): Notify regular routers of request failures.
         }
 
         private boolean handleSessionCreationRequestFailed(@NonNull MediaRoute2Provider provider,
@@ -2812,11 +2799,6 @@
             return true;
         }
 
-        private void notifySessionCreatedToRouter(@NonNull RouterRecord routerRecord,
-                int requestId, @NonNull RoutingSessionInfo sessionInfo) {
-            routerRecord.notifySessionCreated(requestId, sessionInfo);
-        }
-
         private void notifySessionCreationFailedToRouter(@NonNull RouterRecord routerRecord,
                 int requestId) {
             try {
@@ -2917,11 +2899,9 @@
                 currentSystemSessionInfo = mSystemProvider.getDefaultSessionInfo();
             }
 
-            if (currentRoutes.size() == 0) {
-                return;
+            if (!currentRoutes.isEmpty()) {
+                routerRecord.notifyRegistered(currentRoutes, currentSystemSessionInfo);
             }
-
-            routerRecord.notifyRegistered(currentRoutes, currentSystemSessionInfo);
         }
 
         private static void notifyRoutesUpdatedToRouterRecords(
diff --git a/services/core/java/com/android/server/media/MediaSession2Record.java b/services/core/java/com/android/server/media/MediaSession2Record.java
index b424c20..393e7ef 100644
--- a/services/core/java/com/android/server/media/MediaSession2Record.java
+++ b/services/core/java/com/android/server/media/MediaSession2Record.java
@@ -16,6 +16,7 @@
 
 package com.android.server.media;
 
+import android.app.ForegroundServiceDelegationOptions;
 import android.media.MediaController2;
 import android.media.Session2CommandGroup;
 import android.media.Session2Token;
@@ -89,6 +90,12 @@
     }
 
     @Override
+    public ForegroundServiceDelegationOptions getForegroundServiceDelegationOptions() {
+        // TODO: Implement when MediaSession2 knows about its owner pid.
+        return null;
+    }
+
+    @Override
     public boolean isSystemPriority() {
         // System priority session is currently only allowed for telephony, so it's OK to stick to
         // the media1 API at this moment.
@@ -191,7 +198,12 @@
                 mIsConnected = true;
                 service = mService;
             }
-            service.onSessionActiveStateChanged(MediaSession2Record.this);
+
+            // TODO (b/318745416): Add support for FGS in MediaSession2. Passing a
+            // null playback state means the owning process will not be allowed to
+            // run in the foreground.
+            service.onSessionActiveStateChanged(MediaSession2Record.this,
+                    /* playbackState= */ null);
         }
 
         @Override
@@ -217,7 +229,8 @@
             synchronized (mLock) {
                 service = mService;
             }
-            service.onSessionPlaybackStateChanged(MediaSession2Record.this, playbackActive);
+            service.onSessionPlaybackStateChanged(
+                    MediaSession2Record.this, playbackActive, /* playbackState= */ null);
         }
     }
 }
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 994d3ca..53f780e 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -29,6 +29,7 @@
 import android.annotation.RequiresPermission;
 import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
+import android.app.ForegroundServiceDelegationOptions;
 import android.app.PendingIntent;
 import android.app.compat.CompatChanges;
 import android.compat.annotation.ChangeId;
@@ -182,6 +183,8 @@
     private final Context mContext;
     private final boolean mVolumeAdjustmentForRemoteGroupSessions;
 
+    private final ForegroundServiceDelegationOptions mForegroundServiceDelegationOptions;
+
     private final Object mLock = new Object();
     private final CopyOnWriteArrayList<ISessionControllerCallbackHolder>
             mControllerCallbackHolders = new CopyOnWriteArrayList<>();
@@ -244,10 +247,32 @@
         mVolumeAdjustmentForRemoteGroupSessions = mContext.getResources().getBoolean(
                 com.android.internal.R.bool.config_volumeAdjustmentForRemoteGroupSessions);
 
+        mForegroundServiceDelegationOptions = createForegroundServiceDelegationOptions();
+
         // May throw RemoteException if the session app is killed.
         mSessionCb.mCb.asBinder().linkToDeath(this, 0);
     }
 
+    private ForegroundServiceDelegationOptions createForegroundServiceDelegationOptions() {
+        return new ForegroundServiceDelegationOptions.Builder()
+                .setClientPid(mOwnerPid)
+                .setClientUid(getUid())
+                .setClientPackageName(getPackageName())
+                .setClientAppThread(null)
+                .setSticky(false)
+                .setClientInstanceName(
+                        "MediaSessionFgsDelegate_"
+                                + getUid()
+                                + "_"
+                                + mOwnerPid
+                                + "_"
+                                + getPackageName())
+                .setForegroundServiceTypes(0)
+                .setDelegationService(
+                        ForegroundServiceDelegationOptions.DELEGATION_SERVICE_MEDIA_PLAYBACK)
+                .build();
+    }
+
     /**
      * Get the session binder for the {@link MediaSession}.
      *
@@ -681,6 +706,11 @@
         return mPackageName + "/" + mTag + " (userId=" + mUserId + ")";
     }
 
+    @Override
+    public ForegroundServiceDelegationOptions getForegroundServiceDelegationOptions() {
+        return mForegroundServiceDelegationOptions;
+    }
+
     private void postAdjustLocalVolume(final int stream, final int direction, final int flags,
             final String callingOpPackageName, final int callingPid, final int callingUid,
             final boolean asSystemService, final boolean useSuggested,
@@ -1114,7 +1144,7 @@
             mIsActive = active;
             long token = Binder.clearCallingIdentity();
             try {
-                mService.onSessionActiveStateChanged(MediaSessionRecord.this);
+                mService.onSessionActiveStateChanged(MediaSessionRecord.this, mPlaybackState);
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
@@ -1273,7 +1303,7 @@
             final long token = Binder.clearCallingIdentity();
             try {
                 mService.onSessionPlaybackStateChanged(
-                        MediaSessionRecord.this, shouldUpdatePriority);
+                        MediaSessionRecord.this, shouldUpdatePriority, mPlaybackState);
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
diff --git a/services/core/java/com/android/server/media/MediaSessionRecordImpl.java b/services/core/java/com/android/server/media/MediaSessionRecordImpl.java
index 8f01f02..99c8ea9 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecordImpl.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecordImpl.java
@@ -16,7 +16,9 @@
 
 package com.android.server.media;
 
+import android.app.ForegroundServiceDelegationOptions;
 import android.media.AudioManager;
+import android.media.session.PlaybackState;
 import android.os.ResultReceiver;
 import android.view.KeyEvent;
 
@@ -51,6 +53,15 @@
     int getUserId();
 
     /**
+     * Get the {@link ForegroundServiceDelegationOptions} needed for notifying activity manager
+     * service with changes in the {@link PlaybackState} for this session.
+     *
+     * @return the {@link ForegroundServiceDelegationOptions} needed for notifying the activity
+     *     manager service with changes in the {@link PlaybackState} for this session.
+     */
+    ForegroundServiceDelegationOptions getForegroundServiceDelegationOptions();
+
+    /**
      * Check if this session has system priority and should receive media buttons before any other
      * sessions.
      *
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index 2c59511..2cd3ab1 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -29,6 +29,8 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
+import android.app.ForegroundServiceDelegationOptions;
 import android.app.KeyguardManager;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
@@ -59,6 +61,7 @@
 import android.media.session.MediaController;
 import android.media.session.MediaSession;
 import android.media.session.MediaSessionManager;
+import android.media.session.PlaybackState;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Handler;
@@ -86,12 +89,10 @@
 import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
 import com.android.media.flags.Flags;
-import com.android.server.LocalManagerRegistry;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
 import com.android.server.Watchdog;
 import com.android.server.Watchdog.Monitor;
-import com.android.server.am.ActivityManagerLocal;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -143,7 +144,7 @@
     private KeyguardManager mKeyguardManager;
     private AudioManager mAudioManager;
     private boolean mHasFeatureLeanback;
-    private ActivityManagerLocal mActivityManagerLocal;
+    private ActivityManagerInternal mActivityManagerInternal;
 
     // The FullUserRecord of the current users. (i.e. The foreground user that isn't a profile)
     // It's always not null after the MediaSessionService is started.
@@ -228,7 +229,7 @@
                 NotificationManager.ACTION_NOTIFICATION_LISTENER_ENABLED_CHANGED);
         mContext.registerReceiver(mNotificationListenerEnabledChangedReceiver, filter);
 
-        mActivityManagerLocal = LocalManagerRegistry.getManager(ActivityManagerLocal.class);
+        mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
     }
 
     @Override
@@ -259,7 +260,8 @@
         return mGlobalPrioritySession != null && mGlobalPrioritySession.isActive();
     }
 
-    void onSessionActiveStateChanged(MediaSessionRecordImpl record) {
+    void onSessionActiveStateChanged(
+            MediaSessionRecordImpl record, @Nullable PlaybackState playbackState) {
         synchronized (mLock) {
             FullUserRecord user = getFullUserRecordLocked(record.getUserId());
             if (user == null) {
@@ -285,7 +287,10 @@
                 }
                 user.mPriorityStack.onSessionActiveStateChanged(record);
             }
-
+            setForegroundServiceAllowance(
+                    record,
+                    /* allowRunningInForeground= */ record.isActive()
+                            && (playbackState == null || playbackState.isActive()));
             mHandler.postSessionsChanged(record);
         }
     }
@@ -371,8 +376,10 @@
         }
     }
 
-    void onSessionPlaybackStateChanged(MediaSessionRecordImpl record,
-            boolean shouldUpdatePriority) {
+    void onSessionPlaybackStateChanged(
+            MediaSessionRecordImpl record,
+            boolean shouldUpdatePriority,
+            @Nullable PlaybackState playbackState) {
         synchronized (mLock) {
             FullUserRecord user = getFullUserRecordLocked(record.getUserId());
             if (user == null || !user.mPriorityStack.contains(record)) {
@@ -380,6 +387,12 @@
                 return;
             }
             user.mPriorityStack.onPlaybackStateChanged(record, shouldUpdatePriority);
+            if (playbackState != null) {
+                setForegroundServiceAllowance(
+                        record,
+                        /* allowRunningInForeground= */ playbackState.isActive()
+                                && record.isActive());
+            }
         }
     }
 
@@ -543,27 +556,51 @@
         }
 
         session.close();
+        setForegroundServiceAllowance(session, /* allowRunningInForeground= */ false);
         mHandler.postSessionsChanged(session);
     }
 
+    private void setForegroundServiceAllowance(
+            MediaSessionRecordImpl record, boolean allowRunningInForeground) {
+        if (!Flags.enableNotifyingActivityManagerWithMediaSessionStatusChange()) {
+            return;
+        }
+        ForegroundServiceDelegationOptions foregroundServiceDelegationOptions =
+                record.getForegroundServiceDelegationOptions();
+        if (foregroundServiceDelegationOptions == null) {
+            // This record doesn't support FGS delegation. In practice, this is MediaSession2.
+            return;
+        }
+        if (allowRunningInForeground) {
+            mActivityManagerInternal.startForegroundServiceDelegate(
+                    foregroundServiceDelegationOptions, /* connection= */ null);
+        } else {
+            mActivityManagerInternal.stopForegroundServiceDelegate(
+                    foregroundServiceDelegationOptions);
+        }
+    }
+
     void tempAllowlistTargetPkgIfPossible(int targetUid, String targetPackage,
             int callingPid, int callingUid, String callingPackage, String reason) {
         final long token = Binder.clearCallingIdentity();
         try {
             MediaServerUtils.enforcePackageName(mContext, callingPackage, callingUid);
             if (targetUid != callingUid) {
-                boolean canAllowWhileInUse = mActivityManagerLocal
-                        .canAllowWhileInUsePermissionInFgs(callingPid, callingUid, callingPackage);
-                boolean canStartFgs = canAllowWhileInUse
-                        || mActivityManagerLocal.canStartForegroundService(callingPid, callingUid,
-                        callingPackage);
+                boolean canAllowWhileInUse =
+                        mActivityManagerInternal.canAllowWhileInUsePermissionInFgs(
+                                callingPid, callingUid, callingPackage);
+                boolean canStartFgs =
+                        canAllowWhileInUse
+                                || mActivityManagerInternal.canStartForegroundService(
+                                        callingPid, callingUid, callingPackage);
                 Log.i(TAG, "tempAllowlistTargetPkgIfPossible callingPackage:"
                         + callingPackage + " targetPackage:" + targetPackage
                         + " reason:" + reason
                         + (canAllowWhileInUse ? " [WIU]" : "")
                         + (canStartFgs ? " [FGS]" : ""));
                 if (canAllowWhileInUse) {
-                    mActivityManagerLocal.tempAllowWhileInUsePermissionInFgs(targetUid,
+                    mActivityManagerInternal.tempAllowWhileInUsePermissionInFgs(
+                            targetUid,
                             MediaSessionDeviceConfig
                                     .getMediaSessionCallbackFgsWhileInUseTempAllowDurationMs());
                 }
diff --git a/services/core/java/com/android/server/media/TEST_MAPPING b/services/core/java/com/android/server/media/TEST_MAPPING
index 1b49093..b3e5b9e 100644
--- a/services/core/java/com/android/server/media/TEST_MAPPING
+++ b/services/core/java/com/android/server/media/TEST_MAPPING
@@ -3,5 +3,10 @@
     {
       "name": "CtsMediaBetterTogetherTestCases"
     }
+  ],
+  "postsubmit": [
+    {
+      "name": "MediaRouterServiceTests"
+    }
   ]
 }
diff --git a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
index 6deda46..f6571d9 100644
--- a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
+++ b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
@@ -653,14 +653,14 @@
         }
 
         @Override // Binder call
-        public boolean hasProjectionPermission(int uid, String packageName) {
+        public boolean hasProjectionPermission(int processUid, String packageName) {
             final long token = Binder.clearCallingIdentity();
             boolean hasPermission = false;
             try {
                 hasPermission |= checkPermission(packageName,
                         android.Manifest.permission.CAPTURE_VIDEO_OUTPUT)
                         || mAppOps.noteOpNoThrow(
-                                AppOpsManager.OP_PROJECT_MEDIA, uid, packageName)
+                                AppOpsManager.OP_PROJECT_MEDIA, processUid, packageName)
                         == AppOpsManager.MODE_ALLOWED;
             } finally {
                 Binder.restoreCallingIdentity(token);
@@ -669,7 +669,7 @@
         }
 
         @Override // Binder call
-        public IMediaProjection createProjection(int uid, String packageName, int type,
+        public IMediaProjection createProjection(int processUid, String packageName, int type,
                 boolean isPermanentGrant) {
             if (mContext.checkCallingPermission(MANAGE_MEDIA_PROJECTION)
                         != PackageManager.PERMISSION_GRANTED) {
@@ -680,13 +680,13 @@
                 throw new IllegalArgumentException("package name must not be empty");
             }
             final UserHandle callingUser = Binder.getCallingUserHandle();
-            return createProjectionInternal(uid, packageName, type, isPermanentGrant,
+            return createProjectionInternal(processUid, packageName, type, isPermanentGrant,
                     callingUser);
         }
 
         @Override // Binder call
         @EnforcePermission(MANAGE_MEDIA_PROJECTION)
-        public IMediaProjection getProjection(int uid, String packageName) {
+        public IMediaProjection getProjection(int processUid, String packageName) {
             getProjection_enforcePermission();
             if (packageName == null || packageName.isEmpty()) {
                 throw new IllegalArgumentException("package name must not be empty");
@@ -695,7 +695,7 @@
             MediaProjection projection;
             final long callingToken = Binder.clearCallingIdentity();
             try {
-                projection = getProjectionInternal(uid, packageName);
+                projection = getProjectionInternal(processUid, packageName);
             } finally {
                 Binder.restoreCallingIdentity(callingToken);
             }
@@ -869,12 +869,13 @@
 
         @Override // Binder call
         @EnforcePermission(MANAGE_MEDIA_PROJECTION)
-        public void notifyPermissionRequestInitiated(int hostUid, int sessionCreationSource) {
+        public void notifyPermissionRequestInitiated(
+                int hostProcessUid, int sessionCreationSource) {
             notifyPermissionRequestInitiated_enforcePermission();
             final long token = Binder.clearCallingIdentity();
             try {
                 MediaProjectionManagerService.this.notifyPermissionRequestInitiated(
-                        hostUid, sessionCreationSource);
+                        hostProcessUid, sessionCreationSource);
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
@@ -882,11 +883,11 @@
 
         @Override // Binder call
         @EnforcePermission(MANAGE_MEDIA_PROJECTION)
-        public void notifyPermissionRequestDisplayed(int hostUid) {
+        public void notifyPermissionRequestDisplayed(int hostProcessUid) {
             notifyPermissionRequestDisplayed_enforcePermission();
             final long token = Binder.clearCallingIdentity();
             try {
-                MediaProjectionManagerService.this.notifyPermissionRequestDisplayed(hostUid);
+                MediaProjectionManagerService.this.notifyPermissionRequestDisplayed(hostProcessUid);
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
@@ -894,11 +895,11 @@
 
         @Override // Binder call
         @EnforcePermission(MANAGE_MEDIA_PROJECTION)
-        public void notifyPermissionRequestCancelled(int hostUid) {
+        public void notifyPermissionRequestCancelled(int hostProcessUid) {
             notifyPermissionRequestCancelled_enforcePermission();
             final long token = Binder.clearCallingIdentity();
             try {
-                MediaProjectionManagerService.this.notifyPermissionRequestCancelled(hostUid);
+                MediaProjectionManagerService.this.notifyPermissionRequestCancelled(hostProcessUid);
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
@@ -906,11 +907,11 @@
 
         @Override // Binder call
         @EnforcePermission(MANAGE_MEDIA_PROJECTION)
-        public void notifyAppSelectorDisplayed(int hostUid) {
+        public void notifyAppSelectorDisplayed(int hostProcessUid) {
             notifyAppSelectorDisplayed_enforcePermission();
             final long token = Binder.clearCallingIdentity();
             try {
-                MediaProjectionManagerService.this.notifyAppSelectorDisplayed(hostUid);
+                MediaProjectionManagerService.this.notifyAppSelectorDisplayed(hostProcessUid);
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
@@ -919,12 +920,12 @@
         @Override // Binder call
         @EnforcePermission(MANAGE_MEDIA_PROJECTION)
         public void notifyWindowingModeChanged(
-                int contentToRecord, int targetUid, int windowingMode) {
+                int contentToRecord, int targetProcessUid, int windowingMode) {
             notifyWindowingModeChanged_enforcePermission();
             final long token = Binder.clearCallingIdentity();
             try {
                 MediaProjectionManagerService.this.notifyWindowingModeChanged(
-                        contentToRecord, targetUid, windowingMode);
+                        contentToRecord, targetProcessUid, windowingMode);
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
diff --git a/services/core/java/com/android/server/net/NetworkPolicyLogger.java b/services/core/java/com/android/server/net/NetworkPolicyLogger.java
index 85731651..4d19ead 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyLogger.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyLogger.java
@@ -42,6 +42,7 @@
 import android.util.Log;
 import android.util.Slog;
 
+import com.android.internal.annotations.Keep;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.RingBuffer;
 import com.android.server.am.ProcessList;
@@ -693,6 +694,7 @@
      * Note: This class needs to be public for RingBuffer class to be able to create
      * new instances of this.
      */
+    @Keep
     public static final class Data {
         public int type;
         public long timeStamp;
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 46e7041..e4e48bd 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -4090,6 +4090,7 @@
                 }
                 fout.decreaseIndent();
 
+                fout.println();
                 fout.println("Admin restricted uids for metered data:");
                 fout.increaseIndent();
                 size = mMeteredRestrictedUids.size();
@@ -4099,6 +4100,7 @@
                 }
                 fout.decreaseIndent();
 
+                fout.println();
                 fout.println("Network to interfaces:");
                 fout.increaseIndent();
                 for (int i = 0; i < mNetworkToIfaces.size(); ++i) {
@@ -4108,6 +4110,10 @@
                 fout.decreaseIndent();
 
                 fout.println();
+                fout.print("Active notifications: ");
+                fout.println(mActiveNotifs);
+
+                fout.println();
                 mStatLogger.dump(fout);
 
                 mLogger.dumpLogs(fout);
@@ -6672,7 +6678,7 @@
          * Build unique tag that identifies an active {@link NetworkPolicy}
          * notification of a specific type, like {@link #TYPE_LIMIT}.
          */
-        private String buildNotificationTag(NetworkPolicy policy, int type) {
+        private static String buildNotificationTag(NetworkPolicy policy, int type) {
             return TAG + ":" + policy.template.hashCode() + ":" + type;
         }
 
@@ -6683,5 +6689,10 @@
         public int getId() {
             return mId;
         }
+
+        @Override
+        public String toString() {
+            return mTag;
+        }
     }
 }
diff --git a/services/core/java/com/android/server/notification/NotificationAttentionHelper.java b/services/core/java/com/android/server/notification/NotificationAttentionHelper.java
index 6b7db2d..85c4ffe 100644
--- a/services/core/java/com/android/server/notification/NotificationAttentionHelper.java
+++ b/services/core/java/com/android/server/notification/NotificationAttentionHelper.java
@@ -22,6 +22,7 @@
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR;
 import static android.media.AudioAttributes.USAGE_NOTIFICATION_RINGTONE;
+import static android.media.audio.Flags.focusExclusiveWithRecording;
 import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_CALL_EFFECTS;
 import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_EFFECTS;
 import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_NOTIFICATION_EFFECTS;
@@ -94,8 +95,6 @@
 
     private static final float DEFAULT_VOLUME = 1.0f;
     // TODO (b/291899544): remove for release
-    private static final String POLITE_STRATEGY1 = "rule1";
-    private static final String POLITE_STRATEGY2 = "rule2";
     private static final int DEFAULT_NOTIFICATION_COOLDOWN_ENABLED = 1;
     private static final int DEFAULT_NOTIFICATION_COOLDOWN_ENABLED_FOR_WORK = 0;
     private static final int DEFAULT_NOTIFICATION_COOLDOWN_ALL = 1;
@@ -146,7 +145,6 @@
     private boolean mNotificationCooldownApplyToAll;
     private boolean mNotificationCooldownVibrateUnlocked;
 
-    private boolean mEnablePoliteNotificationsFeature;
     private final PolitenessStrategy mStrategy;
     private int mCurrentWorkProfileId = UserHandle.USER_NULL;
 
@@ -192,9 +190,7 @@
                 .build();
         mInCallNotificationVolume = resources.getFloat(R.dimen.config_inCallNotificationVolume);
 
-        mEnablePoliteNotificationsFeature = Flags.politeNotifications();
-
-        if (mEnablePoliteNotificationsFeature) {
+        if (Flags.politeNotifications()) {
             mStrategy = getPolitenessStrategy();
         } else {
             mStrategy = null;
@@ -205,21 +201,23 @@
     }
 
     private PolitenessStrategy getPolitenessStrategy() {
-        final String politenessStrategy = mFlagResolver.getStringValue(
-                NotificationFlags.NOTIF_COOLDOWN_RULE);
-
-        if (POLITE_STRATEGY2.equals(politenessStrategy)) {
-            return new Strategy2(mFlagResolver.getIntValue(NotificationFlags.NOTIF_COOLDOWN_T1),
+        if (Flags.crossAppPoliteNotifications()) {
+            PolitenessStrategy appStrategy = new StrategyPerApp(
+                    mFlagResolver.getIntValue(NotificationFlags.NOTIF_COOLDOWN_T1),
                     mFlagResolver.getIntValue(NotificationFlags.NOTIF_COOLDOWN_T2),
                     mFlagResolver.getIntValue(NotificationFlags.NOTIF_VOLUME1),
-                    mFlagResolver.getIntValue(NotificationFlags.NOTIF_VOLUME2));
-        } else {
-            if (!POLITE_STRATEGY1.equals(politenessStrategy)) {
-                Log.w(TAG, "Invalid cooldown strategy: " + politenessStrategy + ". Defaulting to "
-                        + POLITE_STRATEGY1);
-            }
+                    mFlagResolver.getIntValue(NotificationFlags.NOTIF_VOLUME2),
+                    mFlagResolver.getIntValue(NotificationFlags.NOTIF_COOLDOWN_COUNTER_RESET));
 
-            return new Strategy1(mFlagResolver.getIntValue(NotificationFlags.NOTIF_COOLDOWN_T1),
+            return new StrategyGlobal(
+                    mFlagResolver.getIntValue(NotificationFlags.NOTIF_COOLDOWN_T1),
+                    mFlagResolver.getIntValue(NotificationFlags.NOTIF_COOLDOWN_T2),
+                    mFlagResolver.getIntValue(NotificationFlags.NOTIF_VOLUME1),
+                    mFlagResolver.getIntValue(NotificationFlags.NOTIF_VOLUME2),
+                    appStrategy);
+        } else {
+            return new StrategyPerApp(
+                    mFlagResolver.getIntValue(NotificationFlags.NOTIF_COOLDOWN_T1),
                     mFlagResolver.getIntValue(NotificationFlags.NOTIF_COOLDOWN_T2),
                     mFlagResolver.getIntValue(NotificationFlags.NOTIF_VOLUME1),
                     mFlagResolver.getIntValue(NotificationFlags.NOTIF_VOLUME2),
@@ -266,7 +264,7 @@
         mContext.getContentResolver().registerContentObserver(
                 SettingsObserver.NOTIFICATION_LIGHT_PULSE_URI, false, mSettingsObserver,
                 UserHandle.USER_ALL);
-        if (mEnablePoliteNotificationsFeature) {
+        if (Flags.politeNotifications()) {
             mContext.getContentResolver().registerContentObserver(
                     SettingsObserver.NOTIFICATION_COOLDOWN_ENABLED_URI, false, mSettingsObserver,
                     UserHandle.USER_ALL);
@@ -280,7 +278,7 @@
     }
 
     private void loadUserSettings() {
-        if (mEnablePoliteNotificationsFeature) {
+        if (Flags.politeNotifications()) {
             try {
                 mCurrentWorkProfileId = getManagedProfileId(ActivityManager.getCurrentUser());
 
@@ -301,11 +299,14 @@
                     mContext.getContentResolver(),
                     Settings.System.NOTIFICATION_COOLDOWN_ALL, DEFAULT_NOTIFICATION_COOLDOWN_ALL,
                     UserHandle.USER_CURRENT) != 0;
-                mNotificationCooldownVibrateUnlocked = Settings.System.getIntForUser(
-                    mContext.getContentResolver(),
-                    Settings.System.NOTIFICATION_COOLDOWN_VIBRATE_UNLOCKED,
-                    DEFAULT_NOTIFICATION_COOLDOWN_VIBRATE_UNLOCKED,
-                    UserHandle.USER_CURRENT) != 0;
+                mStrategy.setApplyCooldownPerPackage(mNotificationCooldownApplyToAll);
+                if (Flags.vibrateWhileUnlocked()) {
+                    mNotificationCooldownVibrateUnlocked = Settings.System.getIntForUser(
+                        mContext.getContentResolver(),
+                        Settings.System.NOTIFICATION_COOLDOWN_VIBRATE_UNLOCKED,
+                        DEFAULT_NOTIFICATION_COOLDOWN_VIBRATE_UNLOCKED,
+                        UserHandle.USER_CURRENT) != 0;
+                }
             } catch (Exception e) {
                 Log.e(TAG, "Failed to read Settings: " + e);
             }
@@ -482,10 +483,10 @@
                     getPolitenessState(record));
         }
         record.setAudiblyAlerted(buzz || beep);
-        if (mEnablePoliteNotificationsFeature) {
+        if (Flags.politeNotifications()) {
             // Update last alert time
             if (buzz || beep) {
-                record.getChannel().setLastNotificationUpdateTimeMs(System.currentTimeMillis());
+                mStrategy.setLastNotificationUpdateTimeMs(record, System.currentTimeMillis());
             }
         }
         return buzzBeepBlinkLoggingCode;
@@ -588,37 +589,48 @@
     }
 
     private boolean playSound(final NotificationRecord record, Uri soundUri) {
+        final boolean shouldPlay;
+        if (focusExclusiveWithRecording()) {
+            // flagged path
+            shouldPlay = mAudioManager.shouldNotificationSoundPlay(record.getAudioAttributes());
+        } else {
+            // legacy path
+            // play notifications if there is no user of exclusive audio focus
+            // and the stream volume is not 0 (non-zero volume implies not silenced by SILENT or
+            //   VIBRATE ringer mode)
+            shouldPlay = !mAudioManager.isAudioFocusExclusive()
+                    && (mAudioManager.getStreamVolume(
+                    AudioAttributes.toLegacyStreamType(record.getAudioAttributes())) != 0);
+        }
+        if (!shouldPlay) {
+            if (DEBUG) Slog.v(TAG, "Not playing sound " + soundUri + " due to focus/volume");
+            return false;
+        }
+
         boolean looping = (record.getNotification().flags & FLAG_INSISTENT) != 0;
-        // play notifications if there is no user of exclusive audio focus
-        // and the stream volume is not 0 (non-zero volume implies not silenced by SILENT or
-        //   VIBRATE ringer mode)
-        if (!mAudioManager.isAudioFocusExclusive()
-                && (mAudioManager.getStreamVolume(
-                AudioAttributes.toLegacyStreamType(record.getAudioAttributes())) != 0)) {
-            final long identity = Binder.clearCallingIdentity();
-            try {
-                final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
-                if (player != null) {
-                    if (DEBUG) {
-                        Slog.v(TAG, "Playing sound " + soundUri + " with attributes "
-                                + record.getAudioAttributes());
-                    }
-                    player.playAsync(soundUri, record.getSbn().getUser(), looping,
-                            record.getAudioAttributes(), getSoundVolume(record));
-                    return true;
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
+            if (player != null) {
+                if (DEBUG) {
+                    Slog.v(TAG, "Playing sound " + soundUri + " with attributes "
+                            + record.getAudioAttributes());
                 }
-            } catch (RemoteException e) {
-                Log.e(TAG, "Failed playSound: " + e);
-            } finally {
-                Binder.restoreCallingIdentity(identity);
+                player.playAsync(soundUri, record.getSbn().getUser(), looping,
+                        record.getAudioAttributes(), getSoundVolume(record));
+                return true;
             }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Failed playSound: " + e);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
         }
         return false;
     }
 
     private boolean isPoliteNotificationFeatureEnabled(final NotificationRecord record) {
         // Check feature flag
-        if (!mEnablePoliteNotificationsFeature) {
+        if (!Flags.politeNotifications()) {
             return false;
         }
 
@@ -1064,9 +1076,13 @@
         // Volume for muted state
         protected final float mVolumeMuted;
 
+        protected boolean mApplyPerPackage;
+        protected final Map<String, Long> mLastUpdatedTimestampByPackage;
+
         public PolitenessStrategy(int timeoutPolite, int timeoutMuted, int volumePolite,
                 int volumeMuted) {
             mVolumeStates = new HashMap<>();
+            mLastUpdatedTimestampByPackage = new HashMap<>();
 
             this.mTimeoutPolite = timeoutPolite;
             this.mTimeoutMuted = timeoutMuted;
@@ -1076,10 +1092,38 @@
 
         abstract void onNotificationPosted(NotificationRecord record);
 
+        /**
+         *  Set true if the cooldown strategy should apply per app(package).
+         *  Otherwise apply per conversation channel.
+         * @param applyPerPackage if the cooldown should be applied per app
+         */
+        void setApplyCooldownPerPackage(boolean applyPerPackage) {
+            mApplyPerPackage = applyPerPackage;
+        }
+
+        boolean shouldIgnoreNotification(final NotificationRecord record) {
+            // Ignore group summaries
+            return (record.getSbn().isGroup() && record.getSbn().getNotification()
+                    .isGroupSummary());
+        }
+
+        /**
+         * Get the key that determines the grouping for the cooldown behavior.
+         *
+         * @param record the notification being posted
+         * @return the key to group this notification under
+         */
         String getChannelKey(final NotificationRecord record) {
-            // use conversationId if it's a conversation
+            // Use conversationId if it's a conversation
             String channelId = record.getChannel().getConversationId() != null
                     ? record.getChannel().getConversationId() : record.getChannel().getId();
+
+            // Use only the package name to apply cooldown per app, unless the user explicitly
+            // changed the channel notification sound => treat separately
+            if (mApplyPerPackage && !record.getChannel().hasUserSetSound()) {
+                channelId = "";
+            }
+
             return record.getSbn().getNormalizedUserId() + ":" + record.getSbn().getPackageName()
                     + ":" + channelId;
         }
@@ -1121,12 +1165,59 @@
             final String key = getChannelKey(record);
             // reset to default state after user interaction
             mVolumeStates.put(key, POLITE_STATE_DEFAULT);
-            record.getChannel().setLastNotificationUpdateTimeMs(0);
+            setLastNotificationUpdateTimeMs(record, 0);
         }
 
         public final @PolitenessState int getPolitenessState(final NotificationRecord record) {
             return mVolumeStates.getOrDefault(getChannelKey(record), POLITE_STATE_DEFAULT);
         }
+
+        void setLastNotificationUpdateTimeMs(final NotificationRecord record,
+                long timestampMillis) {
+            record.getChannel().setLastNotificationUpdateTimeMs(timestampMillis);
+            mLastUpdatedTimestampByPackage.put(record.getSbn().getPackageName(), timestampMillis);
+        }
+
+        long getLastNotificationUpdateTimeMs(final NotificationRecord record) {
+            if (record.getChannel().hasUserSetSound() || !mApplyPerPackage) {
+                return record.getChannel().getLastNotificationUpdateTimeMs();
+            } else {
+                return mLastUpdatedTimestampByPackage.getOrDefault(record.getSbn().getPackageName(),
+                        0L);
+            }
+        }
+
+        @PolitenessState int getNextState(@PolitenessState final int currState,
+                final long timeSinceLastNotif) {
+            @PolitenessState int nextState = currState;
+            switch (currState) {
+                case POLITE_STATE_DEFAULT:
+                    if (timeSinceLastNotif < mTimeoutPolite) {
+                        nextState = POLITE_STATE_POLITE;
+                    }
+                    break;
+                case POLITE_STATE_POLITE:
+                    if (timeSinceLastNotif < mTimeoutMuted) {
+                        nextState = POLITE_STATE_MUTED;
+                    } else if (timeSinceLastNotif > mTimeoutPolite) {
+                        nextState = POLITE_STATE_DEFAULT;
+                    } else {
+                        nextState = POLITE_STATE_POLITE;
+                    }
+                    break;
+                case POLITE_STATE_MUTED:
+                    if (timeSinceLastNotif > mTimeoutMuted) {
+                        nextState = POLITE_STATE_POLITE;
+                    } else {
+                        nextState = POLITE_STATE_MUTED;
+                    }
+                    break;
+                default:
+                    Log.w(TAG, "getNextState unexpected volume state: " + currState);
+                    break;
+            }
+            return nextState;
+        }
     }
 
     // TODO b/270456865: Only one of the two strategies will be released.
@@ -1143,72 +1234,51 @@
      *  after timeoutMuted.
      *  - Transitions back to the default state after a user interaction with a notification.
      */
-    public static class Strategy1 extends PolitenessStrategy {
+    private static class StrategyPerApp extends PolitenessStrategy {
         // Keep track of the number of notifications posted per channel
         private final Map<String, Integer> mNumPosted;
         // Reset to default state if number of posted notifications exceed this value when muted
         private final int mMaxPostedForReset;
 
-        public Strategy1(int timeoutPolite, int timeoutMuted, int volumePolite, int volumeMuted,
-                int maxPosted) {
+        public StrategyPerApp(int timeoutPolite, int timeoutMuted, int volumePolite,
+                int volumeMuted, int maxPosted) {
             super(timeoutPolite, timeoutMuted, volumePolite, volumeMuted);
 
             mNumPosted = new HashMap<>();
             mMaxPostedForReset = maxPosted;
 
             if (DEBUG) {
-                Log.i(TAG, "Strategy1: " + timeoutPolite + " " + timeoutMuted);
+                Log.i(TAG, "StrategyPerApp: " + timeoutPolite + " " + timeoutMuted);
             }
         }
 
         @Override
         public void onNotificationPosted(final NotificationRecord record) {
-            long timeSinceLastNotif = System.currentTimeMillis()
-                    - record.getChannel().getLastNotificationUpdateTimeMs();
+            if (shouldIgnoreNotification(record)) {
+                return;
+            }
+
+            long timeSinceLastNotif =
+                    System.currentTimeMillis() - getLastNotificationUpdateTimeMs(record);
 
             final String key = getChannelKey(record);
-            @PolitenessState int volState = getPolitenessState(record);
+            @PolitenessState final int currState = getPolitenessState(record);
+            @PolitenessState int nextState = getNextState(currState, timeSinceLastNotif);
 
+            // Reset to default state if number of posted notifications exceed this value when muted
             int numPosted = mNumPosted.getOrDefault(key, 0) + 1;
             mNumPosted.put(key, numPosted);
-
-            switch (volState) {
-                case POLITE_STATE_DEFAULT:
-                    if (timeSinceLastNotif < mTimeoutPolite) {
-                        volState = POLITE_STATE_POLITE;
-                    }
-                    break;
-                case POLITE_STATE_POLITE:
-                    if (timeSinceLastNotif < mTimeoutMuted) {
-                        volState = POLITE_STATE_MUTED;
-                    } else if (timeSinceLastNotif > mTimeoutPolite) {
-                        volState = POLITE_STATE_DEFAULT;
-                    } else {
-                        volState = POLITE_STATE_POLITE;
-                    }
-                    break;
-                case POLITE_STATE_MUTED:
-                    if (timeSinceLastNotif > mTimeoutMuted) {
-                        volState = POLITE_STATE_POLITE;
-                    } else {
-                        volState = POLITE_STATE_MUTED;
-                    }
-                    if (numPosted >= mMaxPostedForReset) {
-                        volState = POLITE_STATE_DEFAULT;
-                        mNumPosted.put(key, 0);
-                    }
-                    break;
-                default:
-                    Log.w(TAG, "onNotificationPosted unexpected volume state: " + volState);
-                    break;
+            if (currState == POLITE_STATE_MUTED && numPosted >= mMaxPostedForReset) {
+                nextState = POLITE_STATE_DEFAULT;
+                mNumPosted.put(key, 0);
             }
 
             if (DEBUG) {
                 Log.i(TAG, "onNotificationPosted time delta: " + timeSinceLastNotif + " vol state: "
-                        + volState + " key: " + key + " numposted " + numPosted);
+                        + nextState + " key: " + key + " numposted " + numPosted);
             }
 
-            mVolumeStates.put(key, volState);
+            mVolumeStates.put(key, nextState);
         }
 
         @Override
@@ -1219,61 +1289,98 @@
     }
 
     /**
-     *  Polite notification strategy 2:
-     *   - Transitions from default (loud) => muted state if a notification
-     *   alerts the same channel before timeoutPolite.
-     *   - Transitions from polite => default state if a notification
-     *  alerts the same channel before timeoutMuted.
-     *   - Transitions from muted => default state if a notification alerts after timeoutMuted,
-     *  otherwise transitions to the polite state.
-     *   - Transitions back to the default state after a user interaction with a notification.
+     * Global (cross-app) strategy.
      */
-    public static class Strategy2 extends PolitenessStrategy {
-        public Strategy2(int timeoutPolite, int timeoutMuted, int volumePolite, int volumeMuted) {
+    private static class StrategyGlobal extends PolitenessStrategy {
+        private static final String COMMON_KEY = "cross_app_common_key";
+
+        private final PolitenessStrategy mAppStrategy;
+        private long mLastNotificationTimestamp = 0;
+
+        public StrategyGlobal(int timeoutPolite, int timeoutMuted, int volumePolite,
+                int volumeMuted, PolitenessStrategy appStrategy) {
             super(timeoutPolite, timeoutMuted, volumePolite, volumeMuted);
 
+            mAppStrategy = appStrategy;
+
             if (DEBUG) {
-                Log.i(TAG, "Strategy2: " + timeoutPolite + " " + timeoutMuted);
+                Log.i(TAG, "StrategyGlobal: " + timeoutPolite + " " + timeoutMuted);
             }
         }
 
         @Override
-        public void onNotificationPosted(final NotificationRecord record) {
-            long timeSinceLastNotif = System.currentTimeMillis()
-                    - record.getChannel().getLastNotificationUpdateTimeMs();
+        void onNotificationPosted(NotificationRecord record) {
+            if (shouldIgnoreNotification(record)) {
+                return;
+            }
+
+            long timeSinceLastNotif =
+                    System.currentTimeMillis() - getLastNotificationUpdateTimeMs(record);
 
             final String key = getChannelKey(record);
-            @PolitenessState int volState = getPolitenessState(record);
-
-            switch (volState) {
-                case POLITE_STATE_DEFAULT:
-                    if (timeSinceLastNotif < mTimeoutPolite) {
-                        volState = POLITE_STATE_MUTED;
-                    }
-                    break;
-                case POLITE_STATE_POLITE:
-                    if (timeSinceLastNotif > mTimeoutMuted) {
-                        volState = POLITE_STATE_DEFAULT;
-                    }
-                    break;
-                case POLITE_STATE_MUTED:
-                    if (timeSinceLastNotif > mTimeoutMuted) {
-                        volState = POLITE_STATE_DEFAULT;
-                    } else {
-                        volState = POLITE_STATE_POLITE;
-                    }
-                    break;
-                default:
-                    Log.w(TAG, "onNotificationPosted unexpected volume state: " + volState);
-                    break;
-            }
+            @PolitenessState final int currState = getPolitenessState(record);
+            @PolitenessState int nextState = getNextState(currState, timeSinceLastNotif);
 
             if (DEBUG) {
-                Log.i(TAG, "onNotificationPosted time delta: " + timeSinceLastNotif + " vol state: "
-                        + volState + " key: " + key);
+                Log.i(TAG, "StrategyGlobal onNotificationPosted time delta: " + timeSinceLastNotif
+                        + " vol state: " + nextState + " key: " + key);
             }
 
-            mVolumeStates.put(key, volState);
+            mVolumeStates.put(key, nextState);
+
+            mAppStrategy.onNotificationPosted(record);
+        }
+
+        @Override
+        public float getSoundVolume(final NotificationRecord record) {
+            final @PolitenessState int globalVolState = getPolitenessState(record);
+            final @PolitenessState int appVolState = mAppStrategy.getPolitenessState(record);
+
+            // Prioritize the most polite outcome
+            if (globalVolState > appVolState) {
+                return super.getSoundVolume(record);
+            } else {
+                return mAppStrategy.getSoundVolume(record);
+            }
+        }
+
+        @Override
+        public void onUserInteraction(final NotificationRecord record) {
+            super.onUserInteraction(record);
+            mAppStrategy.onUserInteraction(record);
+        }
+
+        @Override
+        String getChannelKey(final NotificationRecord record) {
+            // If the user explicitly changed the channel notification sound:
+            // handle as a separate channel
+            if (record.getChannel().hasUserSetSound()) {
+                return super.getChannelKey(record);
+            } else {
+                // Use one global key per user
+                return record.getSbn().getNormalizedUserId() + ":" + COMMON_KEY;
+            }
+        }
+
+        @Override
+        public void setLastNotificationUpdateTimeMs(NotificationRecord record,
+                long timestampMillis) {
+            super.setLastNotificationUpdateTimeMs(record, timestampMillis);
+            mLastNotificationTimestamp = timestampMillis;
+        }
+
+        long getLastNotificationUpdateTimeMs(final NotificationRecord record) {
+            if (record.getChannel().hasUserSetSound()) {
+                return super.getLastNotificationUpdateTimeMs(record);
+            } else {
+                return mLastNotificationTimestamp;
+            }
+        }
+
+        @Override
+        void setApplyCooldownPerPackage(boolean applyPerPackage) {
+            super.setApplyCooldownPerPackage(applyPerPackage);
+            mAppStrategy.setApplyCooldownPerPackage(applyPerPackage);
         }
     }
 
@@ -1338,7 +1445,7 @@
                     updateLightsLocked();
                 }
             }
-            if (mEnablePoliteNotificationsFeature) {
+            if (Flags.politeNotifications()) {
                 if (NOTIFICATION_COOLDOWN_ENABLED_URI.equals(uri)) {
                     mNotificationCooldownEnabled = Settings.System.getIntForUser(
                             mContext.getContentResolver(),
@@ -1363,13 +1470,16 @@
                             Settings.System.NOTIFICATION_COOLDOWN_ALL,
                             DEFAULT_NOTIFICATION_COOLDOWN_ALL, UserHandle.USER_CURRENT)
                             != 0;
+                    mStrategy.setApplyCooldownPerPackage(mNotificationCooldownApplyToAll);
                 }
-                if (NOTIFICATION_COOLDOWN_VIBRATE_UNLOCKED_URI.equals(uri)) {
-                    mNotificationCooldownVibrateUnlocked = Settings.System.getIntForUser(
+                if (Flags.vibrateWhileUnlocked()) {
+                    if (NOTIFICATION_COOLDOWN_VIBRATE_UNLOCKED_URI.equals(uri)) {
+                        mNotificationCooldownVibrateUnlocked = Settings.System.getIntForUser(
                             mContext.getContentResolver(),
                             Settings.System.NOTIFICATION_COOLDOWN_VIBRATE_UNLOCKED,
                             DEFAULT_NOTIFICATION_COOLDOWN_VIBRATE_UNLOCKED,
                             UserHandle.USER_CURRENT) != 0;
+                    }
                 }
             }
         }
diff --git a/services/core/java/com/android/server/notification/NotificationHistoryManager.java b/services/core/java/com/android/server/notification/NotificationHistoryManager.java
index 6a46048..deb95d8 100644
--- a/services/core/java/com/android/server/notification/NotificationHistoryManager.java
+++ b/services/core/java/com/android/server/notification/NotificationHistoryManager.java
@@ -108,7 +108,7 @@
                 for (int i = 0; i < pendingPackageRemovals.size(); i++) {
                     userHistory.onPackageRemoved(pendingPackageRemovals.get(i));
                 }
-                mUserPendingPackageRemovals.put(userId, null);
+                mUserPendingPackageRemovals.remove(userId);
             }
 
             // delete history if it was disabled when the user was locked
@@ -118,6 +118,10 @@
         }
     }
 
+    public void onUserAdded(@UserIdInt int userId) {
+        mSettingsObserver.update(null, userId);
+    }
+
     public void onUserStopped(@UserIdInt int userId) {
         synchronized (mLock) {
             mUserUnlockedStates.put(userId, false);
@@ -129,7 +133,7 @@
         synchronized (mLock) {
             // Actual data deletion is handled by other parts of the system (the entire directory is
             // removed) - we just need clean up our internal state for GC
-            mUserPendingPackageRemovals.put(userId, null);
+            mUserPendingPackageRemovals.remove(userId);
             mHistoryEnabled.put(userId, false);
             mUserPendingHistoryDisables.put(userId, false);
             onUserStopped(userId);
@@ -401,9 +405,7 @@
                     false, this, UserHandle.USER_ALL);
             synchronized (mLock) {
                 for (UserInfo userInfo : mUserManager.getUsers()) {
-                    if (!userInfo.isProfile()) {
-                        update(null, userInfo.id);
-                    }
+                    update(null, userInfo.id);
                 }
             }
         }
@@ -424,10 +426,7 @@
                 boolean historyEnabled = Settings.Secure.getIntForUser(resolver,
                         Settings.Secure.NOTIFICATION_HISTORY_ENABLED, 0, userId)
                         != 0;
-                int[] profiles = mUserManager.getProfileIds(userId, true);
-                for (int profileId : profiles) {
-                    onHistoryEnabledChanged(profileId, historyEnabled);
-                }
+                onHistoryEnabledChanged(userId, historyEnabled);
             }
         }
     }
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 13bf336c..9ddc362 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -16,7 +16,9 @@
 
 package com.android.server.notification;
 
+import static android.Manifest.permission.CONTROL_KEYGUARD_SECURE_NOTIFICATIONS;
 import static android.Manifest.permission.RECEIVE_SENSITIVE_NOTIFICATIONS;
+import static android.Manifest.permission.STATUS_BAR_SERVICE;
 import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
 import static android.app.ActivityManagerInternal.ServiceNotificationPolicy.NOT_FOREGROUND_SERVICE;
 import static android.app.AppOpsManager.MODE_ALLOWED;
@@ -67,6 +69,8 @@
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_ON;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR;
 import static android.app.Flags.lifetimeExtensionRefactor;
+import static android.app.StatusBarManager.ACTION_KEYGUARD_PRIVATE_NOTIFICATIONS_CHANGED;
+import static android.app.StatusBarManager.EXTRA_KM_PRIVATE_NOTIFS_ALLOWED;
 import static android.content.Context.BIND_ALLOW_WHITELIST_MANAGEMENT;
 import static android.content.Context.BIND_AUTO_CREATE;
 import static android.content.Context.BIND_FOREGROUND_SERVICE;
@@ -79,6 +83,7 @@
 import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
 import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.media.audio.Flags.focusExclusiveWithRecording;
 import static android.media.AudioAttributes.USAGE_NOTIFICATION_RINGTONE;
 import static android.os.Flags.allowPrivateProfile;
 import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_CRITICAL;
@@ -367,6 +372,7 @@
 import java.io.OutputStream;
 import java.io.PrintWriter;
 import java.nio.charset.StandardCharsets;
+import java.time.Clock;
 import java.time.Duration;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -2020,6 +2026,8 @@
                     if (!mUserProfiles.isProfileUser(userId)) {
                         allowDefaultApprovedServices(userId);
                     }
+                    mHistoryManager.onUserAdded(userId);
+                    mSettingsObserver.update(null, userId);
                 }
             } else if (action.equals(Intent.ACTION_USER_REMOVED)) {
                 final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL);
@@ -2132,13 +2140,8 @@
                 mPreferencesHelper.updateBubblesEnabled();
             }
             if (uri == null || NOTIFICATION_HISTORY_ENABLED.equals(uri)) {
-                final IntArray userIds = mUserProfiles.getCurrentProfileIds();
-
-                for (int i = 0; i < userIds.size(); i++) {
-                    mArchive.updateHistoryEnabled(userIds.get(i),
-                            Settings.Secure.getIntForUser(resolver,
-                                    Settings.Secure.NOTIFICATION_HISTORY_ENABLED, 0,
-                                    userIds.get(i)) == 1);
+                for (UserInfo userInfo : mUm.getUsers()) {
+                    update(uri, userInfo.id);
                 }
             }
             if (uri == null || NOTIFICATION_SHOW_MEDIA_ON_QUICK_SETTINGS_URI.equals(uri)) {
@@ -2159,6 +2162,17 @@
                 }
             }
         }
+
+        public void update(Uri uri, int userId) {
+            ContentResolver resolver = getContext().getContentResolver();
+            if (uri == null || NOTIFICATION_HISTORY_ENABLED.equals(uri)) {
+                mArchive.updateHistoryEnabled(userId,
+                        Settings.Secure.getIntForUser(resolver,
+                                Settings.Secure.NOTIFICATION_HISTORY_ENABLED, 0,
+                                userId) == 1);
+                // note: this setting is also handled in NotificationHistoryManager
+            }
+        }
     }
 
     private SettingsObserver mSettingsObserver;
@@ -2452,8 +2466,8 @@
         mMetricsLogger = new MetricsLogger();
         mRankingHandler = rankingHandler;
         mConditionProviders = conditionProviders;
-        mZenModeHelper = new ZenModeHelper(getContext(), mHandler.getLooper(), mConditionProviders,
-                flagResolver, new ZenModeEventLogger(mPackageManagerClient));
+        mZenModeHelper = new ZenModeHelper(getContext(), mHandler.getLooper(), Clock.systemUTC(),
+                mConditionProviders, flagResolver, new ZenModeEventLogger(mPackageManagerClient));
         mZenModeHelper.addCallback(new ZenModeHelper.Callback() {
             @Override
             public void onConfigChanged() {
@@ -3369,9 +3383,7 @@
                             .setChannelName(r.getChannel().getName().toString())
                             .setPostedTimeMs(System.currentTimeMillis())
                             .setTitle(getHistoryTitle(r.getNotification()))
-                            .setText(getHistoryText(
-                                    r.getSbn().getPackageContext(getContext()),
-                                    r.getNotification()))
+                            .setText(getHistoryText(r.getNotification()))
                             .setIcon(r.getNotification().getSmallIcon())
                             .build());
                 }
@@ -3414,12 +3426,11 @@
     /**
      * Returns the appropriate substring for this notification based on the style of notification.
      */
-    private String getHistoryText(Context appContext, Notification n) {
+    private String getHistoryText(Notification n) {
         CharSequence text = null;
         if (n.extras != null) {
             text = n.extras.getCharSequence(EXTRA_TEXT);
-
-            Notification.Builder nb = Notification.Builder.recoverBuilder(appContext, n);
+            Notification.Builder nb = Notification.Builder.recoverBuilder(getContext(), n);
 
             if (nb.getStyle() instanceof Notification.BigTextStyle) {
                 text = ((Notification.BigTextStyle) nb.getStyle()).getBigText();
@@ -5126,8 +5137,10 @@
                             for (int userId : mUm.getProfileIds(info.userid, false)) {
                                 try {
                                     int uid = getUidForPackageAndUser(pkg, UserHandle.of(userId));
-                                    VersionedPackage vp = new VersionedPackage(pkg, uid);
-                                    nlf.addPackage(vp);
+                                    if (uid != INVALID_UID) {
+                                        VersionedPackage vp = new VersionedPackage(pkg, uid);
+                                        nlf.addPackage(vp);
+                                    }
                                 } catch (Exception e) {
                                     // pkg doesn't exist on that user; skip
                                 }
@@ -5383,7 +5396,7 @@
         @Override
         public String addAutomaticZenRule(AutomaticZenRule automaticZenRule, String pkg,
                 boolean fromUser) {
-            validateAutomaticZenRule(automaticZenRule);
+            validateAutomaticZenRule(/* updateId= */ null, automaticZenRule);
             checkCallerIsSameApp(pkg);
             if (automaticZenRule.getZenPolicy() != null
                     && automaticZenRule.getInterruptionFilter() != INTERRUPTION_FILTER_PRIORITY) {
@@ -5409,7 +5422,7 @@
         @Override
         public boolean updateAutomaticZenRule(String id, AutomaticZenRule automaticZenRule,
                 boolean fromUser) throws RemoteException {
-            validateAutomaticZenRule(automaticZenRule);
+            validateAutomaticZenRule(id, automaticZenRule);
             enforcePolicyAccess(Binder.getCallingUid(), "updateAutomaticZenRule");
             enforceUserOriginOnlyFromSystem(fromUser, "updateAutomaticZenRule");
 
@@ -5417,26 +5430,58 @@
                     computeZenOrigin(fromUser), "updateAutomaticZenRule", Binder.getCallingUid());
         }
 
-        private void validateAutomaticZenRule(AutomaticZenRule rule) {
+        private void validateAutomaticZenRule(@Nullable String updateId, AutomaticZenRule rule) {
             Objects.requireNonNull(rule, "automaticZenRule is null");
             Objects.requireNonNull(rule.getName(), "Name is null");
             rule.validate();
-            if (rule.getOwner() == null
-                    && rule.getConfigurationActivity() == null) {
-                throw new NullPointerException(
-                        "Rule must have a conditionproviderservice and/or configuration activity");
+
+            // Implicit rules have no ConditionProvider or Activity. We allow the user to customize
+            // them (via Settings), but not the owner app. Should the app want to start using it as
+            // a "normal" rule, it must provide a CP/ConfigActivity too.
+            if (android.app.Flags.modesApi()) {
+                boolean isImplicitRuleUpdateFromSystem = updateId != null
+                        && ZenModeHelper.isImplicitRuleId(updateId)
+                        && isCallerSystemOrSystemUi();
+                if (!isImplicitRuleUpdateFromSystem
+                        && rule.getOwner() == null
+                        && rule.getConfigurationActivity() == null) {
+                    throw new NullPointerException(
+                            "Rule must have a ConditionProviderService and/or configuration "
+                                    + "activity");
+                }
+            } else {
+                if (rule.getOwner() == null && rule.getConfigurationActivity() == null) {
+                    throw new NullPointerException(
+                            "Rule must have a ConditionProviderService and/or configuration "
+                                    + "activity");
+                }
             }
             Objects.requireNonNull(rule.getConditionId(), "ConditionId is null");
 
             if (android.app.Flags.modesApi()) {
+                if (isCallerSystemOrSystemUi()) {
+                    return; // System callers can use any type.
+                }
+                int uid = Binder.getCallingUid();
+                int userId = UserHandle.getUserId(uid);
+
                 if (rule.getType() == AutomaticZenRule.TYPE_MANAGED) {
-                    int uid = Binder.getCallingUid();
                     boolean isDeviceOwner = Binder.withCleanCallingIdentity(
                             () -> mDpm.isActiveDeviceOwner(uid));
                     if (!isDeviceOwner) {
                         throw new IllegalArgumentException(
                                 "Only Device Owners can use AutomaticZenRules with TYPE_MANAGED");
                     }
+                } else if (rule.getType() == AutomaticZenRule.TYPE_BEDTIME) {
+                    String wellbeingPackage = getContext().getResources().getString(
+                            com.android.internal.R.string.config_systemWellbeing);
+                    boolean isCallerWellbeing = !TextUtils.isEmpty(wellbeingPackage)
+                            && mPackageManagerInternal.isSameApp(wellbeingPackage, uid, userId);
+                    if (!isCallerWellbeing) {
+                        throw new IllegalArgumentException(
+                                "Only the 'Wellbeing' package can use AutomaticZenRules with "
+                                        + "TYPE_BEDTIME");
+                    }
                 }
             }
         }
@@ -5472,20 +5517,13 @@
         }
 
         @Override
-        public void setAutomaticZenRuleState(String id, Condition condition, boolean fromUser) {
+        public void setAutomaticZenRuleState(String id, Condition condition) {
             Objects.requireNonNull(id, "id is null");
             Objects.requireNonNull(condition, "Condition is null");
             condition.validate();
 
             enforcePolicyAccess(Binder.getCallingUid(), "setAutomaticZenRuleState");
-
-            if (android.app.Flags.modesApi()) {
-                if (fromUser != (condition.source == Condition.SOURCE_USER_ACTION)) {
-                    throw new IllegalArgumentException(String.format(
-                            "Mismatch between fromUser (%s) and condition.source (%s)",
-                            fromUser, Condition.sourceToString(condition.source)));
-                }
-            }
+            boolean fromUser = (condition.source == Condition.SOURCE_USER_ACTION);
 
             mZenModeHelper.setAutomaticZenRuleState(id, condition, computeZenOrigin(fromUser),
                     Binder.getCallingUid());
@@ -5508,7 +5546,7 @@
             if (android.app.Flags.modesApi()
                     && fromUser
                     && !isCallerSystemOrSystemUiOrShell()) {
-                throw new SecurityException(String.format(
+                throw new SecurityException(TextUtils.formatSimple(
                         "Calling %s with fromUser == true is only allowed for system", method));
             }
         }
@@ -5576,7 +5614,7 @@
 
         private void enforceSystemOrSystemUI(String message) {
             if (isCallerSystemOrPhone()) return;
-            getContext().enforceCallingPermission(android.Manifest.permission.STATUS_BAR_SERVICE,
+            getContext().enforceCallingPermission(STATUS_BAR_SERVICE,
                     message);
         }
 
@@ -5585,7 +5623,7 @@
                 checkCallerIsSystemOrSameApp(pkg);
             } catch (SecurityException e) {
                 getContext().enforceCallingPermission(
-                        android.Manifest.permission.STATUS_BAR_SERVICE,
+                        STATUS_BAR_SERVICE,
                         message);
             }
         }
@@ -6190,13 +6228,20 @@
         @Override
         public void setPrivateNotificationsAllowed(boolean allow) {
             if (PackageManager.PERMISSION_GRANTED
-                    != getContext().checkCallingPermission(
-                            permission.CONTROL_KEYGUARD_SECURE_NOTIFICATIONS)) {
+                    != getContext().checkCallingPermission(CONTROL_KEYGUARD_SECURE_NOTIFICATIONS)) {
                 throw new SecurityException(
                         "Requires CONTROL_KEYGUARD_SECURE_NOTIFICATIONS permission");
             }
             if (allow != mLockScreenAllowSecureNotifications) {
                 mLockScreenAllowSecureNotifications = allow;
+                if (android.app.Flags.keyguardPrivateNotifications()) {
+                    getContext().sendBroadcast(
+                            new Intent(ACTION_KEYGUARD_PRIVATE_NOTIFICATIONS_CHANGED)
+                                    .putExtra(EXTRA_KM_PRIVATE_NOTIFS_ALLOWED,
+                                            mLockScreenAllowSecureNotifications),
+                            STATUS_BAR_SERVICE);
+                }
+
                 handleSavePolicyFile();
             }
         }
@@ -6204,8 +6249,7 @@
         @Override
         public boolean getPrivateNotificationsAllowed() {
             if (PackageManager.PERMISSION_GRANTED
-                    != getContext().checkCallingPermission(
-                            permission.CONTROL_KEYGUARD_SECURE_NOTIFICATIONS)) {
+                    != getContext().checkCallingPermission(CONTROL_KEYGUARD_SECURE_NOTIFICATIONS)) {
                 throw new SecurityException(
                         "Requires CONTROL_KEYGUARD_SECURE_NOTIFICATIONS permission");
             }
@@ -8376,6 +8420,8 @@
             boolean posted = false;
             try {
                 posted = postNotification();
+            }  catch (Exception e) {
+                Slog.e(TAG, "Error posting", e);
             } finally {
                 if (!posted) {
                     mTracker.cancel();
@@ -9059,27 +9105,40 @@
     }
 
     private boolean playSound(final NotificationRecord record, Uri soundUri) {
+        final boolean shouldPlay;
+        if (focusExclusiveWithRecording()) {
+            // flagged path
+            shouldPlay = mAudioManager.shouldNotificationSoundPlay(record.getAudioAttributes());
+        } else {
+            // legacy path
+            // play notifications if there is no user of exclusive audio focus
+            // and the stream volume is not 0 (non-zero volume implies not silenced by SILENT or
+            //   VIBRATE ringer mode)
+            shouldPlay = !mAudioManager.isAudioFocusExclusive()
+                    && (mAudioManager.getStreamVolume(
+                        AudioAttributes.toLegacyStreamType(record.getAudioAttributes())) != 0);
+        }
+        if (!shouldPlay) {
+            if (DBG) Slog.v(TAG, "Not playing sound " + soundUri + " due to focus/volume");
+            return false;
+        }
+
         boolean looping = (record.getNotification().flags & FLAG_INSISTENT) != 0;
-        // play notifications if there is no user of exclusive audio focus
-        // and the stream volume is not 0 (non-zero volume implies not silenced by SILENT or
-        //   VIBRATE ringer mode)
-        if (!mAudioManager.isAudioFocusExclusive()
-                && (mAudioManager.getStreamVolume(
-                        AudioAttributes.toLegacyStreamType(record.getAudioAttributes())) != 0)) {
-            final long identity = Binder.clearCallingIdentity();
-            try {
-                final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
-                if (player != null) {
-                    if (DBG) Slog.v(TAG, "Playing sound " + soundUri
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
+            if (player != null) {
+                if (DBG) {
+                    Slog.v(TAG, "Playing sound " + soundUri
                             + " with attributes " + record.getAudioAttributes());
-                    player.playAsync(soundUri, record.getSbn().getUser(), looping,
-                            record.getAudioAttributes(), 1.0f);
-                    return true;
                 }
-            } catch (RemoteException e) {
-            } finally {
-                Binder.restoreCallingIdentity(identity);
+                player.playAsync(soundUri, record.getSbn().getUser(), looping,
+                        record.getAudioAttributes(), 1.0f);
+                return true;
             }
+        } catch (RemoteException e) {
+        } finally {
+            Binder.restoreCallingIdentity(identity);
         }
         return false;
     }
@@ -9564,12 +9623,16 @@
     }
 
     private void scheduleListenerHintsChanged(int state) {
-        mHandler.removeMessages(MESSAGE_LISTENER_HINTS_CHANGED);
+        if (!Flags.notificationReduceMessagequeueUsage()) {
+            mHandler.removeMessages(MESSAGE_LISTENER_HINTS_CHANGED);
+        }
         mHandler.obtainMessage(MESSAGE_LISTENER_HINTS_CHANGED, state, 0).sendToTarget();
     }
 
     private void scheduleInterruptionFilterChanged(int listenerInterruptionFilter) {
-        mHandler.removeMessages(MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED);
+        if (!Flags.notificationReduceMessagequeueUsage()) {
+            mHandler.removeMessages(MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED);
+        }
         mHandler.obtainMessage(
                 MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED,
                 listenerInterruptionFilter,
@@ -9649,15 +9712,24 @@
         }
 
         protected void scheduleSendRankingUpdate() {
-            if (!hasMessages(MESSAGE_SEND_RANKING_UPDATE)) {
+            if (Flags.notificationReduceMessagequeueUsage()) {
                 Message m = Message.obtain(this, MESSAGE_SEND_RANKING_UPDATE);
                 sendMessage(m);
+            } else {
+                if (!hasMessages(MESSAGE_SEND_RANKING_UPDATE)) {
+                    Message m = Message.obtain(this, MESSAGE_SEND_RANKING_UPDATE);
+                    sendMessage(m);
+                }
             }
         }
 
         protected void scheduleCancelNotification(CancelNotificationRunnable cancelRunnable) {
-            if (!hasCallbacks(cancelRunnable)) {
+            if (Flags.notificationReduceMessagequeueUsage()) {
                 sendMessage(Message.obtain(this, cancelRunnable));
+            } else {
+                if (!hasCallbacks(cancelRunnable)) {
+                    sendMessage(Message.obtain(this, cancelRunnable));
+                }
             }
         }
 
@@ -9691,7 +9763,9 @@
         }
 
         public void requestSort() {
-            removeMessages(MESSAGE_RANKING_SORT);
+            if (!Flags.notificationReduceMessagequeueUsage()) {
+                removeMessages(MESSAGE_RANKING_SORT);
+            }
             Message msg = Message.obtain();
             msg.what = MESSAGE_RANKING_SORT;
             sendMessage(msg);
@@ -10596,7 +10670,7 @@
         if (isCallerSystemOrPhone()) {
             return true;
         }
-        return getContext().checkCallingPermission(android.Manifest.permission.STATUS_BAR_SERVICE)
+        return getContext().checkCallingPermission(STATUS_BAR_SERVICE)
                 == PERMISSION_GRANTED;
     }
 
@@ -10635,7 +10709,7 @@
         if (isCallerSystemOrPhone()) {
             return;
         }
-        getContext().enforceCallingPermission(android.Manifest.permission.STATUS_BAR_SERVICE,
+        getContext().enforceCallingPermission(STATUS_BAR_SERVICE,
                 message);
     }
 
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 64d3a20..1786ac5 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -25,6 +25,7 @@
 import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_POSITIVE;
 
 import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.Flags;
 import android.app.KeyguardManager;
@@ -167,7 +168,7 @@
     private boolean mPreChannelsNotification = true;
     private Uri mSound;
     private VibrationEffect mVibration;
-    private AudioAttributes mAttributes;
+    private @NonNull AudioAttributes mAttributes;
     private NotificationChannel mChannel;
     private ArrayList<String> mPeopleOverride;
     private ArrayList<SnoozeCriterion> mSnoozeCriteria;
@@ -334,7 +335,7 @@
         return vibration;
     }
 
-    private AudioAttributes calculateAttributes() {
+    private @NonNull AudioAttributes calculateAttributes() {
         final Notification n = getSbn().getNotification();
         AudioAttributes attributes = getChannel().getAudioAttributes();
         if (attributes == null) {
@@ -1003,7 +1004,7 @@
     }
 
     public boolean isAudioAttributesUsage(int usage) {
-        return mAttributes != null && mAttributes.getUsage() == usage;
+        return mAttributes.getUsage() == usage;
     }
 
     /**
@@ -1172,7 +1173,7 @@
         return mVibration;
     }
 
-    public AudioAttributes getAudioAttributes() {
+    public @NonNull AudioAttributes getAudioAttributes() {
         return mAttributes;
     }
 
diff --git a/services/core/java/com/android/server/notification/ZenAdapters.java b/services/core/java/com/android/server/notification/ZenAdapters.java
index 2a65aff..91df04c 100644
--- a/services/core/java/com/android/server/notification/ZenAdapters.java
+++ b/services/core/java/com/android/server/notification/ZenAdapters.java
@@ -16,6 +16,7 @@
 
 package com.android.server.notification;
 
+import android.app.Flags;
 import android.app.NotificationManager.Policy;
 import android.service.notification.ZenModeConfig;
 import android.service.notification.ZenPolicy;
@@ -57,6 +58,12 @@
                     .showStatusBarIcons(policy.showStatusBarIcons());
         }
 
+        if (Flags.modesApi()) {
+            zenPolicyBuilder.allowChannels(
+                    policy.allowPriorityChannels()
+                            ? ZenPolicy.CHANNEL_TYPE_PRIORITY : ZenPolicy.CHANNEL_TYPE_NONE);
+        }
+
         return zenPolicyBuilder.build();
     }
 
diff --git a/services/core/java/com/android/server/notification/ZenModeEventLogger.java b/services/core/java/com/android/server/notification/ZenModeEventLogger.java
index df570a0..0145577 100644
--- a/services/core/java/com/android/server/notification/ZenModeEventLogger.java
+++ b/services/core/java/com/android/server/notification/ZenModeEventLogger.java
@@ -23,6 +23,7 @@
 import static android.service.notification.NotificationServiceProto.RULE_TYPE_UNKNOWN;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.Flags;
 import android.app.NotificationManager;
 import android.content.pm.PackageManager;
@@ -256,13 +257,21 @@
                 return true;
             }
 
+            if (Flags.modesApi() && hasActiveRuleCountDiff()) {
+                // Rules with INTERRUPTION_FILTER_ALL were always possible but before MODES_API
+                // they were completely useless; now they can apply effects, so we want to log
+                // when they become active/inactive, even though DND itself (as in "notification
+                // blocking") is off.
+                return true;
+            }
+
             // If zen mode didn't change, did the policy or number of active rules change? We only
             // care about changes that take effect while zen mode is on, so make sure the current
             // zen mode is not "OFF"
             if (mNewZenMode == ZEN_MODE_OFF) {
                 return false;
             }
-            return hasPolicyDiff() || hasRuleCountDiff();
+            return hasPolicyDiff() || hasActiveRuleCountDiff();
         }
 
         // Does the difference in zen mode go from off to on or vice versa?
@@ -294,6 +303,16 @@
                 }
             }
 
+            if (Flags.modesApi() && mNewZenMode == ZEN_MODE_OFF) {
+                // If the mode is OFF -> OFF then there cannot be any *effective* change to policy.
+                // (Note that, in theory, a policy diff is impossible since we don't merge the
+                // policies of INTERRUPTION_FILTER_ALL rules; this is a "just in case" check).
+                if (hasPolicyDiff() || hasChannelsBypassingDiff()) {
+                    Log.wtf(TAG, "Detected policy diff even though DND is OFF and not toggled");
+                }
+                return ZenStateChangedEvent.DND_ACTIVE_RULES_CHANGED;
+            }
+
             // zen mode didn't change; we must be here because of a policy change or rule change
             if (hasPolicyDiff() || hasChannelsBypassingDiff()) {
                 return ZenStateChangedEvent.DND_POLICY_CHANGED;
@@ -345,7 +364,7 @@
          * Returns whether the previous config and new config have a different number of active
          * automatic or manual rules.
          */
-        private boolean hasRuleCountDiff() {
+        private boolean hasActiveRuleCountDiff() {
             return numActiveRulesInConfig(mPrevConfig) != numActiveRulesInConfig(mNewConfig);
         }
 
@@ -381,9 +400,11 @@
 
         // Determine the number of (automatic & manual) rules active after the change takes place.
         int getNumRulesActive() {
-            // If the zen mode has turned off, that means nothing can be active.
-            if (mNewZenMode == ZEN_MODE_OFF) {
-                return 0;
+            if (!Flags.modesApi()) {
+                // If the zen mode has turned off, that means nothing can be active.
+                if (mNewZenMode == ZEN_MODE_OFF) {
+                    return 0;
+                }
             }
             return numActiveRulesInConfig(mNewConfig);
         }
@@ -478,8 +499,19 @@
 
         /**
          * Convert the new policy to a DNDPolicyProto format for output in logs.
+         *
+         * <p>If {@code mNewZenMode} is {@code ZEN_MODE_OFF} (which can mean either no rules
+         * active, or only rules with {@code INTERRUPTION_FILTER_ALL} active) then this returns
+         * {@code null} (which will be mapped to a missing submessage in the proto). Although this
+         * is not the value of {@code NotificationManager#getConsolidatedNotificationPolicy()}, it
+         * makes sense for logging since that policy is not actually influencing anything.
          */
+        @Nullable
         byte[] getDNDPolicyProto() {
+            if (Flags.modesApi() && mNewZenMode == ZEN_MODE_OFF) {
+                return null;
+            }
+
             ByteArrayOutputStream bytes = new ByteArrayOutputStream();
             ProtoOutputStream proto = new ProtoOutputStream(bytes);
 
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 0a46901..afbf08d 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -117,6 +117,9 @@
 
 import java.io.IOException;
 import java.io.PrintWriter;
+import java.time.Clock;
+import java.time.Duration;
+import java.time.Instant;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
@@ -130,9 +133,14 @@
     static final String TAG = "ZenModeHelper";
     static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
+    private static final String PACKAGE_ANDROID = "android";
+
     // The amount of time rules instances can exist without their owning app being installed.
     private static final int RULE_INSTANCE_GRACE_PERIOD = 1000 * 60 * 60 * 72;
     static final int RULE_LIMIT_PER_PACKAGE = 100;
+    private static final Duration DELETED_RULE_KEPT_FOR = Duration.ofDays(30);
+
+    private static final String IMPLICIT_RULE_ID_PREFIX = "implicit_"; // + pkg_name
 
     /**
      * Send new activation AutomaticZenRule statuses to apps with a min target SDK version
@@ -146,6 +154,7 @@
 
     private final Context mContext;
     private final H mHandler;
+    private final Clock mClock;
     private final SettingsObserver mSettingsObserver;
     private final AppOpsManager mAppOps;
     private final NotificationManager mNotificationManager;
@@ -187,11 +196,13 @@
 
     private String[] mPriorityOnlyDndExemptPackages;
 
-    public ZenModeHelper(Context context, Looper looper, ConditionProviders conditionProviders,
+    public ZenModeHelper(Context context, Looper looper, Clock clock,
+            ConditionProviders conditionProviders,
             SystemUiSystemPropertiesFlags.FlagResolver flagResolver,
             ZenModeEventLogger zenModeEventLogger) {
         mContext = context;
         mHandler = new H(looper);
+        mClock = clock;
         addCallback(mMetrics);
         mAppOps = context.getSystemService(AppOpsManager.class);
         mNotificationManager = context.getSystemService(NotificationManager.class);
@@ -450,7 +461,10 @@
             newConfig = mConfig.copy();
             ZenRule rule = new ZenRule();
             populateZenRule(pkg, automaticZenRule, rule, origin, /* isNew= */ true);
+            rule = maybeRestoreRemovedRule(newConfig, rule, automaticZenRule, origin);
             newConfig.automaticRules.put(rule.id, rule);
+            maybeReplaceDefaultRule(newConfig, automaticZenRule);
+
             if (setConfigLocked(newConfig, origin, reason, rule.component, true, callingUid)) {
                 return rule.id;
             } else {
@@ -459,6 +473,56 @@
         }
     }
 
+    private ZenRule maybeRestoreRemovedRule(ZenModeConfig config, ZenRule ruleToAdd,
+            AutomaticZenRule azrToAdd, @ConfigChangeOrigin int origin) {
+        if (!Flags.modesApi()) {
+            return ruleToAdd;
+        }
+        String deletedKey = ZenModeConfig.deletedRuleKey(ruleToAdd);
+        if (deletedKey == null) {
+            // Couldn't calculate the deletedRuleKey (condition or pkg null?). This should
+            // never happen for an app-provided rule because NMS validates both.
+            return ruleToAdd;
+        }
+        ZenRule ruleToRestore = config.deletedRules.get(deletedKey);
+        if (ruleToRestore == null) {
+            return ruleToAdd; // Cannot restore.
+        }
+
+        // We have found a previous rule to maybe restore. Whether we do that or not, we don't need
+        // to keep it around (if not restored now, it won't be in future calls either).
+        config.deletedRules.remove(deletedKey);
+        ruleToRestore.deletionInstant = null;
+
+        if (origin != UPDATE_ORIGIN_APP) {
+            return ruleToAdd; // Okay to create anew.
+        }
+
+        // "Preserve" the previous rule by considering the azrToAdd an update instead.
+        // Only app-modifiable fields will actually be modified.
+        populateZenRule(ruleToRestore.pkg, azrToAdd, ruleToRestore, origin, /* isNew= */ false);
+        return ruleToRestore;
+    }
+
+    private static void maybeReplaceDefaultRule(ZenModeConfig config, AutomaticZenRule addedRule) {
+        if (!Flags.modesApi()) {
+            return;
+        }
+        if (addedRule.getType() == AutomaticZenRule.TYPE_BEDTIME) {
+            // Delete a built-in disabled "Sleeping" rule when a BEDTIME rule is added; it may have
+            // smarter triggers and it will prevent confusion about which one to use.
+            // Note: we must not verify canManageAutomaticZenRule here, since most likely they
+            // won't have the same owner (sleeping - system; bedtime - DWB).
+            ZenRule sleepingRule = config.automaticRules.get(
+                    ZenModeConfig.EVERY_NIGHT_DEFAULT_RULE_ID);
+            if (sleepingRule != null
+                    && !sleepingRule.enabled
+                    && sleepingRule.canBeUpdatedByApp() /* meaning it's not user-customized */) {
+                config.automaticRules.remove(ZenModeConfig.EVERY_NIGHT_DEFAULT_RULE_ID);
+            }
+        }
+    }
+
     public boolean updateAutomaticZenRule(String ruleId, AutomaticZenRule automaticZenRule,
             @ConfigChangeOrigin int origin, String reason, int callingUid) {
         ZenModeConfig newConfig;
@@ -621,7 +685,7 @@
         ZenRule rule = new ZenRule();
         rule.id = implicitRuleId(pkg);
         rule.pkg = pkg;
-        rule.creationTime = System.currentTimeMillis();
+        rule.creationTime = mClock.millis();
 
         Binder.withCleanCallingIdentity(() -> {
             try {
@@ -641,7 +705,7 @@
         rule.condition = null;
         rule.conditionId = new Uri.Builder()
                 .scheme(Condition.SCHEME)
-                .authority("android")
+                .authority(PACKAGE_ANDROID)
                 .appendPath("implicit")
                 .appendPath(pkg)
                 .build();
@@ -653,7 +717,11 @@
     }
 
     private static String implicitRuleId(String forPackage) {
-        return "implicit_" + forPackage;
+        return IMPLICIT_RULE_ID_PREFIX + forPackage;
+    }
+
+    static boolean isImplicitRuleId(@NonNull String ruleId) {
+        return ruleId.startsWith(IMPLICIT_RULE_ID_PREFIX);
     }
 
     boolean removeAutomaticZenRule(String id, @ConfigChangeOrigin int origin, String reason,
@@ -666,7 +734,9 @@
             if (ruleToRemove == null) return false;
             if (canManageAutomaticZenRule(ruleToRemove)) {
                 newConfig.automaticRules.remove(id);
-                if (ruleToRemove.getPkg() != null && !"android".equals(ruleToRemove.getPkg())) {
+                maybePreserveRemovedRule(newConfig, ruleToRemove, origin);
+                if (ruleToRemove.getPkg() != null
+                        && !PACKAGE_ANDROID.equals(ruleToRemove.getPkg())) {
                     for (ZenRule currRule : newConfig.automaticRules.values()) {
                         if (currRule.getPkg() != null
                                 && currRule.getPkg().equals(ruleToRemove.getPkg())) {
@@ -696,12 +766,44 @@
                 ZenRule rule = newConfig.automaticRules.get(newConfig.automaticRules.keyAt(i));
                 if (Objects.equals(rule.getPkg(), packageName) && canManageAutomaticZenRule(rule)) {
                     newConfig.automaticRules.removeAt(i);
+                    maybePreserveRemovedRule(newConfig, rule, origin);
+                }
+            }
+            // If the system is clearing all rules this means DND access is revoked or the package
+            // was uninstalled, so also clear the preserved-deleted rules.
+            if (origin == UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI) {
+                for (int i = newConfig.deletedRules.size() - 1; i >= 0; i--) {
+                    ZenRule rule = newConfig.deletedRules.get(newConfig.deletedRules.keyAt(i));
+                    if (Objects.equals(rule.getPkg(), packageName)) {
+                        newConfig.deletedRules.removeAt(i);
+                    }
                 }
             }
             return setConfigLocked(newConfig, origin, reason, null, true, callingUid);
         }
     }
 
+    private void maybePreserveRemovedRule(ZenModeConfig config, ZenRule ruleToRemove,
+            @ConfigChangeOrigin int origin) {
+        if (!Flags.modesApi()) {
+            return;
+        }
+        // If an app deletes a previously customized rule, keep it around to preserve
+        // the user's customization when/if it's recreated later.
+        // We don't try to preserve system-owned rules because their conditionIds (used as
+        // deletedRuleKey) are not stable. This is almost moot anyway because an app cannot
+        // delete a system-owned rule.
+        if (origin == UPDATE_ORIGIN_APP && !ruleToRemove.canBeUpdatedByApp()
+                && !PACKAGE_ANDROID.equals(ruleToRemove.pkg)) {
+            String deletedKey = ZenModeConfig.deletedRuleKey(ruleToRemove);
+            if (deletedKey != null) {
+                ruleToRemove.deletionInstant = Instant.now(mClock);
+                // Overwrites a previously-deleted rule with the same conditionId, but that's okay.
+                config.deletedRules.put(deletedKey, ruleToRemove);
+            }
+        }
+    }
+
     void setAutomaticZenRuleState(String id, Condition condition, @ConfigChangeOrigin int origin,
             int callingUid) {
         ZenModeConfig newConfig;
@@ -887,48 +989,206 @@
     }
 
     void populateZenRule(String pkg, AutomaticZenRule automaticZenRule, ZenRule rule,
-            @ConfigChangeOrigin int origin, boolean isNew) {
-        // TODO: b/308671593,b/311406021 - Handle origins more precisely:
-        //  - USER can override anything and updates bitmask of user-modified fields;
-        //  - SYSTEM_OR_SYSTEMUI can override anything and preserves bitmask;
-        //  - APP can only update if not user-modified.
-        if (rule.enabled != automaticZenRule.isEnabled()) {
-            rule.snoozing = false;
-        }
-        rule.name = automaticZenRule.getName();
-        rule.condition = null;
-        rule.conditionId = automaticZenRule.getConditionId();
-        rule.enabled = automaticZenRule.isEnabled();
-        rule.modified = automaticZenRule.isModified();
-        rule.zenPolicy = automaticZenRule.getZenPolicy();
+                         @ConfigChangeOrigin int origin, boolean isNew) {
         if (Flags.modesApi()) {
-            rule.zenDeviceEffects = fixZenDeviceEffects(
-                    rule.zenDeviceEffects,
-                    automaticZenRule.getDeviceEffects(),
-                    origin);
-        }
-        rule.zenMode = NotificationManager.zenModeFromInterruptionFilter(
-                automaticZenRule.getInterruptionFilter(), Global.ZEN_MODE_OFF);
-        rule.configurationActivity = automaticZenRule.getConfigurationActivity();
+            // These values can always be edited by the app, so we apply changes immediately.
+            if (isNew) {
+                rule.id = ZenModeConfig.newRuleId();
+                rule.creationTime = mClock.millis();
+                rule.component = automaticZenRule.getOwner();
+                rule.pkg = pkg;
+            }
 
-        if (isNew) {
-            rule.id = ZenModeConfig.newRuleId();
-            rule.creationTime = System.currentTimeMillis();
-            rule.component = automaticZenRule.getOwner();
-            rule.pkg = pkg;
-        }
-
-        if (Flags.modesApi()) {
+            rule.condition = null;
+            rule.conditionId = automaticZenRule.getConditionId();
+            if (rule.enabled != automaticZenRule.isEnabled()) {
+                rule.snoozing = false;
+            }
+            rule.enabled = automaticZenRule.isEnabled();
+            rule.configurationActivity = automaticZenRule.getConfigurationActivity();
             rule.allowManualInvocation = automaticZenRule.isManualInvocationAllowed();
-            rule.iconResName = drawableResIdToResName(rule.pkg, automaticZenRule.getIconResId());
+            rule.iconResName =
+                    drawableResIdToResName(rule.pkg, automaticZenRule.getIconResId());
             rule.triggerDescription = automaticZenRule.getTriggerDescription();
             rule.type = automaticZenRule.getType();
+            // TODO: b/310620812 - Remove this once FLAG_MODES_API is inlined.
+            rule.modified = automaticZenRule.isModified();
+
+            // Name is treated differently than other values:
+            // App is allowed to update name if the name was not modified by the user (even if
+            // other values have been modified). In this way, if the locale of an app changes,
+            // i18n of the rule name can still occur even if the user has customized the rule
+            // contents.
+            String previousName = rule.name;
+            if (isNew || doesOriginAlwaysUpdateValues(origin)
+                    || (rule.userModifiedFields & AutomaticZenRule.FIELD_NAME) == 0) {
+                rule.name = automaticZenRule.getName();
+            }
+
+            // For the remaining values, rules can always have all values updated if:
+            // * the rule is newly added, or
+            // * the request comes from an origin that can always update values, like the user, or
+            // * the rule has not yet been user modified, and thus can be updated by the app.
+            boolean updateValues = isNew || doesOriginAlwaysUpdateValues(origin)
+                    || rule.canBeUpdatedByApp();
+
+            // For all other values, if updates are not allowed, we discard the update.
+            if (!updateValues) {
+                return;
+            }
+
+            // Updates the bitmasks if the origin of the change is the user.
+            boolean updateBitmask = (origin == UPDATE_ORIGIN_USER);
+
+            if (updateBitmask && !TextUtils.equals(previousName, automaticZenRule.getName())) {
+                rule.userModifiedFields |= AutomaticZenRule.FIELD_NAME;
+            }
+            int newZenMode = NotificationManager.zenModeFromInterruptionFilter(
+                    automaticZenRule.getInterruptionFilter(), Global.ZEN_MODE_OFF);
+            if (updateBitmask && rule.zenMode != newZenMode) {
+                rule.userModifiedFields |= AutomaticZenRule.FIELD_INTERRUPTION_FILTER;
+            }
+
+            // Updates the values in the ZenRule itself.
+            rule.zenMode = newZenMode;
+
+            // Updates the bitmask and values for all policy fields, based on the origin.
+            rule.zenPolicy = updatePolicy(rule.zenPolicy, automaticZenRule.getZenPolicy(),
+                    updateBitmask);
+            // Updates the bitmask and values for all device effect fields, based on the origin.
+            rule.zenDeviceEffects = updateZenDeviceEffects(
+                    rule.zenDeviceEffects, automaticZenRule.getDeviceEffects(),
+                    origin == UPDATE_ORIGIN_APP, updateBitmask);
+        } else {
+            if (rule.enabled != automaticZenRule.isEnabled()) {
+                rule.snoozing = false;
+            }
+            rule.name = automaticZenRule.getName();
+            rule.condition = null;
+            rule.conditionId = automaticZenRule.getConditionId();
+            rule.enabled = automaticZenRule.isEnabled();
+            rule.modified = automaticZenRule.isModified();
+            rule.zenPolicy = automaticZenRule.getZenPolicy();
+            if (Flags.modesApi()) {
+                rule.zenDeviceEffects = updateZenDeviceEffects(
+                        rule.zenDeviceEffects,
+                        automaticZenRule.getDeviceEffects(),
+                        origin == UPDATE_ORIGIN_APP,
+                        origin == UPDATE_ORIGIN_USER);
+            }
+            rule.zenMode = NotificationManager.zenModeFromInterruptionFilter(
+                    automaticZenRule.getInterruptionFilter(), Global.ZEN_MODE_OFF);
+            rule.configurationActivity = automaticZenRule.getConfigurationActivity();
+
+            if (isNew) {
+                rule.id = ZenModeConfig.newRuleId();
+                rule.creationTime = System.currentTimeMillis();
+                rule.component = automaticZenRule.getOwner();
+                rule.pkg = pkg;
+            }
         }
     }
 
     /**
-     * Fix {@link ZenDeviceEffects} that are being stored as part of a new or updated ZenRule.
-     *
+     * Returns true when fields can always be updated, based on the provided origin of an AZR
+     * change. (Note that regardless of origin, fields can always be updated if they're not already
+     * user modified.)
+     */
+    private static boolean doesOriginAlwaysUpdateValues(@ConfigChangeOrigin int origin) {
+        return origin == UPDATE_ORIGIN_USER || origin == UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI;
+    }
+
+    /**
+     * Modifies {@link ZenPolicy} that is being stored as part of a new or updated ZenRule.
+     * Returns a policy based on {@code oldPolicy}, but with fields updated to match
+     * {@code newPolicy} where they differ, and updating the internal user-modified bitmask to
+     * track these changes, if applicable based on {@code origin}.
+     */
+    @Nullable
+    private ZenPolicy updatePolicy(@Nullable ZenPolicy oldPolicy, @Nullable ZenPolicy newPolicy,
+                                   boolean updateBitmask) {
+        // If the update is to make the policy null, we don't need to update the bitmask,
+        // because it won't be stored anywhere anyway.
+        if (newPolicy == null) {
+            return null;
+        }
+
+        // If oldPolicy is null, we compare against the default policy when determining which
+        // fields in the bitmask should be marked as updated.
+        if (oldPolicy == null) {
+            oldPolicy = mDefaultConfig.toZenPolicy();
+        }
+
+        int userModifiedFields = oldPolicy.getUserModifiedFields();
+        if (updateBitmask) {
+            if (oldPolicy.getPriorityMessageSenders() != newPolicy.getPriorityMessageSenders()) {
+                userModifiedFields |= ZenPolicy.FIELD_MESSAGES;
+            }
+            if (oldPolicy.getPriorityCallSenders() != newPolicy.getPriorityCallSenders()) {
+                userModifiedFields |= ZenPolicy.FIELD_CALLS;
+            }
+            if (oldPolicy.getPriorityConversationSenders()
+                    != newPolicy.getPriorityConversationSenders()) {
+                userModifiedFields |= ZenPolicy.FIELD_CONVERSATIONS;
+            }
+            if (oldPolicy.getAllowedChannels() != newPolicy.getAllowedChannels()) {
+                userModifiedFields |= ZenPolicy.FIELD_ALLOW_CHANNELS;
+            }
+            if (oldPolicy.getPriorityCategoryReminders()
+                    != newPolicy.getPriorityCategoryReminders()) {
+                userModifiedFields |= ZenPolicy.FIELD_PRIORITY_CATEGORY_REMINDERS;
+            }
+            if (oldPolicy.getPriorityCategoryEvents() != newPolicy.getPriorityCategoryEvents()) {
+                userModifiedFields |= ZenPolicy.FIELD_PRIORITY_CATEGORY_EVENTS;
+            }
+            if (oldPolicy.getPriorityCategoryRepeatCallers()
+                    != newPolicy.getPriorityCategoryRepeatCallers()) {
+                userModifiedFields |= ZenPolicy.FIELD_PRIORITY_CATEGORY_REPEAT_CALLERS;
+            }
+            if (oldPolicy.getPriorityCategoryAlarms() != newPolicy.getPriorityCategoryAlarms()) {
+                userModifiedFields |= ZenPolicy.FIELD_PRIORITY_CATEGORY_ALARMS;
+            }
+            if (oldPolicy.getPriorityCategoryMedia() != newPolicy.getPriorityCategoryMedia()) {
+                userModifiedFields |= ZenPolicy.FIELD_PRIORITY_CATEGORY_MEDIA;
+            }
+            if (oldPolicy.getPriorityCategorySystem() != newPolicy.getPriorityCategorySystem()) {
+                userModifiedFields |= ZenPolicy.FIELD_PRIORITY_CATEGORY_SYSTEM;
+            }
+            // Visual effects
+            if (oldPolicy.getVisualEffectFullScreenIntent()
+                    != newPolicy.getVisualEffectFullScreenIntent()) {
+                userModifiedFields |= ZenPolicy.FIELD_VISUAL_EFFECT_FULL_SCREEN_INTENT;
+            }
+            if (oldPolicy.getVisualEffectLights() != newPolicy.getVisualEffectLights()) {
+                userModifiedFields |= ZenPolicy.FIELD_VISUAL_EFFECT_LIGHTS;
+            }
+            if (oldPolicy.getVisualEffectPeek() != newPolicy.getVisualEffectPeek()) {
+                userModifiedFields |= ZenPolicy.FIELD_VISUAL_EFFECT_PEEK;
+            }
+            if (oldPolicy.getVisualEffectStatusBar() != newPolicy.getVisualEffectStatusBar()) {
+                userModifiedFields |= ZenPolicy.FIELD_VISUAL_EFFECT_STATUS_BAR;
+            }
+            if (oldPolicy.getVisualEffectBadge() != newPolicy.getVisualEffectBadge()) {
+                userModifiedFields |= ZenPolicy.FIELD_VISUAL_EFFECT_BADGE;
+            }
+            if (oldPolicy.getVisualEffectAmbient() != newPolicy.getVisualEffectAmbient()) {
+                userModifiedFields |= ZenPolicy.FIELD_VISUAL_EFFECT_AMBIENT;
+            }
+            if (oldPolicy.getVisualEffectNotificationList()
+                    != newPolicy.getVisualEffectNotificationList()) {
+                userModifiedFields |= ZenPolicy.FIELD_VISUAL_EFFECT_NOTIFICATION_LIST;
+            }
+        }
+
+        // After all bitmask changes have been made, sets the bitmask.
+        return new ZenPolicy.Builder(newPolicy).setUserModifiedFields(userModifiedFields).build();
+    }
+
+    /**
+     * Modifies {@link ZenDeviceEffects} that are being stored as part of a new or updated ZenRule.
+     * Returns a {@link ZenDeviceEffects} based on {@code oldEffects}, but with fields updated to
+     * match {@code newEffects} where they differ, and updating the internal user-modified bitmask
+     * to track these changes, if applicable based on {@code origin}.
      * <ul>
      *     <li> Apps cannot turn on hidden effects (those tagged as {@code @hide}) since they are
      *     intended for platform-specific rules (e.g. wearables). If it's a new rule, we blank them
@@ -936,38 +1196,85 @@
      * </ul>
      */
     @Nullable
-    private static ZenDeviceEffects fixZenDeviceEffects(@Nullable ZenDeviceEffects oldEffects,
-            @Nullable ZenDeviceEffects newEffects, @ConfigChangeOrigin int origin) {
-        // TODO: b/308671593,b/311406021 - Handle origins more precisely:
-        //  - USER can override anything and updates bitmask of user-modified fields;
-        //  - SYSTEM_OR_SYSTEMUI can override anything and preserves bitmask;
-        //  - APP can only update if not user-modified.
-        if (origin != UPDATE_ORIGIN_APP) {
-            return newEffects;
-        }
-
+    private static ZenDeviceEffects updateZenDeviceEffects(@Nullable ZenDeviceEffects oldEffects,
+                                                           @Nullable ZenDeviceEffects newEffects,
+                                                           boolean isFromApp,
+                                                           boolean updateBitmask) {
         if (newEffects == null) {
             return null;
         }
-        if (oldEffects != null) {
-            return new ZenDeviceEffects.Builder(newEffects)
-                    .setShouldDisableAutoBrightness(oldEffects.shouldDisableAutoBrightness())
-                    .setShouldDisableTapToWake(oldEffects.shouldDisableTapToWake())
-                    .setShouldDisableTiltToWake(oldEffects.shouldDisableTiltToWake())
-                    .setShouldDisableTouch(oldEffects.shouldDisableTouch())
-                    .setShouldMinimizeRadioUsage(oldEffects.shouldMinimizeRadioUsage())
-                    .setShouldMaximizeDoze(oldEffects.shouldMaximizeDoze())
-                    .build();
-        } else {
-            return new ZenDeviceEffects.Builder(newEffects)
-                    .setShouldDisableAutoBrightness(false)
-                    .setShouldDisableTapToWake(false)
-                    .setShouldDisableTiltToWake(false)
-                    .setShouldDisableTouch(false)
-                    .setShouldMinimizeRadioUsage(false)
-                    .setShouldMaximizeDoze(false)
-                    .build();
+
+        // Since newEffects is not null, we want to adopt all the new provided device effects.
+        ZenDeviceEffects.Builder builder = new ZenDeviceEffects.Builder(newEffects);
+
+        if (isFromApp) {
+            if (oldEffects != null) {
+                // We can do this because we know we don't need to update the bitmask FROM_APP.
+                return builder
+                        .setShouldDisableAutoBrightness(oldEffects.shouldDisableAutoBrightness())
+                        .setShouldDisableTapToWake(oldEffects.shouldDisableTapToWake())
+                        .setShouldDisableTiltToWake(oldEffects.shouldDisableTiltToWake())
+                        .setShouldDisableTouch(oldEffects.shouldDisableTouch())
+                        .setShouldMinimizeRadioUsage(oldEffects.shouldMinimizeRadioUsage())
+                        .setShouldMaximizeDoze(oldEffects.shouldMaximizeDoze())
+                        .build();
+            } else {
+                return builder
+                        .setShouldDisableAutoBrightness(false)
+                        .setShouldDisableTapToWake(false)
+                        .setShouldDisableTiltToWake(false)
+                        .setShouldDisableTouch(false)
+                        .setShouldMinimizeRadioUsage(false)
+                        .setShouldMaximizeDoze(false)
+                        .build();
+            }
         }
+
+        // If oldEffects is null, we compare against the default device effects object when
+        // determining which fields in the bitmask should be marked as updated.
+        if (oldEffects == null) {
+            oldEffects = new ZenDeviceEffects.Builder().build();
+        }
+
+        int userModifiedFields = oldEffects.getUserModifiedFields();
+        if (updateBitmask) {
+            if (oldEffects.shouldDisplayGrayscale() != newEffects.shouldDisplayGrayscale()) {
+                userModifiedFields |= ZenDeviceEffects.FIELD_GRAYSCALE;
+            }
+            if (oldEffects.shouldSuppressAmbientDisplay()
+                    != newEffects.shouldSuppressAmbientDisplay()) {
+                userModifiedFields |= ZenDeviceEffects.FIELD_SUPPRESS_AMBIENT_DISPLAY;
+            }
+            if (oldEffects.shouldDimWallpaper() != newEffects.shouldDimWallpaper()) {
+                userModifiedFields |= ZenDeviceEffects.FIELD_DIM_WALLPAPER;
+            }
+            if (oldEffects.shouldUseNightMode() != newEffects.shouldUseNightMode()) {
+                userModifiedFields |= ZenDeviceEffects.FIELD_NIGHT_MODE;
+            }
+            if (oldEffects.shouldDisableAutoBrightness()
+                    != newEffects.shouldDisableAutoBrightness()) {
+                userModifiedFields |= ZenDeviceEffects.FIELD_DISABLE_AUTO_BRIGHTNESS;
+            }
+            if (oldEffects.shouldDisableTapToWake() != newEffects.shouldDisableTapToWake()) {
+                userModifiedFields |= ZenDeviceEffects.FIELD_DISABLE_TAP_TO_WAKE;
+            }
+            if (oldEffects.shouldDisableTiltToWake() != newEffects.shouldDisableTiltToWake()) {
+                userModifiedFields |= ZenDeviceEffects.FIELD_DISABLE_TILT_TO_WAKE;
+            }
+            if (oldEffects.shouldDisableTouch() != newEffects.shouldDisableTouch()) {
+                userModifiedFields |= ZenDeviceEffects.FIELD_DISABLE_TOUCH;
+            }
+            if (oldEffects.shouldMinimizeRadioUsage() != newEffects.shouldMinimizeRadioUsage()) {
+                userModifiedFields |= ZenDeviceEffects.FIELD_MINIMIZE_RADIO_USAGE;
+            }
+            if (oldEffects.shouldMaximizeDoze() != newEffects.shouldMaximizeDoze()) {
+                userModifiedFields |= ZenDeviceEffects.FIELD_MAXIMIZE_DOZE;
+            }
+        }
+
+        // Since newEffects is not null, we want to adopt all the new provided device effects.
+        // Set the usermodifiedFields value separately, to reflect the updated bitmask.
+        return builder.setUserModifiedFields(userModifiedFields).build();
     }
 
     private AutomaticZenRule zenRuleToAutomaticZenRule(ZenRule rule) {
@@ -986,6 +1293,7 @@
                     .setOwner(rule.component)
                     .setConfigurationActivity(rule.configurationActivity)
                     .setTriggerDescription(rule.triggerDescription)
+                    .setUserModifiedFields(rule.userModifiedFields)
                     .build();
         } else {
             azr = new AutomaticZenRule(rule.name, rule.component,
@@ -1146,7 +1454,7 @@
             boolean hasDefaultRules = config.automaticRules.containsAll(
                     ZenModeConfig.DEFAULT_RULE_IDS);
 
-            long time = System.currentTimeMillis();
+            long time = Flags.modesApi() ? mClock.millis() : System.currentTimeMillis();
             if (config.automaticRules != null && config.automaticRules.size() > 0) {
                 for (ZenRule automaticRule : config.automaticRules.values()) {
                     if (forRestore) {
@@ -1165,6 +1473,10 @@
                 // reset zen automatic rules to default on restore or upgrade if:
                 // - doesn't already have default rules and
                 // - all previous automatic rules were disabled
+                //
+                // Note: we don't need to check to avoid restoring the Sleeping rule if there is a
+                // TYPE_BEDTIME rule because the config is from an old version and thus by
+                // definition cannot have a rule with TYPE_BEDTIME (or any other type).
                 config.automaticRules = new ArrayMap<>();
                 for (ZenRule rule : mDefaultConfig.automaticRules.values()) {
                     config.automaticRules.put(rule.id, rule);
@@ -1182,6 +1494,12 @@
                 Settings.Secure.putIntForUser(mContext.getContentResolver(),
                         Settings.Secure.ZEN_SETTINGS_UPDATED, 1, userId);
             }
+
+            if (Flags.modesApi() && forRestore) {
+                // Note: forBackup doesn't write deletedRules, but just in case.
+                config.deletedRules.clear();
+            }
+
             if (DEBUG) Log.d(TAG, reason);
             synchronized (mConfigLock) {
                 setConfigLocked(config, null,
@@ -1199,7 +1517,7 @@
                 if (forBackup && mConfigs.keyAt(i) != userId) {
                     continue;
                 }
-                mConfigs.valueAt(i).writeXml(out, version);
+                mConfigs.valueAt(i).writeXml(out, version, forBackup);
             }
         }
     }
@@ -1231,28 +1549,51 @@
     }
 
     /**
-     * Removes old rule instances whose owner is not installed.
+     * Cleans up obsolete rules:
+     * <ul>
+     *     <li>Rule instances whose owner is not installed.
+     *     <li>Deleted rules that were deleted more than 30 days ago.
+     * </ul>
      */
     private void cleanUpZenRules() {
-        long currentTime = System.currentTimeMillis();
+        Instant keptRuleThreshold = mClock.instant().minus(DELETED_RULE_KEPT_FOR);
         synchronized (mConfigLock) {
             final ZenModeConfig newConfig = mConfig.copy();
-            if (newConfig.automaticRules != null) {
-                for (int i = newConfig.automaticRules.size() - 1; i >= 0; i--) {
-                    ZenRule rule = newConfig.automaticRules.get(newConfig.automaticRules.keyAt(i));
-                    if (RULE_INSTANCE_GRACE_PERIOD < (currentTime - rule.creationTime)) {
-                        try {
-                            if (rule.getPkg() != null) {
-                                mPm.getPackageInfo(rule.getPkg(), PackageManager.MATCH_ANY_USER);
-                            }
-                        } catch (PackageManager.NameNotFoundException e) {
-                            newConfig.automaticRules.removeAt(i);
-                        }
+
+            deleteRulesWithoutOwner(newConfig.automaticRules);
+            if (Flags.modesApi()) {
+                deleteRulesWithoutOwner(newConfig.deletedRules);
+                for (int i = newConfig.deletedRules.size() - 1; i >= 0; i--) {
+                    ZenRule deletedRule = newConfig.deletedRules.valueAt(i);
+                    if (deletedRule.deletionInstant == null
+                            || deletedRule.deletionInstant.isBefore(keptRuleThreshold)) {
+                        newConfig.deletedRules.removeAt(i);
                     }
                 }
             }
-            setConfigLocked(newConfig, null, UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "cleanUpZenRules",
-                    Process.SYSTEM_UID);
+
+            if (!newConfig.equals(mConfig)) {
+                setConfigLocked(newConfig, null, UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI,
+                        "cleanUpZenRules", Process.SYSTEM_UID);
+            }
+        }
+    }
+
+    private void deleteRulesWithoutOwner(ArrayMap<String, ZenRule> ruleList) {
+        long currentTime = Flags.modesApi() ? mClock.millis() : System.currentTimeMillis();
+        if (ruleList != null) {
+            for (int i = ruleList.size() - 1; i >= 0; i--) {
+                ZenRule rule = ruleList.valueAt(i);
+                if (RULE_INSTANCE_GRACE_PERIOD < (currentTime - rule.creationTime)) {
+                    try {
+                        if (rule.getPkg() != null) {
+                            mPm.getPackageInfo(rule.getPkg(), PackageManager.MATCH_ANY_USER);
+                        }
+                    } catch (PackageManager.NameNotFoundException e) {
+                        ruleList.removeAt(i);
+                    }
+                }
+            }
         }
     }
 
@@ -1490,7 +1831,12 @@
 
             for (ZenRule automaticRule : mConfig.automaticRules.values()) {
                 if (automaticRule.isAutomaticActive()) {
-                    applyCustomPolicy(policy, automaticRule);
+                    // Active rules with INTERRUPTION_FILTER_ALL are not included in consolidated
+                    // policy. This is relevant in case some other active rule has a more
+                    // restrictive INTERRUPTION_FILTER but a more lenient ZenPolicy!
+                    if (!Flags.modesApi() || automaticRule.zenMode != Global.ZEN_MODE_OFF) {
+                        applyCustomPolicy(policy, automaticRule);
+                    }
                     if (Flags.modesApi()) {
                         deviceEffectsBuilder.add(automaticRule.zenDeviceEffects);
                     }
@@ -2012,6 +2358,7 @@
         if (resId == 0) {
             return null;
         }
+        Objects.requireNonNull(packageName);
         try {
             final Resources res = mPm.getResourcesForApplication(packageName);
             return res.getResourceName(resId);
diff --git a/services/core/java/com/android/server/notification/flags.aconfig b/services/core/java/com/android/server/notification/flags.aconfig
index 8e79922a..47967db 100644
--- a/services/core/java/com/android/server/notification/flags.aconfig
+++ b/services/core/java/com/android/server/notification/flags.aconfig
@@ -50,3 +50,17 @@
   # Referenced in WM where WM starts before DeviceConfig
   is_fixed_read_only: true
 }
+
+flag {
+  name: "notification_reduce_messagequeue_usage"
+  namespace: "systemui"
+  description: "When this flag is on, NMS will no longer call removeMessage() and hasCallbacks() on Handler"
+  bug: "311051285"
+}
+
+flag {
+  name: "notification_test"
+  namespace: "systemui"
+  description: "Timing test, no functionality"
+  bug: "316931130"
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/os/NativeTombstoneManager.java b/services/core/java/com/android/server/os/NativeTombstoneManager.java
index f6e7ef3..9ce3cb3 100644
--- a/services/core/java/com/android/server/os/NativeTombstoneManager.java
+++ b/services/core/java/com/android/server/os/NativeTombstoneManager.java
@@ -41,14 +41,13 @@
 import android.system.StructStat;
 import android.util.Slog;
 import android.util.SparseArray;
-import android.util.proto.ProtoInputStream;
-import android.util.proto.ProtoParseException;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.server.BootReceiver;
 import com.android.server.ServiceThread;
 import com.android.server.os.TombstoneProtos.Cause;
 import com.android.server.os.TombstoneProtos.Tombstone;
+import com.android.server.os.protobuf.CodedInputStream;
 
 import libcore.io.IoUtils;
 
@@ -130,18 +129,21 @@
             return;
         }
 
-        String processName = "UNKNOWN";
         final boolean isProtoFile = filename.endsWith(".pb");
-        File protoPath = isProtoFile ? path : new File(path.getAbsolutePath() + ".pb");
-
-        Optional<TombstoneFile> parsedTombstone = handleProtoTombstone(protoPath, isProtoFile);
-        if (parsedTombstone.isPresent()) {
-            processName = parsedTombstone.get().getProcessName();
+        if (!isProtoFile) {
+            return;
         }
-        BootReceiver.addTombstoneToDropBox(mContext, path, isProtoFile, processName, mTmpFileLock);
+
+        Optional<ParsedTombstone> parsedTombstone = handleProtoTombstone(path, true);
+        if (parsedTombstone.isPresent()) {
+            BootReceiver.addTombstoneToDropBox(
+                    mContext, path, parsedTombstone.get().getTombstone(),
+                    parsedTombstone.get().getProcessName(), mTmpFileLock);
+        }
     }
 
-    private Optional<TombstoneFile> handleProtoTombstone(File path, boolean addToList) {
+    private Optional<ParsedTombstone> handleProtoTombstone(
+            File path, boolean addToList) {
         final String filename = path.getName();
         if (!filename.endsWith(".pb")) {
             Slog.w(TAG, "unexpected tombstone name: " + path);
@@ -171,7 +173,7 @@
             return Optional.empty();
         }
 
-        final Optional<TombstoneFile> parsedTombstone = TombstoneFile.parse(pfd);
+        final Optional<ParsedTombstone> parsedTombstone = TombstoneFile.parse(pfd);
         if (!parsedTombstone.isPresent()) {
             IoUtils.closeQuietly(pfd);
             return Optional.empty();
@@ -184,7 +186,7 @@
                     previous.dispose();
                 }
 
-                mTombstones.put(number, parsedTombstone.get());
+                mTombstones.put(number, parsedTombstone.get().getTombstoneFile());
             }
         }
 
@@ -332,6 +334,27 @@
         }
     }
 
+    static class ParsedTombstone {
+        TombstoneFile mTombstoneFile;
+        Tombstone mTombstone;
+        ParsedTombstone(TombstoneFile tombstoneFile, Tombstone tombstone) {
+            mTombstoneFile = tombstoneFile;
+            mTombstone = tombstone;
+        }
+
+        public String getProcessName() {
+            return mTombstoneFile.getProcessName();
+        }
+
+        public TombstoneFile getTombstoneFile() {
+            return mTombstoneFile;
+        }
+
+        public Tombstone getTombstone() {
+            return mTombstone;
+        }
+    }
+
     static class TombstoneFile {
         final ParcelFileDescriptor mPfd;
 
@@ -414,67 +437,21 @@
             }
         }
 
-        static Optional<TombstoneFile> parse(ParcelFileDescriptor pfd) {
-            final FileInputStream is = new FileInputStream(pfd.getFileDescriptor());
-            final ProtoInputStream stream = new ProtoInputStream(is);
+        static Optional<ParsedTombstone> parse(ParcelFileDescriptor pfd) {
+            Tombstone tombstoneProto;
+            try (FileInputStream is = new FileInputStream(pfd.getFileDescriptor())) {
+                final byte[] tombstoneBytes = is.readAllBytes();
 
-            int pid = 0;
-            int uid = 0;
-            String processName = null;
-            String crashReason = "";
-            String selinuxLabel = "";
-
-            try {
-                while (stream.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
-                    switch (stream.getFieldNumber()) {
-                        case (int) Tombstone.PID:
-                            pid = stream.readInt(Tombstone.PID);
-                            break;
-
-                        case (int) Tombstone.UID:
-                            uid = stream.readInt(Tombstone.UID);
-                            break;
-
-                        case (int) Tombstone.COMMAND_LINE:
-                            if (processName == null) {
-                                processName = stream.readString(Tombstone.COMMAND_LINE);
-                            }
-                            break;
-
-                        case (int) Tombstone.CAUSES:
-                            if (!crashReason.equals("")) {
-                                // Causes appear in decreasing order of likelihood. For now we only
-                                // want the most likely crash reason here, so ignore all others.
-                                break;
-                            }
-                            long token = stream.start(Tombstone.CAUSES);
-                        cause:
-                            while (stream.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
-                                switch (stream.getFieldNumber()) {
-                                    case (int) Cause.HUMAN_READABLE:
-                                        crashReason = stream.readString(Cause.HUMAN_READABLE);
-                                        break cause;
-
-                                    default:
-                                        break;
-                                }
-                            }
-                            stream.end(token);
-                            break;
-
-                        case (int) Tombstone.SELINUX_LABEL:
-                            selinuxLabel = stream.readString(Tombstone.SELINUX_LABEL);
-                            break;
-
-                        default:
-                            break;
-                    }
-                }
-            } catch (IOException | ProtoParseException ex) {
+                tombstoneProto = Tombstone.parseFrom(
+                        CodedInputStream.newInstance(tombstoneBytes));
+            } catch (IOException ex) {
                 Slog.e(TAG, "Failed to parse tombstone", ex);
                 return Optional.empty();
             }
 
+            int pid = tombstoneProto.getPid();
+            int uid = tombstoneProto.getUid();
+
             if (!UserHandle.isApp(uid)) {
                 Slog.e(TAG, "Tombstone's UID (" + uid + ") not an app, ignoring");
                 return Optional.empty();
@@ -491,6 +468,7 @@
             final int userId = UserHandle.getUserId(uid);
             final int appId = UserHandle.getAppId(uid);
 
+            String selinuxLabel = tombstoneProto.getSelinuxLabel();
             if (!selinuxLabel.startsWith("u:r:untrusted_app")) {
                 Slog.e(TAG, "Tombstone has invalid selinux label (" + selinuxLabel + "), ignoring");
                 return Optional.empty();
@@ -502,11 +480,30 @@
             result.mAppId = appId;
             result.mPid = pid;
             result.mUid = uid;
-            result.mProcessName = processName == null ? "" : processName;
+            result.mProcessName = getCmdLineProcessName(tombstoneProto);
             result.mTimestampMs = timestampMs;
-            result.mCrashReason = crashReason;
+            result.mCrashReason = getCrashReason(tombstoneProto);
 
-            return Optional.of(result);
+            return Optional.of(new ParsedTombstone(result, tombstoneProto));
+        }
+
+        private static String getCmdLineProcessName(Tombstone tombstoneProto) {
+            for (String cmdline : tombstoneProto.getCommandLineList()) {
+                if (cmdline != null) {
+                    return cmdline;
+                }
+            }
+            return "";
+        }
+
+        private static String getCrashReason(Tombstone tombstoneProto) {
+            for (Cause cause : tombstoneProto.getCausesList()) {
+                if (cause.getHumanReadable() != null
+                        && !cause.getHumanReadable().equals("")) {
+                    return cause.getHumanReadable();
+                }
+            }
+            return "";
         }
 
         public IParcelFileDescriptorRetriever getPfdRetriever() {
diff --git a/services/core/java/com/android/server/pdb/TEST_MAPPING b/services/core/java/com/android/server/pdb/TEST_MAPPING
index 1aa8601..9e98023 100644
--- a/services/core/java/com/android/server/pdb/TEST_MAPPING
+++ b/services/core/java/com/android/server/pdb/TEST_MAPPING
@@ -1,5 +1,5 @@
 {
-    "postsubmit": [
+    "presubmit": [
         {
             "name": "FrameworksServicesTests",
             "options": [
diff --git a/services/core/java/com/android/server/pm/BroadcastHelper.java b/services/core/java/com/android/server/pm/BroadcastHelper.java
index 7f58e75e..e984e9c 100644
--- a/services/core/java/com/android/server/pm/BroadcastHelper.java
+++ b/services/core/java/com/android/server/pm/BroadcastHelper.java
@@ -262,6 +262,7 @@
             // Deliver LOCKED_BOOT_COMPLETED first
             Intent lockedBcIntent = new Intent(Intent.ACTION_LOCKED_BOOT_COMPLETED)
                     .setPackage(packageName);
+            lockedBcIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
             if (includeStopped) {
                 lockedBcIntent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
             }
@@ -275,6 +276,7 @@
             // Deliver BOOT_COMPLETED only if user is unlocked
             if (mUmInternal.isUserUnlockingOrUnlocked(userId)) {
                 Intent bcIntent = new Intent(Intent.ACTION_BOOT_COMPLETED).setPackage(packageName);
+                bcIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
                 if (includeStopped) {
                     bcIntent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
                 }
@@ -827,7 +829,8 @@
             // action. When the targetPkg is set, it sends the broadcast to specific app, e.g.
             // installer app or null for registered apps. The callback only need to send back to the
             // registered apps so we check the null condition here.
-            notifyPackageMonitor(action, pkg, extras, userIds, instantUserIds, broadcastAllowList);
+            notifyPackageMonitor(action, pkg, extras, userIds, instantUserIds, broadcastAllowList,
+                    null /* filterExtras */);
         }
     }
 
@@ -975,14 +978,16 @@
         final Bundle options = new BroadcastOptions()
                 .setDeferralPolicy(BroadcastOptions.DEFERRAL_POLICY_UNTIL_ACTIVE)
                 .toBundle();
+        BiFunction<Integer, Bundle, Bundle> filterExtrasForReceiver =
+                (callingUid, intentExtras) -> BroadcastHelper.filterExtrasChangedPackageList(
+                        snapshot, callingUid, intentExtras);
         mHandler.post(() -> sendPackageBroadcast(intent, null /* pkg */,
                 extras, flags, null /* targetPkg */, null /* finishedReceiver */,
                 new int[]{userId}, null /* instantUserIds */, null /* broadcastAllowList */,
-                (callingUid, intentExtras) -> BroadcastHelper.filterExtrasChangedPackageList(
-                        snapshot, callingUid, intentExtras),
+                filterExtrasForReceiver,
                 options));
         notifyPackageMonitor(intent, null /* pkg */, extras, new int[]{userId},
-                null /* instantUserIds */, null /* broadcastAllowList */);
+                null /* instantUserIds */, null /* broadcastAllowList */, filterExtrasForReceiver);
     }
 
     void sendMyPackageSuspendedOrUnsuspended(@NonNull Computer snapshot,
@@ -1068,9 +1073,10 @@
                                       @Nullable Bundle extras,
                                       @NonNull int[] userIds,
                                       @NonNull int[] instantUserIds,
-                                      @Nullable SparseArray<int[]> broadcastAllowList) {
+                                      @Nullable SparseArray<int[]> broadcastAllowList,
+                                      @Nullable BiFunction<Integer, Bundle, Bundle> filterExtras) {
         mPackageMonitorCallbackHelper.notifyPackageMonitor(action, pkg, extras, userIds,
-                instantUserIds, broadcastAllowList, mHandler);
+                instantUserIds, broadcastAllowList, mHandler, filterExtras);
     }
 
     private void notifyResourcesChanged(boolean mediaStatus,
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index 3cb2420..0555d90 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -78,6 +78,7 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.AuxiliaryResolveInfo;
 import android.content.pm.ComponentInfo;
+import android.content.pm.Flags;
 import android.content.pm.InstallSourceInfo;
 import android.content.pm.InstantAppRequest;
 import android.content.pm.InstantAppResolveInfo;
@@ -1511,6 +1512,13 @@
             packageInfo.packageName = packageInfo.applicationInfo.packageName =
                     resolveExternalPackageName(p);
 
+            if (Flags.provideInfoOfApkInApex()) {
+                final String apexModuleName =  ps.getApexModuleName();
+                if (apexModuleName != null) {
+                    packageInfo.setApexPackageName(
+                            mApexManager.getActivePackageNameForApexModuleName(apexModuleName));
+                }
+            }
             return packageInfo;
         } else if ((flags & (MATCH_UNINSTALLED_PACKAGES | MATCH_ARCHIVED_PACKAGES)) != 0
                 && PackageUserStateUtils.isAvailable(state, flags)) {
diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java
index b96b704..588c629 100644
--- a/services/core/java/com/android/server/pm/DeletePackageHelper.java
+++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java
@@ -487,7 +487,7 @@
                     // Do not uninstall the APK if an app should be cached
                     boolean keepUninstalledPackage =
                             mPm.shouldKeepUninstalledPackageLPr(packageName);
-                    if (ps.isInstalledOrHasDataOnAnyOtherUser(
+                    if (ps.isInstalledOnAnyOtherUser(
                             mUserManagerInternal.getUserIds(), userId) || keepUninstalledPackage) {
                         // Other users still have this package installed, so all
                         // we need to do is clear this user's data and save that
@@ -533,7 +533,7 @@
                 // artifacts are not stored in the same directory as the APKs
                 deleteArtDexoptArtifacts(packageName);
             }
-            deleteInstalledPackageLIF(ps, deleteCodeAndResources, flags, allUserHandles,
+            deleteInstalledPackageLIF(ps, userId, deleteCodeAndResources, flags, allUserHandles,
                     outInfo, writeSettings);
         }
 
@@ -554,7 +554,7 @@
     }
 
     @GuardedBy("mPm.mInstallLock")
-    private void deleteInstalledPackageLIF(PackageSetting ps,
+    private void deleteInstalledPackageLIF(PackageSetting ps, int userId,
             boolean deleteCodeAndResources, int flags, @NonNull int[] allUserHandles,
             @NonNull PackageRemovedInfo outInfo, boolean writeSettings) {
         synchronized (mPm.mLock) {
@@ -567,7 +567,7 @@
 
         // Delete package data from internal structures and also remove data if flag is set
         mRemovePackageHelper.removePackageDataLIF(
-                ps, allUserHandles, outInfo, flags, writeSettings);
+                ps, userId, allUserHandles, outInfo, flags, writeSettings);
 
         // Delete application code and resources only for parent packages
         if (deleteCodeAndResources) {
@@ -677,8 +677,8 @@
             flags |= PackageManager.DELETE_KEEP_DATA;
         }
         synchronized (mPm.mInstallLock) {
-            deleteInstalledPackageLIF(deletedPs, true, flags, allUserHandles, outInfo,
-                    writeSettings);
+            deleteInstalledPackageLIF(deletedPs, UserHandle.USER_ALL, true, flags, allUserHandles,
+                    outInfo, writeSettings);
         }
     }
 
@@ -829,6 +829,9 @@
                         int returnCodeOfChild;
                         for (int childId : childUserIds) {
                             if (childId == userId) continue;
+                            if (mUserManagerInternal.getProfileParentId(childId) != userId) {
+                                continue;
+                            }
 
                             // If package is not present in child then don't attempt to delete.
                             if (!packageState.getUserStateOrDefault(childId).isInstalled()) {
diff --git a/services/core/java/com/android/server/pm/InitAppsHelper.java b/services/core/java/com/android/server/pm/InitAppsHelper.java
index 41d0176..22951d5 100644
--- a/services/core/java/com/android/server/pm/InitAppsHelper.java
+++ b/services/core/java/com/android/server/pm/InitAppsHelper.java
@@ -180,7 +180,9 @@
         // priority of system overlays.
         final ArrayMap<String, File> apkInApexPreInstalledPaths = new ArrayMap<>();
         for (ApexManager.ActiveApexInfo apexInfo : mApexManager.getActiveApexInfos()) {
-            for (String packageName : mApexManager.getApksInApex(apexInfo.apexModuleName)) {
+            final String apexPackageName = mApexManager.getActivePackageNameForApexModuleName(
+                    apexInfo.apexModuleName);
+            for (String packageName : mApexManager.getApksInApex(apexPackageName)) {
                 apkInApexPreInstalledPaths.put(packageName, apexInfo.preInstalledApexPath);
             }
         }
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index b638d30..92be4ee 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -175,6 +175,7 @@
 import com.android.server.art.model.ArtFlags;
 import com.android.server.art.model.DexoptParams;
 import com.android.server.art.model.DexoptResult;
+import com.android.server.criticalevents.CriticalEventLog;
 import com.android.server.pm.Installer.LegacyDexoptDisabledException;
 import com.android.server.pm.dex.ArtManagerService;
 import com.android.server.pm.dex.DexManager;
@@ -697,7 +698,7 @@
                     pkgSetting.setUninstallReason(PackageManager.UNINSTALL_REASON_UNKNOWN, userId);
                     pkgSetting.setFirstInstallTime(System.currentTimeMillis(), userId);
                     // Clear any existing archive state.
-                    pkgSetting.setArchiveState(null, userId);
+                    mPm.mInstallerService.mPackageArchiver.clearArchiveState(packageName, userId);
                     mPm.mSettings.writePackageRestrictionsLPr(userId);
                     mPm.mSettings.writeKernelMappingLPr(pkgSetting);
                     installed = true;
@@ -957,6 +958,7 @@
         final Set<String> scannedPackages = new ArraySet<>(requests.size());
         final Map<String, Settings.VersionInfo> versionInfos = new ArrayMap<>(requests.size());
         final Map<String, Boolean> createdAppId = new ArrayMap<>(requests.size());
+        CriticalEventLog.getInstance().logInstallPackagesStarted();
         boolean success = false;
         try {
             Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackagesLI");
@@ -2279,7 +2281,7 @@
                                 installerPackageName);
                     }
                     // Clear any existing archive state.
-                    ps.setArchiveState(null, userId);
+                    mPm.mInstallerService.mPackageArchiver.clearArchiveState(pkgName, userId);
                 } else if (allUsers != null) {
                     // The caller explicitly specified INSTALL_ALL_USERS flag.
                     // Thus, updating the settings to install the app for all users.
@@ -2303,7 +2305,8 @@
                                         installerPackageName);
                             }
                             // Clear any existing archive state.
-                            ps.setArchiveState(null, currentUserId);
+                            mPm.mInstallerService.mPackageArchiver.clearArchiveState(pkgName,
+                                    currentUserId);
                         } else {
                             ps.setInstalled(false, currentUserId);
                         }
@@ -3264,9 +3267,9 @@
                 Slog.e(TAG, "updateAllSharedLibrariesLPw failed: " + e.getMessage());
             }
         }
-        mAppDataHelper.prepareAppDataAfterInstallLIF(pkg);
-
         setPackageInstalledForSystemPackage(pkg, allUserHandles, origUserHandles, writeSettings);
+
+        mAppDataHelper.prepareAppDataAfterInstallLIF(pkg);
     }
 
     private void setPackageInstalledForSystemPackage(@NonNull AndroidPackage pkg,
@@ -4590,7 +4593,9 @@
 
     private void assertPackageWithSharedUserIdIsPrivileged(AndroidPackage pkg)
             throws PackageManagerException {
-        if (!AndroidPackageLegacyUtils.isPrivileged(pkg) && (pkg.getSharedUserId() != null)) {
+        if (!AndroidPackageLegacyUtils.isPrivileged(pkg)
+                && (pkg.getSharedUserId() != null)
+                && !pkg.isLeavingSharedUser()) {
             SharedUserSetting sharedUserSetting = null;
             try {
                 synchronized (mPm.mLock) {
@@ -4630,7 +4635,8 @@
         if (((scanFlags & SCAN_AS_PRIVILEGED) == 0)
                 && !AndroidPackageLegacyUtils.isPrivileged(pkg)
                 && (pkg.getSharedUserId() != null)
-                && !skipVendorPrivilegeScan) {
+                && !skipVendorPrivilegeScan
+                && !pkg.isLeavingSharedUser()) {
             SharedUserSetting sharedUserSetting = null;
             synchronized (mPm.mLock) {
                 try {
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 127bf49..9915554 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -1610,21 +1610,117 @@
                     "Can't access AppMarketActivity for another user")) {
                 return null;
             }
+            final int callingUser = getCallingUserId();
             final long identity = Binder.clearCallingIdentity();
+
             try {
-                // TODO(b/316118005): Add code to launch the app installer for the packageName.
-                Intent appMarketIntent = new Intent(Intent.ACTION_MAIN);
-                appMarketIntent.addCategory(Intent.CATEGORY_APP_MARKET);
-                final PendingIntent pi = PendingIntent.getActivityAsUser(
-                        mContext, /* requestCode */ 0, appMarketIntent, PendingIntent.FLAG_ONE_SHOT
-                                | PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_CANCEL_CURRENT,
-                        /* options */ null, user);
-                return pi == null ? null : pi.getIntentSender();
+                if (packageName == null) {
+                    return buildAppMarketIntentSenderForUser(user);
+                }
+
+                String installerPackageName = getInstallerPackage(packageName, callingUser);
+                if (installerPackageName == null
+                        || mPackageManagerInternal.getPackageUid(
+                                        installerPackageName, /* flags= */ 0, user.getIdentifier())
+                                < 0) {
+                    if (DEBUG) {
+                        Log.d(
+                                TAG,
+                                "Can't find installer for "
+                                        + packageName
+                                        + " in user: "
+                                        + user.getIdentifier());
+                    }
+                    return buildAppMarketIntentSenderForUser(user);
+                }
+
+                Intent packageInfoIntent =
+                        buildMarketPackageInfoIntent(
+                                packageName, installerPackageName, callingPackage);
+                if (mPackageManagerInternal
+                        .queryIntentActivities(
+                                packageInfoIntent,
+                                packageInfoIntent.resolveTypeIfNeeded(
+                                        mContext.getContentResolver()),
+                                PackageManager.MATCH_ALL,
+                                Process.myUid(),
+                                user.getIdentifier())
+                        .isEmpty()) {
+                    if (DEBUG) {
+                        Log.d(
+                                TAG,
+                                "Can't resolve package info intent for package "
+                                        + packageName
+                                        + " and installer:  "
+                                        + installerPackageName);
+                    }
+                    return buildAppMarketIntentSenderForUser(user);
+                }
+
+                return buildIntentSenderForUser(packageInfoIntent, user);
             } finally {
                 Binder.restoreCallingIdentity(identity);
             }
         }
 
+        @Nullable
+        private IntentSender buildAppMarketIntentSenderForUser(@NonNull UserHandle user) {
+            Intent appMarketIntent = new Intent(Intent.ACTION_MAIN);
+            appMarketIntent.addCategory(Intent.CATEGORY_APP_MARKET);
+            return buildIntentSenderForUser(appMarketIntent, user);
+        }
+
+        @Nullable
+        private IntentSender buildIntentSenderForUser(
+                @NonNull Intent intent, @NonNull UserHandle user) {
+            final PendingIntent pi =
+                    PendingIntent.getActivityAsUser(
+                            mContext,
+                            /* requestCode */ 0,
+                            intent,
+                            PendingIntent.FLAG_ONE_SHOT
+                                    | PendingIntent.FLAG_IMMUTABLE
+                                    | PendingIntent.FLAG_CANCEL_CURRENT,
+                            /* options */ null,
+                            user);
+            return pi == null ? null : pi.getIntentSender();
+        }
+
+        @Nullable
+        private String getInstallerPackage(@NonNull String packageName, int callingUserId) {
+            String installerPackageName = null;
+            try {
+                installerPackageName =
+                        mIPM.getInstallSourceInfo(packageName, callingUserId)
+                                .getInstallingPackageName();
+            } catch (RemoteException re) {
+                Slog.e(TAG, "Couldn't find installer for " + packageName, re);
+            }
+
+            return installerPackageName;
+        }
+
+        @NonNull
+        private Intent buildMarketPackageInfoIntent(
+                @NonNull String packageName,
+                @NonNull String installerPackageName,
+                @NonNull String callingPackage) {
+            return new Intent(Intent.ACTION_VIEW)
+                    .setData(
+                            new Uri.Builder()
+                                    .scheme("market")
+                                    .authority("details")
+                                    .appendQueryParameter("id", packageName)
+                                    .build())
+                    .putExtra(
+                            Intent.EXTRA_REFERRER,
+                            new Uri.Builder()
+                                    .scheme("android-app")
+                                    .authority(callingPackage)
+                                    .build())
+                    .setPackage(installerPackageName);
+        }
+
         @Override
         public void startActivityAsUser(IApplicationThread caller, String callingPackage,
                 String callingFeatureId, ComponentName component, Rect sourceBounds,
diff --git a/services/core/java/com/android/server/pm/ModuleInfoProvider.java b/services/core/java/com/android/server/pm/ModuleInfoProvider.java
index 230f555..6561d46 100644
--- a/services/core/java/com/android/server/pm/ModuleInfoProvider.java
+++ b/services/core/java/com/android/server/pm/ModuleInfoProvider.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.content.Context;
+import android.content.pm.Flags;
 import android.content.pm.IPackageManager;
 import android.content.pm.ModuleInfo;
 import android.content.pm.PackageInfo;
@@ -165,6 +166,10 @@
                 mi.setApexModuleName(
                         mApexManager.getApexModuleNameForPackageName(modulePackageName));
 
+                if (Flags.provideInfoOfApkInApex()) {
+                    mi.setApkInApexPackageNames(mApexManager.getApksInApex(modulePackageName));
+                }
+
                 mModuleInfo.put(modulePackageName, mi);
             }
         } catch (XmlPullParserException | IOException e) {
diff --git a/services/core/java/com/android/server/pm/PackageArchiver.java b/services/core/java/com/android/server/pm/PackageArchiver.java
index dcfc855d..3e5759a 100644
--- a/services/core/java/com/android/server/pm/PackageArchiver.java
+++ b/services/core/java/com/android/server/pm/PackageArchiver.java
@@ -25,7 +25,9 @@
 import static android.content.pm.ArchivedActivityInfo.bytesFromBitmap;
 import static android.content.pm.ArchivedActivityInfo.drawableToBitmap;
 import static android.content.pm.PackageInstaller.EXTRA_UNARCHIVE_STATUS;
+import static android.content.pm.PackageInstaller.STATUS_PENDING_USER_ACTION;
 import static android.content.pm.PackageInstaller.UNARCHIVAL_OK;
+import static android.content.pm.PackageInstaller.UNARCHIVAL_STATUS_UNSET;
 import static android.content.pm.PackageManager.DELETE_ARCHIVE;
 import static android.content.pm.PackageManager.DELETE_KEEP_DATA;
 import static android.content.pm.PackageManager.INSTALL_UNARCHIVE_DRAFT;
@@ -69,13 +71,16 @@
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Environment;
+import android.os.FileUtils;
 import android.os.IBinder;
 import android.os.ParcelableException;
 import android.os.Process;
+import android.os.RemoteException;
 import android.os.SELinux;
 import android.os.UserHandle;
 import android.text.TextUtils;
 import android.util.ExceptionUtils;
+import android.util.Pair;
 import android.util.Slog;
 
 import com.android.internal.R;
@@ -93,8 +98,11 @@
 import java.io.IOException;
 import java.nio.file.Path;
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
+import java.util.Set;
 import java.util.concurrent.CompletableFuture;
 
 /**
@@ -140,15 +148,23 @@
     private final Context mContext;
     private final PackageManagerService mPm;
 
+    private final AppStateHelper mAppStateHelper;
+
     @Nullable
     private LauncherApps mLauncherApps;
 
     @Nullable
     private AppOpsManager mAppOpsManager;
 
+    /* IntentSender store that maps key: {userId, appPackageName} to respective existing attached
+     unarchival intent sender. */
+    private final Map<Pair<Integer, String>, IntentSender> mLauncherIntentSenders;
+
     PackageArchiver(Context context, PackageManagerService mPm) {
         this.mContext = context;
         this.mPm = mPm;
+        this.mAppStateHelper = new AppStateHelper(mContext);
+        this.mLauncherIntentSenders = new HashMap<>();
     }
 
     /** Returns whether a package is archived for a user. */
@@ -197,7 +213,6 @@
                                 return;
                             }
 
-                            // TODO(b/278553670) Add special strings for the delete dialog
                             mPm.mInstallerService.uninstall(
                                     new VersionedPackage(packageName,
                                             PackageManager.VERSION_CODE_HIGHEST),
@@ -235,37 +250,32 @@
             // Return early as the calling UID does not match caller package's UID.
             return START_CLASS_NOT_FOUND;
         }
+
         String currentLauncherPackageName = getCurrentLauncherPackageName(userId);
         if ((currentLauncherPackageName == null || !callerPackageName.equals(
                 currentLauncherPackageName)) && callingUid != Process.SHELL_UID) {
             // TODO(b/311619990): Remove dependency on SHELL_UID for testing
             Slog.e(TAG, TextUtils.formatSimple(
-                    "callerPackageName: %s does not qualify for archival of package: " + "%s!",
+                    "callerPackageName: %s does not qualify for unarchival of package: " + "%s!",
                     callerPackageName, packageName));
             return START_PERMISSION_DENIED;
         }
-        // TODO(b/302114464): Handle edge cases & also divert to a dialog based on
-        //  permissions + compat options
-        Slog.i(TAG, TextUtils.formatSimple("Unarchival is starting for: %s", packageName));
-        try {
-            final IIntentSender.Stub mLocalSender = new IIntentSender.Stub() {
-                @Override
-                public void send(int code, Intent intent, String resolvedType,
-                        IBinder allowlistToken,
-                        IIntentReceiver finishedReceiver, String requiredPermission,
-                        Bundle options) {
-                    // TODO(b/302114464): Handle intent sender status codes
-                }
-            };
 
+        Slog.i(TAG, TextUtils.formatSimple("Unarchival is starting for: %s", packageName));
+
+        try {
+            // TODO(b/311709794) Make showUnarchivalConfirmation dependent on the compat options.
             requestUnarchive(packageName, callerPackageName,
-                    new IntentSender((IIntentSender) mLocalSender), UserHandle.of(userId));
+                    getOrCreateLauncherListener(userId, packageName),
+                    UserHandle.of(userId),
+                    false /* showUnarchivalConfirmation= */);
         } catch (Throwable t) {
             Slog.e(TAG, TextUtils.formatSimple(
                     "Unexpected error occurred while unarchiving package %s: %s.", packageName,
                     t.getLocalizedMessage()));
             return START_ABORTED;
         }
+
         return START_SUCCESS;
     }
 
@@ -300,6 +310,26 @@
         return false;
     }
 
+    void clearArchiveState(String packageName, int userId) {
+        synchronized (mPm.mLock) {
+            PackageSetting ps = mPm.mSettings.getPackageLPr(packageName);
+            if (ps != null) {
+                ps.setArchiveState(/* archiveState= */ null, userId);
+            }
+        }
+        mPm.mBackgroundHandler.post(
+                () -> {
+                    File iconsDir = getIconsDir(packageName, userId);
+                    if (!iconsDir.exists()) {
+                        return;
+                    }
+                    // TODO(b/319238030) Move this into installd.
+                    if (!FileUtils.deleteContentsAndDir(iconsDir)) {
+                        Slog.e(TAG, "Failed to clean up archive files for " + packageName);
+                    }
+                });
+    }
+
     @Nullable
     private String getCurrentLauncherPackageName(int userId) {
         ComponentName defaultLauncherComponent = mPm.snapshotComputer().getDefaultHomeActivity(
@@ -321,6 +351,20 @@
         return true;
     }
 
+    private IntentSender getOrCreateLauncherListener(int userId, String packageName) {
+        Pair<Integer, String> key = Pair.create(userId, packageName);
+        synchronized (mLauncherIntentSenders) {
+            IntentSender intentSender = mLauncherIntentSenders.get(key);
+            if (intentSender != null) {
+                return intentSender;
+            }
+            IntentSender unarchiveIntentSender = new IntentSender(
+                    (IIntentSender) new UnarchiveIntentSender());
+            mLauncherIntentSenders.put(key, unarchiveIntentSender);
+            return unarchiveIntentSender;
+        }
+    }
+
     /** Creates archived state for the package and user. */
     private CompletableFuture<ArchiveState> createArchiveState(String packageName, int userId)
             throws PackageManager.NameNotFoundException {
@@ -414,8 +458,8 @@
         if (mainActivity.iconBitmap == null) {
             return null;
         }
-        File iconsDir = createIconsDir(userId);
-        File iconFile = new File(iconsDir, packageName + "-" + index + ".png");
+        File iconsDir = createIconsDir(packageName, userId);
+        File iconFile = new File(iconsDir, index + ".png");
         try (FileOutputStream out = new FileOutputStream(iconFile)) {
             out.write(mainActivity.iconBitmap);
             out.flush();
@@ -431,14 +475,14 @@
             // The app doesn't define an icon. No need to store anything.
             return null;
         }
-        File iconsDir = createIconsDir(userId);
-        File iconFile = new File(iconsDir, packageName + "-" + index + ".png");
+        File iconsDir = createIconsDir(packageName, userId);
+        File iconFile = new File(iconsDir, index + ".png");
         Bitmap icon = drawableToBitmap(mainActivity.getIcon(/* density= */ 0), iconSize);
         try (FileOutputStream out = new FileOutputStream(iconFile)) {
             // Note: Quality is ignored for PNGs.
             if (!icon.compress(Bitmap.CompressFormat.PNG, /* quality= */ 100, out)) {
                 throw new IOException(TextUtils.formatSimple("Failure to store icon file %s",
-                        iconFile.getName()));
+                        iconFile.getAbsolutePath()));
             }
             out.flush();
         }
@@ -493,7 +537,6 @@
     /**
      * Returns true if the app is archivable.
      */
-    // TODO(b/299299569) Exclude system apps
     public boolean isAppArchivable(@NonNull String packageName, @NonNull UserHandle user) {
         Objects.requireNonNull(packageName);
         Objects.requireNonNull(user);
@@ -553,6 +596,15 @@
             @NonNull String callerPackageName,
             @NonNull IntentSender statusReceiver,
             @NonNull UserHandle userHandle) {
+        requestUnarchive(packageName, callerPackageName, statusReceiver, userHandle,
+                false /* showUnarchivalConfirmation= */);
+    }
+
+    private void requestUnarchive(
+            @NonNull String packageName,
+            @NonNull String callerPackageName,
+            @NonNull IntentSender statusReceiver,
+            @NonNull UserHandle userHandle, boolean showUnarchivalConfirmation) {
         Objects.requireNonNull(packageName);
         Objects.requireNonNull(callerPackageName);
         Objects.requireNonNull(statusReceiver);
@@ -597,8 +649,8 @@
                     + "an unarchival.");
         }
 
-        if (!hasInstallPackages) {
-            requestUnarchiveConfirmation(packageName, statusReceiver);
+        if (!hasInstallPackages || showUnarchivalConfirmation) {
+            requestUnarchiveConfirmation(packageName, statusReceiver, userHandle);
             return;
         }
 
@@ -622,7 +674,8 @@
                 () -> unarchiveInternal(packageName, userHandle, installerPackage, draftSessionId));
     }
 
-    private void requestUnarchiveConfirmation(String packageName, IntentSender statusReceiver) {
+    private void requestUnarchiveConfirmation(String packageName, IntentSender statusReceiver,
+            UserHandle user) {
         final Intent dialogIntent = new Intent(ACTION_UNARCHIVE_DIALOG);
         dialogIntent.putExtra(EXTRA_UNARCHIVE_INTENT_SENDER, statusReceiver);
         dialogIntent.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, packageName);
@@ -632,6 +685,7 @@
         broadcastIntent.putExtra(EXTRA_UNARCHIVE_STATUS,
                 PackageInstaller.STATUS_PENDING_USER_ACTION);
         broadcastIntent.putExtra(Intent.EXTRA_INTENT, dialogIntent);
+        broadcastIntent.putExtra(Intent.EXTRA_USER, user);
         sendIntent(statusReceiver, packageName, /* message= */ "", broadcastIntent);
     }
 
@@ -652,7 +706,6 @@
                 PackageInstaller.SessionParams.MODE_FULL_INSTALL);
         sessionParams.setAppPackageName(packageName);
         sessionParams.installFlags = INSTALL_UNARCHIVE_DRAFT;
-        sessionParams.unarchiveIntentSender = statusReceiver;
 
         int installerUid = mPm.snapshotComputer().getPackageUid(installerPackage, 0, userId);
         // Handles case of repeated unarchival calls for the same package.
@@ -660,6 +713,7 @@
                 sessionParams,
                 userId);
         if (existingSessionId != PackageInstaller.SessionInfo.INVALID_ID) {
+            attachListenerToSession(statusReceiver, existingSessionId, userId);
             return existingSessionId;
         }
 
@@ -668,12 +722,34 @@
                 installerPackage, mContext.getAttributionTag(),
                 installerUid,
                 userId);
+        attachListenerToSession(statusReceiver, sessionId, userId);
+
         // TODO(b/297358628) Also cleanup sessions upon device restart.
         mPm.mHandler.postDelayed(() -> mPm.mInstallerService.cleanupDraftIfUnclaimed(sessionId),
                 getUnarchiveForegroundTimeout());
         return sessionId;
     }
 
+    private void attachListenerToSession(IntentSender statusReceiver, int existingSessionId,
+            int userId) {
+        PackageInstallerSession session = mPm.mInstallerService.getSession(existingSessionId);
+        int status = session.getUnarchivalStatus();
+        // Here we handle a race condition that might happen when an installer reports UNARCHIVAL_OK
+        // but hasn't created a session yet. Without this the listener would never receive a success
+        // response.
+        if (status == UNARCHIVAL_OK) {
+            notifyUnarchivalListener(UNARCHIVAL_OK, session.getInstallerPackageName(),
+                    session.params.appPackageName, /* requiredStorageBytes= */ 0,
+                    /* userActionIntent= */ null, Set.of(statusReceiver), userId);
+            return;
+        } else if (status != UNARCHIVAL_STATUS_UNSET) {
+            throw new IllegalStateException(TextUtils.formatSimple("Session %s has unarchive status"
+                    + "%s but is still active.", session.sessionId, status));
+        }
+
+        session.registerUnarchivalListener(statusReceiver);
+    }
+
     /**
      * Returns the icon of an archived app. This is the icon of the main activity of the app.
      *
@@ -738,8 +814,20 @@
     }
 
     @VisibleForTesting
-    Bitmap decodeIcon(ArchiveActivityInfo archiveActivityInfo) {
-        return BitmapFactory.decodeFile(archiveActivityInfo.getIconBitmap().toString());
+    @Nullable
+    Bitmap decodeIcon(ArchiveActivityInfo activityInfo) {
+        Path iconBitmap = activityInfo.getIconBitmap();
+        if (iconBitmap == null) {
+            return null;
+        }
+        Bitmap bitmap = BitmapFactory.decodeFile(iconBitmap.toString());
+        // TODO(b/278553670) We should throw here after some time. Failing graciously now because
+        // we've just changed the place where we store icons.
+        if (bitmap == null) {
+            Slog.e(TAG, "Archived icon cannot be decoded " + iconBitmap.toAbsolutePath());
+            return null;
+        }
+        return bitmap;
     }
 
     Bitmap includeCloudOverlay(Bitmap bitmap) {
@@ -849,7 +937,7 @@
 
     void notifyUnarchivalListener(int status, String installerPackageName, String appPackageName,
             long requiredStorageBytes, @Nullable PendingIntent userActionIntent,
-            IntentSender unarchiveIntentSender, int userId) {
+            Set<IntentSender> unarchiveIntentSenders, int userId) {
         final Intent broadcastIntent = new Intent();
         broadcastIntent.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, appPackageName);
         broadcastIntent.putExtra(EXTRA_UNARCHIVE_STATUS, status);
@@ -863,17 +951,23 @@
                 return;
             }
             broadcastIntent.putExtra(Intent.EXTRA_INTENT, dialogIntent);
+            broadcastIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(userId));
         }
 
         final BroadcastOptions options = BroadcastOptions.makeBasic();
         options.setPendingIntentBackgroundActivityStartMode(
                 MODE_BACKGROUND_ACTIVITY_START_DENIED);
-        try {
-            unarchiveIntentSender.sendIntent(mContext, 0, broadcastIntent, /* onFinished= */ null,
-                    /* handler= */ null, /* requiredPermission= */ null,
-                    options.toBundle());
-        } catch (IntentSender.SendIntentException e) {
-            Slog.e(TAG, TextUtils.formatSimple("Failed to send unarchive intent"), e);
+        for (IntentSender intentSender : unarchiveIntentSenders) {
+            try {
+                intentSender.sendIntent(mContext, 0, broadcastIntent, /* onFinished= */ null,
+                        /* handler= */ null, /* requiredPermission= */ null, options.toBundle());
+            } catch (IntentSender.SendIntentException e) {
+                Slog.e(TAG, TextUtils.formatSimple("Failed to send unarchive intent"), e);
+            } finally {
+                synchronized (mLauncherIntentSenders) {
+                    mLauncherIntentSenders.remove(Pair.create(userId, appPackageName));
+                }
+            }
         }
     }
 
@@ -883,6 +977,7 @@
             long requiredStorageBytes, PendingIntent userActionIntent, int userId) {
         final Intent dialogIntent = new Intent(ACTION_UNARCHIVE_ERROR_DIALOG);
         dialogIntent.putExtra(EXTRA_UNARCHIVE_STATUS, status);
+        dialogIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(userId));
         if (requiredStorageBytes > 0) {
             dialogIntent.putExtra(EXTRA_REQUIRED_BYTES, requiredStorageBytes);
         }
@@ -1013,8 +1108,9 @@
         }
     }
 
-    private static File createIconsDir(@UserIdInt int userId) throws IOException {
-        File iconsDir = getIconsDir(userId);
+    private static File createIconsDir(String packageName, @UserIdInt int userId)
+            throws IOException {
+        File iconsDir = getIconsDir(packageName, userId);
         if (!iconsDir.isDirectory()) {
             iconsDir.delete();
             iconsDir.mkdirs();
@@ -1026,8 +1122,10 @@
         return iconsDir;
     }
 
-    private static File getIconsDir(int userId) {
-        return new File(Environment.getDataSystemCeDirectory(userId), ARCHIVE_ICONS_DIR);
+    private static File getIconsDir(String packageName, int userId) {
+        return new File(
+                new File(Environment.getDataSystemCeDirectory(userId), ARCHIVE_ICONS_DIR),
+                packageName);
     }
 
     private static byte[] bytesFromBitmapFile(Path path) throws IOException {
@@ -1118,4 +1216,25 @@
 
         return activities.toArray(new ArchivedActivityParcel[activities.size()]);
     }
+
+    private class UnarchiveIntentSender extends IIntentSender.Stub {
+        @Override
+        public void send(int code, Intent intent, String resolvedType, IBinder whitelistToken,
+                IIntentReceiver finishedReceiver, String requiredPermission, Bundle options)
+                throws RemoteException {
+            int status = intent.getExtras().getInt(PackageInstaller.EXTRA_UNARCHIVE_STATUS,
+                    STATUS_PENDING_USER_ACTION);
+            if (status == UNARCHIVAL_OK) {
+                return;
+            }
+            Intent extraIntent = intent.getParcelableExtra(Intent.EXTRA_INTENT, Intent.class);
+            UserHandle user = intent.getParcelableExtra(Intent.EXTRA_USER, UserHandle.class);
+            if (extraIntent != null && user != null
+                    && mAppStateHelper.isAppTopVisible(
+                    getCurrentLauncherPackageName(user.getIdentifier()))) {
+                extraIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                mContext.startActivityAsUser(extraIntent, user);
+            }
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index cbd65a4..fdcd28b 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -25,6 +25,7 @@
 import static android.content.pm.PackageInstaller.UNARCHIVAL_ERROR_USER_ACTION_NEEDED;
 import static android.content.pm.PackageInstaller.UNARCHIVAL_GENERIC_ERROR;
 import static android.content.pm.PackageInstaller.UNARCHIVAL_OK;
+import static android.content.pm.PackageManager.DELETE_ARCHIVE;
 import static android.content.pm.PackageManager.INSTALL_UNARCHIVE_DRAFT;
 import static android.os.Process.INVALID_UID;
 import static android.os.Process.SYSTEM_UID;
@@ -291,9 +292,7 @@
     @NonNull
     private final RequestThrottle mSettingsWriteRequest = new RequestThrottle(IoThread.getHandler(),
             () -> {
-                synchronized (mSessions) {
-                    return writeSessionsLocked();
-                }
+                return writeSessions();
             });
 
     public PackageInstallerService(Context context, PackageManagerService pm,
@@ -600,9 +599,16 @@
                 mHistoricalSessionsByInstaller.get(installerUid) + 1);
     }
 
-    @GuardedBy("mSessions")
-    private boolean writeSessionsLocked() {
-        if (LOGD) Slog.v(TAG, "writeSessionsLocked()");
+    private boolean writeSessions() {
+        if (LOGD) Slog.v(TAG, "writeSessions()");
+        final PackageInstallerSession[] sessions;
+        synchronized (mSessions) {
+            final int size = mSessions.size();
+            sessions = new PackageInstallerSession[size];
+            for (int i = 0; i < size; i++) {
+                sessions[i] = mSessions.valueAt(i);
+            }
+        }
 
         FileOutputStream fos = null;
         try {
@@ -611,9 +617,7 @@
             final TypedXmlSerializer out = Xml.resolveSerializer(fos);
             out.startDocument(null, true);
             out.startTag(null, TAG_SESSIONS);
-            final int size = mSessions.size();
-            for (int i = 0; i < size; i++) {
-                final PackageInstallerSession session = mSessions.valueAt(i);
+            for (var session : sessions) {
                 session.write(out, mSessionsDir);
             }
             out.endTag(null, TAG_SESSIONS);
@@ -1402,7 +1406,7 @@
 
         final PackageDeleteObserverAdapter adapter = new PackageDeleteObserverAdapter(mContext,
                 statusReceiver, versionedPackage.getPackageName(),
-                canSilentlyInstallPackage, userId);
+                canSilentlyInstallPackage, userId, mPackageArchiver, flags);
         final boolean shouldShowConfirmationDialog =
                 (flags & PackageManager.DELETE_SHOW_DIALOG) != 0;
         if (!shouldShowConfirmationDialog
@@ -1756,26 +1760,8 @@
                         binderUid, unarchiveId));
             }
 
-            IntentSender unarchiveIntentSender = session.params.unarchiveIntentSender;
-            if (unarchiveIntentSender == null) {
-                throw new IllegalStateException(
-                        TextUtils.formatSimple(
-                                "Unarchival status for ID %s has already been set or a "
-                                        + "session has been created for it already by the "
-                                        + "caller.",
-                                unarchiveId));
-            }
-
-            // Execute expensive calls outside the sync block.
-            mPm.mHandler.post(
-                    () -> mPackageArchiver.notifyUnarchivalListener(status,
-                            session.getInstallerPackageName(),
-                            session.params.appPackageName, requiredStorageBytes, userActionIntent,
-                            unarchiveIntentSender, userId));
-            session.params.unarchiveIntentSender = null;
-            if (status != UNARCHIVAL_OK) {
-                Binder.withCleanCallingIdentity(session::abandon);
-            }
+            session.reportUnarchivalStatus(status, unarchiveId, requiredStorageBytes,
+                    userActionIntent);
         }
     }
 
@@ -1843,9 +1829,23 @@
         private final IntentSender mTarget;
         private final String mPackageName;
         private final Notification mNotification;
+        private final int mUserId;
 
-        public PackageDeleteObserverAdapter(Context context, IntentSender target,
+        @DeleteFlags
+        private final int mFlags;
+
+        @Nullable
+        private final PackageArchiver mPackageArchiver;
+
+        PackageDeleteObserverAdapter(Context context, IntentSender target,
                 String packageName, boolean showNotification, int userId) {
+            this(context, target, packageName, showNotification, userId,
+                    /* packageArchiver= */ null, /* flags= */ 0);
+        }
+
+        PackageDeleteObserverAdapter(Context context, IntentSender target,
+                String packageName, boolean showNotification, int userId,
+                PackageArchiver packageArchiver, @DeleteFlags int flags) {
             mContext = context;
             mTarget = target;
             mPackageName = packageName;
@@ -1857,6 +1857,9 @@
             } else {
                 mNotification = null;
             }
+            mUserId = userId;
+            mPackageArchiver = packageArchiver;
+            mFlags = flags;
         }
 
         private String getDeviceOwnerDeletedPackageMsg() {
@@ -1898,6 +1901,11 @@
                         SystemMessage.NOTE_PACKAGE_STATE,
                         mNotification);
             }
+            if (mPackageArchiver != null
+                    && PackageManager.DELETE_SUCCEEDED != returnCode
+                    && (mFlags & DELETE_ARCHIVE) != 0) {
+                mPackageArchiver.clearArchiveState(mPackageName, mUserId);
+            }
             if (mTarget == null) {
                 return;
             }
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 4adb60c..117d03f 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -22,6 +22,8 @@
 import static android.content.pm.DataLoaderType.INCREMENTAL;
 import static android.content.pm.DataLoaderType.STREAMING;
 import static android.content.pm.PackageInstaller.LOCATION_DATA_APP;
+import static android.content.pm.PackageInstaller.UNARCHIVAL_OK;
+import static android.content.pm.PackageInstaller.UNARCHIVAL_STATUS_UNSET;
 import static android.content.pm.PackageItemInfo.MAX_SAFE_LABEL_LENGTH;
 import static android.content.pm.PackageManager.INSTALL_FAILED_ABORTED;
 import static android.content.pm.PackageManager.INSTALL_FAILED_BAD_SIGNATURE;
@@ -65,6 +67,7 @@
 import android.app.BroadcastOptions;
 import android.app.Notification;
 import android.app.NotificationManager;
+import android.app.PendingIntent;
 import android.app.admin.DevicePolicyEventLogger;
 import android.app.admin.DevicePolicyManager;
 import android.app.admin.DevicePolicyManagerInternal;
@@ -97,6 +100,7 @@
 import android.content.pm.PackageInstaller.PreapprovalDetails;
 import android.content.pm.PackageInstaller.SessionInfo;
 import android.content.pm.PackageInstaller.SessionParams;
+import android.content.pm.PackageInstaller.UnarchivalStatus;
 import android.content.pm.PackageInstaller.UserActionReason;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.PackageInfoFlags;
@@ -771,6 +775,10 @@
     private final List<String> mResolvedInstructionSets = new ArrayList<>();
     @GuardedBy("mLock")
     private final List<String> mResolvedNativeLibPaths = new ArrayList<>();
+
+    @GuardedBy("mLock")
+    private final Set<IntentSender> mUnarchivalListeners = new ArraySet<>();
+
     @GuardedBy("mLock")
     private File mInheritedFilesBase;
     @GuardedBy("mLock")
@@ -796,6 +804,9 @@
     @GuardedBy("mLock")
     private int mValidatedTargetSdk = INVALID_TARGET_SDK_VERSION;
 
+    @UnarchivalStatus
+    private int mUnarchivalStatus = UNARCHIVAL_STATUS_UNSET;
+
     private static final FileFilter sAddedApkFilter = new FileFilter() {
         @Override
         public boolean accept(File file) {
@@ -5088,6 +5099,44 @@
         }
     }
 
+    void registerUnarchivalListener(IntentSender intentSender) {
+        synchronized (mLock) {
+            this.mUnarchivalListeners.add(intentSender);
+        }
+    }
+
+    Set<IntentSender> getUnarchivalListeners() {
+        synchronized (mLock) {
+            return new ArraySet<>(mUnarchivalListeners);
+        }
+    }
+
+    void reportUnarchivalStatus(@UnarchivalStatus int status, int unarchiveId,
+            long requiredStorageBytes, PendingIntent userActionIntent) {
+        if (getUnarchivalStatus() != UNARCHIVAL_STATUS_UNSET) {
+            throw new IllegalStateException(
+                    TextUtils.formatSimple(
+                            "Unarchival status for ID %s has already been set or a session has "
+                                    + "been created for it already by the caller.",
+                            unarchiveId));
+        }
+        mUnarchivalStatus = status;
+
+        // Execute expensive calls outside the sync block.
+        mPm.mHandler.post(
+                () -> mPm.mInstallerService.mPackageArchiver.notifyUnarchivalListener(status,
+                        getInstallerPackageName(), params.appPackageName, requiredStorageBytes,
+                        userActionIntent, getUnarchivalListeners(), userId));
+        if (status != UNARCHIVAL_OK) {
+            Binder.withCleanCallingIdentity(this::abandon);
+        }
+    }
+
+    @UnarchivalStatus
+    int getUnarchivalStatus() {
+        return this.mUnarchivalStatus;
+    }
+
     /**
      * Free up storage used by this session and its children.
      * Must not be called on a child session.
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 81d5d81..5225529 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -607,6 +607,8 @@
     private final boolean mIsUpgrade;
     private final boolean mIsPreNMR1Upgrade;
     private final boolean mIsPreQUpgrade;
+    // If mIsUpgrade == true, contains the prior SDK version, else -1.
+    private final int mPriorSdkVersion;
 
     // Used for privilege escalation. MUST NOT BE CALLED WITH mPackages
     // LOCK HELD.  Can be called with mInstallLock held.
@@ -1891,6 +1893,7 @@
         mInstantAppResolverSettingsComponent = testParams.instantAppResolverSettingsComponent;
         mIsPreNMR1Upgrade = testParams.isPreNmr1Upgrade;
         mIsPreQUpgrade = testParams.isPreQupgrade;
+        mPriorSdkVersion = testParams.priorSdkVersion;
         mIsUpgrade = testParams.isUpgrade;
         mMetrics = testParams.Metrics;
         mModuleInfoProvider = testParams.moduleInfoProvider;
@@ -2230,7 +2233,7 @@
                         "Upgrading from " + ver.fingerprint + " (" + ver.buildFingerprint + ") to "
                                 + PackagePartitions.FINGERPRINT + " (" + Build.FINGERPRINT + ")");
             }
-
+            mPriorSdkVersion = mIsUpgrade ? ver.sdkVersion : -1;
             mInitAppsHelper = new InitAppsHelper(this, mApexManager, mInstallPackageHelper,
                     mInjector.getSystemPartitions());
 
@@ -4623,7 +4626,7 @@
                 });
                 mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_UNSTOPPED,
                         packageName, extras, userIds, null /* instantUserIds */,
-                        broadcastAllowList, mHandler);
+                        broadcastAllowList, mHandler, null /* filterExtras */);
             }
         }
     }
@@ -7073,7 +7076,7 @@
             }
             mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_RESTARTED,
                     packageName, extras, userIds, null /* instantUserIds */,
-                    broadcastAllowList, mHandler);
+                    broadcastAllowList, mHandler, null /* filterExtras */);
         }
 
         @Override
@@ -7099,6 +7102,12 @@
             mPackageMonitorCallbackHelper.notifyPackageMonitorWithIntent(intent, userId,
                     visibilityAllowList, mHandler);
         }
+
+        @Override
+        public boolean isUpgradingFromLowerThan(int sdkVersion) {
+            final boolean isUpgrading = mPriorSdkVersion != -1;
+            return isUpgrading && mPriorSdkVersion < sdkVersion;
+        }
     }
 
     private void setEnabledOverlayPackages(@UserIdInt int userId,
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java b/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
index 86d78dc..2d79718 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
@@ -65,6 +65,7 @@
     public ComponentName instantAppResolverSettingsComponent;
     public boolean isPreNmr1Upgrade;
     public boolean isPreQupgrade;
+    public int priorSdkVersion = -1;
     public boolean isUpgrade;
     public LegacyPermissionManagerInternal legacyPermissionManagerInternal;
     public DisplayMetrics Metrics;
diff --git a/services/core/java/com/android/server/pm/PackageMonitorCallbackHelper.java b/services/core/java/com/android/server/pm/PackageMonitorCallbackHelper.java
index 1bb0730..cf5de89 100644
--- a/services/core/java/com/android/server/pm/PackageMonitorCallbackHelper.java
+++ b/services/core/java/com/android/server/pm/PackageMonitorCallbackHelper.java
@@ -41,12 +41,14 @@
 import com.android.internal.util.ArrayUtils;
 
 import java.util.ArrayList;
+import java.util.function.BiFunction;
 
 /** Helper class to handle PackageMonitorCallback and notify the registered client. This is mainly
  * used by PackageMonitor to improve the broadcast latency. */
 class PackageMonitorCallbackHelper {
 
     private static final boolean DEBUG = false;
+    private static final String TAG = "PackageMonitorCallbackHelper";
 
     @NonNull
     private final Object mLock = new Object();
@@ -105,8 +107,9 @@
             extras.putBoolean(Intent.EXTRA_ARCHIVAL, true);
         }
         extras.putInt(PackageInstaller.EXTRA_DATA_LOADER_TYPE, dataLoaderType);
-        notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED, packageName, extras ,
-                userIds /* userIds */, instantUserIds, broadcastAllowList, handler);
+        notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED, packageName, extras,
+                userIds /* userIds */, instantUserIds, broadcastAllowList, handler,
+                null /* filterExtras */);
     }
 
     public void notifyResourcesChanged(boolean mediaStatus, boolean replacing,
@@ -120,7 +123,8 @@
         String action = mediaStatus ? Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE
                 : Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE;
         notifyPackageMonitor(action, null /* pkg */, extras, null /* userIds */,
-                null /* instantUserIds */, null /* broadcastAllowList */, handler);
+                null /* instantUserIds */, null /* broadcastAllowList */, handler,
+                null /* filterExtras */);
     }
 
     public void notifyPackageChanged(String packageName, boolean dontKillApp,
@@ -137,12 +141,12 @@
             extras.putString(Intent.EXTRA_REASON, reason);
         }
         notifyPackageMonitor(Intent.ACTION_PACKAGE_CHANGED, packageName, extras, userIds,
-                instantUserIds, broadcastAllowList, handler);
+                instantUserIds, broadcastAllowList, handler, null /* filterExtras */);
     }
 
     public void notifyPackageMonitor(String action, String pkg, Bundle extras,
             int[] userIds, int[] instantUserIds, SparseArray<int[]> broadcastAllowList,
-            Handler handler) {
+            Handler handler, BiFunction<Integer, Bundle, Bundle> filterExtras) {
         if (!isAllowedCallbackAction(action)) {
             return;
         }
@@ -160,10 +164,11 @@
 
             if (ArrayUtils.isEmpty(instantUserIds)) {
                 doNotifyCallbacksByAction(
-                        action, pkg, extras, resolvedUserIds, broadcastAllowList, handler);
+                        action, pkg, extras, resolvedUserIds, broadcastAllowList, handler,
+                        filterExtras);
             } else {
                 doNotifyCallbacksByAction(action, pkg, extras, instantUserIds, broadcastAllowList,
-                        handler);
+                        handler, filterExtras);
             }
         } catch (RemoteException e) {
             // do nothing
@@ -199,11 +204,13 @@
         synchronized (mLock) {
             callbacks = mCallbacks;
         }
-        doNotifyCallbacks(callbacks, intent, userId, broadcastAllowList, handler);
+        doNotifyCallbacks(callbacks, intent, userId, broadcastAllowList, handler,
+                null /* filterExtrasFunction */);
     }
 
     private void doNotifyCallbacksByAction(String action, String pkg, Bundle extras, int[] userIds,
-            SparseArray<int[]> broadcastAllowList, Handler handler) {
+            SparseArray<int[]> broadcastAllowList, Handler handler,
+            BiFunction<Integer, Bundle, Bundle> filterExtrasFunction) {
         RemoteCallbackList<IRemoteCallback> callbacks;
         synchronized (mLock) {
             callbacks = mCallbacks;
@@ -223,12 +230,13 @@
 
             final int[] allowUids =
                     broadcastAllowList != null ? broadcastAllowList.get(userId) : null;
-            doNotifyCallbacks(callbacks, intent, userId, allowUids, handler);
+            doNotifyCallbacks(callbacks, intent, userId, allowUids, handler, filterExtrasFunction);
         }
     }
 
     private void doNotifyCallbacks(RemoteCallbackList<IRemoteCallback> callbacks,
-            Intent intent, int userId, int[] allowUids, Handler handler) {
+            Intent intent, int userId, int[] allowUids, Handler handler,
+            BiFunction<Integer, Bundle, Bundle> filterExtrasFunction) {
         handler.post(() -> callbacks.broadcast((callback, user) -> {
             RegisterUser registerUser = (RegisterUser) user;
             if ((registerUser.getUserId() != UserHandle.USER_ALL) && (registerUser.getUserId()
@@ -239,13 +247,30 @@
             if (allowUids != null && registerUid != Process.SYSTEM_UID
                     && !ArrayUtils.contains(allowUids, registerUid)) {
                 if (DEBUG) {
-                    Slog.w("PackageMonitorCallbackHelper",
-                            "Skip invoke PackageMonitorCallback for " + intent.getAction()
-                                    + ", uid " + registerUid);
+                    Slog.w(TAG, "Skip invoke PackageMonitorCallback for " + intent.getAction()
+                            + ", uid " + registerUid);
                 }
                 return;
             }
-            invokeCallback(callback, intent);
+            Intent newIntent = intent;
+            if (filterExtrasFunction != null) {
+                final Bundle extras = intent.getExtras();
+                if (extras != null) {
+                    final Bundle filteredExtras = filterExtrasFunction.apply(registerUid, extras);
+                    if (filteredExtras == null) {
+                        // caller is unable to access this intent
+                        if (DEBUG) {
+                            Slog.w(TAG,
+                                    "Skip invoke PackageMonitorCallback for " + intent.getAction()
+                                            + " because null filteredExtras");
+                        }
+                        return;
+                    }
+                    newIntent = new Intent(newIntent);
+                    newIntent.replaceExtras(filteredExtras);
+                }
+            }
+            invokeCallback(callback, newIntent);
         }));
     }
 
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index 02ce159..45fc49a 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -825,7 +825,7 @@
         return changed;
     }
 
-    boolean isInstalledOrHasDataOnAnyOtherUser(int[] allUsers, int currentUser) {
+    boolean isInstalledOnAnyOtherUser(int[] allUsers, int currentUser) {
         for (int user: allUsers) {
             if (user == currentUser) {
                 continue;
@@ -834,6 +834,16 @@
             if (userState.isInstalled()) {
                 return true;
             }
+        }
+        return false;
+    }
+
+    boolean hasDataOnAnyOtherUser(int[] allUsers, int currentUser) {
+        for (int user: allUsers) {
+            if (user == currentUser) {
+                continue;
+            }
+            final PackageUserStateInternal userState = readUserState(user);
             if (userState.dataExists()) {
                 return true;
             }
diff --git a/services/core/java/com/android/server/pm/RemovePackageHelper.java b/services/core/java/com/android/server/pm/RemovePackageHelper.java
index 2854453..7bd6a43 100644
--- a/services/core/java/com/android/server/pm/RemovePackageHelper.java
+++ b/services/core/java/com/android/server/pm/RemovePackageHelper.java
@@ -355,16 +355,22 @@
     // Called to clean up disabled system packages
     public void removePackageData(final PackageSetting deletedPs, @NonNull int[] allUserHandles) {
         synchronized (mPm.mInstallLock) {
-            removePackageDataLIF(deletedPs, allUserHandles, new PackageRemovedInfo(),
-                    /* flags= */ 0, /* writeSettings= */ false);
+            removePackageDataLIF(deletedPs, UserHandle.USER_ALL, allUserHandles,
+                    new PackageRemovedInfo(), /* flags= */ 0, /* writeSettings= */ false);
         }
     }
 
-    /*
+    /**
      * This method deletes the package from internal data structures such as mPackages / mSettings.
+     *
+     * @param targetUserId indicates the target user of the deletion. It equals to
+     *                     {@link UserHandle.USER_ALL} if the deletion was initiated for all users,
+     *                     otherwise it equals to the specific user id that the deletion was meant
+     *                     for.
      */
     @GuardedBy("mPm.mInstallLock")
-    public void removePackageDataLIF(final PackageSetting deletedPs, @NonNull int[] allUserHandles,
+    public void removePackageDataLIF(final PackageSetting deletedPs, int targetUserId,
+            @NonNull int[] allUserHandles,
             @NonNull PackageRemovedInfo outInfo, int flags, boolean writeSettings) {
         String packageName = deletedPs.getPackageName();
         if (DEBUG_REMOVE) Slog.d(TAG, "removePackageDataLI: " + deletedPs);
@@ -372,7 +378,7 @@
         final AndroidPackage deletedPkg = deletedPs.getPkg();
 
         // Delete all the data and states related to this package.
-        clearPackageStateForUserLIF(deletedPs, UserHandle.USER_ALL, flags);
+        clearPackageStateForUserLIF(deletedPs, targetUserId, flags);
 
         // Delete from mPackages
         removePackageLI(packageName, (flags & PackageManager.DELETE_CHATTY) != 0);
@@ -384,7 +390,7 @@
             deletedPs.setPkg(null);
         }
 
-        if ((flags & PackageManager.DELETE_KEEP_DATA) == 0) {
+        if (shouldDeletePackageSetting(deletedPs, targetUserId, allUserHandles, flags)) {
             // Delete from mSettings
             final SparseBooleanArray changedUsers = new SparseBooleanArray();
             synchronized (mPm.mLock) {
@@ -457,11 +463,32 @@
         }
     }
 
+    private static boolean shouldDeletePackageSetting(PackageSetting deletedPs, int userId,
+                                                      int[] allUserHandles, int flags) {
+        if ((flags & PackageManager.DELETE_KEEP_DATA) != 0) {
+            return false;
+        }
+        if (userId == UserHandle.USER_ALL) {
+            // Deleting for ALL. Let's wipe the PackageSetting.
+            return true;
+        }
+        if (deletedPs.hasDataOnAnyOtherUser(allUserHandles, userId)) {
+            // We arrived here because we are uninstalling the package for a specified user, and the
+            // package isn't installed on any other user. Before we proceed to completely delete the
+            // PackageSetting from mSettings, let's first check if data exists on any other user.
+            // If so, do not wipe the PackageSetting.
+            return false;
+        }
+        return true;
+    }
+
     void cleanUpResources(@Nullable String packageName, @Nullable File codeFile,
                           @Nullable String[] instructionSets) {
         synchronized (mPm.mInstallLock) {
             cleanUpResourcesLI(codeFile, instructionSets);
         }
+        // TODO: open logging to help debug, will delete or add debug flag
+        Slog.d(TAG, "cleanUpResources for " + codeFile);
         if (packageName == null) {
             return;
         }
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index edae273..b286b12 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -64,6 +64,7 @@
 import android.os.Message;
 import android.os.PatternMatcher;
 import android.os.PersistableBundle;
+import android.os.Process;
 import android.os.SELinux;
 import android.os.SystemClock;
 import android.os.Trace;
@@ -3189,6 +3190,9 @@
             pkg.isScannedAsStoppedSystemApp());
         if (!pkg.hasSharedUser()) {
             serializer.attributeInt(null, "userId", pkg.getAppId());
+
+            serializer.attributeBoolean(null, "isSdkLibrary",
+                    pkg.getAndroidPackage() != null && pkg.getAndroidPackage().isSdkLibrary());
         } else {
             serializer.attributeInt(null, "sharedUserId", pkg.getAppId());
         }
@@ -4039,10 +4043,12 @@
         int targetSdkVersion = 0;
         byte[] restrictUpdateHash = null;
         boolean isScannedAsStoppedSystemApp = false;
+        boolean isSdkLibrary = false;
         try {
             name = parser.getAttributeValue(null, ATTR_NAME);
             realName = parser.getAttributeValue(null, "realName");
             appId = parseAppId(parser);
+            isSdkLibrary = parser.getAttributeBoolean(null, "isSdkLibrary", false);
             sharedUserAppId = parseSharedUserAppId(parser);
             codePathStr = parser.getAttributeValue(null, "codePath");
 
@@ -4157,7 +4163,8 @@
                 PackageManagerService.reportSettingsProblem(Log.WARN,
                         "Error in package manager settings: <package> has no codePath at "
                                 + parser.getPositionDescription());
-            } else if (appId > 0) {
+            } else if (appId > 0 || (appId == Process.INVALID_UID && isSdkLibrary
+                    && Flags.disallowSdkLibsToBeApps())) {
                 packageSetting = addPackageLPw(name.intern(), realName, new File(codePathStr),
                         appId, pkgFlags, pkgPrivateFlags, domainSetId);
                 if (PackageManagerService.DEBUG_SETTINGS)
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 3adeb4b..446c629 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -77,6 +77,7 @@
 import android.os.Environment;
 import android.os.FileUtils;
 import android.os.Handler;
+import android.os.HandlerThread;
 import android.os.IBinder;
 import android.os.LocaleList;
 import android.os.Looper;
@@ -113,7 +114,6 @@
 import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
 import com.android.internal.infra.AndroidFuture;
 import com.android.internal.logging.MetricsLogger;
-import com.android.internal.os.BackgroundThread;
 import com.android.internal.util.CollectionUtils;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.Preconditions;
@@ -485,7 +485,14 @@
     }
 
     public ShortcutService(Context context) {
-        this(context, BackgroundThread.get().getLooper(), /*onyForPackgeManagerApis*/ false);
+        this(context, getBgLooper(), /*onyForPackgeManagerApis*/ false);
+    }
+
+    private static Looper getBgLooper() {
+        final HandlerThread handlerThread = new HandlerThread("shortcut",
+                android.os.Process.THREAD_PRIORITY_BACKGROUND);
+        handlerThread.start();
+        return handlerThread.getLooper();
     }
 
     @VisibleForTesting
diff --git a/services/core/java/com/android/server/pm/StorageEventHelper.java b/services/core/java/com/android/server/pm/StorageEventHelper.java
index 7d87d1b..cef3244 100644
--- a/services/core/java/com/android/server/pm/StorageEventHelper.java
+++ b/services/core/java/com/android/server/pm/StorageEventHelper.java
@@ -193,7 +193,7 @@
             }
 
             try {
-                sm.prepareUserStorage(volumeUuid, user.id, user.serialNumber, flags);
+                sm.prepareUserStorage(volumeUuid, user.id, flags);
                 synchronized (mPm.mInstallLock) {
                     appDataHelper.reconcileAppsDataLI(volumeUuid, user.id, flags,
                             true /* migrateAppData */);
diff --git a/services/core/java/com/android/server/pm/UserDataPreparer.java b/services/core/java/com/android/server/pm/UserDataPreparer.java
index 8adb566..4c42c2d 100644
--- a/services/core/java/com/android/server/pm/UserDataPreparer.java
+++ b/services/core/java/com/android/server/pm/UserDataPreparer.java
@@ -92,7 +92,7 @@
                 volumeUuid, userId, flags, isNewUser);
         try {
             // Prepare CE and/or DE storage.
-            storage.prepareUserStorage(volumeUuid, userId, userSerial, flags);
+            storage.prepareUserStorage(volumeUuid, userId, flags);
 
             // Ensure that the data directories of a removed user with the same ID are not being
             // reused.  New users must get fresh data directories, to avoid leaking data.
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 2305d6c..c1b7489 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -2285,6 +2285,11 @@
             throw new SecurityException("You need MANAGE_USERS permission to query if u=" + userId
                     + " is a demo user");
         }
+
+        if (SystemProperties.getBoolean("ro.boot.arc_demo_mode", false)) {
+            return true;
+        }
+
         synchronized (mUsersLock) {
             UserInfo userInfo = getUserInfoLU(userId);
             return userInfo != null && userInfo.isDemo();
@@ -2332,8 +2337,18 @@
         final long identity = Binder.clearCallingIdentity();
         try {
             final TelecomManager telecomManager = mContext.getSystemService(TelecomManager.class);
-            if (telecomManager != null && telecomManager.isInCall()) {
-                flags |= UserManager.SWITCHABILITY_STATUS_USER_IN_CALL;
+            if (com.android.internal.telephony.flags
+                    .Flags.enforceTelephonyFeatureMappingForPublicApis()) {
+                if (mContext.getPackageManager().hasSystemFeature(
+                        PackageManager.FEATURE_TELECOM)) {
+                    if (telecomManager != null && telecomManager.isInCall()) {
+                        flags |= UserManager.SWITCHABILITY_STATUS_USER_IN_CALL;
+                    }
+                }
+            } else {
+                if (telecomManager != null && telecomManager.isInCall()) {
+                    flags |= UserManager.SWITCHABILITY_STATUS_USER_IN_CALL;
+                }
             }
         } finally {
             Binder.restoreCallingIdentity(identity);
@@ -5131,7 +5146,7 @@
 
             t.traceBegin("createUserStorageKeys");
             final StorageManager storage = mContext.getSystemService(StorageManager.class);
-            storage.createUserStorageKeys(userId, userInfo.serialNumber, userInfo.isEphemeral());
+            storage.createUserStorageKeys(userId, userInfo.isEphemeral());
             t.traceEnd();
 
             // Only prepare DE storage here.  CE storage will be prepared later, when the user is
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index d3931a3..5d710d2 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -26,6 +26,7 @@
 import static android.app.AppOpsManager.MODE_ALLOWED;
 import static android.app.AppOpsManager.MODE_ERRORED;
 import static android.app.AppOpsManager.MODE_IGNORED;
+import static android.app.AppOpsManager.OP_BLUETOOTH_CONNECT;
 import static android.content.pm.ApplicationInfo.AUTO_REVOKE_DISALLOWED;
 import static android.content.pm.ApplicationInfo.AUTO_REVOKE_DISCOURAGED;
 
@@ -1402,6 +1403,12 @@
 
                 switch (opMode) {
                     case AppOpsManager.MODE_ERRORED: {
+                        if (permission.equals(Manifest.permission.BLUETOOTH_CONNECT)) {
+                            Slog.e(LOG_TAG, "BLUETOOTH_CONNECT permission hard denied as op"
+                                    + " mode is MODE_ERRORED. Permission check was requested for: "
+                                    + attributionSource + " and op transaction was invoked for "
+                                    + current);
+                        }
                         return PermissionChecker.PERMISSION_HARD_DENIED;
                     }
                     case AppOpsManager.MODE_IGNORED: {
@@ -1722,7 +1729,22 @@
                         throw new SecurityException(msg + ":" + e.getMessage());
                     }
                 }
-                return Math.max(checkedOpResult, notedOpResult);
+                int result = Math.max(checkedOpResult, notedOpResult);
+                // TODO(b/302609140): Remove extra logging after this issue is diagnosed.
+                if (op == OP_BLUETOOTH_CONNECT && result == MODE_ERRORED) {
+                    if (result == checkedOpResult) {
+                        Slog.e(LOG_TAG, "BLUETOOTH_CONNECT permission hard denied as"
+                                + " checkOp for resolvedAttributionSource "
+                                + resolvedAttributionSource + " and op " + op
+                                + " returned MODE_ERRORED");
+                    } else {
+                        Slog.e(LOG_TAG, "BLUETOOTH_CONNECT permission hard denied as"
+                                + " noteOp for resolvedAttributionSource "
+                                + resolvedAttributionSource + " and op " + notedOp
+                                + " returned MODE_ERRORED");
+                    }
+                }
+                return result;
             }
         }
 
diff --git a/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyV1.java b/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyV1.java
index 752eb53..17c901e 100644
--- a/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyV1.java
+++ b/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyV1.java
@@ -254,6 +254,14 @@
             String packageName = verifications.valueAt(index).second;
             AndroidPackage pkg = mConnection.getPackage(packageName);
 
+            if (pkg == null) {
+                if (DEBUG_BROADCASTS) {
+                    Slog.d(TAG,
+                            "Skip sendBroadcasts because null AndroidPackage for " + packageName);
+                }
+                continue;
+            }
+
             String hostsString = buildHostsString(pkg);
 
             Intent intent = new Intent(Intent.ACTION_INTENT_FILTER_NEEDS_VERIFICATION)
diff --git a/services/core/java/com/android/server/policy/Android.bp b/services/core/java/com/android/server/policy/Android.bp
new file mode 100644
index 0000000..fa55bf0
--- /dev/null
+++ b/services/core/java/com/android/server/policy/Android.bp
@@ -0,0 +1,10 @@
+aconfig_declarations {
+    name: "policy_flags",
+    package: "com.android.server.policy",
+    srcs: ["*.aconfig"],
+}
+
+java_aconfig_library {
+    name: "policy_flags_lib",
+    aconfig_declarations: "policy_flags",
+}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 3000a1c..bf669fb 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -473,6 +473,8 @@
 
     private TalkbackShortcutController mTalkbackShortcutController;
 
+    private WindowWakeUpPolicy mWindowWakeUpPolicy;
+
     boolean mSafeMode;
 
     // Whether to allow dock apps with METADATA_DOCK_HOME to temporarily take over the Home key.
@@ -640,15 +642,6 @@
     // Whether to lock the device after the next dreaming transition has finished.
     private boolean mLockAfterDreamingTransitionFinished;
 
-    // Allowed theater mode wake actions
-    private boolean mAllowTheaterModeWakeFromKey;
-    private boolean mAllowTheaterModeWakeFromPowerKey;
-    private boolean mAllowTheaterModeWakeFromMotion;
-    private boolean mAllowTheaterModeWakeFromMotionWhenNotDreaming;
-    private boolean mAllowTheaterModeWakeFromCameraLens;
-    private boolean mAllowTheaterModeWakeFromLidSwitch;
-    private boolean mAllowTheaterModeWakeFromWakeGesture;
-
     // If true, the power button long press behavior will be invoked even if the default display is
     // non-interactive. If false, the power button long press behavior will be skipped if the
     // default display is non-interactive.
@@ -930,8 +923,7 @@
                 if (shouldEnableWakeGestureLp()) {
                     performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY, false,
                             "Wake Up");
-                    wakeUp(SystemClock.uptimeMillis(), mAllowTheaterModeWakeFromWakeGesture,
-                            PowerManager.WAKE_REASON_GESTURE, "android.policy:GESTURE");
+                    mWindowWakeUpPolicy.wakeUpFromWakeGesture();
                 }
             }
         }
@@ -1067,7 +1059,7 @@
                 || handledByPowerManager || mKeyCombinationManager.isPowerKeyIntercepted();
         if (!mPowerKeyHandled) {
             if (!interactive) {
-                wakeUpFromPowerKey(event.getDownTime());
+                wakeUpFromWakeKey(event);
             }
         } else {
             // handled by another power key policy.
@@ -1309,7 +1301,7 @@
                     Settings.Global.putInt(mContext.getContentResolver(),
                             Settings.Global.THEATER_MODE_ON, 0);
                     if (!interactive) {
-                        wakeUpFromPowerKey(eventTime);
+                        wakeUpFromWakeKey(eventTime, KEYCODE_POWER, /* isDown= */ false);
                     }
                 } else {
                     Slog.i(TAG, "Toggling theater mode on.");
@@ -1325,7 +1317,7 @@
             case MULTI_PRESS_POWER_BRIGHTNESS_BOOST:
                 Slog.i(TAG, "Starting brightness boost.");
                 if (!interactive) {
-                    wakeUpFromPowerKey(eventTime);
+                    wakeUpFromWakeKey(eventTime, KEYCODE_POWER, /* isDown= */ false);
                 }
                 mPowerManager.boostScreenBrightness(eventTime);
                 break;
@@ -2312,22 +2304,6 @@
         mLidNavigationAccessibility = mContext.getResources().getInteger(
                 com.android.internal.R.integer.config_lidNavigationAccessibility);
 
-        mAllowTheaterModeWakeFromKey = mContext.getResources().getBoolean(
-                com.android.internal.R.bool.config_allowTheaterModeWakeFromKey);
-        mAllowTheaterModeWakeFromPowerKey = mAllowTheaterModeWakeFromKey
-                || mContext.getResources().getBoolean(
-                    com.android.internal.R.bool.config_allowTheaterModeWakeFromPowerKey);
-        mAllowTheaterModeWakeFromMotion = mContext.getResources().getBoolean(
-                com.android.internal.R.bool.config_allowTheaterModeWakeFromMotion);
-        mAllowTheaterModeWakeFromMotionWhenNotDreaming = mContext.getResources().getBoolean(
-                com.android.internal.R.bool.config_allowTheaterModeWakeFromMotionWhenNotDreaming);
-        mAllowTheaterModeWakeFromCameraLens = mContext.getResources().getBoolean(
-                com.android.internal.R.bool.config_allowTheaterModeWakeFromCameraLens);
-        mAllowTheaterModeWakeFromLidSwitch = mContext.getResources().getBoolean(
-                com.android.internal.R.bool.config_allowTheaterModeWakeFromLidSwitch);
-        mAllowTheaterModeWakeFromWakeGesture = mContext.getResources().getBoolean(
-                com.android.internal.R.bool.config_allowTheaterModeWakeFromGesture);
-
         mGoToSleepOnButtonPressTheaterMode = mContext.getResources().getBoolean(
                 com.android.internal.R.bool.config_goToSleepOnButtonPressTheaterMode);
 
@@ -2457,6 +2433,7 @@
                 com.android.internal.R.integer.config_keyguardDrawnTimeout);
         mKeyguardDelegate = injector.getKeyguardServiceDelegate();
         mTalkbackShortcutController = injector.getTalkbackShortcutController();
+        mWindowWakeUpPolicy = new WindowWakeUpPolicy(mContext);
         initKeyCombinationRules();
         initSingleKeyGestureRules(injector.getLooper());
         mButtonOverridePermissionChecker = injector.getButtonOverridePermissionChecker();
@@ -4234,6 +4211,14 @@
         return redoLayout;
     }
 
+    /**
+     * Shows the keyguard without immediately locking the device.
+     */
+    @Override
+    public void showDismissibleKeyguard() {
+        mKeyguardDelegate.showDismissibleKeyguard();
+    }
+
     // There are several different flavors of "assistant" that can be launched from
     // various parts of the UI.
 
@@ -4483,8 +4468,7 @@
         updateRotation(true);
 
         if (lidOpen) {
-            wakeUp(SystemClock.uptimeMillis(), mAllowTheaterModeWakeFromLidSwitch,
-                    PowerManager.WAKE_REASON_LID, "android.policy:LID");
+            mWindowWakeUpPolicy.wakeUpFromLid();
         } else if (getLidBehavior() != LID_BEHAVIOR_SLEEP) {
             mPowerManager.userActivity(SystemClock.uptimeMillis(), false);
         }
@@ -4510,8 +4494,7 @@
             } else {
                 intent = new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA);
             }
-            wakeUp(whenNanos / 1000000, mAllowTheaterModeWakeFromCameraLens,
-                    PowerManager.WAKE_REASON_CAMERA_LAUNCH, "android.policy:CAMERA_COVER");
+            mWindowWakeUpPolicy.wakeUpFromCameraCover(whenNanos / 1000000);
             startActivityAsUser(intent, UserHandle.CURRENT_OR_SELF);
         }
         mCameraLensCoverState = lensCoverState;
@@ -4589,7 +4572,7 @@
             boolean shouldTurnOnTv = false;
             if (down && (keyCode == KeyEvent.KEYCODE_POWER
                     || keyCode == KeyEvent.KEYCODE_TV_POWER)) {
-                wakeUpFromPowerKey(event.getDownTime());
+                wakeUpFromWakeKey(event);
                 shouldTurnOnTv = true;
             } else if (down && (isWakeKey || keyCode == KeyEvent.KEYCODE_WAKEUP)
                     && isWakeKeyWhenScreenOff(keyCode)) {
@@ -5104,9 +5087,7 @@
         if (mRequestedOrSleepingDefaultDisplay) {
             mCameraGestureTriggeredDuringGoingToSleep = true;
             // Wake device up early to prevent display doing redundant turning off/on stuff.
-            wakeUp(SystemClock.uptimeMillis(), mAllowTheaterModeWakeFromPowerKey,
-                    PowerManager.WAKE_REASON_CAMERA_LAUNCH,
-                    "android.policy:CAMERA_GESTURE_PREVENT_LOCK");
+            mWindowWakeUpPolicy.wakeUpFromPowerKeyCameraGesture();
         }
         return true;
     }
@@ -5201,11 +5182,11 @@
     // TODO(b/117479243): handle it in InputPolicy
     /** {@inheritDoc} */
     @Override
-    public int interceptMotionBeforeQueueingNonInteractive(int displayId, long whenNanos,
-            int policyFlags) {
+    public int interceptMotionBeforeQueueingNonInteractive(int displayId, int source, int action,
+            long whenNanos, int policyFlags) {
         if ((policyFlags & FLAG_WAKE) != 0) {
-            if (wakeUp(whenNanos / 1000000, mAllowTheaterModeWakeFromMotion,
-                    PowerManager.WAKE_REASON_WAKE_MOTION, "android.policy:MOTION")) {
+            if (mWindowWakeUpPolicy.wakeUpFromMotion(
+                        whenNanos / 1000000, source, action == MotionEvent.ACTION_DOWN)) {
                 // Woke up. Pass motion events to user.
                 return ACTION_PASS_TO_USER;
             }
@@ -5219,8 +5200,8 @@
         // there will be no dream to intercept the touch and wake into ambient.  The device should
         // wake up in this case.
         if (isTheaterModeEnabled() && (policyFlags & FLAG_WAKE) != 0) {
-            if (wakeUp(whenNanos / 1000000, mAllowTheaterModeWakeFromMotionWhenNotDreaming,
-                    PowerManager.WAKE_REASON_WAKE_MOTION, "android.policy:MOTION")) {
+            if (mWindowWakeUpPolicy.wakeUpFromMotion(
+                        whenNanos / 1000000, source, action == MotionEvent.ACTION_DOWN)) {
                 // Woke up. Pass motion events to user.
                 return ACTION_PASS_TO_USER;
             }
@@ -5554,37 +5535,25 @@
         return sleepDurationRealtime > mWakeUpToLastStateTimeout;
     }
 
-    private void wakeUpFromPowerKey(long eventTime) {
-        if (wakeUp(eventTime, mAllowTheaterModeWakeFromPowerKey,
-                PowerManager.WAKE_REASON_POWER_BUTTON, "android.policy:POWER")) {
-            // Start HOME with "reason" extra if sleeping for more than mWakeUpToLastStateTimeout
-            if (shouldWakeUpWithHomeIntent()) {
-                startDockOrHome(DEFAULT_DISPLAY, /*fromHomeKey*/ false, /*wakenFromDreams*/ true,
-                        PowerManager.wakeReasonToString(PowerManager.WAKE_REASON_POWER_BUTTON));
-            }
-        }
-    }
-
     private void wakeUpFromWakeKey(KeyEvent event) {
-        if (wakeUp(event.getEventTime(), mAllowTheaterModeWakeFromKey,
-                PowerManager.WAKE_REASON_WAKE_KEY, "android.policy:KEY")) {
-            // Start HOME with "reason" extra if sleeping for more than mWakeUpToLastStateTimeout
-            if (shouldWakeUpWithHomeIntent() && event.getKeyCode() == KEYCODE_HOME) {
-                startDockOrHome(DEFAULT_DISPLAY, /*fromHomeKey*/ true, /*wakenFromDreams*/ true,
-                        PowerManager.wakeReasonToString(PowerManager.WAKE_REASON_WAKE_KEY));
-            }
-        }
+        wakeUpFromWakeKey(
+                event.getEventTime(),
+                event.getKeyCode(),
+                event.getAction() == KeyEvent.ACTION_DOWN);
     }
 
-    private boolean wakeUp(long wakeTime, boolean wakeInTheaterMode, @WakeReason int reason,
-            String details) {
-        final boolean theaterModeEnabled = isTheaterModeEnabled();
-        if (!wakeInTheaterMode && theaterModeEnabled) {
-            return false;
+    private void wakeUpFromWakeKey(long eventTime, int keyCode, boolean isDown) {
+        if (mWindowWakeUpPolicy.wakeUpFromKey(eventTime, keyCode, isDown)) {
+            final boolean keyCanLaunchHome = keyCode == KEYCODE_HOME || keyCode == KEYCODE_POWER;
+            // Start HOME with "reason" extra if sleeping for more than mWakeUpToLastStateTimeout
+            if (shouldWakeUpWithHomeIntent() &&  keyCanLaunchHome) {
+                startDockOrHome(
+                        DEFAULT_DISPLAY,
+                        /*fromHomeKey*/ keyCode == KEYCODE_HOME,
+                        /*wakenFromDreams*/ true,
+                        "Wake from " + KeyEvent. keyCodeToString(keyCode));
+            }
         }
-
-        mPowerManager.wakeUp(wakeTime, reason, details);
-        return true;
     }
 
     private void finishKeyguardDrawn() {
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index 03a7bd3..2174fd6 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -178,6 +178,12 @@
     int applyKeyguardOcclusionChange();
 
     /**
+     * Shows the keyguard immediately if not already shown.
+     * Does NOT immediately request the device to lock.
+     */
+    void showDismissibleKeyguard();
+
+    /**
      * Interface to the Window Manager state associated with a particular
      * window. You can hold on to an instance of this interface from the call
      * to prepareAddWindow() until removeWindow().
@@ -706,12 +712,14 @@
      * Generally, it's best to keep as little as possible in the queue thread
      * because it's the most fragile.
      * @param displayId The display ID of the motion event.
+     * @param source the {@link InputDevice} source that caused the motion.
+     * @param action the {@link MotionEvent} action for the motion.
      * @param policyFlags The policy flags associated with the motion.
      *
      * @return Actions flags: may be {@link #ACTION_PASS_TO_USER}.
      */
-    int interceptMotionBeforeQueueingNonInteractive(int displayId, long whenNanos,
-            int policyFlags);
+    int interceptMotionBeforeQueueingNonInteractive(int displayId, int source, int action,
+            long whenNanos, int policyFlags);
 
     /**
      * Called from the input dispatcher thread before a key is dispatched to a window.
diff --git a/services/core/java/com/android/server/policy/WindowWakeUpPolicy.java b/services/core/java/com/android/server/policy/WindowWakeUpPolicy.java
new file mode 100644
index 0000000..a790950
--- /dev/null
+++ b/services/core/java/com/android/server/policy/WindowWakeUpPolicy.java
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.policy;
+
+import static android.os.PowerManager.WAKE_REASON_CAMERA_LAUNCH;
+import static android.os.PowerManager.WAKE_REASON_GESTURE;
+import static android.os.PowerManager.WAKE_REASON_LID;
+import static android.os.PowerManager.WAKE_REASON_POWER_BUTTON;
+import static android.os.PowerManager.WAKE_REASON_WAKE_KEY;
+import static android.os.PowerManager.WAKE_REASON_WAKE_MOTION;
+import static android.view.KeyEvent.KEYCODE_POWER;
+
+import static com.android.server.policy.Flags.supportInputWakeupDelegate;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.res.Resources;
+import android.os.PowerManager;
+import android.os.PowerManager.WakeReason;
+import android.os.SystemClock;
+import android.provider.Settings;
+import android.util.Slog;
+import android.view.KeyEvent;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.Clock;
+import com.android.server.LocalServices;
+
+/** Policy controlling the decision and execution of window-related wake ups. */
+class WindowWakeUpPolicy {
+    private static final String TAG = "WindowWakeUpPolicy";
+
+    private static final boolean DEBUG = false;
+
+    private final Context mContext;
+    private final PowerManager mPowerManager;
+    private final Clock mClock;
+
+    private final boolean mAllowTheaterModeWakeFromKey;
+    private final boolean mAllowTheaterModeWakeFromPowerKey;
+    private final boolean mAllowTheaterModeWakeFromMotion;
+    private final boolean mAllowTheaterModeWakeFromCameraLens;
+    private final boolean mAllowTheaterModeWakeFromLidSwitch;
+    private final boolean mAllowTheaterModeWakeFromWakeGesture;
+
+    // The policy will handle input-based wake ups if this delegate is null.
+    @Nullable private WindowWakeUpPolicyInternal.InputWakeUpDelegate mInputWakeUpDelegate;
+
+    WindowWakeUpPolicy(Context context) {
+        this(context, Clock.SYSTEM_CLOCK);
+    }
+
+    @VisibleForTesting
+    WindowWakeUpPolicy(Context context, Clock clock) {
+        mContext = context;
+        mPowerManager = context.getSystemService(PowerManager.class);
+        mClock = clock;
+
+        final Resources res = context.getResources();
+        mAllowTheaterModeWakeFromKey = res.getBoolean(
+                com.android.internal.R.bool.config_allowTheaterModeWakeFromKey);
+        mAllowTheaterModeWakeFromPowerKey = mAllowTheaterModeWakeFromKey
+                || res.getBoolean(
+                    com.android.internal.R.bool.config_allowTheaterModeWakeFromPowerKey);
+        mAllowTheaterModeWakeFromMotion = res.getBoolean(
+                com.android.internal.R.bool.config_allowTheaterModeWakeFromMotion);
+        mAllowTheaterModeWakeFromCameraLens = res.getBoolean(
+                com.android.internal.R.bool.config_allowTheaterModeWakeFromCameraLens);
+        mAllowTheaterModeWakeFromLidSwitch = res.getBoolean(
+                com.android.internal.R.bool.config_allowTheaterModeWakeFromLidSwitch);
+        mAllowTheaterModeWakeFromWakeGesture = res.getBoolean(
+                com.android.internal.R.bool.config_allowTheaterModeWakeFromGesture);
+        if (supportInputWakeupDelegate()) {
+            LocalServices.addService(WindowWakeUpPolicyInternal.class, new LocalService());
+        }
+    }
+
+    private final class LocalService implements WindowWakeUpPolicyInternal {
+        @Override
+        public void setInputWakeUpDelegate(@Nullable InputWakeUpDelegate delegate) {
+            if (!supportInputWakeupDelegate()) {
+                Slog.w(TAG, "Input wake up delegates not supported.");
+                return;
+            }
+            mInputWakeUpDelegate = delegate;
+        }
+    }
+
+    /**
+     * Wakes up from a key event.
+     *
+     * @param eventTime the timestamp of the event in {@link SystemClock#uptimeMillis()}.
+     * @param keyCode the {@link android.view.KeyEvent} key code of the key event.
+     * @param isDown {@code true} if the event's action is {@link KeyEvent#ACTION_DOWN}.
+     * @return {@code true} if the policy allows the requested wake up and the request has been
+     *      executed; {@code false} otherwise.
+     */
+    boolean wakeUpFromKey(long eventTime, int keyCode, boolean isDown) {
+        final boolean wakeAllowedDuringTheaterMode =
+                keyCode == KEYCODE_POWER
+                        ? mAllowTheaterModeWakeFromPowerKey
+                        : mAllowTheaterModeWakeFromKey;
+        if (!canWakeUp(wakeAllowedDuringTheaterMode)) {
+            if (DEBUG) Slog.d(TAG, "Unable to wake up from " + KeyEvent.keyCodeToString(keyCode));
+            return false;
+        }
+        if (mInputWakeUpDelegate != null
+                && mInputWakeUpDelegate.wakeUpFromKey(eventTime, keyCode, isDown)) {
+            return true;
+        }
+        wakeUp(
+                eventTime,
+                keyCode == KEYCODE_POWER ? WAKE_REASON_POWER_BUTTON : WAKE_REASON_WAKE_KEY,
+                keyCode == KEYCODE_POWER ? "POWER" : "KEY");
+        return true;
+    }
+
+    /**
+     * Wakes up from a motion event.
+     *
+     * @param eventTime the timestamp of the event in {@link SystemClock#uptimeMillis()}.
+     * @param isDown {@code true} if the event's action is {@link MotionEvent#ACTION_DOWN}.
+     * @return {@code true} if the policy allows the requested wake up and the request has been
+     *      executed; {@code false} otherwise.
+     */
+    boolean wakeUpFromMotion(long eventTime, int source, boolean isDown) {
+        if (!canWakeUp(mAllowTheaterModeWakeFromMotion)) {
+            if (DEBUG) Slog.d(TAG, "Unable to wake up from motion.");
+            return false;
+        }
+        if (mInputWakeUpDelegate != null
+                && mInputWakeUpDelegate.wakeUpFromMotion(eventTime, source, isDown)) {
+            return true;
+        }
+        wakeUp(eventTime, WAKE_REASON_WAKE_MOTION, "MOTION");
+        return true;
+    }
+
+    /**
+     * Wakes up due to an opened camera cover.
+     *
+     * @param eventTime the timestamp of the event in {@link SystemClock#uptimeMillis()}.
+     * @return {@code true} if the policy allows the requested wake up and the request has been
+     *      executed; {@code false} otherwise.
+     */
+    boolean wakeUpFromCameraCover(long eventTime) {
+        if (!canWakeUp(mAllowTheaterModeWakeFromCameraLens)) {
+            if (DEBUG) Slog.d(TAG, "Unable to wake up from camera cover.");
+            return false;
+        }
+        wakeUp(eventTime, WAKE_REASON_CAMERA_LAUNCH, "CAMERA_COVER");
+        return true;
+    }
+
+    /**
+     * Wakes up due to an opened lid.
+     *
+     * @return {@code true} if the policy allows the requested wake up and the request has been
+     *      executed; {@code false} otherwise.
+     */
+    boolean wakeUpFromLid() {
+        if (!canWakeUp(mAllowTheaterModeWakeFromLidSwitch)) {
+            if (DEBUG) Slog.d(TAG, "Unable to wake up from lid.");
+            return false;
+        }
+        wakeUp(mClock.uptimeMillis(), WAKE_REASON_LID, "LID");
+        return true;
+    }
+
+    /**
+     * Wakes up to prevent sleeping when opening camera through power button.
+     *
+     * @return {@code true} if the policy allows the requested wake up and the request has been
+     *      executed; {@code false} otherwise.
+     */
+    boolean wakeUpFromPowerKeyCameraGesture() {
+        if (!canWakeUp(mAllowTheaterModeWakeFromPowerKey)) {
+            if (DEBUG) Slog.d(TAG, "Unable to wake up from power key camera gesture.");
+            return false;
+        }
+        wakeUp(mClock.uptimeMillis(), WAKE_REASON_CAMERA_LAUNCH, "CAMERA_GESTURE_PREVENT_LOCK");
+        return true;
+    }
+
+    /**
+     * Wake up from a wake gesture.
+     *
+     * @return {@code true} if the policy allows the requested wake up and the request has been
+     *      executed; {@code false} otherwise.
+     */
+    boolean wakeUpFromWakeGesture() {
+        if (!canWakeUp(mAllowTheaterModeWakeFromWakeGesture)) {
+            if (DEBUG) Slog.d(TAG, "Unable to wake up from gesture.");
+            return false;
+        }
+        wakeUp(mClock.uptimeMillis(), WAKE_REASON_GESTURE, "GESTURE");
+        return true;
+    }
+
+    private boolean canWakeUp(boolean wakeInTheaterMode) {
+        final boolean isTheaterModeEnabled =
+                Settings.Global.getInt(
+                        mContext.getContentResolver(), Settings.Global.THEATER_MODE_ON, 0) == 1;
+        return wakeInTheaterMode || !isTheaterModeEnabled;
+    }
+
+    /** Wakes up {@link PowerManager}. */
+    private void wakeUp(long wakeTime, @WakeReason int reason, String details) {
+        mPowerManager.wakeUp(wakeTime, reason, "android.policy:" + details);
+    }
+}
diff --git a/services/core/java/com/android/server/policy/WindowWakeUpPolicyInternal.java b/services/core/java/com/android/server/policy/WindowWakeUpPolicyInternal.java
new file mode 100644
index 0000000..66a0035
--- /dev/null
+++ b/services/core/java/com/android/server/policy/WindowWakeUpPolicyInternal.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.policy;
+
+import android.annotation.Nullable;
+import android.os.SystemClock;
+
+import com.android.internal.annotations.Keep;
+import com.android.server.LocalServices;
+
+/** Policy controlling the decision and execution of window-related wake ups. */
+@Keep
+public interface WindowWakeUpPolicyInternal {
+
+    /**
+     * A delegate that can choose to intercept Input-related wake ups.
+     *
+     * <p>This delegate is not meant to control policy decisions on whether or not to wake up. The
+     * policy makes that decision, and forwards the wake up request to the delegate as necessary.
+     * Therefore, the role of the delegate is to handle the actual "waking" of the device in
+     * response to the respective input event.
+     */
+    @Keep
+    interface InputWakeUpDelegate {
+        /**
+         * Wakes up the device in response to a key event.
+         *
+         * @param eventTime the timestamp of the event in {@link SystemClock#uptimeMillis()}.
+         * @param keyCode the {@link android.view.KeyEvent} key code of the key event.
+         * @param isDown {@code true} if the event's action is {@link KeyEvent#ACTION_DOWN}.
+         * @return {@code true} if the delegate handled the wake up. {@code false} if the delegate
+         *      decided not to handle the wake up. The policy will execute the wake up in this case.
+         */
+        boolean wakeUpFromKey(long eventTime, int keyCode, boolean isDown);
+        /**
+         * Wakes up the device in response to a motion event.
+         *
+         * @param eventTime the timestamp of the event in {@link SystemClock#uptimeMillis()}.
+         * @param source the {@link android.view.InputDevice} source that caused the event.
+         * @param isDown {@code true} if the event's action is {@link MotionEvent#ACTION_DOWN}.
+         * @return {@code true} if the delegate handled the wake up. {@code false} if the delegate
+         *      decided not to handle the wake up. The policy will execute the wake up in this case.
+         */
+        boolean wakeUpFromMotion(long eventTime, int source, boolean isDown);
+    }
+
+    /**
+     * Allows injecting a delegate for controlling input-based wake ups.
+     *
+     * <p>A delegate can be injected to the policy by system_server components only, and should be
+     * done via the {@link LocalServices} interface.
+     *
+     * <p>There can at most be one active delegate. If there's no delegate set (or if a {@code null}
+     * delegate is set), the policy will handle waking up the device in response to input events.
+     *
+     * @param delegate an implementation of {@link InputWakeUpDelegate} that handles input-based
+     *      wake up requests. {@code null} to let the policy handle these wake ups.
+     */
+    @Keep
+    void setInputWakeUpDelegate(@Nullable InputWakeUpDelegate delegate);
+}
diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
index 495e239..d0b70c3 100644
--- a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
+++ b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
@@ -410,6 +410,15 @@
         }
     }
 
+    /**
+     * Request to show the keyguard immediately without immediately locking the device.
+     */
+    public void showDismissibleKeyguard() {
+        if (mKeyguardService != null) {
+            mKeyguardService.showDismissibleKeyguard();
+        }
+    }
+
     public void setCurrentUser(int newUserId) {
         if (mKeyguardService != null) {
             mKeyguardService.setCurrentUser(newUserId);
diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java
index 774e261..cd789ea 100644
--- a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java
+++ b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java
@@ -209,6 +209,16 @@
         }
     }
 
+    // Binder interface
+    @Override
+    public void showDismissibleKeyguard() {
+        try {
+            mService.showDismissibleKeyguard();
+        } catch (RemoteException e) {
+            Slog.w(TAG , "Remote Exception", e);
+        }
+    }
+
     @Override // Binder interface
     public void setSwitchingUser(boolean switching) {
         try {
diff --git a/services/core/java/com/android/server/policy/window_policy_flags.aconfig b/services/core/java/com/android/server/policy/window_policy_flags.aconfig
new file mode 100644
index 0000000..ed981e0
--- /dev/null
+++ b/services/core/java/com/android/server/policy/window_policy_flags.aconfig
@@ -0,0 +1,8 @@
+package: "com.android.server.policy"
+
+flag {
+    name: "support_input_wakeup_delegate"
+    namespace: "wear_frameworks"
+    description: "Whether or not window policy allows injecting input wake-up delegate."
+    bug: "298055811"
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index 3ecc985..652cf18 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -411,6 +411,11 @@
         mWakeLockLog.onWakeLockReleased(tag, ownerUid);
     }
 
+    /** Shows the keyguard without requesting the device to immediately lock. */
+    public void showDismissibleKeyguard() {
+        mPolicy.showDismissibleKeyguard();
+    }
+
     private int getBatteryStatsWakeLockMonitorType(int flags) {
         switch (flags & PowerManager.WAKE_LOCK_LEVEL_MASK) {
             case PowerManager.PARTIAL_WAKE_LOCK:
@@ -958,7 +963,8 @@
             final boolean vibrate = Settings.Secure.getIntForUser(mContext.getContentResolver(),
                     Settings.Secure.CHARGING_VIBRATION_ENABLED, 1, userId) != 0;
             if (vibrate) {
-                mVibrator.vibrate(CHARGING_VIBRATION_EFFECT,
+                mVibrator.vibrate(Process.SYSTEM_UID, mContext.getOpPackageName(),
+                        CHARGING_VIBRATION_EFFECT, /* reason= */ "Charging started",
                         HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES);
             }
 
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index ec5172f..2128c991 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -115,6 +115,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.IBatteryStats;
 import com.android.internal.display.BrightnessSynchronizer;
+import com.android.internal.foldables.FoldGracePeriodProvider;
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.FrameworkStatsLog;
@@ -308,6 +309,7 @@
     private final Context mContext;
     private final ServiceThread mHandlerThread;
     private final Handler mHandler;
+    private final FoldGracePeriodProvider mFoldGracePeriodProvider;
     private final AmbientDisplayConfiguration mAmbientDisplayConfiguration;
     @Nullable
     private final BatterySaverStateMachine mBatterySaverStateMachine;
@@ -1007,6 +1009,10 @@
             return new InattentiveSleepWarningController();
         }
 
+        FoldGracePeriodProvider createFoldGracePeriodProvider() {
+            return new FoldGracePeriodProvider();
+        }
+
         public SystemPropertiesWrapper createSystemPropertiesWrapper() {
             return new SystemPropertiesWrapper() {
                 @Override
@@ -1147,6 +1153,7 @@
         mHandler = injector.createHandler(mHandlerThread.getLooper(),
                 new PowerManagerHandlerCallback());
         mConstants = new Constants(mHandler);
+        mFoldGracePeriodProvider = injector.createFoldGracePeriodProvider();
         mAmbientDisplayConfiguration = mInjector.createAmbientDisplayConfiguration(context);
         mAmbientDisplaySuppressionController =
                 mInjector.createAmbientDisplaySuppressionController(
@@ -6933,8 +6940,15 @@
                                 + ") doesn't exist");
                     }
                     if ((flags & PowerManager.GO_TO_SLEEP_FLAG_SOFT_SLEEP) != 0) {
-                        if (powerGroup.hasWakeLockKeepingScreenOnLocked()) {
-                            continue;
+                        if (mFoldGracePeriodProvider.isEnabled()) {
+                            if (!powerGroup.hasWakeLockKeepingScreenOnLocked()) {
+                                mNotifier.showDismissibleKeyguard();
+                            }
+                            continue; // never actually goes to sleep for SOFT_SLEEP
+                        } else {
+                            if (powerGroup.hasWakeLockKeepingScreenOnLocked()) {
+                                continue;
+                            }
                         }
                     }
                     if (isNoDoze) {
diff --git a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
index 03f3763..09b19e6 100644
--- a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
+++ b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
@@ -190,15 +190,11 @@
     // The maximum number of names wakelocks we will keep track of
     // per uid; once the limit is reached, we batch the remaining wakelocks
     // in to one common name.
-    private static final int MAX_WAKELOCKS_PER_UID;
+    private static final int MAX_WAKELOCKS_PER_UID = isLowRamDevice() ? 40 : 200;
 
-    static {
-        if (ActivityManager.isLowRamDeviceStatic()) {
-            MAX_WAKELOCKS_PER_UID = 40;
-        } else {
-            MAX_WAKELOCKS_PER_UID = 200;
-        }
-    }
+    private static final int CELL_SIGNAL_STRENGTH_LEVEL_COUNT = getCellSignalStrengthLevelCount();
+
+    private static final int MODEM_TX_POWER_LEVEL_COUNT = getModemTxPowerLevelCount();
 
     // Number of transmit power states the Wifi controller can be in.
     private static final int NUM_WIFI_TX_LEVELS = 1;
@@ -278,11 +274,9 @@
     @VisibleForTesting
     protected KernelSingleUidTimeReader mKernelSingleUidTimeReader;
     @VisibleForTesting
-    protected SystemServerCpuThreadReader mSystemServerCpuThreadReader =
-            SystemServerCpuThreadReader.create();
+    protected SystemServerCpuThreadReader mSystemServerCpuThreadReader;
 
-    private final KernelMemoryBandwidthStats mKernelMemoryBandwidthStats
-            = new KernelMemoryBandwidthStats();
+    private KernelMemoryBandwidthStats mKernelMemoryBandwidthStats;
     private final LongSparseArray<SamplingTimer> mKernelMemoryStats = new LongSparseArray<>();
     private int[] mCpuPowerBracketMap;
     private final CpuPowerStatsCollector mCpuPowerStatsCollector;
@@ -323,7 +317,7 @@
     private long mLastRpmStatsUpdateTimeMs = -RPM_STATS_UPDATE_FREQ_MS;
 
     /** Container for Rail Energy Data stats. */
-    private final RailStats mTmpRailStats = new RailStats();
+    private RailStats mTmpRailStats;
 
     /**
      * Estimate UID modem power usage based on their estimated mobile radio active time.
@@ -1031,7 +1025,7 @@
     int mPhoneSignalStrengthBin = -1;
     int mPhoneSignalStrengthBinRaw = -1;
     final StopwatchTimer[] mPhoneSignalStrengthsTimer =
-            new StopwatchTimer[CellSignalStrength.getNumSignalStrengthLevels()];
+            new StopwatchTimer[CELL_SIGNAL_STRENGTH_LEVEL_COUNT];
 
     StopwatchTimer mPhoneSignalScanningTimer;
 
@@ -1723,7 +1717,8 @@
     @VisibleForTesting
     public BatteryStatsImpl(Clock clock, File historyDirectory, @NonNull Handler handler,
             @NonNull PowerStatsUidResolver powerStatsUidResolver) {
-        init(clock);
+        mClock = clock;
+        initKernelStatsReaders();
         mBatteryStatsConfig = new BatteryStatsConfig.Builder().build();
         mHandler = handler;
         mPowerStatsUidResolver = powerStatsUidResolver;
@@ -1748,13 +1743,19 @@
         mCpuPowerStatsCollector = null;
     }
 
-    private void init(Clock clock) {
-        mClock = clock;
-        mCpuUidUserSysTimeReader = new KernelCpuUidUserSysTimeReader(true, clock);
-        mCpuUidFreqTimeReader = new KernelCpuUidFreqTimeReader(true, clock);
-        mCpuUidActiveTimeReader = new KernelCpuUidActiveTimeReader(true, clock);
-        mCpuUidClusterTimeReader = new KernelCpuUidClusterTimeReader(true, clock);
+    private void initKernelStatsReaders() {
+        if (!isKernelStatsAvailable()) {
+            return;
+        }
+
+        mCpuUidUserSysTimeReader = new KernelCpuUidUserSysTimeReader(true, mClock);
+        mCpuUidFreqTimeReader = new KernelCpuUidFreqTimeReader(true, mClock);
+        mCpuUidActiveTimeReader = new KernelCpuUidActiveTimeReader(true, mClock);
+        mCpuUidClusterTimeReader = new KernelCpuUidClusterTimeReader(true, mClock);
         mKernelWakelockReader = new KernelWakelockReader();
+        mSystemServerCpuThreadReader = SystemServerCpuThreadReader.create();
+        mKernelMemoryBandwidthStats = new KernelMemoryBandwidthStats();
+        mTmpRailStats = new RailStats();
     }
 
     /**
@@ -5878,7 +5879,7 @@
 
     @GuardedBy("this")
     void stopAllPhoneSignalStrengthTimersLocked(int except, long elapsedRealtimeMs) {
-        for (int i = 0; i < CellSignalStrength.getNumSignalStrengthLevels(); i++) {
+        for (int i = 0; i < CELL_SIGNAL_STRENGTH_LEVEL_COUNT; i++) {
             if (i == except) {
                 continue;
             }
@@ -8450,7 +8451,7 @@
         public ControllerActivityCounterImpl getOrCreateModemControllerActivityLocked() {
             if (mModemControllerActivity == null) {
                 mModemControllerActivity = new ControllerActivityCounterImpl(mBsi.mClock,
-                        mBsi.mOnBatteryTimeBase, ModemActivityInfo.getNumTxPowerLevels());
+                        mBsi.mOnBatteryTimeBase, mBsi.MODEM_TX_POWER_LEVEL_COUNT);
             }
             return mModemControllerActivity;
         }
@@ -10896,7 +10897,8 @@
             @NonNull UserInfoProvider userInfoProvider, @NonNull PowerProfile powerProfile,
             @NonNull CpuScalingPolicies cpuScalingPolicies,
             @NonNull PowerStatsUidResolver powerStatsUidResolver) {
-        init(clock);
+        mClock = clock;
+        initKernelStatsReaders();
 
         mBatteryStatsConfig = config;
         mMonotonicClock = monotonicClock;
@@ -10992,7 +10994,7 @@
         mDeviceLightIdlingTimer = new StopwatchTimer(mClock, null, -15, null, mOnBatteryTimeBase);
         mDeviceIdlingTimer = new StopwatchTimer(mClock, null, -12, null, mOnBatteryTimeBase);
         mPhoneOnTimer = new StopwatchTimer(mClock, null, -3, null, mOnBatteryTimeBase);
-        for (int i = 0; i < CellSignalStrength.getNumSignalStrengthLevels(); i++) {
+        for (int i = 0; i < CELL_SIGNAL_STRENGTH_LEVEL_COUNT; i++) {
             mPhoneSignalStrengthsTimer[i] = new StopwatchTimer(mClock, null, -200 - i, null,
                     mOnBatteryTimeBase);
         }
@@ -11012,7 +11014,7 @@
         mBluetoothActivity = new ControllerActivityCounterImpl(mClock, mOnBatteryTimeBase,
                 NUM_BT_TX_LEVELS);
         mModemActivity = new ControllerActivityCounterImpl(mClock, mOnBatteryTimeBase,
-                ModemActivityInfo.getNumTxPowerLevels());
+                MODEM_TX_POWER_LEVEL_COUNT);
         mMobileRadioActiveTimer = new StopwatchTimer(mClock, null, -400, null, mOnBatteryTimeBase);
         mMobileRadioActivePerAppTimer = new StopwatchTimer(mClock, null, -401, null,
                 mOnBatteryTimeBase);
@@ -11611,7 +11613,7 @@
         mFlashlightOnTimer.reset(false, elapsedRealtimeUs);
         mCameraOnTimer.reset(false, elapsedRealtimeUs);
         mBluetoothScanTimer.reset(false, elapsedRealtimeUs);
-        for (int i = 0; i < CellSignalStrength.getNumSignalStrengthLevels(); i++) {
+        for (int i = 0; i < CELL_SIGNAL_STRENGTH_LEVEL_COUNT; i++) {
             mPhoneSignalStrengthsTimer[i].reset(false, elapsedRealtimeUs);
         }
         mPhoneSignalScanningTimer.reset(false, elapsedRealtimeUs);
@@ -11834,7 +11836,7 @@
     private String[] mWifiIfaces = EmptyArray.STRING;
 
     @GuardedBy("mWifiNetworkLock")
-    private NetworkStats mLastWifiNetworkStats = new NetworkStats(0, -1);
+    private NetworkStats mLastWifiNetworkStats;
 
     private final Object mModemNetworkLock = new Object();
 
@@ -11842,7 +11844,7 @@
     private String[] mModemIfaces = EmptyArray.STRING;
 
     @GuardedBy("mModemNetworkLock")
-    private NetworkStats mLastModemNetworkStats = new NetworkStats(0, -1);
+    private NetworkStats mLastModemNetworkStats;
 
     @VisibleForTesting
     protected NetworkStats readMobileNetworkStatsLocked(
@@ -11875,7 +11877,9 @@
         synchronized (mWifiNetworkLock) {
             final NetworkStats latestStats = readWifiNetworkStatsLocked(networkStatsManager);
             if (latestStats != null) {
-                delta = latestStats.subtract(mLastWifiNetworkStats);
+                delta = mLastWifiNetworkStats != null
+                        ? latestStats.subtract(mLastWifiNetworkStats)
+                        : latestStats.subtract(new NetworkStats(0, -1));
                 mLastWifiNetworkStats = latestStats;
             }
         }
@@ -12248,7 +12252,9 @@
         synchronized (mModemNetworkLock) {
             final NetworkStats latestStats = readMobileNetworkStatsLocked(networkStatsManager);
             if (latestStats != null) {
-                delta = latestStats.subtract(mLastModemNetworkStats);
+                delta = latestStats.subtract(mLastModemNetworkStats != null
+                        ? mLastModemNetworkStats
+                        : new NetworkStats(0, -1));
                 mLastModemNetworkStats = latestStats;
             }
         }
@@ -12301,7 +12307,7 @@
                         deltaInfo.getSleepTimeMillis());
                 mModemActivity.getOrCreateRxTimeCounter()
                         .increment(deltaInfo.getReceiveTimeMillis(), elapsedRealtimeMs);
-                for (int lvl = 0; lvl < ModemActivityInfo.getNumTxPowerLevels(); lvl++) {
+                for (int lvl = 0; lvl < MODEM_TX_POWER_LEVEL_COUNT; lvl++) {
                     mModemActivity.getOrCreateTxTimeCounters()[lvl]
                             .increment(deltaInfo.getTransmitDurationMillisAtPowerLevel(lvl),
                                     elapsedRealtimeMs);
@@ -12318,8 +12324,8 @@
                             mPowerProfile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_IDLE)
                             + deltaInfo.getReceiveTimeMillis() *
                             mPowerProfile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_RX);
-                    for (int i = 0; i < Math.min(ModemActivityInfo.getNumTxPowerLevels(),
-                            CellSignalStrength.getNumSignalStrengthLevels()); i++) {
+                    for (int i = 0; i < Math.min(MODEM_TX_POWER_LEVEL_COUNT,
+                            CELL_SIGNAL_STRENGTH_LEVEL_COUNT); i++) {
                         energyUsed += deltaInfo.getTransmitDurationMillisAtPowerLevel(i)
                                 * mPowerProfile.getAveragePower(
                                         PowerProfile.POWER_MODEM_CONTROLLER_TX, i);
@@ -12441,7 +12447,7 @@
                             }
 
                             if (totalTxPackets > 0 && entry.getTxPackets() > 0) {
-                                for (int lvl = 0; lvl < ModemActivityInfo.getNumTxPowerLevels();
+                                for (int lvl = 0; lvl < MODEM_TX_POWER_LEVEL_COUNT;
                                         lvl++) {
                                     long txMs = entry.getTxPackets()
                                             * deltaInfo.getTransmitDurationMillisAtPowerLevel(lvl);
@@ -12550,7 +12556,7 @@
                 && deltaInfo.getSpecificInfoFrequencyRange(0)
                 == ServiceState.FREQUENCY_RANGE_UNKNOWN) {
             // Specific info data unavailable. Proportionally smear Rx and Tx times across each RAT.
-            final int levelCount = CellSignalStrength.getNumSignalStrengthLevels();
+            final int levelCount = CELL_SIGNAL_STRENGTH_LEVEL_COUNT;
             long[] perSignalStrengthActiveTimeMs = new long[levelCount];
             long totalActiveTimeMs = 0;
 
@@ -12726,13 +12732,13 @@
             return;
         }
         int levelMaxTimeSpent = 0;
-        for (int i = 1; i < ModemActivityInfo.getNumTxPowerLevels(); i++) {
+        for (int i = 1; i < MODEM_TX_POWER_LEVEL_COUNT; i++) {
             if (activityInfo.getTransmitDurationMillisAtPowerLevel(i)
                     > activityInfo.getTransmitDurationMillisAtPowerLevel(levelMaxTimeSpent)) {
                 levelMaxTimeSpent = i;
             }
         }
-        if (levelMaxTimeSpent == ModemActivityInfo.getNumTxPowerLevels() - 1) {
+        if (levelMaxTimeSpent == MODEM_TX_POWER_LEVEL_COUNT - 1) {
             mHistory.recordState2StartEvent(elapsedRealtimeMs, uptimeMs,
                     HistoryItem.STATE2_CELLULAR_HIGH_TX_POWER_FLAG);
         }
@@ -14821,12 +14827,12 @@
             timeInRatMs[i] = getPhoneDataConnectionTime(i, rawRealTimeUs, which) / 1000;
         }
         long[] timeInRxSignalStrengthLevelMs =
-                new long[CellSignalStrength.getNumSignalStrengthLevels()];
+                new long[CELL_SIGNAL_STRENGTH_LEVEL_COUNT];
         for (int i = 0; i < timeInRxSignalStrengthLevelMs.length; i++) {
             timeInRxSignalStrengthLevelMs[i] =
                 getPhoneSignalStrengthTime(i, rawRealTimeUs, which) / 1000;
         }
-        long[] txTimeMs = new long[Math.min(ModemActivityInfo.getNumTxPowerLevels(),
+        long[] txTimeMs = new long[Math.min(MODEM_TX_POWER_LEVEL_COUNT,
             counter.getTxTimeCounters().length)];
         long totalTxTimeMs = 0;
         for (int i = 0; i < txTimeMs.length; i++) {
@@ -15458,7 +15464,7 @@
 
         public Constants(Handler handler) {
             super(handler);
-            if (ActivityManager.isLowRamDeviceStatic()) {
+            if (isLowRamDevice()) {
                 MAX_HISTORY_FILES = DEFAULT_MAX_HISTORY_FILES_LOW_RAM_DEVICE;
                 MAX_HISTORY_BUFFER = DEFAULT_MAX_HISTORY_BUFFER_LOW_RAM_DEVICE_KB * 1024;
             } else {
@@ -15528,12 +15534,10 @@
                         KEY_PROC_STATE_CHANGE_COLLECTION_DELAY_MS,
                         DEFAULT_PROC_STATE_CHANGE_COLLECTION_DELAY_MS);
                 MAX_HISTORY_FILES = mParser.getInt(KEY_MAX_HISTORY_FILES,
-                        ActivityManager.isLowRamDeviceStatic() ?
-                                DEFAULT_MAX_HISTORY_FILES_LOW_RAM_DEVICE
-                        : DEFAULT_MAX_HISTORY_FILES);
+                        isLowRamDevice() ? DEFAULT_MAX_HISTORY_FILES_LOW_RAM_DEVICE
+                                : DEFAULT_MAX_HISTORY_FILES);
                 MAX_HISTORY_BUFFER = mParser.getInt(KEY_MAX_HISTORY_BUFFER_KB,
-                        ActivityManager.isLowRamDeviceStatic() ?
-                                DEFAULT_MAX_HISTORY_BUFFER_LOW_RAM_DEVICE_KB
+                        isLowRamDevice() ? DEFAULT_MAX_HISTORY_BUFFER_LOW_RAM_DEVICE_KB
                                 : DEFAULT_MAX_HISTORY_BUFFER_KB)
                         * 1024;
                 final String perUidModemModel = mParser.getString(KEY_PER_UID_MODEM_POWER_MODEL,
@@ -16014,7 +16018,7 @@
         mDeviceLightIdlingTimer.readSummaryFromParcelLocked(in);
         mDeviceIdlingTimer.readSummaryFromParcelLocked(in);
         mPhoneOnTimer.readSummaryFromParcelLocked(in);
-        for (int i = 0; i < CellSignalStrength.getNumSignalStrengthLevels(); i++) {
+        for (int i = 0; i < CELL_SIGNAL_STRENGTH_LEVEL_COUNT; i++) {
             mPhoneSignalStrengthsTimer[i].readSummaryFromParcelLocked(in);
         }
         mPhoneSignalScanningTimer.readSummaryFromParcelLocked(in);
@@ -16520,7 +16524,7 @@
         mDeviceLightIdlingTimer.writeSummaryFromParcelLocked(out, nowRealtime);
         mDeviceIdlingTimer.writeSummaryFromParcelLocked(out, nowRealtime);
         mPhoneOnTimer.writeSummaryFromParcelLocked(out, nowRealtime);
-        for (int i = 0; i < CellSignalStrength.getNumSignalStrengthLevels(); i++) {
+        for (int i = 0; i < CELL_SIGNAL_STRENGTH_LEVEL_COUNT; i++) {
             mPhoneSignalStrengthsTimer[i].writeSummaryFromParcelLocked(out, nowRealtime);
         }
         mPhoneSignalScanningTimer.writeSummaryFromParcelLocked(out, nowRealtime);
@@ -17015,7 +17019,7 @@
             mDeviceIdlingTimer.logState(pr, "  ");
             pr.println("*** Phone timer:");
             mPhoneOnTimer.logState(pr, "  ");
-            for (int i = 0; i < CellSignalStrength.getNumSignalStrengthLevels(); i++) {
+            for (int i = 0; i < CELL_SIGNAL_STRENGTH_LEVEL_COUNT; i++) {
                 pr.println("*** Phone signal strength #" + i + ":");
                 mPhoneSignalStrengthsTimer[i].logState(pr, "  ");
             }
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index 8d93408..13f1141 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -1279,8 +1279,8 @@
                 ipw.println();
             }
 
-            PackageWatchdog.getInstance(mContext).dump(ipw);
         });
+        PackageWatchdog.getInstance(mContext).dump(ipw);
     }
 
     @AnyThread
diff --git a/services/core/java/com/android/server/trust/TEST_MAPPING b/services/core/java/com/android/server/trust/TEST_MAPPING
index fa46acd..0de7c28 100644
--- a/services/core/java/com/android/server/trust/TEST_MAPPING
+++ b/services/core/java/com/android/server/trust/TEST_MAPPING
@@ -12,6 +12,19 @@
         ]
       }
     ],
+    "postsubmit": [
+      {
+        "name": "FrameworksMockingServicesTests",
+        "options": [
+          {
+            "include-filter": "com.android.server.trust"
+          },
+          {
+            "exclude-annotation": "androidx.test.filters.FlakyTest"
+          }
+        ]
+      }
+    ],
     "trust-tablet": [
       {
         "name": "TrustTests",
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index 9a85c42..e3eb5b5 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -74,6 +74,7 @@
 import android.view.WindowManagerGlobal;
 
 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.util.DumpUtils;
@@ -1439,6 +1440,13 @@
         if (biometricManager == null) {
             return new long[0];
         }
+        if (android.security.Flags.fixUnlockedDeviceRequiredKeysV2()
+                && mLockPatternUtils.isProfileWithUnifiedChallenge(userId)) {
+            // Profiles with unified challenge have their own set of biometrics, but the device
+            // unlock happens via the parent user.  In this case Keystore needs to be given the list
+            // of biometric SIDs from the parent user, not the profile.
+            userId = resolveProfileParent(userId);
+        }
         return biometricManager.getAuthenticatorIds(userId);
     }
 
@@ -1758,6 +1766,11 @@
         }
     };
 
+    @VisibleForTesting
+    void waitForIdle() {
+        mHandler.runWithScissors(() -> {}, 0);
+    }
+
     private boolean isTrustUsuallyManagedInternal(int userId) {
         synchronized (mTrustUsuallyManagedForUser) {
             int i = mTrustUsuallyManagedForUser.indexOfKey(userId);
@@ -1878,7 +1891,8 @@
         };
     }
 
-    private final PackageMonitor mPackageMonitor = new PackageMonitor() {
+    @VisibleForTesting
+    final PackageMonitor mPackageMonitor = new PackageMonitor() {
         @Override
         public void onSomePackagesChanged() {
             refreshAgentList(UserHandle.USER_ALL);
diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
index 58acbe0..c0e3308 100755
--- a/services/core/java/com/android/server/tv/TvInputHardwareManager.java
+++ b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
@@ -1339,16 +1339,22 @@
                     String inputId = mHardwareInputIdMap.get(deviceId);
 
                     if (inputId != null) {
-                        if (connection.updateCableConnectionStatusLocked(cableConnectionStatus)) {
-                            if (previousCableConnectionStatus != connection.getInputStateLocked()) {
-                                mHandler.obtainMessage(ListenerHandler.STATE_CHANGED,
-                                    connection.getInputStateLocked(), 0, inputId).sendToTarget();
-                            }
-                        } else {
-                            if ((previousConfigsLength == 0)
-                                    != (connection.getConfigsLengthLocked() == 0)) {
-                                mHandler.obtainMessage(ListenerHandler.STATE_CHANGED,
-                                    connection.getInputStateLocked(), 0, inputId).sendToTarget();
+                        synchronized (mLock) {
+                            if (connection.updateCableConnectionStatusLocked(
+                                        cableConnectionStatus)) {
+                                if (previousCableConnectionStatus
+                                        != connection.getInputStateLocked()) {
+                                    mHandler.obtainMessage(ListenerHandler.STATE_CHANGED,
+                                                    connection.getInputStateLocked(), 0, inputId)
+                                            .sendToTarget();
+                                }
+                            } else {
+                                if ((previousConfigsLength == 0)
+                                        != (connection.getConfigsLengthLocked() == 0)) {
+                                    mHandler.obtainMessage(ListenerHandler.STATE_CHANGED,
+                                                    connection.getInputStateLocked(), 0, inputId)
+                                            .sendToTarget();
+                                }
                             }
                         }
                     }
diff --git a/services/core/java/com/android/server/utils/AnrTimer.java b/services/core/java/com/android/server/utils/AnrTimer.java
index 7b5192c..e3aba0f 100644
--- a/services/core/java/com/android/server/utils/AnrTimer.java
+++ b/services/core/java/com/android/server/utils/AnrTimer.java
@@ -16,21 +16,30 @@
 
 package com.android.server.utils;
 
+import static android.text.TextUtils.formatSimple;
+
 import android.annotation.NonNull;
 import android.os.Handler;
 import android.os.Message;
 import android.os.SystemClock;
 import android.os.Trace;
+import android.text.TextUtils;
 import android.text.format.TimeMigrationUtils;
+import android.util.ArrayMap;
 import android.util.IndentingPrintWriter;
 import android.util.Log;
+import android.util.LongSparseArray;
+import android.util.SparseArray;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.Keep;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.RingBuffer;
 
+import java.lang.ref.WeakReference;
 import java.io.PrintWriter;
 import java.util.Arrays;
+import java.util.ArrayList;
 import java.util.Objects;
 
 /**
@@ -60,9 +69,14 @@
  * is restarted with the extension timeout.  If extensions are disabled or if the extension is zero,
  * the client process is notified of the expiration.
  *
+ * <p>Instances use native resources but not system resources when the feature is enabled.
+ * Instances should be explicitly closed unless they are being closed as part of process
+ * exit. (So, instances in system server generally need not be explicitly closed since they are
+ * created during process start and will last until process exit.)
+ *
  * @hide
  */
-public class AnrTimer<V> {
+public class AnrTimer<V> implements AutoCloseable {
 
     /**
      * The log tag.
@@ -87,6 +101,12 @@
     private static final long TRACE_TAG = Trace.TRACE_TAG_ACTIVITY_MANAGER;
 
     /**
+     * Enable tracing from the time a timer expires until it is accepted or discarded.  This is
+     * used to diagnose long latencies in the client.
+     */
+    private static final boolean ENABLE_TRACING = false;
+
+    /**
      * Return true if the feature is enabled.  By default, the value is take from the Flags class
      * but it can be changed for local testing.
      */
@@ -103,6 +123,9 @@
         }
     }
 
+    /** The default injector. */
+    private static final Injector sDefaultInjector = new Injector();
+
     /**
      * An error is defined by its issue, the operation that detected the error, the tag of the
      * affected service, a short stack of the bad call, and the stringified arg associated with
@@ -160,41 +183,46 @@
     /** A lock for the AnrTimer instance. */
     private final Object mLock = new Object();
 
-    /**
-     * The total number of timers started.
-     */
+    /** The map from client argument to the associated timer ID. */
+    @GuardedBy("mLock")
+    private final ArrayMap<V, Integer> mTimerIdMap = new ArrayMap<>();
+
+    /** Reverse map from timer ID to client argument. */
+    @GuardedBy("mLock")
+    private final SparseArray<V> mTimerArgMap = new SparseArray<>();
+
+    /** The highwater mark of started, but not closed, timers. */
+    @GuardedBy("mLock")
+    private int mMaxStarted = 0;
+
+    /** The total number of timers started. */
     @GuardedBy("mLock")
     private int mTotalStarted = 0;
 
-    /**
-     * The total number of errors detected.
-     */
+    /** The total number of errors detected. */
     @GuardedBy("mLock")
     private int mTotalErrors = 0;
 
-    /**
-     * The handler for messages sent from this instance.
-     */
+    /** The total number of timers that have expired. */
+    @GuardedBy("mLock")
+    private int mTotalExpired = 0;
+
+    /** The handler for messages sent from this instance. */
     private final Handler mHandler;
 
-    /**
-     * The message type for messages sent from this interface.
-     */
+    /** The message type for messages sent from this interface. */
     private final int mWhat;
 
-    /**
-     * A label that identifies the AnrTimer associated with a Timer in log messages.
-     */
+    /** A label that identifies the AnrTimer associated with a Timer in log messages. */
     private final String mLabel;
 
-    /**
-     * Whether this timer instance supports extending timeouts.
-     */
+    /** Whether this timer instance supports extending timeouts. */
     private final boolean mExtend;
 
-    /**
-     * The top-level switch for the feature enabled or disabled.
-     */
+    /** The injector used to create this instance.  This is only used for testing. */
+    private final Injector mInjector;
+
+    /** The top-level switch for the feature enabled or disabled. */
     private final FeatureSwitch mFeature;
 
     /**
@@ -223,7 +251,27 @@
         mWhat = what;
         mLabel = label;
         mExtend = extend;
-        mFeature = new FeatureDisabled();
+        mInjector = injector;
+        boolean enabled = mInjector.anrTimerServiceEnabled() && nativeTimersSupported();
+        mFeature = createFeatureSwitch(enabled);
+    }
+
+    // Return the correct feature.  FeatureEnabled is returned if and only if the feature is
+    // flag-enabled and if the native shadow was successfully created.  Otherwise, FeatureDisabled
+    // is returned.
+    private FeatureSwitch createFeatureSwitch(boolean enabled) {
+        if (!enabled) {
+            return new FeatureDisabled();
+        } else {
+            try {
+                return new FeatureEnabled();
+            } catch (RuntimeException e) {
+                // Something went wrong in the native layer.  Log the error and fall back on the
+                // feature-disabled logic.
+                Log.e(TAG, e.toString());
+                return new FeatureDisabled();
+            }
+        }
     }
 
     /**
@@ -245,7 +293,7 @@
      * @param extend A flag to indicate if expired timers can be granted extensions.
      */
     public AnrTimer(@NonNull Handler handler, int what, @NonNull String label, boolean extend) {
-        this(handler, what, label, extend, new Injector());
+        this(handler, what, label, extend, sDefaultInjector);
     }
 
     /**
@@ -272,19 +320,44 @@
     }
 
     /**
+     * Start a trace on the timer.  The trace is laid down in the AnrTimerTrack.
+     */
+    private void traceBegin(int timerId, int pid, int uid, String what) {
+        if (ENABLE_TRACING) {
+            final String label = formatSimple("%s(%d,%d,%s)", what, pid, uid, mLabel);
+            final int cookie = timerId;
+            Trace.asyncTraceForTrackBegin(TRACE_TAG, TRACK, label, cookie);
+        }
+    }
+
+    /**
+     * End a trace on the timer.
+     */
+    private void traceEnd(int timerId) {
+        if (ENABLE_TRACING) {
+            final int cookie = timerId;
+            Trace.asyncTraceForTrackEnd(TRACE_TAG, TRACK, cookie);
+        }
+    }
+
+    /**
      * The FeatureSwitch class provides a quick switch between feature-enabled behavior and
      * feature-disabled behavior.
      */
     private abstract class FeatureSwitch {
         abstract void start(@NonNull V arg, int pid, int uid, long timeoutMs);
 
-        abstract void cancel(@NonNull V arg);
+        abstract boolean cancel(@NonNull V arg);
 
-        abstract void accept(@NonNull V arg);
+        abstract boolean accept(@NonNull V arg);
 
-        abstract void discard(@NonNull V arg);
+        abstract boolean discard(@NonNull V arg);
 
         abstract boolean enabled();
+
+        abstract void dump(PrintWriter pw, boolean verbose);
+
+        abstract void close();
     }
 
     /**
@@ -301,18 +374,21 @@
 
         /** Cancel a timer by removing the message from the client's handler. */
         @Override
-        void cancel(@NonNull V arg) {
+        boolean cancel(@NonNull V arg) {
             mHandler.removeMessages(mWhat, arg);
+            return true;
         }
 
         /** accept() is a no-op when the feature is disabled. */
         @Override
-        void accept(@NonNull V arg) {
+        boolean accept(@NonNull V arg) {
+            return true;
         }
 
         /** discard() is a no-op when the feature is disabled. */
         @Override
-        void discard(@NonNull V arg) {
+        boolean discard(@NonNull V arg) {
+            return true;
         }
 
         /** The feature is not enabled. */
@@ -320,12 +396,179 @@
         boolean enabled() {
             return false;
         }
+
+        /** dump() is a no-op when the feature is disabled. */
+        @Override
+        void dump(PrintWriter pw, boolean verbose) {
+        }
+
+        /** close() is a no-op when the feature is disabled. */
+        @Override
+        void close() {
+        }
+    }
+
+    /**
+     * A static list of AnrTimer instances.  The list is traversed by dumpsys.  Only instances
+     * using native resources are included.
+     */
+    @GuardedBy("sAnrTimerList")
+    private static final LongSparseArray<WeakReference<AnrTimer>> sAnrTimerList =
+        new LongSparseArray<>();
+
+    /**
+     * The FeatureEnabled class enables the AnrTimer logic.  It is used when the AnrTimer service
+     * is enabled via Flags.anrTimerServiceEnabled.
+     */
+    private class FeatureEnabled extends FeatureSwitch {
+
+        /**
+         * The native timer that supports this instance. The value is set to non-zero when the
+         * native timer is created and it is set back to zero when the native timer is freed.
+         */
+        private long mNative = 0;
+
+        /** Fetch the native tag (an integer) for the given label. */
+        FeatureEnabled() {
+            mNative = nativeAnrTimerCreate(mLabel);
+            if (mNative == 0) throw new IllegalArgumentException("unable to create native timer");
+            synchronized (sAnrTimerList) {
+                sAnrTimerList.put(mNative, new WeakReference(AnrTimer.this));
+            }
+        }
+
+        /**
+         * Start a timer.
+         */
+        @Override
+        void start(@NonNull V arg, int pid, int uid, long timeoutMs) {
+            synchronized (mLock) {
+                if (mTimerIdMap.containsKey(arg)) {
+                    // There is an existing timer.  Cancel it.
+                    cancel(arg);
+                }
+                int timerId = nativeAnrTimerStart(mNative, pid, uid, timeoutMs, mExtend);
+                if (timerId > 0) {
+                    mTimerIdMap.put(arg, timerId);
+                    mTimerArgMap.put(timerId, arg);
+                    mTotalStarted++;
+                    mMaxStarted = Math.max(mMaxStarted, mTimerIdMap.size());
+                } else {
+                    throw new RuntimeException("unable to start timer");
+                }
+            }
+        }
+
+        /**
+         * Cancel a timer.  No error is reported if the timer is not found because some clients
+         * cancel timers from common code that runs even if a timer was never started.
+         */
+        @Override
+        boolean cancel(@NonNull V arg) {
+            synchronized (mLock) {
+                Integer timer = removeLocked(arg);
+                if (timer == null) {
+                    return false;
+                }
+                if (!nativeAnrTimerCancel(mNative, timer)) {
+                    // There may be an expiration message in flight.  Cancel it.
+                    mHandler.removeMessages(mWhat, arg);
+                    return false;
+                }
+                return true;
+            }
+        }
+
+        /**
+         * Accept a timer in the framework-level handler.  The timeout has been accepted and the
+         * timeout handler is executing.
+         */
+        @Override
+        boolean accept(@NonNull V arg) {
+            synchronized (mLock) {
+                Integer timer = removeLocked(arg);
+                if (timer == null) {
+                    notFoundLocked("accept", arg);
+                    return false;
+                }
+                nativeAnrTimerAccept(mNative, timer);
+                traceEnd(timer);
+                return true;
+            }
+        }
+
+        /**
+         * Discard a timer in the framework-level handler.  For whatever reason, the timer is no
+         * longer interesting.  No statistics are collected.  Return false if the time was not
+         * found.
+         */
+        @Override
+        boolean discard(@NonNull V arg) {
+            synchronized (mLock) {
+                Integer timer = removeLocked(arg);
+                if (timer == null) {
+                    notFoundLocked("discard", arg);
+                    return false;
+                }
+                nativeAnrTimerDiscard(mNative, timer);
+                traceEnd(timer);
+                return true;
+            }
+        }
+
+        /** The feature is enabled. */
+        @Override
+        boolean enabled() {
+            return true;
+        }
+
+        /** Dump statistics from the native layer. */
+        @Override
+        void dump(PrintWriter pw, boolean verbose) {
+            synchronized (mLock) {
+                if (mNative != 0) {
+                    nativeAnrTimerDump(mNative, verbose);
+                } else {
+                    pw.println("closed");
+                }
+            }
+        }
+
+        /** Free native resources. */
+        @Override
+        void close() {
+            // Remove self from the list of active timers.
+            synchronized (sAnrTimerList) {
+                sAnrTimerList.remove(mNative);
+            }
+            synchronized (mLock) {
+                if (mNative != 0) nativeAnrTimerClose(mNative);
+                mNative = 0;
+            }
+        }
+
+        /**
+         * Delete the entries associated with arg from the maps and return the ID of the timer, if
+         * any.
+         */
+        @GuardedBy("mLock")
+        private Integer removeLocked(V arg) {
+            Integer r = mTimerIdMap.remove(arg);
+            if (r != null) {
+                synchronized (mTimerArgMap) {
+                    mTimerArgMap.remove(r);
+                }
+            }
+            return r;
+        }
     }
 
     /**
      * Start a timer associated with arg.  The same object must be used to cancel, accept, or
      * discard a timer later.  If a timer already exists with the same arg, then the existing timer
-     * is canceled and a new timer is created.
+     * is canceled and a new timer is created.  The timeout is signed but negative delays are
+     * nonsensical.  Rather than throw an exception, timeouts less than 0ms are forced to 0ms.  This
+     * allows a client to deliver an immediate timeout via the AnrTimer.
      *
      * @param arg The key by which the timer is known.  This is never examined or modified.
      * @param pid The Linux process ID of the target being timed.
@@ -333,25 +576,39 @@
      * @param timeoutMs The timer timeout, in milliseconds.
      */
     public void start(@NonNull V arg, int pid, int uid, long timeoutMs) {
+        if (timeoutMs < 0) timeoutMs = 0;
         mFeature.start(arg, pid, uid, timeoutMs);
     }
 
     /**
      * Cancel the running timer associated with arg.  The timer is forgotten.  If the timer has
-     * expired, the call is treated as a discard.  No errors are reported if the timer does not
-     * exist or if the timer has expired.
+     * expired, the call is treated as a discard.  The function returns true if a running timer was
+     * found, and false if an expired timer was found or if no timer was found.  After this call,
+     * the timer does not exist.
+     *
+     * Note: the return value is always true if the feature is not enabled.
+     *
+     * @param arg The key by which the timer is known.  This is never examined or modified.
+     * @return True if a running timer was canceled.
      */
-    public void cancel(@NonNull V arg) {
-        mFeature.cancel(arg);
+    public boolean cancel(@NonNull V arg) {
+        return mFeature.cancel(arg);
     }
 
     /**
      * Accept the expired timer associated with arg.  This indicates that the caller considers the
-     * timer expiration to be a true ANR.  (See {@link #discard} for an alternate response.)  It is
-     * an error to accept a running timer, however the running timer will be canceled.
+     * timer expiration to be a true ANR.  (See {@link #discard} for an alternate response.)  The
+     * function returns true if an expired timer was found and false if a running timer was found or
+     * if no timer was found.  After this call, the timer does not exist.  It is an error to accept
+     * a running timer, however, the running timer will be canceled.
+     *
+     * Note: the return value is always true if the feature is not enabled.
+     *
+     * @param arg The key by which the timer is known.  This is never examined or modified.
+     * @return True if an expired timer was accepted.
      */
-    public void accept(@NonNull V arg) {
-        mFeature.accept(arg);
+    public boolean accept(@NonNull V arg) {
+        return mFeature.accept(arg);
     }
 
     /**
@@ -359,11 +616,57 @@
      * timer expiration to be a false ANR.  ((See {@link #accept} for an alternate response.)  One
      * reason to discard an expired timer is if the process being timed was also being debugged:
      * such a process could be stopped at a breakpoint and its failure to respond would not be an
-     * error.  It is an error to discard a running timer, however the running timer will be
-     * canceled.
+     * error.  After this call thie timer does not exist. It is an error to discard a running timer,
+     * however the running timer will be canceled.
+     *
+     * Note: the return value is always true if the feature is not enabled.
+     *
+     * @param arg The key by which the timer is known.  This is never examined or modified.
+     * @return True if an expired timer was discarded.
      */
-    public void discard(@NonNull V arg) {
-        mFeature.discard(arg);
+    public boolean discard(@NonNull V arg) {
+        return mFeature.discard(arg);
+    }
+
+    /**
+     * The notifier that a timer has fired.  The timerId and original pid/uid are supplied.  This
+     * method is called from native code.  This method takes mLock so that a timer cannot expire
+     * in the middle of another operation (like start or cancel).
+     */
+    @Keep
+    private boolean expire(int timerId, int pid, int uid) {
+        traceBegin(timerId, pid, uid, "expired");
+        V arg = null;
+        synchronized (mLock) {
+            arg = mTimerArgMap.get(timerId);
+            if (arg == null) {
+                Log.e(TAG, formatSimple("failed to expire timer %s:%d : arg not found",
+                                mLabel, timerId));
+                mTotalErrors++;
+                return false;
+            }
+            mTotalExpired++;
+        }
+        mHandler.sendMessage(Message.obtain(mHandler, mWhat, arg));
+        return true;
+    }
+
+    /**
+     * Close the object and free any native resources.
+     */
+    public void close() {
+        mFeature.close();
+    }
+
+    /**
+     * Ensure any native resources are freed when the object is GC'ed.  Best practice is to close
+     * the object explicitly, but overriding finalize() avoids accidental leaks.
+     */
+    @SuppressWarnings("Finalize")
+    @Override
+    protected void finalize() throws Throwable {
+        close();
+        super.finalize();
     }
 
     /**
@@ -373,8 +676,11 @@
         synchronized (mLock) {
             pw.format("timer: %s\n", mLabel);
             pw.increaseIndent();
-            pw.format("started=%d errors=%d\n", mTotalStarted, mTotalErrors);
+            pw.format("started=%d maxStarted=%d running=%d expired=%d errors=%d\n",
+                    mTotalStarted, mMaxStarted, mTimerIdMap.size(),
+                    mTotalExpired, mTotalErrors);
             pw.decreaseIndent();
+            mFeature.dump(pw, false);
         }
     }
 
@@ -386,6 +692,13 @@
     }
 
     /**
+     * The current time in milliseconds.
+     */
+    private static long now() {
+        return SystemClock.uptimeMillis();
+    }
+
+    /**
      * Dump all errors to the output stream.
      */
     private static void dumpErrors(IndentingPrintWriter ipw) {
@@ -422,23 +735,89 @@
         mTotalErrors++;
     }
 
-    /**
-     * Log an error about  a timer not found.
-     */
+    /** Record an error about a timer not found. */
     @GuardedBy("mLock")
     private void notFoundLocked(String operation, Object arg) {
         recordErrorLocked(operation, "notFound", arg);
     }
 
-    /**
-     * Dumpsys output.
-     */
-    public static void dump(@NonNull PrintWriter pw, boolean verbose) {
+    /** Dumpsys output, allowing for overrides. */
+    @VisibleForTesting
+    static void dump(@NonNull PrintWriter pw, boolean verbose, @NonNull Injector injector) {
+        if (!injector.anrTimerServiceEnabled()) return;
+
         final IndentingPrintWriter ipw = new IndentingPrintWriter(pw);
         ipw.println("AnrTimer statistics");
         ipw.increaseIndent();
+        synchronized (sAnrTimerList) {
+            final int size = sAnrTimerList.size();
+            ipw.println("reporting " + size + " timers");
+            for (int i = 0; i < size; i++) {
+                AnrTimer a = sAnrTimerList.valueAt(i).get();
+                if (a != null) a.dump(ipw);
+            }
+        }
         if (verbose) dumpErrors(ipw);
         ipw.format("AnrTimerEnd\n");
         ipw.decreaseIndent();
     }
+
+    /** Dumpsys output.  There is no output if the feature is not enabled. */
+    public static void dump(@NonNull PrintWriter pw, boolean verbose) {
+        dump(pw, verbose, sDefaultInjector);
+    }
+
+    /**
+     * Return true if the native timers are supported.  Native timers are supported if the method
+     * nativeAnrTimerSupported() can be executed and it returns true.
+     */
+    private static boolean nativeTimersSupported() {
+        try {
+            return nativeAnrTimerSupported();
+        } catch (java.lang.UnsatisfiedLinkError e) {
+            return false;
+        }
+    }
+
+    /**
+     * Native methods
+     */
+
+    /** Return true if the native AnrTimer code is operational. */
+    private static native boolean nativeAnrTimerSupported();
+
+    /**
+     * Create a new native timer with the given key and name.  The key is not used by the native
+     * code but it is returned to the Java layer in the expiration handler.  The name is only for
+     * logging.  Unlike the other methods, this is an instance method: the "this" parameter is
+     * passed into the native layer.
+     */
+    private native long nativeAnrTimerCreate(String name);
+
+    /** Release the native resources.  No further operations are premitted. */
+    private static native int nativeAnrTimerClose(long service);
+
+    /** Start a timer and return its ID.  Zero is returned on error. */
+    private static native int nativeAnrTimerStart(long service, int pid, int uid, long timeoutMs,
+            boolean extend);
+
+    /**
+     * Cancel a timer by ID.  Return true if the timer was running and canceled.  Return false if
+     * the timer was not found or if the timer had already expired.
+     */
+    private static native boolean nativeAnrTimerCancel(long service, int timerId);
+
+    /** Accept an expired timer by ID.  Return true if the timer was found. */
+    private static native boolean nativeAnrTimerAccept(long service, int timerId);
+
+    /** Discard an expired timer by ID.  Return true if the timer was found.  */
+    private static native boolean nativeAnrTimerDiscard(long service, int timerId);
+
+    /** Prod the native library to log a few statistics. */
+    private static native void nativeAnrTimerDump(long service, boolean verbose);
+
+    // This is not a native method but it is a native interface, in the sense that it is called from
+    // the native layer to report timer expiration.  The function must return true if the expiration
+    // message is delivered to the upper layers and false if it could not be delivered.
+    // private boolean expire(int timerId, int pid, int uid);
 }
diff --git a/services/core/java/com/android/server/vcn/VcnContext.java b/services/core/java/com/android/server/vcn/VcnContext.java
index 9213d96..ed04e5f 100644
--- a/services/core/java/com/android/server/vcn/VcnContext.java
+++ b/services/core/java/com/android/server/vcn/VcnContext.java
@@ -34,6 +34,7 @@
     @NonNull private final Looper mLooper;
     @NonNull private final VcnNetworkProvider mVcnNetworkProvider;
     @NonNull private final FeatureFlags mFeatureFlags;
+    @NonNull private final com.android.net.flags.FeatureFlags mCoreNetFeatureFlags;
     private final boolean mIsInTestMode;
 
     public VcnContext(
@@ -48,6 +49,7 @@
 
         // Auto-generated class
         mFeatureFlags = new FeatureFlagsImpl();
+        mCoreNetFeatureFlags = new com.android.net.flags.FeatureFlagsImpl();
     }
 
     @NonNull
@@ -69,11 +71,23 @@
         return mIsInTestMode;
     }
 
+    public boolean isFlagNetworkMetricMonitorEnabled() {
+        return mFeatureFlags.networkMetricMonitor();
+    }
+
+    public boolean isFlagIpSecTransformStateEnabled() {
+        return mCoreNetFeatureFlags.ipsecTransformState();
+    }
+
     @NonNull
     public FeatureFlags getFeatureFlags() {
         return mFeatureFlags;
     }
 
+    public boolean isFlagSafeModeTimeoutConfigEnabled() {
+        return mFeatureFlags.safeModeTimeoutConfig();
+    }
+
     /**
      * Verifies that the caller is running on the VcnContext Thread.
      *
diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
index 54c97dd..fcc0de1 100644
--- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
+++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
@@ -915,9 +915,11 @@
             // TODO(b/180132994): explore safely removing this Thread check
             mVcnContext.ensureRunningOnLooperThread();
 
-            logInfo(
-                    "Selected underlying network changed: "
-                            + (underlying == null ? null : underlying.network));
+            if (!UnderlyingNetworkRecord.isSameNetwork(mUnderlying, underlying)) {
+                logInfo(
+                        "Selected underlying network changed: "
+                                + (underlying == null ? null : underlying.network));
+            }
 
             // TODO(b/179091925): Move the delayed-message handling to BaseState
 
@@ -1242,9 +1244,28 @@
                 createScheduledAlarm(
                         SAFEMODE_TIMEOUT_ALARM,
                         delayedMessage,
-                        mVcnContext.isInTestMode()
-                                ? TimeUnit.SECONDS.toMillis(SAFEMODE_TIMEOUT_SECONDS_TEST_MODE)
-                                : TimeUnit.SECONDS.toMillis(SAFEMODE_TIMEOUT_SECONDS));
+                        getSafeModeTimeoutMs(mVcnContext, mLastSnapshot, mSubscriptionGroup));
+    }
+
+    /** Gets the safe mode timeout */
+    @VisibleForTesting(visibility = Visibility.PRIVATE)
+    public static long getSafeModeTimeoutMs(
+            VcnContext vcnContext, TelephonySubscriptionSnapshot snapshot, ParcelUuid subGrp) {
+        final int defaultSeconds =
+                vcnContext.isInTestMode()
+                        ? SAFEMODE_TIMEOUT_SECONDS_TEST_MODE
+                        : SAFEMODE_TIMEOUT_SECONDS;
+
+        final PersistableBundleWrapper carrierConfig = snapshot.getCarrierConfigForSubGrp(subGrp);
+        int resultSeconds = defaultSeconds;
+
+        if (vcnContext.isFlagSafeModeTimeoutConfigEnabled() && carrierConfig != null) {
+            resultSeconds =
+                    carrierConfig.getInt(
+                            VcnManager.VCN_SAFE_MODE_TIMEOUT_SECONDS_KEY, defaultSeconds);
+        }
+
+        return TimeUnit.SECONDS.toMillis(resultSeconds);
     }
 
     private void cancelSafeModeAlarm() {
diff --git a/services/core/java/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java b/services/core/java/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java
new file mode 100644
index 0000000..5f4852f
--- /dev/null
+++ b/services/core/java/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java
@@ -0,0 +1,387 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.vcn.routeselection;
+
+import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.IpSecTransformState;
+import android.net.Network;
+import android.net.vcn.VcnManager;
+import android.os.Handler;
+import android.os.HandlerExecutor;
+import android.os.OutcomeReceiver;
+import android.os.PowerManager;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.annotations.VisibleForTesting.Visibility;
+import com.android.server.vcn.VcnContext;
+
+import java.util.BitSet;
+import java.util.Objects;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * IpSecPacketLossDetector is responsible for continuously monitoring IPsec packet loss
+ *
+ * <p>When the packet loss rate surpass the threshold, IpSecPacketLossDetector will report it to the
+ * caller
+ *
+ * <p>IpSecPacketLossDetector will start monitoring when the network being monitored is selected AND
+ * an inbound IpSecTransform has been applied to this network.
+ *
+ * <p>This class is flag gated by "network_metric_monitor" and "ipsec_tramsform_state"
+ */
+public class IpSecPacketLossDetector extends NetworkMetricMonitor {
+    private static final String TAG = IpSecPacketLossDetector.class.getSimpleName();
+
+    @VisibleForTesting(visibility = Visibility.PRIVATE)
+    static final int PACKET_LOSS_UNAVALAIBLE = -1;
+
+    // For VoIP, losses between 5% and 10% of the total packet stream will affect the quality
+    // significantly (as per "Computer Networking for LANS to WANS: Hardware, Software and
+    // Security"). For audio and video streaming, above 10-12% packet loss is unacceptable (as per
+    // "ICTP-SDU: About PingER"). Thus choose 12% as a conservative default threshold to declare a
+    // validation failure.
+    private static final int IPSEC_PACKET_LOSS_PERCENT_THRESHOLD_DEFAULT = 12;
+
+    private static final int POLL_IPSEC_STATE_INTERVAL_SECONDS_DEFAULT = 20;
+
+    private long mPollIpSecStateIntervalMs;
+    private final int mPacketLossRatePercentThreshold;
+
+    @NonNull private final Handler mHandler;
+    @NonNull private final PowerManager mPowerManager;
+    @NonNull private final Object mCancellationToken = new Object();
+    @NonNull private final PacketLossCalculator mPacketLossCalculator;
+
+    @Nullable private IpSecTransformWrapper mInboundTransform;
+    @Nullable private IpSecTransformState mLastIpSecTransformState;
+
+    @VisibleForTesting(visibility = Visibility.PRIVATE)
+    public IpSecPacketLossDetector(
+            @NonNull VcnContext vcnContext,
+            @NonNull Network network,
+            @Nullable PersistableBundleWrapper carrierConfig,
+            @NonNull NetworkMetricMonitorCallback callback,
+            @NonNull Dependencies deps)
+            throws IllegalAccessException {
+        super(vcnContext, network, carrierConfig, callback);
+
+        Objects.requireNonNull(deps, "Missing deps");
+
+        if (!vcnContext.isFlagIpSecTransformStateEnabled()) {
+            // Caller error
+            logWtf("ipsecTransformState flag disabled");
+            throw new IllegalAccessException("ipsecTransformState flag disabled");
+        }
+
+        mHandler = new Handler(getVcnContext().getLooper());
+
+        mPowerManager = getVcnContext().getContext().getSystemService(PowerManager.class);
+
+        mPacketLossCalculator = deps.getPacketLossCalculator();
+
+        mPollIpSecStateIntervalMs = getPollIpSecStateIntervalMs(carrierConfig);
+        mPacketLossRatePercentThreshold = getPacketLossRatePercentThreshold(carrierConfig);
+
+        // Register for system broadcasts to monitor idle mode change
+        final IntentFilter intentFilter = new IntentFilter();
+        intentFilter.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
+        getVcnContext()
+                .getContext()
+                .registerReceiver(
+                        new BroadcastReceiver() {
+                            @Override
+                            public void onReceive(Context context, Intent intent) {
+                                if (PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED.equals(
+                                                intent.getAction())
+                                        && mPowerManager.isDeviceIdleMode()) {
+                                    mLastIpSecTransformState = null;
+                                }
+                            }
+                        },
+                        intentFilter,
+                        null /* broadcastPermission not required */,
+                        mHandler);
+    }
+
+    public IpSecPacketLossDetector(
+            @NonNull VcnContext vcnContext,
+            @NonNull Network network,
+            @Nullable PersistableBundleWrapper carrierConfig,
+            @NonNull NetworkMetricMonitorCallback callback)
+            throws IllegalAccessException {
+        this(vcnContext, network, carrierConfig, callback, new Dependencies());
+    }
+
+    @VisibleForTesting(visibility = Visibility.PRIVATE)
+    public static class Dependencies {
+        public PacketLossCalculator getPacketLossCalculator() {
+            return new PacketLossCalculator();
+        }
+    }
+
+    private static long getPollIpSecStateIntervalMs(
+            @Nullable PersistableBundleWrapper carrierConfig) {
+        final int seconds;
+
+        if (carrierConfig != null) {
+            seconds =
+                    carrierConfig.getInt(
+                            VcnManager.VCN_NETWORK_SELECTION_POLL_IPSEC_STATE_INTERVAL_SECONDS_KEY,
+                            POLL_IPSEC_STATE_INTERVAL_SECONDS_DEFAULT);
+        } else {
+            seconds = POLL_IPSEC_STATE_INTERVAL_SECONDS_DEFAULT;
+        }
+
+        return TimeUnit.SECONDS.toMillis(seconds);
+    }
+
+    private static int getPacketLossRatePercentThreshold(
+            @Nullable PersistableBundleWrapper carrierConfig) {
+        if (carrierConfig != null) {
+            return carrierConfig.getInt(
+                    VcnManager.VCN_NETWORK_SELECTION_IPSEC_PACKET_LOSS_PERCENT_THRESHOLD_KEY,
+                    IPSEC_PACKET_LOSS_PERCENT_THRESHOLD_DEFAULT);
+        }
+        return IPSEC_PACKET_LOSS_PERCENT_THRESHOLD_DEFAULT;
+    }
+
+    @Override
+    protected void onSelectedUnderlyingNetworkChanged() {
+        if (!isSelectedUnderlyingNetwork()) {
+            mInboundTransform = null;
+            stop();
+        }
+
+        // No action when the underlying network got selected. Wait for the inbound transform to
+        // start the monitor
+    }
+
+    @Override
+    public void setInboundTransformInternal(@NonNull IpSecTransformWrapper inboundTransform) {
+        Objects.requireNonNull(inboundTransform, "inboundTransform is null");
+
+        if (Objects.equals(inboundTransform, mInboundTransform)) {
+            return;
+        }
+
+        if (!isSelectedUnderlyingNetwork()) {
+            logWtf("setInboundTransform called but network not selected");
+            return;
+        }
+
+        // When multiple parallel inbound transforms are created, NetworkMetricMonitor will be
+        // enabled on the last one as a sample
+        mInboundTransform = inboundTransform;
+        start();
+    }
+
+    @Override
+    public void setCarrierConfig(@Nullable PersistableBundleWrapper carrierConfig) {
+        // The already scheduled event will not be affected. The followup events will be scheduled
+        // with the new interval
+        mPollIpSecStateIntervalMs = getPollIpSecStateIntervalMs(carrierConfig);
+    }
+
+    @Override
+    protected void start() {
+        super.start();
+        clearTransformStateAndPollingEvents();
+        mHandler.postDelayed(new PollIpSecStateRunnable(), mCancellationToken, 0L);
+    }
+
+    @Override
+    public void stop() {
+        super.stop();
+        clearTransformStateAndPollingEvents();
+    }
+
+    private void clearTransformStateAndPollingEvents() {
+        mHandler.removeCallbacksAndEqualMessages(mCancellationToken);
+        mLastIpSecTransformState = null;
+    }
+
+    @Override
+    public void close() {
+        super.close();
+
+        if (mInboundTransform != null) {
+            mInboundTransform.close();
+        }
+    }
+
+    @VisibleForTesting(visibility = Visibility.PRIVATE)
+    @Nullable
+    public IpSecTransformState getLastTransformState() {
+        return mLastIpSecTransformState;
+    }
+
+    @VisibleForTesting(visibility = Visibility.PROTECTED)
+    @Nullable
+    public IpSecTransformWrapper getInboundTransformInternal() {
+        return mInboundTransform;
+    }
+
+    private class PollIpSecStateRunnable implements Runnable {
+        @Override
+        public void run() {
+            if (!isStarted()) {
+                logWtf("Monitor stopped but PollIpSecStateRunnable not removed from Handler");
+                return;
+            }
+
+            getInboundTransformInternal()
+                    .getIpSecTransformState(
+                            new HandlerExecutor(mHandler), new IpSecTransformStateReceiver());
+
+            // Schedule for next poll
+            mHandler.postDelayed(
+                    new PollIpSecStateRunnable(), mCancellationToken, mPollIpSecStateIntervalMs);
+        }
+    }
+
+    private class IpSecTransformStateReceiver
+            implements OutcomeReceiver<IpSecTransformState, RuntimeException> {
+        @Override
+        public void onResult(@NonNull IpSecTransformState state) {
+            getVcnContext().ensureRunningOnLooperThread();
+
+            if (!isStarted()) {
+                return;
+            }
+
+            onIpSecTransformStateReceived(state);
+        }
+
+        @Override
+        public void onError(@NonNull RuntimeException error) {
+            getVcnContext().ensureRunningOnLooperThread();
+
+            // Nothing we can do here
+            logW("TransformStateReceiver#onError " + error.toString());
+        }
+    }
+
+    private void onIpSecTransformStateReceived(@NonNull IpSecTransformState state) {
+        if (mLastIpSecTransformState == null) {
+            // This is first time to poll the state
+            mLastIpSecTransformState = state;
+            return;
+        }
+
+        final int packetLossRate =
+                mPacketLossCalculator.getPacketLossRatePercentage(
+                        mLastIpSecTransformState, state, getLogPrefix());
+
+        if (packetLossRate == PACKET_LOSS_UNAVALAIBLE) {
+            return;
+        }
+
+        final String logMsg =
+                "packetLossRate: "
+                        + packetLossRate
+                        + "% in the past "
+                        + (state.getTimestamp() - mLastIpSecTransformState.getTimestamp())
+                        + "ms";
+
+        mLastIpSecTransformState = state;
+        if (packetLossRate < mPacketLossRatePercentThreshold) {
+            logV(logMsg);
+            onValidationResultReceivedInternal(false /* isFailed */);
+        } else {
+            logInfo(logMsg);
+            onValidationResultReceivedInternal(true /* isFailed */);
+        }
+    }
+
+    @VisibleForTesting(visibility = Visibility.PRIVATE)
+    public static class PacketLossCalculator {
+        /** Calculate the packet loss rate between two timestamps */
+        public int getPacketLossRatePercentage(
+                @NonNull IpSecTransformState oldState,
+                @NonNull IpSecTransformState newState,
+                String logPrefix) {
+            logVIpSecTransform("oldState", oldState, logPrefix);
+            logVIpSecTransform("newState", newState, logPrefix);
+
+            final int replayWindowSize = oldState.getReplayBitmap().length * 8;
+            final long oldSeqHi = oldState.getRxHighestSequenceNumber();
+            final long oldSeqLow = Math.max(0L, oldSeqHi - replayWindowSize + 1);
+            final long newSeqHi = newState.getRxHighestSequenceNumber();
+            final long newSeqLow = Math.max(0L, newSeqHi - replayWindowSize + 1);
+
+            if (oldSeqHi == newSeqHi || newSeqHi < replayWindowSize) {
+                // The replay window did not proceed and all packets might have been delivered out
+                // of order
+                return PACKET_LOSS_UNAVALAIBLE;
+            }
+
+            // Get the expected packet count by assuming there is no packet loss. In this case, SA
+            // should receive all packets whose sequence numbers are smaller than the lower bound of
+            // the replay window AND the packets received within the window.
+            // When the lower bound is 0, it's not possible to tell whether packet with seqNo 0 is
+            // received or not. For simplicity just assume that packet is received.
+            final long newExpectedPktCnt = newSeqLow + getPacketCntInReplayWindow(newState);
+            final long oldExpectedPktCnt = oldSeqLow + getPacketCntInReplayWindow(oldState);
+
+            final long expectedPktCntDiff = newExpectedPktCnt - oldExpectedPktCnt;
+            final long actualPktCntDiff = newState.getPacketCount() - oldState.getPacketCount();
+
+            logV(
+                    TAG,
+                    logPrefix
+                            + " expectedPktCntDiff: "
+                            + expectedPktCntDiff
+                            + " actualPktCntDiff: "
+                            + actualPktCntDiff);
+
+            if (expectedPktCntDiff < 0
+                    || expectedPktCntDiff == 0
+                    || actualPktCntDiff < 0
+                    || actualPktCntDiff > expectedPktCntDiff) {
+                logWtf(TAG, "Impossible values for expectedPktCntDiff or" + " actualPktCntDiff");
+                return PACKET_LOSS_UNAVALAIBLE;
+            }
+
+            return 100 - (int) (actualPktCntDiff * 100 / expectedPktCntDiff);
+        }
+    }
+
+    private static void logVIpSecTransform(
+            String transformTag, IpSecTransformState state, String logPrefix) {
+        final String stateString =
+                " seqNo: "
+                        + state.getRxHighestSequenceNumber()
+                        + " | pktCnt: "
+                        + state.getPacketCount()
+                        + " | pktCntInWindow: "
+                        + getPacketCntInReplayWindow(state);
+        logV(TAG, logPrefix + " " + transformTag + stateString);
+    }
+
+    /** Get the number of received packets within the replay window */
+    private static long getPacketCntInReplayWindow(@NonNull IpSecTransformState state) {
+        return BitSet.valueOf(state.getReplayBitmap()).cardinality();
+    }
+}
diff --git a/services/core/java/com/android/server/vcn/routeselection/NetworkMetricMonitor.java b/services/core/java/com/android/server/vcn/routeselection/NetworkMetricMonitor.java
new file mode 100644
index 0000000..a79f188
--- /dev/null
+++ b/services/core/java/com/android/server/vcn/routeselection/NetworkMetricMonitor.java
@@ -0,0 +1,269 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.vcn.routeselection;
+
+import static com.android.server.VcnManagementService.LOCAL_LOG;
+import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.IpSecTransform;
+import android.net.IpSecTransformState;
+import android.net.Network;
+import android.os.OutcomeReceiver;
+import android.util.CloseGuard;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.annotations.VisibleForTesting.Visibility;
+import com.android.server.vcn.VcnContext;
+
+import java.util.Objects;
+import java.util.concurrent.Executor;
+
+/**
+ * NetworkMetricMonitor is responsible for managing metric monitoring and tracking validation
+ * results.
+ *
+ * <p>This class is flag gated by "network_metric_monitor"
+ */
+public abstract class NetworkMetricMonitor implements AutoCloseable {
+    private static final String TAG = NetworkMetricMonitor.class.getSimpleName();
+
+    private static final boolean VDBG = false; // STOPSHIP: if true
+
+    @NonNull private final CloseGuard mCloseGuard = new CloseGuard();
+
+    @NonNull private final VcnContext mVcnContext;
+    @NonNull private final Network mNetwork;
+    @NonNull private final NetworkMetricMonitorCallback mCallback;
+
+    private boolean mIsSelectedUnderlyingNetwork;
+    private boolean mIsStarted;
+    private boolean mIsValidationFailed;
+
+    protected NetworkMetricMonitor(
+            @NonNull VcnContext vcnContext,
+            @NonNull Network network,
+            @Nullable PersistableBundleWrapper carrierConfig,
+            @NonNull NetworkMetricMonitorCallback callback)
+            throws IllegalAccessException {
+        if (!vcnContext.isFlagNetworkMetricMonitorEnabled()) {
+            // Caller error
+            logWtf("networkMetricMonitor flag disabled");
+            throw new IllegalAccessException("networkMetricMonitor flag disabled");
+        }
+
+        mVcnContext = Objects.requireNonNull(vcnContext, "Missing vcnContext");
+        mNetwork = Objects.requireNonNull(network, "Missing network");
+        mCallback = Objects.requireNonNull(callback, "Missing callback");
+
+        mIsSelectedUnderlyingNetwork = false;
+        mIsStarted = false;
+        mIsValidationFailed = false;
+    }
+
+    /** Callback to notify caller of the validation result */
+    public interface NetworkMetricMonitorCallback {
+        /** Called when there is a validation result is ready */
+        void onValidationResultReceived();
+    }
+
+    /**
+     * Start monitoring
+     *
+     * <p>This method might be called on a an already started monitor for updating monitor
+     * properties (e.g. IpSecTransform, carrier config)
+     *
+     * <p>Subclasses MUST call super.start() when overriding this method
+     */
+    protected void start() {
+        mIsStarted = true;
+    }
+
+    /**
+     * Stop monitoring
+     *
+     * <p>Subclasses MUST call super.stop() when overriding this method
+     */
+    public void stop() {
+        mIsValidationFailed = false;
+        mIsStarted = false;
+    }
+
+    /** Called by the subclasses when the validation result is ready */
+    protected void onValidationResultReceivedInternal(boolean isFailed) {
+        mIsValidationFailed = isFailed;
+        mCallback.onValidationResultReceived();
+    }
+
+    /** Called when the underlying network changes to selected or unselected */
+    protected abstract void onSelectedUnderlyingNetworkChanged();
+
+    /**
+     * Mark the network being monitored selected or unselected
+     *
+     * <p>Subclasses MUST call super when overriding this method
+     */
+    public void setIsSelectedUnderlyingNetwork(boolean isSelectedUnderlyingNetwork) {
+        if (mIsSelectedUnderlyingNetwork == isSelectedUnderlyingNetwork) {
+            return;
+        }
+
+        mIsSelectedUnderlyingNetwork = isSelectedUnderlyingNetwork;
+        onSelectedUnderlyingNetworkChanged();
+    }
+
+    /** Wrapper that allows injection for testing purposes */
+    @VisibleForTesting(visibility = Visibility.PROTECTED)
+    public static class IpSecTransformWrapper {
+        @NonNull public final IpSecTransform ipSecTransform;
+
+        public IpSecTransformWrapper(@NonNull IpSecTransform ipSecTransform) {
+            this.ipSecTransform = ipSecTransform;
+        }
+
+        /** Poll an IpSecTransformState */
+        public void getIpSecTransformState(
+                @NonNull Executor executor,
+                @NonNull OutcomeReceiver<IpSecTransformState, RuntimeException> callback) {
+            ipSecTransform.getIpSecTransformState(executor, callback);
+        }
+
+        /** Close this instance and release the underlying resources */
+        public void close() {
+            ipSecTransform.close();
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(ipSecTransform);
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (!(o instanceof IpSecTransformWrapper)) {
+                return false;
+            }
+
+            final IpSecTransformWrapper other = (IpSecTransformWrapper) o;
+
+            return Objects.equals(ipSecTransform, other.ipSecTransform);
+        }
+    }
+
+    /** Set the IpSecTransform that applied to the Network being monitored */
+    public void setInboundTransform(@NonNull IpSecTransform inTransform) {
+        setInboundTransformInternal(new IpSecTransformWrapper(inTransform));
+    }
+
+    /**
+     * Set the IpSecTransform that applied to the Network being monitored *
+     *
+     * <p>Subclasses MUST call super when overriding this method
+     */
+    @VisibleForTesting(visibility = Visibility.PRIVATE)
+    public void setInboundTransformInternal(@NonNull IpSecTransformWrapper inTransform) {
+        // Subclasses MUST override it if they care
+    }
+
+    /** Update the carrierconfig */
+    public void setCarrierConfig(@Nullable PersistableBundleWrapper carrierConfig) {
+        // Subclasses MUST override it if they care
+    }
+
+    public boolean isValidationFailed() {
+        return mIsValidationFailed;
+    }
+
+    public boolean isSelectedUnderlyingNetwork() {
+        return mIsSelectedUnderlyingNetwork;
+    }
+
+    public boolean isStarted() {
+        return mIsStarted;
+    }
+
+    @NonNull
+    public VcnContext getVcnContext() {
+        return mVcnContext;
+    }
+
+    // Override methods for AutoCloseable. Subclasses MUST call super when overriding this method
+    @Override
+    public void close() {
+        mCloseGuard.close();
+
+        stop();
+    }
+
+    // Override #finalize() to use closeGuard for flagging that #close() was not called
+    @SuppressWarnings("Finalize")
+    @Override
+    protected void finalize() throws Throwable {
+        try {
+            if (mCloseGuard != null) {
+                mCloseGuard.warnIfOpen();
+            }
+            close();
+        } finally {
+            super.finalize();
+        }
+    }
+
+    private String getClassName() {
+        return this.getClass().getSimpleName();
+    }
+
+    protected String getLogPrefix() {
+        return " [Network " + mNetwork + "] ";
+    }
+
+    protected void logV(String msg) {
+        if (VDBG) {
+            Slog.v(getClassName(), getLogPrefix() + msg);
+            LOCAL_LOG.log("[VERBOSE ] " + getClassName() + getLogPrefix() + msg);
+        }
+    }
+
+    protected void logInfo(String msg) {
+        Slog.i(getClassName(), getLogPrefix() + msg);
+        LOCAL_LOG.log("[INFO ] " + getClassName() + getLogPrefix() + msg);
+    }
+
+    protected void logW(String msg) {
+        Slog.w(getClassName(), getLogPrefix() + msg);
+        LOCAL_LOG.log("[WARN ] " + getClassName() + getLogPrefix() + msg);
+    }
+
+    protected void logWtf(String msg) {
+        Slog.wtf(getClassName(), getLogPrefix() + msg);
+        LOCAL_LOG.log("[WTF ] " + getClassName() + getLogPrefix() + msg);
+    }
+
+    protected static void logV(String className, String msgWithPrefix) {
+        if (VDBG) {
+            Slog.wtf(className, msgWithPrefix);
+            LOCAL_LOG.log("[VERBOSE ] " + className + msgWithPrefix);
+        }
+    }
+
+    protected static void logWtf(String className, String msgWithPrefix) {
+        Slog.wtf(className, msgWithPrefix);
+        LOCAL_LOG.log("[WTF ] " + className + msgWithPrefix);
+    }
+}
diff --git a/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java b/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java
index 7f129ea..d32e5cc 100644
--- a/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java
+++ b/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java
@@ -47,7 +47,6 @@
 
 import java.util.List;
 import java.util.Map;
-import java.util.Objects;
 import java.util.Set;
 
 /** @hide */
@@ -86,7 +85,6 @@
      * <p>VCN MUST never select a non-INTERNET network that are unvalidated or fail to match any
      * template as the underlying network.
      */
-    @VisibleForTesting(visibility = Visibility.PRIVATE)
     static final int PRIORITY_INVALID = -1;
 
     /** Gives networks a priority class, based on configured VcnGatewayConnectionConfig */
@@ -96,7 +94,7 @@
             List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates,
             ParcelUuid subscriptionGroup,
             TelephonySubscriptionSnapshot snapshot,
-            UnderlyingNetworkRecord currentlySelected,
+            boolean isSelected,
             PersistableBundleWrapper carrierConfig) {
         // mRouteSelectionNetworkRequest requires a network be both VALIDATED and NOT_SUSPENDED
 
@@ -118,7 +116,7 @@
                     networkRecord,
                     subscriptionGroup,
                     snapshot,
-                    currentlySelected,
+                    isSelected,
                     carrierConfig)) {
                 return priorityIndex;
             }
@@ -140,12 +138,9 @@
             UnderlyingNetworkRecord networkRecord,
             ParcelUuid subscriptionGroup,
             TelephonySubscriptionSnapshot snapshot,
-            UnderlyingNetworkRecord currentlySelected,
+            boolean isSelected,
             PersistableBundleWrapper carrierConfig) {
         final NetworkCapabilities caps = networkRecord.networkCapabilities;
-        final boolean isSelectedUnderlyingNetwork =
-                currentlySelected != null
-                        && Objects.equals(currentlySelected.network, networkRecord.network);
 
         final int meteredMatch = networkPriority.getMetered();
         final boolean isMetered = !caps.hasCapability(NET_CAPABILITY_NOT_METERED);
@@ -159,7 +154,7 @@
         if (caps.getLinkUpstreamBandwidthKbps() < networkPriority.getMinExitUpstreamBandwidthKbps()
                 || (caps.getLinkUpstreamBandwidthKbps()
                                 < networkPriority.getMinEntryUpstreamBandwidthKbps()
-                        && !isSelectedUnderlyingNetwork)) {
+                        && !isSelected)) {
             return false;
         }
 
@@ -167,7 +162,7 @@
                         < networkPriority.getMinExitDownstreamBandwidthKbps()
                 || (caps.getLinkDownstreamBandwidthKbps()
                                 < networkPriority.getMinEntryDownstreamBandwidthKbps()
-                        && !isSelectedUnderlyingNetwork)) {
+                        && !isSelected)) {
             return false;
         }
 
@@ -191,7 +186,7 @@
             return checkMatchesWifiPriorityRule(
                     (VcnWifiUnderlyingNetworkTemplate) networkPriority,
                     networkRecord,
-                    currentlySelected,
+                    isSelected,
                     carrierConfig);
         }
 
@@ -214,7 +209,7 @@
     public static boolean checkMatchesWifiPriorityRule(
             VcnWifiUnderlyingNetworkTemplate networkPriority,
             UnderlyingNetworkRecord networkRecord,
-            UnderlyingNetworkRecord currentlySelected,
+            boolean isSelected,
             PersistableBundleWrapper carrierConfig) {
         final NetworkCapabilities caps = networkRecord.networkCapabilities;
 
@@ -223,7 +218,7 @@
         }
 
         // TODO: Move the Network Quality check to the network metric monitor framework.
-        if (!isWifiRssiAcceptable(networkRecord, currentlySelected, carrierConfig)) {
+        if (!isWifiRssiAcceptable(networkRecord, isSelected, carrierConfig)) {
             return false;
         }
 
@@ -237,15 +232,11 @@
 
     private static boolean isWifiRssiAcceptable(
             UnderlyingNetworkRecord networkRecord,
-            UnderlyingNetworkRecord currentlySelected,
+            boolean isSelected,
             PersistableBundleWrapper carrierConfig) {
         final NetworkCapabilities caps = networkRecord.networkCapabilities;
-        final boolean isSelectedNetwork =
-                currentlySelected != null
-                        && networkRecord.network.equals(currentlySelected.network);
 
-        if (isSelectedNetwork
-                && caps.getSignalStrength() >= getWifiExitRssiThreshold(carrierConfig)) {
+        if (isSelected && caps.getSignalStrength() >= getWifiExitRssiThreshold(carrierConfig)) {
             return true;
         }
 
diff --git a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java
index 6afa795..48df44b 100644
--- a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java
+++ b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java
@@ -48,6 +48,7 @@
 import android.util.Slog;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.annotations.VisibleForTesting.Visibility;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
 import com.android.server.vcn.VcnContext;
@@ -83,6 +84,9 @@
     @NonNull private final TelephonyCallback mActiveDataSubIdListener =
             new VcnActiveDataSubscriptionIdListener();
 
+    private final Map<Network, UnderlyingNetworkEvaluator> mUnderlyingNetworkRecords =
+            new ArrayMap<>();
+
     @NonNull private final List<NetworkCallback> mCellBringupCallbacks = new ArrayList<>();
     @Nullable private NetworkCallback mWifiBringupCallback;
     @Nullable private NetworkCallback mWifiEntryRssiThresholdCallback;
@@ -105,7 +109,8 @@
         this(vcnContext, connectionConfig, subscriptionGroup, snapshot, cb, new Dependencies());
     }
 
-    private UnderlyingNetworkController(
+    @VisibleForTesting(visibility = Visibility.PRIVATE)
+    UnderlyingNetworkController(
             @NonNull VcnContext vcnContext,
             @NonNull VcnGatewayConnectionConfig connectionConfig,
             @NonNull ParcelUuid subscriptionGroup,
@@ -196,6 +201,7 @@
         NetworkCallback oldWifiExitRssiThresholdCallback = mWifiExitRssiThresholdCallback;
         List<NetworkCallback> oldCellCallbacks = new ArrayList<>(mCellBringupCallbacks);
         mCellBringupCallbacks.clear();
+        mUnderlyingNetworkRecords.clear();
 
         // Register new callbacks. Make-before-break; always register new callbacks before removal
         // of old callbacks
@@ -395,6 +401,18 @@
         // Update carrier config
         mCarrierConfig = mLastSnapshot.getCarrierConfigForSubGrp(mSubscriptionGroup);
 
+        // Make sure all evaluators use the same updated TelephonySubscriptionSnapshot and carrier
+        // config to calculate their cached priority classes. For simplicity, the
+        // UnderlyingNetworkController does not listen for changes in VCN-related carrier config
+        // keys, and changes are applied at restart of the VcnGatewayConnection
+        for (UnderlyingNetworkEvaluator evaluator : mUnderlyingNetworkRecords.values()) {
+            evaluator.reevaluate(
+                    mConnectionConfig.getVcnUnderlyingNetworkPriorities(),
+                    mSubscriptionGroup,
+                    mLastSnapshot,
+                    mCarrierConfig);
+        }
+
         // Only trigger re-registration if subIds in this group have changed
         if (oldSnapshot
                 .getAllSubIdsInGroup(mSubscriptionGroup)
@@ -418,32 +436,62 @@
                 .unregisterTelephonyCallback(mActiveDataSubIdListener);
     }
 
+    private TreeSet<UnderlyingNetworkEvaluator> getSortedUnderlyingNetworks() {
+        TreeSet<UnderlyingNetworkEvaluator> sorted =
+                new TreeSet<>(UnderlyingNetworkEvaluator.getComparator());
+
+        for (UnderlyingNetworkEvaluator evaluator : mUnderlyingNetworkRecords.values()) {
+            if (evaluator.getPriorityClass() != NetworkPriorityClassifier.PRIORITY_INVALID) {
+                sorted.add(evaluator);
+            }
+        }
+
+        return sorted;
+    }
+
     private void reevaluateNetworks() {
         if (mIsQuitting || mRouteSelectionCallback == null) {
             return; // UnderlyingNetworkController has quit.
         }
 
-        TreeSet<UnderlyingNetworkRecord> sorted =
-                mRouteSelectionCallback.getSortedUnderlyingNetworks();
-        UnderlyingNetworkRecord candidate = sorted.isEmpty() ? null : sorted.first();
+        TreeSet<UnderlyingNetworkEvaluator> sorted = getSortedUnderlyingNetworks();
+
+        UnderlyingNetworkEvaluator candidateEvaluator = sorted.isEmpty() ? null : sorted.first();
+        UnderlyingNetworkRecord candidate =
+                candidateEvaluator == null ? null : candidateEvaluator.getNetworkRecord();
         if (Objects.equals(mCurrentRecord, candidate)) {
             return;
         }
 
         String allNetworkPriorities = "";
-        for (UnderlyingNetworkRecord record : sorted) {
+        for (UnderlyingNetworkEvaluator recordEvaluator : sorted) {
             if (!allNetworkPriorities.isEmpty()) {
                 allNetworkPriorities += ", ";
             }
-            allNetworkPriorities += record.network + ": " + record.priorityClass;
+            allNetworkPriorities +=
+                    recordEvaluator.getNetwork() + ": " + recordEvaluator.getPriorityClass();
         }
-        logInfo(
-                "Selected network changed to "
-                        + (candidate == null ? null : candidate.network)
-                        + ", selected from list: "
-                        + allNetworkPriorities);
+
+        if (!UnderlyingNetworkRecord.isSameNetwork(mCurrentRecord, candidate)) {
+            logInfo(
+                    "Selected network changed to "
+                            + (candidate == null ? null : candidate.network)
+                            + ", selected from list: "
+                            + allNetworkPriorities);
+        }
+
         mCurrentRecord = candidate;
         mCb.onSelectedUnderlyingNetworkChanged(mCurrentRecord);
+
+        // Need to update all evaluators to ensure the previously selected one is unselected
+        for (UnderlyingNetworkEvaluator evaluator : mUnderlyingNetworkRecords.values()) {
+            evaluator.setIsSelected(
+                    candidateEvaluator == evaluator,
+                    mConnectionConfig.getVcnUnderlyingNetworkPriorities(),
+                    mSubscriptionGroup,
+                    mLastSnapshot,
+                    mCarrierConfig);
+        }
     }
 
     /**
@@ -463,46 +511,26 @@
      */
     @VisibleForTesting
     class UnderlyingNetworkListener extends NetworkCallback {
-        private final Map<Network, UnderlyingNetworkRecord.Builder>
-                mUnderlyingNetworkRecordBuilders = new ArrayMap<>();
-
         UnderlyingNetworkListener() {
             super(NetworkCallback.FLAG_INCLUDE_LOCATION_INFO);
         }
 
-        private TreeSet<UnderlyingNetworkRecord> getSortedUnderlyingNetworks() {
-            TreeSet<UnderlyingNetworkRecord> sorted =
-                    new TreeSet<>(UnderlyingNetworkRecord.getComparator());
-
-            for (UnderlyingNetworkRecord.Builder builder :
-                    mUnderlyingNetworkRecordBuilders.values()) {
-                if (builder.isValid()) {
-                    final UnderlyingNetworkRecord record =
-                            builder.build(
-                                    mVcnContext,
-                                    mConnectionConfig.getVcnUnderlyingNetworkPriorities(),
-                                    mSubscriptionGroup,
-                                    mLastSnapshot,
-                                    mCurrentRecord,
-                                    mCarrierConfig);
-                    if (record.priorityClass != NetworkPriorityClassifier.PRIORITY_INVALID) {
-                        sorted.add(record);
-                    }
-                }
-            }
-
-            return sorted;
-        }
-
         @Override
         public void onAvailable(@NonNull Network network) {
-            mUnderlyingNetworkRecordBuilders.put(
-                    network, new UnderlyingNetworkRecord.Builder(network));
+            mUnderlyingNetworkRecords.put(
+                    network,
+                    mDeps.newUnderlyingNetworkEvaluator(
+                            mVcnContext,
+                            network,
+                            mConnectionConfig.getVcnUnderlyingNetworkPriorities(),
+                            mSubscriptionGroup,
+                            mLastSnapshot,
+                            mCarrierConfig));
         }
 
         @Override
         public void onLost(@NonNull Network network) {
-            mUnderlyingNetworkRecordBuilders.remove(network);
+            mUnderlyingNetworkRecords.remove(network);
 
             reevaluateNetworks();
         }
@@ -510,15 +538,20 @@
         @Override
         public void onCapabilitiesChanged(
                 @NonNull Network network, @NonNull NetworkCapabilities networkCapabilities) {
-            final UnderlyingNetworkRecord.Builder builder =
-                    mUnderlyingNetworkRecordBuilders.get(network);
-            if (builder == null) {
+            final UnderlyingNetworkEvaluator evaluator = mUnderlyingNetworkRecords.get(network);
+            if (evaluator == null) {
                 logWtf("Got capabilities change for unknown key: " + network);
                 return;
             }
 
-            builder.setNetworkCapabilities(networkCapabilities);
-            if (builder.isValid()) {
+            evaluator.setNetworkCapabilities(
+                    networkCapabilities,
+                    mConnectionConfig.getVcnUnderlyingNetworkPriorities(),
+                    mSubscriptionGroup,
+                    mLastSnapshot,
+                    mCarrierConfig);
+
+            if (evaluator.isValid()) {
                 reevaluateNetworks();
             }
         }
@@ -526,30 +559,40 @@
         @Override
         public void onLinkPropertiesChanged(
                 @NonNull Network network, @NonNull LinkProperties linkProperties) {
-            final UnderlyingNetworkRecord.Builder builder =
-                    mUnderlyingNetworkRecordBuilders.get(network);
-            if (builder == null) {
+            final UnderlyingNetworkEvaluator evaluator = mUnderlyingNetworkRecords.get(network);
+            if (evaluator == null) {
                 logWtf("Got link properties change for unknown key: " + network);
                 return;
             }
 
-            builder.setLinkProperties(linkProperties);
-            if (builder.isValid()) {
+            evaluator.setLinkProperties(
+                    linkProperties,
+                    mConnectionConfig.getVcnUnderlyingNetworkPriorities(),
+                    mSubscriptionGroup,
+                    mLastSnapshot,
+                    mCarrierConfig);
+
+            if (evaluator.isValid()) {
                 reevaluateNetworks();
             }
         }
 
         @Override
         public void onBlockedStatusChanged(@NonNull Network network, boolean isBlocked) {
-            final UnderlyingNetworkRecord.Builder builder =
-                    mUnderlyingNetworkRecordBuilders.get(network);
-            if (builder == null) {
+            final UnderlyingNetworkEvaluator evaluator = mUnderlyingNetworkRecords.get(network);
+            if (evaluator == null) {
                 logWtf("Got blocked status change for unknown key: " + network);
                 return;
             }
 
-            builder.setIsBlocked(isBlocked);
-            if (builder.isValid()) {
+            evaluator.setIsBlocked(
+                    isBlocked,
+                    mConnectionConfig.getVcnUnderlyingNetworkPriorities(),
+                    mSubscriptionGroup,
+                    mLastSnapshot,
+                    mCarrierConfig);
+
+            if (evaluator.isValid()) {
                 reevaluateNetworks();
             }
         }
@@ -614,16 +657,8 @@
         pw.println("Underlying networks:");
         pw.increaseIndent();
         if (mRouteSelectionCallback != null) {
-            for (UnderlyingNetworkRecord record :
-                    mRouteSelectionCallback.getSortedUnderlyingNetworks()) {
-                record.dump(
-                        mVcnContext,
-                        pw,
-                        mConnectionConfig.getVcnUnderlyingNetworkPriorities(),
-                        mSubscriptionGroup,
-                        mLastSnapshot,
-                        mCurrentRecord,
-                        mCarrierConfig);
+            for (UnderlyingNetworkEvaluator recordEvaluator : getSortedUnderlyingNetworks()) {
+                recordEvaluator.dump(pw);
             }
         }
         pw.decreaseIndent();
@@ -653,5 +688,23 @@
                 @Nullable UnderlyingNetworkRecord underlyingNetworkRecord);
     }
 
-    private static class Dependencies {}
+    @VisibleForTesting(visibility = Visibility.PRIVATE)
+    public static class Dependencies {
+        /** Construct a new UnderlyingNetworkEvaluator */
+        public UnderlyingNetworkEvaluator newUnderlyingNetworkEvaluator(
+                @NonNull VcnContext vcnContext,
+                @NonNull Network network,
+                @NonNull List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates,
+                @NonNull ParcelUuid subscriptionGroup,
+                @NonNull TelephonySubscriptionSnapshot lastSnapshot,
+                @Nullable PersistableBundleWrapper carrierConfig) {
+            return new UnderlyingNetworkEvaluator(
+                    vcnContext,
+                    network,
+                    underlyingNetworkTemplates,
+                    subscriptionGroup,
+                    lastSnapshot,
+                    carrierConfig);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluator.java b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluator.java
new file mode 100644
index 0000000..c124a19
--- /dev/null
+++ b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluator.java
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.vcn.routeselection;
+
+import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.LinkProperties;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.vcn.VcnUnderlyingNetworkTemplate;
+import android.os.ParcelUuid;
+
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
+import com.android.server.vcn.VcnContext;
+
+import java.util.Comparator;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * UnderlyingNetworkEvaluator evaluates the quality and priority class of a network candidate for
+ * route selection.
+ *
+ * @hide
+ */
+public class UnderlyingNetworkEvaluator {
+    private static final String TAG = UnderlyingNetworkEvaluator.class.getSimpleName();
+
+    @NonNull private final VcnContext mVcnContext;
+    @NonNull private final UnderlyingNetworkRecord.Builder mNetworkRecordBuilder;
+
+    private boolean mIsSelected;
+    private int mPriorityClass = NetworkPriorityClassifier.PRIORITY_INVALID;
+
+    public UnderlyingNetworkEvaluator(
+            @NonNull VcnContext vcnContext,
+            @NonNull Network network,
+            @NonNull List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates,
+            @NonNull ParcelUuid subscriptionGroup,
+            @NonNull TelephonySubscriptionSnapshot lastSnapshot,
+            @Nullable PersistableBundleWrapper carrierConfig) {
+        mVcnContext = Objects.requireNonNull(vcnContext, "Missing vcnContext");
+
+        Objects.requireNonNull(underlyingNetworkTemplates, "Missing underlyingNetworkTemplates");
+        Objects.requireNonNull(subscriptionGroup, "Missing subscriptionGroup");
+        Objects.requireNonNull(lastSnapshot, "Missing lastSnapshot");
+
+        mNetworkRecordBuilder =
+                new UnderlyingNetworkRecord.Builder(
+                        Objects.requireNonNull(network, "Missing network"));
+        mIsSelected = false;
+
+        updatePriorityClass(
+                underlyingNetworkTemplates, subscriptionGroup, lastSnapshot, carrierConfig);
+    }
+
+    private void updatePriorityClass(
+            @NonNull List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates,
+            @NonNull ParcelUuid subscriptionGroup,
+            @NonNull TelephonySubscriptionSnapshot lastSnapshot,
+            @Nullable PersistableBundleWrapper carrierConfig) {
+        if (mNetworkRecordBuilder.isValid()) {
+            mPriorityClass =
+                    NetworkPriorityClassifier.calculatePriorityClass(
+                            mVcnContext,
+                            mNetworkRecordBuilder.build(),
+                            underlyingNetworkTemplates,
+                            subscriptionGroup,
+                            lastSnapshot,
+                            mIsSelected,
+                            carrierConfig);
+        } else {
+            mPriorityClass = NetworkPriorityClassifier.PRIORITY_INVALID;
+        }
+    }
+
+    public static Comparator<UnderlyingNetworkEvaluator> getComparator() {
+        return (left, right) -> {
+            final int leftIndex = left.mPriorityClass;
+            final int rightIndex = right.mPriorityClass;
+
+            // In the case of networks in the same priority class, prioritize based on other
+            // criteria (eg. actively selected network, link metrics, etc)
+            if (leftIndex == rightIndex) {
+                // TODO: Improve the strategy of network selection when both UnderlyingNetworkRecord
+                // fall into the same priority class.
+                if (left.mIsSelected) {
+                    return -1;
+                }
+                if (right.mIsSelected) {
+                    return 1;
+                }
+            }
+            return Integer.compare(leftIndex, rightIndex);
+        };
+    }
+
+    /** Set the NetworkCapabilities */
+    public void setNetworkCapabilities(
+            @NonNull NetworkCapabilities nc,
+            @NonNull List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates,
+            @NonNull ParcelUuid subscriptionGroup,
+            @NonNull TelephonySubscriptionSnapshot lastSnapshot,
+            @Nullable PersistableBundleWrapper carrierConfig) {
+        mNetworkRecordBuilder.setNetworkCapabilities(nc);
+
+        updatePriorityClass(
+                underlyingNetworkTemplates, subscriptionGroup, lastSnapshot, carrierConfig);
+    }
+
+    /** Set the LinkProperties */
+    public void setLinkProperties(
+            @NonNull LinkProperties lp,
+            @NonNull List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates,
+            @NonNull ParcelUuid subscriptionGroup,
+            @NonNull TelephonySubscriptionSnapshot lastSnapshot,
+            @Nullable PersistableBundleWrapper carrierConfig) {
+        mNetworkRecordBuilder.setLinkProperties(lp);
+
+        updatePriorityClass(
+                underlyingNetworkTemplates, subscriptionGroup, lastSnapshot, carrierConfig);
+    }
+
+    /** Set whether the network is blocked */
+    public void setIsBlocked(
+            boolean isBlocked,
+            @NonNull List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates,
+            @NonNull ParcelUuid subscriptionGroup,
+            @NonNull TelephonySubscriptionSnapshot lastSnapshot,
+            @Nullable PersistableBundleWrapper carrierConfig) {
+        mNetworkRecordBuilder.setIsBlocked(isBlocked);
+
+        updatePriorityClass(
+                underlyingNetworkTemplates, subscriptionGroup, lastSnapshot, carrierConfig);
+    }
+
+    /** Set whether the network is selected as VCN's underlying network */
+    public void setIsSelected(
+            boolean isSelected,
+            @NonNull List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates,
+            @NonNull ParcelUuid subscriptionGroup,
+            @NonNull TelephonySubscriptionSnapshot lastSnapshot,
+            @Nullable PersistableBundleWrapper carrierConfig) {
+        mIsSelected = isSelected;
+
+        updatePriorityClass(
+                underlyingNetworkTemplates, subscriptionGroup, lastSnapshot, carrierConfig);
+    }
+
+    /**
+     * Update the last TelephonySubscriptionSnapshot and carrier config to reevaluate the network
+     */
+    public void reevaluate(
+            @NonNull List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates,
+            @NonNull ParcelUuid subscriptionGroup,
+            @NonNull TelephonySubscriptionSnapshot lastSnapshot,
+            @Nullable PersistableBundleWrapper carrierConfig) {
+        updatePriorityClass(
+                underlyingNetworkTemplates, subscriptionGroup, lastSnapshot, carrierConfig);
+    }
+
+    /** Return whether this network evaluator is valid */
+    public boolean isValid() {
+        return mNetworkRecordBuilder.isValid();
+    }
+
+    /** Return the network */
+    public Network getNetwork() {
+        return mNetworkRecordBuilder.getNetwork();
+    }
+
+    /** Return the network record */
+    public UnderlyingNetworkRecord getNetworkRecord() {
+        return mNetworkRecordBuilder.build();
+    }
+
+    /** Return the priority class for network selection */
+    public int getPriorityClass() {
+        return mPriorityClass;
+    }
+
+    /** Dump the information of this instance */
+    public void dump(IndentingPrintWriter pw) {
+        pw.println("UnderlyingNetworkEvaluator:");
+        pw.increaseIndent();
+
+        if (mNetworkRecordBuilder.isValid()) {
+            getNetworkRecord().dump(pw);
+        } else {
+            pw.println(
+                    "UnderlyingNetworkRecord incomplete: mNetwork: "
+                            + mNetworkRecordBuilder.getNetwork());
+        }
+
+        pw.println("mIsSelected: " + mIsSelected);
+        pw.println("mPriorityClass: " + mPriorityClass);
+
+        pw.decreaseIndent();
+    }
+}
diff --git a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java
index aea9f4d..7ab8e55 100644
--- a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java
+++ b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java
@@ -16,24 +16,17 @@
 
 package com.android.server.vcn.routeselection;
 
-import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.net.LinkProperties;
 import android.net.Network;
 import android.net.NetworkCapabilities;
-import android.net.vcn.VcnUnderlyingNetworkTemplate;
-import android.os.ParcelUuid;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.annotations.VisibleForTesting.Visibility;
 import com.android.internal.util.IndentingPrintWriter;
-import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
-import com.android.server.vcn.VcnContext;
 
-import java.util.Comparator;
-import java.util.List;
 import java.util.Objects;
 
 /**
@@ -46,54 +39,17 @@
     @NonNull public final NetworkCapabilities networkCapabilities;
     @NonNull public final LinkProperties linkProperties;
     public final boolean isBlocked;
-    public final boolean isSelected;
-    public final int priorityClass;
 
     @VisibleForTesting(visibility = Visibility.PRIVATE)
     public UnderlyingNetworkRecord(
             @NonNull Network network,
             @NonNull NetworkCapabilities networkCapabilities,
             @NonNull LinkProperties linkProperties,
-            boolean isBlocked,
-            VcnContext vcnContext,
-            List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates,
-            ParcelUuid subscriptionGroup,
-            TelephonySubscriptionSnapshot snapshot,
-            UnderlyingNetworkRecord currentlySelected,
-            PersistableBundleWrapper carrierConfig) {
+            boolean isBlocked) {
         this.network = network;
         this.networkCapabilities = networkCapabilities;
         this.linkProperties = linkProperties;
         this.isBlocked = isBlocked;
-
-        this.isSelected = isSelected(this.network, currentlySelected);
-
-        priorityClass =
-                NetworkPriorityClassifier.calculatePriorityClass(
-                        vcnContext,
-                        this,
-                        underlyingNetworkTemplates,
-                        subscriptionGroup,
-                        snapshot,
-                        currentlySelected,
-                        carrierConfig);
-    }
-
-    @VisibleForTesting(visibility = Visibility.PRIVATE)
-    public UnderlyingNetworkRecord(
-            @NonNull Network network,
-            @NonNull NetworkCapabilities networkCapabilities,
-            @NonNull LinkProperties linkProperties,
-            boolean isBlocked,
-            boolean isSelected,
-            int priorityClass) {
-        this.network = network;
-        this.networkCapabilities = networkCapabilities;
-        this.linkProperties = linkProperties;
-        this.isBlocked = isBlocked;
-        this.isSelected = isSelected;
-
-        this.priorityClass = priorityClass;
     }
 
     @Override
@@ -113,64 +69,20 @@
         return Objects.hash(network, networkCapabilities, linkProperties, isBlocked);
     }
 
-    /** Returns if two records are equal including their priority classes. */
-    public static boolean isEqualIncludingPriorities(
-            UnderlyingNetworkRecord left, UnderlyingNetworkRecord right) {
-        if (left != null && right != null) {
-            return left.equals(right)
-                    && left.isSelected == right.isSelected
-                    && left.priorityClass == right.priorityClass;
-        }
-
-        return left == right;
-    }
-
-    static Comparator<UnderlyingNetworkRecord> getComparator() {
-        return (left, right) -> {
-            final int leftIndex = left.priorityClass;
-            final int rightIndex = right.priorityClass;
-
-            // In the case of networks in the same priority class, prioritize based on other
-            // criteria (eg. actively selected network, link metrics, etc)
-            if (leftIndex == rightIndex) {
-                // TODO: Improve the strategy of network selection when both UnderlyingNetworkRecord
-                // fall into the same priority class.
-                if (left.isSelected) {
-                    return -1;
-                }
-                if (right.isSelected) {
-                    return 1;
-                }
-            }
-            return Integer.compare(leftIndex, rightIndex);
-        };
-    }
-
-    private static boolean isSelected(
-            Network networkToCheck, UnderlyingNetworkRecord currentlySelected) {
-        if (currentlySelected == null) {
-            return false;
-        }
-        if (currentlySelected.network.equals(networkToCheck)) {
-            return true;
-        }
-        return false;
+    /** Return whether two records represent the same network */
+    public static boolean isSameNetwork(
+            @Nullable UnderlyingNetworkRecord leftRecord,
+            @Nullable UnderlyingNetworkRecord rightRecord) {
+        final Network left = leftRecord == null ? null : leftRecord.network;
+        final Network right = rightRecord == null ? null : rightRecord.network;
+        return Objects.equals(left, right);
     }
 
     /** Dumps the state of this record for logging and debugging purposes. */
-    void dump(
-            VcnContext vcnContext,
-            IndentingPrintWriter pw,
-            List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates,
-            ParcelUuid subscriptionGroup,
-            TelephonySubscriptionSnapshot snapshot,
-            UnderlyingNetworkRecord currentlySelected,
-            PersistableBundleWrapper carrierConfig) {
+    void dump(IndentingPrintWriter pw) {
         pw.println("UnderlyingNetworkRecord:");
         pw.increaseIndent();
 
-        pw.println("priorityClass: " + priorityClass);
-        pw.println("isSelected: " + isSelected);
         pw.println("mNetwork: " + network);
         pw.println("mNetworkCapabilities: " + networkCapabilities);
         pw.println("mLinkProperties: " + linkProperties);
@@ -218,29 +130,14 @@
             return mNetworkCapabilities != null && mLinkProperties != null && mWasIsBlockedSet;
         }
 
-        UnderlyingNetworkRecord build(
-                VcnContext vcnContext,
-                List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates,
-                ParcelUuid subscriptionGroup,
-                TelephonySubscriptionSnapshot snapshot,
-                UnderlyingNetworkRecord currentlySelected,
-                PersistableBundleWrapper carrierConfig) {
+        UnderlyingNetworkRecord build() {
             if (!isValid()) {
                 throw new IllegalArgumentException(
                         "Called build before UnderlyingNetworkRecord was valid");
             }
 
             return new UnderlyingNetworkRecord(
-                    mNetwork,
-                    mNetworkCapabilities,
-                    mLinkProperties,
-                    mIsBlocked,
-                    vcnContext,
-                    underlyingNetworkTemplates,
-                    subscriptionGroup,
-                    snapshot,
-                    currentlySelected,
-                    carrierConfig);
+                    mNetwork, mNetworkCapabilities, mLinkProperties, mIsBlocked);
         }
     }
 }
diff --git a/services/core/java/com/android/server/vibrator/VibrationScaler.java b/services/core/java/com/android/server/vibrator/VibrationScaler.java
index 59b55bf7..0a7872f 100644
--- a/services/core/java/com/android/server/vibrator/VibrationScaler.java
+++ b/services/core/java/com/android/server/vibrator/VibrationScaler.java
@@ -17,11 +17,13 @@
 package com.android.server.vibrator;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.Context;
 import android.hardware.vibrator.V1_0.EffectStrength;
 import android.os.IExternalVibratorService;
 import android.os.VibrationEffect;
 import android.os.Vibrator;
+import android.os.vibrator.Flags;
 import android.os.vibrator.PrebakedSegment;
 import android.os.vibrator.VibrationEffectSegment;
 import android.util.Slog;
@@ -56,6 +58,8 @@
     private final VibrationSettings mSettingsController;
     private final int mDefaultVibrationAmplitude;
 
+    private SparseArray<Float> mAdaptiveHapticsScales;
+
     VibrationScaler(Context context, VibrationSettings settingsController) {
         mSettingsController = settingsController;
         mDefaultVibrationAmplitude = context.getResources().getInteger(
@@ -140,6 +144,15 @@
             if (scaleLevel != null) {
                 segment = segment.scale(scaleLevel.factor);
             }
+
+            // If adaptive haptics scaling is available for this usage, apply it to the segment.
+            if (Flags.adaptiveHapticsEnabled()
+                    && mAdaptiveHapticsScales != null && mAdaptiveHapticsScales.size() > 0
+                    && mAdaptiveHapticsScales.contains(usageHint)) {
+                float adaptiveScale = mAdaptiveHapticsScales.get(usageHint);
+                segment = segment.scale(adaptiveScale);
+            }
+
             segments.set(i, segment);
         }
         if (segments.equals(composedEffect.getSegments())) {
@@ -173,6 +186,16 @@
         return prebaked.applyEffectStrength(newEffectStrength);
     }
 
+    /**
+     * Updates the adaptive haptics scales.
+     * @param scales the new vibration scales to apply.
+     *
+     * @hide
+     */
+    public void updateAdaptiveHapticsScales(@Nullable SparseArray<Float> scales) {
+        mAdaptiveHapticsScales = scales;
+    }
+
     /** Mapping of Vibrator.VIBRATION_INTENSITY_* values to {@link EffectStrength}. */
     private static int intensityToEffectStrength(int intensity) {
         switch (intensity) {
diff --git a/services/core/java/com/android/server/vibrator/VibratorControlService.java b/services/core/java/com/android/server/vibrator/VibratorControlService.java
index 2eeb903..9d75249 100644
--- a/services/core/java/com/android/server/vibrator/VibratorControlService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorControlService.java
@@ -16,14 +16,26 @@
 
 package com.android.server.vibrator;
 
+import static android.os.VibrationAttributes.USAGE_ALARM;
+import static android.os.VibrationAttributes.USAGE_COMMUNICATION_REQUEST;
+import static android.os.VibrationAttributes.USAGE_HARDWARE_FEEDBACK;
+import static android.os.VibrationAttributes.USAGE_MEDIA;
+import static android.os.VibrationAttributes.USAGE_NOTIFICATION;
+import static android.os.VibrationAttributes.USAGE_RINGTONE;
+import static android.os.VibrationAttributes.USAGE_TOUCH;
+import static android.os.VibrationAttributes.USAGE_UNKNOWN;
+
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.SuppressLint;
 import android.frameworks.vibrator.IVibratorControlService;
 import android.frameworks.vibrator.IVibratorController;
+import android.frameworks.vibrator.ScaleParam;
 import android.frameworks.vibrator.VibrationParam;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.Slog;
+import android.util.SparseArray;
 
 import java.util.Objects;
 
@@ -37,10 +49,13 @@
     private static final String TAG = "VibratorControlService";
 
     private final VibratorControllerHolder mVibratorControllerHolder;
+    private final VibrationScaler mVibrationScaler;
     private final Object mLock;
 
-    public VibratorControlService(VibratorControllerHolder vibratorControllerHolder, Object lock) {
+    public VibratorControlService(VibratorControllerHolder vibratorControllerHolder,
+            VibrationScaler vibrationScaler, Object lock) {
         mVibratorControllerHolder = vibratorControllerHolder;
+        mVibrationScaler = vibrationScaler;
         mLock = lock;
     }
 
@@ -70,25 +85,62 @@
                         + "controller doesn't match the registered one. " + this);
                 return;
             }
+            updateAdaptiveHapticsScales(/* params= */ null);
             mVibratorControllerHolder.setVibratorController(null);
         }
     }
 
     @Override
-    public void setVibrationParams(
-            @SuppressLint("ArrayReturn") VibrationParam[] params, IVibratorController token)
-            throws RemoteException {
-        // TODO(b/305939964): Add set vibration implementation.
+    public void setVibrationParams(@SuppressLint("ArrayReturn") VibrationParam[] params,
+            @NonNull IVibratorController token) throws RemoteException {
+        Objects.requireNonNull(token);
+
+        synchronized (mLock) {
+            if (mVibratorControllerHolder.getVibratorController() == null) {
+                Slog.w(TAG, "Received request to set VibrationParams for IVibratorController = "
+                        + token + ", but no controller was previously registered. Request "
+                        + "Ignored.");
+                return;
+            }
+            if (!Objects.equals(mVibratorControllerHolder.getVibratorController().asBinder(),
+                    token.asBinder())) {
+                Slog.wtf(TAG, "Failed to set new VibrationParams. The provided "
+                        + "controller doesn't match the registered one. " + this);
+                return;
+            }
+
+            updateAdaptiveHapticsScales(params);
+        }
     }
 
     @Override
-    public void clearVibrationParams(int types, IVibratorController token) throws RemoteException {
-        // TODO(b/305939964): Add clear vibration implementation.
+    public void clearVibrationParams(int types, @NonNull IVibratorController token)
+            throws RemoteException {
+        Objects.requireNonNull(token);
+
+        synchronized (mLock) {
+            if (mVibratorControllerHolder.getVibratorController() == null) {
+                Slog.w(TAG, "Received request to clear VibrationParams for IVibratorController = "
+                        + token + ", but no controller was previously registered. Request "
+                        + "Ignored.");
+                return;
+            }
+            if (!Objects.equals(mVibratorControllerHolder.getVibratorController().asBinder(),
+                    token.asBinder())) {
+                Slog.wtf(TAG, "Failed to clear VibrationParams. The provided "
+                        + "controller doesn't match the registered one. " + this);
+                return;
+            }
+            //TODO(305942827): Update this method to only clear the specified vibration types.
+            // Perhaps look into whether it makes more sense to have this clear all scales and
+            // rely on setVibrationParams for clearing the scales for specific vibrations.
+            updateAdaptiveHapticsScales(/* params= */ null);
+        }
     }
 
     @Override
     public void onRequestVibrationParamsComplete(
-            IBinder requestToken, @SuppressLint("ArrayReturn") VibrationParam[] result)
+            @NonNull IBinder requestToken, @SuppressLint("ArrayReturn") VibrationParam[] result)
             throws RemoteException {
         // TODO(305942827): Cache the vibration params in VibrationScaler
     }
@@ -102,4 +154,52 @@
     public String getInterfaceHash() throws RemoteException {
         return this.HASH;
     }
+
+    /**
+     * Extracts the vibration scales and caches them in {@link VibrationScaler}.
+     *
+     * @param params the new vibration params to cache.
+     */
+    private void updateAdaptiveHapticsScales(@Nullable VibrationParam[] params) {
+        if (params == null || params.length == 0) {
+            mVibrationScaler.updateAdaptiveHapticsScales(null);
+            return;
+        }
+
+        SparseArray<Float> vibrationScales = new SparseArray<>();
+        for (int i = 0; i < params.length; i++) {
+            ScaleParam scaleParam = params[i].getScale();
+            extractVibrationScales(scaleParam, vibrationScales);
+        }
+        mVibrationScaler.updateAdaptiveHapticsScales(vibrationScales);
+    }
+
+    /**
+     * Extracts the vibration scales and map them to their corresponding
+     * {@link android.os.VibrationAttributes} usages.
+     */
+    private void extractVibrationScales(ScaleParam scaleParam, SparseArray<Float> vibrationScales) {
+        if ((ScaleParam.TYPE_ALARM & scaleParam.typesMask) != 0) {
+            vibrationScales.put(USAGE_ALARM, scaleParam.scale);
+        }
+
+        if ((ScaleParam.TYPE_NOTIFICATION & scaleParam.typesMask) != 0) {
+            vibrationScales.put(USAGE_NOTIFICATION, scaleParam.scale);
+            vibrationScales.put(USAGE_COMMUNICATION_REQUEST, scaleParam.scale);
+        }
+
+        if ((ScaleParam.TYPE_RINGTONE & scaleParam.typesMask) != 0) {
+            vibrationScales.put(USAGE_RINGTONE, scaleParam.scale);
+        }
+
+        if ((ScaleParam.TYPE_MEDIA & scaleParam.typesMask) != 0) {
+            vibrationScales.put(USAGE_MEDIA, scaleParam.scale);
+            vibrationScales.put(USAGE_UNKNOWN, scaleParam.scale);
+        }
+
+        if ((ScaleParam.TYPE_INTERACTIVE & scaleParam.typesMask) != 0) {
+            vibrationScales.put(USAGE_TOUCH, scaleParam.scale);
+            vibrationScales.put(USAGE_HARDWARE_FEEDBACK, scaleParam.scale);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
index fc824ab..2c1ab95 100644
--- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
@@ -273,7 +273,8 @@
         injector.addService(EXTERNAL_VIBRATOR_SERVICE, new ExternalVibratorService());
         if (ServiceManager.isDeclared(VIBRATOR_CONTROL_SERVICE)) {
             injector.addService(VIBRATOR_CONTROL_SERVICE,
-                    new VibratorControlService(new VibratorControllerHolder(), mLock));
+                    new VibratorControlService(new VibratorControllerHolder(), mVibrationScaler,
+                            mLock));
         }
 
     }
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperData.java b/services/core/java/com/android/server/wallpaper/WallpaperData.java
index b0b66cf..5c86701 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperData.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperData.java
@@ -130,6 +130,28 @@
      */
     final Rect cropHint = new Rect(0, 0, 0, 0);
 
+    // Describes the context of a call to WallpaperManagerService#bindWallpaperComponentLocked
+    enum BindSource {
+        UNKNOWN,
+        CONNECT_LOCKED,
+        CONNECTION_TRY_TO_REBIND,
+        INITIALIZE_FALLBACK,
+        PACKAGE_UPDATE_FINISHED,
+        RESTORE_SETTINGS_LIVE_FAILURE,
+        RESTORE_SETTINGS_LIVE_SUCCESS,
+        RESTORE_SETTINGS_STATIC,
+        SET_LIVE,
+        SET_LIVE_TO_CLEAR,
+        SET_STATIC,
+        SWITCH_WALLPAPER_FAILURE,
+        SWITCH_WALLPAPER_SWITCH_USER,
+        SWITCH_WALLPAPER_UNLOCK_USER,
+    }
+
+    // Context in which this wallpaper was bound. Intended for use in resolving b/301073479 but may
+    // be useful after the issue is resolved as well.
+    BindSource mBindSource = BindSource.UNKNOWN;
+
     // map of which -> File
     private final SparseArray<File> mWallpaperFiles = new SparseArray<>();
     private final SparseArray<File> mCropFiles = new SparseArray<>();
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperDataParser.java b/services/core/java/com/android/server/wallpaper/WallpaperDataParser.java
index 5f8bbe5..de98df5 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperDataParser.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperDataParser.java
@@ -46,6 +46,7 @@
 import com.android.internal.util.JournaledFile;
 import com.android.modules.utils.TypedXmlPullParser;
 import com.android.modules.utils.TypedXmlSerializer;
+import com.android.server.wallpaper.WallpaperData.BindSource;
 
 import libcore.io.IoUtils;
 
@@ -314,6 +315,14 @@
         wpData.mPadding.right = getAttributeInt(parser, "paddingRight", 0);
         wpData.mPadding.bottom = getAttributeInt(parser, "paddingBottom", 0);
         wallpaper.mWallpaperDimAmount = getAttributeFloat(parser, "dimAmount", 0f);
+        BindSource bindSource;
+        try {
+            bindSource = Enum.valueOf(BindSource.class,
+                    getAttributeString(parser, "bindSource", BindSource.UNKNOWN.name()));
+        } catch (IllegalArgumentException | NullPointerException e) {
+            bindSource = BindSource.UNKNOWN;
+        }
+        wallpaper.mBindSource = bindSource;
         int dimAmountsCount = getAttributeInt(parser, "dimAmountsCount", 0);
         if (dimAmountsCount > 0) {
             SparseArray<Float> allDimAmounts = new SparseArray<>(dimAmountsCount);
@@ -364,6 +373,11 @@
         return parser.getAttributeFloat(null, name, defValue);
     }
 
+    private String getAttributeString(XmlPullParser parser, String name, String defValue) {
+        String s = parser.getAttributeValue(null, name);
+        return (s != null) ? s : defValue;
+    }
+
     void saveSettingsLocked(int userId, WallpaperData wallpaper, WallpaperData lockWallpaper) {
         JournaledFile journal = makeJournaledFile(userId);
         FileOutputStream fstream = null;
@@ -423,6 +437,7 @@
         }
 
         out.attributeFloat(null, "dimAmount", wallpaper.mWallpaperDimAmount);
+        out.attribute(null, "bindSource", wallpaper.mBindSource.name());
         int dimAmountsCount = wallpaper.mUidToDimAmount.size();
         out.attributeInt(null, "dimAmountsCount", dimAmountsCount);
         if (dimAmountsCount > 0) {
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 1485b96..3782b42 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -122,6 +122,7 @@
 import com.android.server.SystemService;
 import com.android.server.pm.UserManagerInternal;
 import com.android.server.utils.TimingsTraceAndSlog;
+import com.android.server.wallpaper.WallpaperData.BindSource;
 import com.android.server.wm.ActivityTaskManagerInternal;
 import com.android.server.wm.WindowManagerInternal;
 
@@ -335,6 +336,7 @@
                     };
 
                     // If this was the system wallpaper, rebind...
+                    wallpaper.mBindSource = BindSource.SET_STATIC;
                     bindWallpaperComponentLocked(mImageWallpaper, true, false, wallpaper,
                             callback);
                 }
@@ -354,6 +356,7 @@
                         }
                     };
 
+                    wallpaper.mBindSource = BindSource.SET_STATIC;
                     bindWallpaperComponentLocked(mImageWallpaper, true /* force */,
                             false /* fromUser */, wallpaper, callback);
                 } else if (isAppliedToLock) {
@@ -811,6 +814,7 @@
                 Slog.w(TAG, "Failed attaching wallpaper on display", e);
                 if (wallpaper != null && !wallpaper.wallpaperUpdating
                         && connection.getConnectedEngineSize() == 0) {
+                    wallpaper.mBindSource = BindSource.CONNECT_LOCKED;
                     bindWallpaperComponentLocked(null /* componentName */, false /* force */,
                             false /* fromUser */, wallpaper, null /* reply */);
                 }
@@ -1035,6 +1039,7 @@
                 final ComponentName wpService = mWallpaper.wallpaperComponent;
                 // The broadcast of package update could be delayed after service disconnected. Try
                 // to re-bind the service for 10 seconds.
+                mWallpaper.mBindSource = BindSource.CONNECTION_TRY_TO_REBIND;
                 if (bindWallpaperComponentLocked(
                         wpService, true, false, mWallpaper, null)) {
                     mWallpaper.connection.scheduleTimeoutLocked();
@@ -1321,6 +1326,7 @@
                         }
                         wallpaper.wallpaperUpdating = false;
                         clearWallpaperComponentLocked(wallpaper);
+                        wallpaper.mBindSource = BindSource.PACKAGE_UPDATE_FINISHED;
                         if (!bindWallpaperComponentLocked(wpService, false, false,
                                 wallpaper, null)) {
                             Slog.w(TAG, "Wallpaper " + wpService
@@ -1711,6 +1717,7 @@
                 if (mHomeWallpaperWaitingForUnlock) {
                     final WallpaperData systemWallpaper =
                             getWallpaperSafeLocked(userId, FLAG_SYSTEM);
+                    systemWallpaper.mBindSource = BindSource.SWITCH_WALLPAPER_UNLOCK_USER;
                     switchWallpaper(systemWallpaper, null);
                     // TODO(b/278261563): call notifyCallbacksLocked inside switchWallpaper
                     notifyCallbacksLocked(systemWallpaper);
@@ -1718,6 +1725,7 @@
                 if (mLockWallpaperWaitingForUnlock) {
                     final WallpaperData lockWallpaper =
                             getWallpaperSafeLocked(userId, FLAG_LOCK);
+                    lockWallpaper.mBindSource = BindSource.SWITCH_WALLPAPER_UNLOCK_USER;
                     switchWallpaper(lockWallpaper, null);
                     notifyCallbacksLocked(lockWallpaper);
                 }
@@ -1838,6 +1846,7 @@
         // delete them in order to show the default wallpaper.
         clearWallpaperBitmaps(wallpaper);
 
+        fallback.mBindSource = BindSource.SWITCH_WALLPAPER_FAILURE;
         bindWallpaperComponentLocked(mImageWallpaper, true, false, fallback, reply);
         if ((wallpaper.mWhich & FLAG_SYSTEM) != 0) mHomeWallpaperWaitingForUnlock = true;
         if ((wallpaper.mWhich & FLAG_LOCK) != 0) mLockWallpaperWaitingForUnlock = true;
@@ -2963,6 +2972,8 @@
                  */
                 boolean forceRebind = force || (same && systemIsBoth && which == FLAG_SYSTEM);
 
+                newWallpaper.mBindSource =
+                        (name == null) ? BindSource.SET_LIVE_TO_CLEAR : BindSource.SET_LIVE;
                 bindSuccess = bindWallpaperComponentLocked(name, /* force */
                         forceRebind, /* fromUser */ true, newWallpaper, reply);
                 if (bindSuccess) {
@@ -3530,6 +3541,7 @@
             mFallbackWallpaper = new WallpaperData(systemUserId, FLAG_SYSTEM);
             mFallbackWallpaper.allowBackup = false;
             mFallbackWallpaper.wallpaperId = makeWallpaperIdLocked();
+            mFallbackWallpaper.mBindSource = BindSource.INITIALIZE_FALLBACK;
             bindWallpaperComponentLocked(mDefaultWallpaperComponent, true, false,
                     mFallbackWallpaper, null);
         }
@@ -3553,11 +3565,13 @@
             wallpaper.allowBackup = true;   // by definition if it was restored
             if (wallpaper.nextWallpaperComponent != null
                     && !wallpaper.nextWallpaperComponent.equals(mImageWallpaper)) {
+                wallpaper.mBindSource = BindSource.RESTORE_SETTINGS_LIVE_SUCCESS;
                 if (!bindWallpaperComponentLocked(wallpaper.nextWallpaperComponent, false, false,
                         wallpaper, null)) {
                     // No such live wallpaper or other failure; fall back to the default
                     // live wallpaper (since the profile being restored indicated that the
                     // user had selected a live rather than static one).
+                    wallpaper.mBindSource = BindSource.RESTORE_SETTINGS_LIVE_FAILURE;
                     bindWallpaperComponentLocked(null, false, false, wallpaper, null);
                 }
                 success = true;
@@ -3575,6 +3589,7 @@
                         + " id=" + wallpaper.wallpaperId);
                 if (success) {
                     mWallpaperCropper.generateCrop(wallpaper); // based on the new image + metadata
+                    wallpaper.mBindSource = BindSource.RESTORE_SETTINGS_STATIC;
                     bindWallpaperComponentLocked(wallpaper.nextWallpaperComponent, true, false,
                             wallpaper, null);
                 }
@@ -3608,7 +3623,8 @@
         pw.print(" User "); pw.print(wallpaper.userId);
         pw.print(": id="); pw.print(wallpaper.wallpaperId);
         pw.print(": mWhich="); pw.print(wallpaper.mWhich);
-        pw.print(": mSystemWasBoth="); pw.println(wallpaper.mSystemWasBoth);
+        pw.print(": mSystemWasBoth="); pw.print(wallpaper.mSystemWasBoth);
+        pw.print(": mBindSource="); pw.println(wallpaper.mBindSource.name());
         pw.println(" Display state:");
         mWallpaperDisplayHelper.forEachDisplayData(wpSize -> {
             pw.print("  displayId=");
diff --git a/services/core/java/com/android/server/wearable/OWNERS b/services/core/java/com/android/server/wearable/OWNERS
index 073e2d7..eca48b7 100644
--- a/services/core/java/com/android/server/wearable/OWNERS
+++ b/services/core/java/com/android/server/wearable/OWNERS
@@ -1,3 +1 @@
-charliewang@google.com
-oni@google.com
-volnov@google.com
\ No newline at end of file
+include /core/java/android/app/wearable/OWNERS
\ No newline at end of file
diff --git a/services/core/java/com/android/server/wearable/WearableSensingManagerService.java b/services/core/java/com/android/server/wearable/WearableSensingManagerService.java
index cd48f5d..106be5f 100644
--- a/services/core/java/com/android/server/wearable/WearableSensingManagerService.java
+++ b/services/core/java/com/android/server/wearable/WearableSensingManagerService.java
@@ -218,7 +218,7 @@
                 PersistableBundle data,
                 SharedMemory sharedMemory,
                 RemoteCallback callback) {
-            Slog.i(TAG, "WearableSensingManagerInternal provideData.");
+            Slog.d(TAG, "WearableSensingManagerInternal provideData.");
             Objects.requireNonNull(data);
             Objects.requireNonNull(callback);
             mContext.enforceCallingOrSelfPermission(
diff --git a/services/core/java/com/android/server/wm/AbsAppSnapshotController.java b/services/core/java/com/android/server/wm/AbsAppSnapshotController.java
index 05da9df..e5c743c 100644
--- a/services/core/java/com/android/server/wm/AbsAppSnapshotController.java
+++ b/services/core/java/com/android/server/wm/AbsAppSnapshotController.java
@@ -18,6 +18,7 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
+
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREENSHOT;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -180,16 +181,8 @@
         if (snapshot == null) {
             return null;
         }
-        final HardwareBuffer buffer = snapshot.getHardwareBuffer();
-        if (buffer.getWidth() == 0 || buffer.getHeight() == 0) {
-            buffer.close();
-            Slog.e(TAG, "Invalid snapshot dimensions " + buffer.getWidth() + "x"
-                    + buffer.getHeight());
-            return null;
-        } else {
-            mCache.putSnapshot(source, snapshot);
-            return snapshot;
-        }
+        mCache.putSnapshot(source, snapshot);
+        return snapshot;
     }
 
     @VisibleForTesting
@@ -210,6 +203,11 @@
 
     @Nullable
     TaskSnapshot snapshot(TYPE source) {
+        return snapshot(source, mHighResSnapshotScale);
+    }
+
+    @Nullable
+    TaskSnapshot snapshot(TYPE source, float scale) {
         TaskSnapshot.Builder builder = new TaskSnapshot.Builder();
         final Rect crop = prepareTaskSnapshot(source, builder);
         if (crop == null) {
@@ -218,7 +216,7 @@
         }
         Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "createSnapshot");
         final ScreenCapture.ScreenshotHardwareBuffer screenshotBuffer = createSnapshot(source,
-                mHighResSnapshotScale, crop, builder);
+                scale, crop, builder);
         Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
         if (screenshotBuffer == null) {
             // Failed to acquire image. Has been logged.
@@ -227,7 +225,19 @@
         builder.setCaptureTime(SystemClock.elapsedRealtimeNanos());
         builder.setSnapshot(screenshotBuffer.getHardwareBuffer());
         builder.setColorSpace(screenshotBuffer.getColorSpace());
-        return builder.build();
+        final TaskSnapshot snapshot = builder.build();
+        return validateSnapshot(snapshot);
+    }
+
+    private static TaskSnapshot validateSnapshot(@NonNull TaskSnapshot snapshot) {
+        final HardwareBuffer buffer = snapshot.getHardwareBuffer();
+        if (buffer.getWidth() == 0 || buffer.getHeight() == 0) {
+            buffer.close();
+            Slog.e(TAG, "Invalid snapshot dimensions " + buffer.getWidth() + "x"
+                    + buffer.getHeight());
+            return null;
+        }
+        return snapshot;
     }
 
     @Nullable
@@ -432,7 +442,7 @@
         InsetUtils.addInsets(contentInsets, letterboxInsets);
         // Note, the app theme snapshot is never translucent because we enforce a non-translucent
         // color above
-        return new TaskSnapshot(
+        final TaskSnapshot taskSnapshot = new TaskSnapshot(
                 System.currentTimeMillis() /* id */,
                 SystemClock.elapsedRealtimeNanos() /* captureTime */,
                 topActivity.mActivityComponent, hwBitmap.getHardwareBuffer(),
@@ -441,6 +451,7 @@
                 contentInsets, letterboxInsets, false /* isLowResolution */,
                 false /* isRealSnapshot */, source.getWindowingMode(),
                 getAppearance(source), false /* isTranslucent */, false /* hasImeSurface */);
+        return validateSnapshot(taskSnapshot);
     }
 
     static Rect getSystemBarInsets(Rect frame, InsetsState state) {
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 1577cef..2d584c4 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -98,6 +98,8 @@
 import android.view.animation.Interpolator;
 
 import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.os.SomeArgs;
 import com.android.internal.util.TraceBuffer;
 import com.android.internal.util.function.pooled.PooledLambda;
@@ -297,6 +299,18 @@
         }
     }
 
+    /** It is only used by unit test. */
+    @VisibleForTesting
+    Surface forceShowMagnifierSurface(int displayId) {
+        final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
+        if (displayMagnifier != null) {
+            displayMagnifier.mMagnifedViewport.mWindow.setAlpha(DisplayMagnifier.MagnifiedViewport
+                    .ViewportWindow.AnimationController.MAX_ALPHA);
+            return displayMagnifier.mMagnifedViewport.mWindow.mSurface;
+        }
+        return null;
+    }
+
     void onWindowLayersChanged(int displayId) {
         if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK
                 | FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) {
@@ -448,6 +462,7 @@
         }
     }
 
+    // TODO(b/318327737): Remove parameter 't' when removing flag DRAW_IN_WM_LOCK.
     void drawMagnifiedRegionBorderIfNeeded(int displayId, SurfaceControl.Transaction t) {
         if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
             mAccessibilityTracing.logTrace(
@@ -1106,11 +1121,19 @@
             }
 
             void setMagnifiedRegionBorderShown(boolean shown, boolean animate) {
-                if (shown) {
+                if (ViewportWindow.DRAW_IN_WM_LOCK) {
+                    if (shown) {
+                        mFullRedrawNeeded = true;
+                        mOldMagnificationRegion.set(0, 0, 0, 0);
+                    }
+                    mWindow.setShown(shown, animate);
+                    return;
+                }
+                if (mWindow.setShown(shown, animate)) {
                     mFullRedrawNeeded = true;
+                    // Clear the old region, so recomputeBounds will refresh the current region.
                     mOldMagnificationRegion.set(0, 0, 0, 0);
                 }
-                mWindow.setShown(shown, animate);
             }
 
             void getMagnifiedFrameInContentCoords(Rect rect) {
@@ -1130,7 +1153,11 @@
 
             void drawWindowIfNeeded(SurfaceControl.Transaction t) {
                 recomputeBounds();
-                mWindow.drawIfNeeded(t);
+                if (ViewportWindow.DRAW_IN_WM_LOCK) {
+                    mWindow.drawOrRemoveIfNeeded(t);
+                    return;
+                }
+                mWindow.postDrawIfNeeded();
             }
 
             void destroyWindow() {
@@ -1158,23 +1185,28 @@
                 mWindow.dump(pw, prefix);
             }
 
-            private final class ViewportWindow {
+            private final class ViewportWindow implements Runnable {
                 private static final String SURFACE_TITLE = "Magnification Overlay";
+                // TODO(b/318327737): Remove if it is stable.
+                static final boolean DRAW_IN_WM_LOCK = !Flags.drawMagnifierBorderOutsideWmlock();
 
                 private final Region mBounds = new Region();
                 private final Rect mDirtyRect = new Rect();
                 private final Paint mPaint = new Paint();
 
                 private final SurfaceControl mSurfaceControl;
+                /** After initialization, it should only be accessed from animation thread. */
+                private final SurfaceControl.Transaction mTransaction;
                 private final BLASTBufferQueue mBlastBufferQueue;
                 private final Surface mSurface;
 
                 private final AnimationController mAnimationController;
 
                 private boolean mShown;
+                private boolean mLastSurfaceShown;
                 private int mAlpha;
 
-                private boolean mInvalidated;
+                private volatile boolean mInvalidated;
 
                 ViewportWindow(Context context) {
                     SurfaceControl surfaceControl = null;
@@ -1202,6 +1234,7 @@
                     InputMonitor.setTrustedOverlayInputInfo(mSurfaceControl, t,
                             mDisplayContent.getDisplayId(), "Magnification Overlay");
                     t.apply();
+                    mTransaction = t;
                     mSurface = mBlastBufferQueue.createSurface();
 
                     mAnimationController = new AnimationController(context,
@@ -1219,10 +1252,11 @@
                     mInvalidated = true;
                 }
 
-                void setShown(boolean shown, boolean animate) {
+                /** Returns {@code true} if the state is changed to shown. */
+                boolean setShown(boolean shown, boolean animate) {
                     synchronized (mService.mGlobalLock) {
                         if (mShown == shown) {
-                            return;
+                            return false;
                         }
                         mShown = shown;
                         mAnimationController.onFrameShownStateChanged(shown, animate);
@@ -1230,6 +1264,7 @@
                             Slog.i(LOG_TAG, "ViewportWindow shown: " + mShown);
                         }
                     }
+                    return shown;
                 }
 
                 @SuppressWarnings("unused")
@@ -1285,7 +1320,22 @@
                     mService.scheduleAnimationLocked();
                 }
 
-                void drawIfNeeded(SurfaceControl.Transaction t) {
+                void postDrawIfNeeded() {
+                    if (mInvalidated) {
+                        mService.mAnimationHandler.post(this);
+                    }
+                }
+
+                @Override
+                public void run() {
+                    drawOrRemoveIfNeeded(mTransaction);
+                }
+
+                /**
+                 * This method must only be called by animation handler directly to make sure
+                 * thread safe and there is no lock held outside.
+                 */
+                private void drawOrRemoveIfNeeded(SurfaceControl.Transaction t) {
                     // Drawing variables (alpha, dirty rect, and bounds) access is synchronized
                     // using WindowManagerGlobalLock. Grab copies of these values before
                     // drawing on the canvas so that drawing can be performed outside of the lock.
@@ -1293,6 +1343,14 @@
                     Rect drawingRect = null;
                     Region drawingBounds = null;
                     synchronized (mService.mGlobalLock) {
+                        if (!DRAW_IN_WM_LOCK && mBlastBufferQueue.mNativeObject == 0) {
+                            // Complete removal since releaseSurface has been called.
+                            if (mSurface.isValid()) {
+                                mTransaction.remove(mSurfaceControl).apply();
+                                mSurface.release();
+                            }
+                            return;
+                        }
                         if (!mInvalidated) {
                             return;
                         }
@@ -1314,6 +1372,7 @@
                         }
                     }
 
+                    final boolean showSurface;
                     // Draw without holding WindowManagerGlobalLock.
                     if (alpha > 0) {
                         Canvas canvas = null;
@@ -1329,18 +1388,38 @@
                         mPaint.setAlpha(alpha);
                         canvas.drawPath(drawingBounds.getBoundaryPath(), mPaint);
                         mSurface.unlockCanvasAndPost(canvas);
-                        t.show(mSurfaceControl);
+                        if (DRAW_IN_WM_LOCK) {
+                            t.show(mSurfaceControl);
+                            return;
+                        }
+                        showSurface = true;
                     } else {
-                        t.hide(mSurfaceControl);
+                        if (DRAW_IN_WM_LOCK) {
+                            t.hide(mSurfaceControl);
+                            return;
+                        }
+                        showSurface = false;
+                    }
+
+                    if (showSurface && !mLastSurfaceShown) {
+                        mTransaction.show(mSurfaceControl).apply();
+                        mLastSurfaceShown = true;
+                    } else if (!showSurface && mLastSurfaceShown) {
+                        mTransaction.hide(mSurfaceControl).apply();
+                        mLastSurfaceShown = false;
                     }
                 }
 
+                @GuardedBy("mService.mGlobalLock")
                 void releaseSurface() {
-                    if (mBlastBufferQueue != null) {
-                        mBlastBufferQueue.destroy();
+                    mBlastBufferQueue.destroy();
+                    if (DRAW_IN_WM_LOCK) {
+                        mService.mTransactionFactory.get().remove(mSurfaceControl).apply();
+                        mSurface.release();
+                        return;
                     }
-                    mService.mTransactionFactory.get().remove(mSurfaceControl).apply();
-                    mSurface.release();
+                    // Post to perform cleanup on the thread which handles mSurface.
+                    mService.mAnimationHandler.post(this);
                 }
 
                 void dump(PrintWriter pw, String prefix) {
diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java
index 4b55bec..676203b 100644
--- a/services/core/java/com/android/server/wm/ActivityClientController.java
+++ b/services/core/java/com/android/server/wm/ActivityClientController.java
@@ -1166,11 +1166,12 @@
             transition.abort();
             return;
         }
-        transition.collect(topFocusedRootTask);
-        executeMultiWindowFullscreenRequest(fullscreenRequest, topFocusedRootTask);
-        r.mTransitionController.requestStartTransition(transition, topFocusedRootTask,
+        final Task requestingTask = r.getTask();
+        transition.collect(requestingTask);
+        executeMultiWindowFullscreenRequest(fullscreenRequest, requestingTask);
+        r.mTransitionController.requestStartTransition(transition, requestingTask,
                 null /* remoteTransition */, null /* displayChange */);
-        transition.setReady(topFocusedRootTask, true);
+        transition.setReady(requestingTask, true);
     }
 
     private static void reportMultiwindowFullscreenRequestValidatingResult(IRemoteCallback callback,
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 145eb3b..3a792d0 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -183,6 +183,8 @@
 import static com.android.server.wm.ActivityRecordProto.REPORTED_DRAWN;
 import static com.android.server.wm.ActivityRecordProto.REPORTED_VISIBLE;
 import static com.android.server.wm.ActivityRecordProto.SHOULD_FORCE_ROTATE_FOR_CAMERA_COMPAT;
+import static com.android.server.wm.ActivityRecordProto.SHOULD_IGNORE_ORIENTATION_REQUEST_LOOP;
+import static com.android.server.wm.ActivityRecordProto.SHOULD_OVERRIDE_FORCE_RESIZE_APP;
 import static com.android.server.wm.ActivityRecordProto.SHOULD_OVERRIDE_MIN_ASPECT_RATIO;
 import static com.android.server.wm.ActivityRecordProto.SHOULD_REFRESH_ACTIVITY_FOR_CAMERA_COMPAT;
 import static com.android.server.wm.ActivityRecordProto.SHOULD_REFRESH_ACTIVITY_VIA_PAUSE_FOR_CAMERA_COMPAT;
@@ -223,7 +225,6 @@
 import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE;
 import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
 import static com.android.server.wm.ActivityTaskManagerService.getInputDispatchingTimeoutMillisLocked;
-import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
 import static com.android.server.wm.IdentifierProto.HASH_CODE;
 import static com.android.server.wm.IdentifierProto.TITLE;
 import static com.android.server.wm.IdentifierProto.USER_ID;
@@ -496,10 +497,7 @@
     private final boolean componentSpecified;  // did caller specify an explicit component?
     final boolean rootVoiceInteraction;  // was this the root activity of a voice interaction?
 
-    private CharSequence nonLocalizedLabel;  // the label information from the package mgr.
-    private int labelRes;           // the label information from the package mgr.
-    private int icon;               // resource identifier of activity's icon.
-    private int theme;              // resource identifier of activity's theme.
+    private final int theme;        // resource identifier of activity's theme.
     private Task task;              // the task this is in.
     private long createTime = System.currentTimeMillis();
     long lastVisibleTime;         // last time this activity became visible
@@ -507,7 +505,7 @@
     long launchTickTime;          // base time for launch tick messages
     long topResumedStateLossTime; // last time we reported top resumed state loss to an activity
     // Last configuration reported to the activity in the client process.
-    private MergedConfiguration mLastReportedConfiguration;
+    private final MergedConfiguration mLastReportedConfiguration;
     private int mLastReportedDisplayId;
     boolean mLastReportedMultiWindowMode;
     boolean mLastReportedPictureInPictureMode;
@@ -949,7 +947,7 @@
     private int mConfigurationSeq;
 
     /**
-     * Temp configs used in {@link #ensureActivityConfiguration(int, boolean)}
+     * Temp configs used in {@link #ensureActivityConfiguration()}
      */
     private final Configuration mTmpConfig = new Configuration();
     private final Rect mTmpBounds = new Rect();
@@ -1059,17 +1057,15 @@
         pw.print(prefix); pw.print("taskAffinity="); pw.println(taskAffinity);
         pw.print(prefix); pw.print("mActivityComponent=");
                 pw.println(mActivityComponent.flattenToShortString());
-        if (info != null && info.applicationInfo != null) {
-            final ApplicationInfo appInfo = info.applicationInfo;
-            pw.print(prefix); pw.print("baseDir="); pw.println(appInfo.sourceDir);
-            if (!Objects.equals(appInfo.sourceDir, appInfo.publicSourceDir)) {
-                pw.print(prefix); pw.print("resDir="); pw.println(appInfo.publicSourceDir);
-            }
-            pw.print(prefix); pw.print("dataDir="); pw.println(appInfo.dataDir);
-            if (appInfo.splitSourceDirs != null) {
-                pw.print(prefix); pw.print("splitDir=");
-                        pw.println(Arrays.toString(appInfo.splitSourceDirs));
-            }
+        final ApplicationInfo appInfo = info.applicationInfo;
+        pw.print(prefix); pw.print("baseDir="); pw.println(appInfo.sourceDir);
+        if (!Objects.equals(appInfo.sourceDir, appInfo.publicSourceDir)) {
+            pw.print(prefix); pw.print("resDir="); pw.println(appInfo.publicSourceDir);
+        }
+        pw.print(prefix); pw.print("dataDir="); pw.println(appInfo.dataDir);
+        if (appInfo.splitSourceDirs != null) {
+            pw.print(prefix); pw.print("splitDir=");
+            pw.println(Arrays.toString(appInfo.splitSourceDirs));
         }
         pw.print(prefix); pw.print("stateNotNeeded="); pw.print(stateNotNeeded);
                 pw.print(" componentSpecified="); pw.print(componentSpecified);
@@ -1080,8 +1076,6 @@
         }
         pw.print(prefix); pw.print("compat=");
         pw.print(mAtmService.compatibilityInfoForPackageLocked(info.applicationInfo));
-                pw.print(" labelRes=0x"); pw.print(Integer.toHexString(labelRes));
-                pw.print(" icon=0x"); pw.print(Integer.toHexString(icon));
                 pw.print(" theme=0x"); pw.println(Integer.toHexString(theme));
         pw.println(prefix + "mLastReportedConfigurations:");
         mLastReportedConfiguration.dump(pw, prefix + "  ");
@@ -1511,7 +1505,7 @@
                 updatePictureInPictureMode(null, false);
             } else {
                 mLastReportedMultiWindowMode = inMultiWindowMode;
-                ensureActivityConfiguration(0 /* globalChanges */, PRESERVE_WINDOWS);
+                ensureActivityConfiguration();
             }
         }
     }
@@ -1530,8 +1524,7 @@
             // precede the configuration change from the resize.
             mLastReportedPictureInPictureMode = inPictureInPictureMode;
             mLastReportedMultiWindowMode = inPictureInPictureMode;
-            ensureActivityConfiguration(0 /* globalChanges */, PRESERVE_WINDOWS,
-                    true /* ignoreVisibility */);
+            ensureActivityConfiguration(true /* ignoreVisibility */);
             if (inPictureInPictureMode && findMainWindow() == null) {
                 // Prevent malicious app entering PiP without valid WindowState, which can in turn
                 // result a non-touchable PiP window since the InputConsumer for PiP requires it.
@@ -2159,14 +2152,6 @@
             sConstrainDisplayApisConfig = new ConstrainDisplayApisConfig();
         }
         stateNotNeeded = (aInfo.flags & FLAG_STATE_NOT_NEEDED) != 0;
-        nonLocalizedLabel = aInfo.nonLocalizedLabel;
-        labelRes = aInfo.labelRes;
-        if (nonLocalizedLabel == null && labelRes == 0) {
-            ApplicationInfo app = aInfo.applicationInfo;
-            nonLocalizedLabel = app.nonLocalizedLabel;
-            labelRes = app.labelRes;
-        }
-        icon = aInfo.getIconResource();
         theme = aInfo.getThemeResource();
         if ((aInfo.flags & FLAG_MULTIPROCESS) != 0 && _caller != null
                 && (aInfo.applicationInfo.uid == SYSTEM_UID
@@ -2196,7 +2181,8 @@
             // The result receiver is the transition receiver, which will handle the shared element
             // exit transition.
             mHasSceneTransition = options.getAnimationType() == ANIM_SCENE_TRANSITION
-                    && options.getResultReceiver() != null;
+                    && options.getSceneTransitionInfo() != null
+                    && options.getSceneTransitionInfo().getResultReceiver() != null;
             final PendingIntent usageReport = options.getUsageTimeReport();
             if (usageReport != null) {
                 appTimeTracker = new AppTimeTracker(usageReport);
@@ -2509,7 +2495,14 @@
 
         ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Creating SnapshotStartingData");
         mStartingData = new SnapshotStartingData(mWmService, snapshot, typeParams);
-        if (task.forAllLeafTaskFragments(TaskFragment::isEmbedded)) {
+        if ((!mStyleFillsParent && task.getChildCount() > 1)
+                || task.forAllLeafTaskFragments(TaskFragment::isEmbedded)) {
+            // Case 1:
+            // If it is moving a Task{[0]=main activity, [1]=translucent activity} to front, use
+            // shared starting window so that the transition doesn't need to wait for the activity
+            // behind the translucent activity. Also, onFirstWindowDrawn will check all visible
+            // activities are drawn in the task to remove the snapshot starting window.
+            // Case 2:
             // Associate with the task so if this activity is resized by task fragment later, the
             // starting window can keep the same bounds as the task.
             associateStartingDataWithTask();
@@ -3107,7 +3100,7 @@
         // {@link #returningOptions} of the activity under this one can be applied in
         // {@link #handleAlreadyVisible()}.
         if (changed || !occludesParent) {
-            mRootWindowContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
+            mRootWindowContainer.ensureActivitiesVisible();
         }
         return changed;
     }
@@ -3747,8 +3740,8 @@
             }
 
             if (ensureVisibility) {
-                mDisplayContent.ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
-                        false /* preserveWindows */, true /* notifyClients */);
+                mDisplayContent.ensureActivitiesVisible(null /* starting */,
+                        true /* notifyClients */);
             }
         }
 
@@ -4165,8 +4158,7 @@
         if (rootTask != null && rootTask.shouldSleepOrShutDownActivities()) {
             // Activity is always relaunched to either resumed or paused state. If it was
             // relaunched while hidden (by keyguard or smth else), it should be stopped.
-            rootTask.ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
-                    false /* preserveWindows */);
+            rootTask.ensureActivitiesVisible(null /* starting */);
         }
     }
 
@@ -4330,7 +4322,6 @@
         mTaskSupervisor.getActivityMetricsLogger().notifyActivityRemoved(this);
         mTaskSupervisor.mStoppingActivities.remove(this);
         mLetterboxUiController.destroy();
-        waitingToShow = false;
 
         // Defer removal of this activity when either a child is animating, or app transition is on
         // going. App transition animation might be applied on the parent task not on the activity,
@@ -4681,14 +4672,12 @@
 
     void setShowWhenLocked(boolean showWhenLocked) {
         mShowWhenLocked = showWhenLocked;
-        mAtmService.mRootWindowContainer.ensureActivitiesVisible(null /* starting */,
-                0 /* configChanges */, false /* preserveWindows */);
+        mAtmService.mRootWindowContainer.ensureActivitiesVisible();
     }
 
     void setInheritShowWhenLocked(boolean inheritShowWhenLocked) {
         mInheritShownWhenLocked = inheritShowWhenLocked;
-        mAtmService.mRootWindowContainer.ensureActivitiesVisible(null /* starting */,
-                0 /* configChanges */, false /* preserveWindows */);
+        mAtmService.mRootWindowContainer.ensureActivitiesVisible();
     }
 
     /**
@@ -5197,16 +5186,15 @@
         return mPendingOptions;
     }
 
-    ActivityOptions takeOptions() {
-        if (DEBUG_TRANSITION) Slog.i(TAG, "Taking options for " + this + " callers="
-                + Debug.getCallers(6));
+    ActivityOptions.SceneTransitionInfo takeSceneTransitionInfo() {
+        if (DEBUG_TRANSITION) {
+            Slog.i(TAG, "Taking SceneTransitionInfo for " + this + " callers="
+                    + Debug.getCallers(6));
+        }
         if (mPendingOptions == null) return null;
         final ActivityOptions opts = mPendingOptions;
         mPendingOptions = null;
-        // Strip sensitive information from options before sending it to app.
-        opts.setRemoteTransition(null);
-        opts.setRemoteAnimationAdapter(null);
-        return opts;
+        return opts.getSceneTransitionInfo();
     }
 
     RemoteTransition takeRemoteTransition() {
@@ -5406,7 +5394,6 @@
         final DisplayContent displayContent = getDisplayContent();
         displayContent.mOpeningApps.remove(this);
         displayContent.mClosingApps.remove(this);
-        waitingToShow = false;
         setVisibleRequested(visible);
         mLastDeferHidingClient = deferHidingClient;
 
@@ -5431,25 +5418,16 @@
             // stopped, then we need to set up to wait for its windows to be ready.
             if (!isVisible() || mAppStopped) {
                 clearAllDrawn();
-
-                // If the app was already visible, don't reset the waitingToShow state.
-                if (!isVisible()) {
-                    waitingToShow = true;
-
-                    // If the client isn't hidden, we don't need to reset the drawing state.
-                    if (!isClientVisible()) {
-                        // Let's reset the draw state in order to prevent the starting window to be
-                        // immediately dismissed when the app still has the surface.
-                        forAllWindows(w -> {
-                            if (w.mWinAnimator.mDrawState == HAS_DRAWN) {
-                                w.mWinAnimator.resetDrawState();
-
-                                // Force add to mResizingWindows, so that we are guaranteed to get
-                                // another reportDrawn callback.
-                                w.forceReportingResized();
-                            }
-                        }, true /* traverseTopToBottom */);
-                    }
+                // Reset the draw state in order to prevent the starting window to be immediately
+                // dismissed when the app still has the surface.
+                if (!isVisible() && !isClientVisible()) {
+                    forAllWindows(w -> {
+                        if (w.mWinAnimator.mDrawState == HAS_DRAWN) {
+                            w.mWinAnimator.resetDrawState();
+                            // Force add to mResizingWindows, so the window will report drawn.
+                            w.forceReportingResized();
+                        }
+                    }, true /* traverseTopToBottom */);
                 }
             }
 
@@ -6182,7 +6160,7 @@
 
             try {
                 mAtmService.getLifecycleManager().scheduleTransactionItem(app.getThread(),
-                        StartActivityItem.obtain(token, takeOptions()));
+                        StartActivityItem.obtain(token, takeSceneTransitionInfo()));
             } catch (Exception e) {
                 Slog.w(TAG, "Exception thrown sending start: " + intent.getComponent(), e);
             }
@@ -6287,8 +6265,11 @@
 
     void handleAlreadyVisible() {
         try {
-            if (returningOptions != null) {
-                app.getThread().scheduleOnNewActivityOptions(token, returningOptions.toBundle());
+            if (returningOptions != null
+                    && returningOptions.getAnimationType() == ANIM_SCENE_TRANSITION
+                    && returningOptions.getSceneTransitionInfo() != null) {
+                app.getThread().scheduleOnNewSceneTransitionInfo(token,
+                        returningOptions.getSceneTransitionInfo());
             }
         } catch(RemoteException e) {
         }
@@ -6413,7 +6394,7 @@
         }
 
         mDisplayContent.handleActivitySizeCompatModeIfNeeded(this);
-        mRootWindowContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
+        mRootWindowContainer.ensureActivitiesVisible();
     }
 
     /**
@@ -6637,10 +6618,6 @@
         return hasProcess() && !app.isCrashing() && !app.isNotResponding();
     }
 
-    void startFreezingScreenLocked(int configChanges) {
-        startFreezingScreenLocked(app, configChanges);
-    }
-
     void startFreezingScreenLocked(WindowProcessController app, int configChanges) {
         if (mayFreezeScreenLocked(app)) {
             if (getParent() == null) {
@@ -7894,7 +7871,7 @@
     void applyFixedRotationTransform(DisplayInfo info, DisplayFrames displayFrames,
             Configuration config) {
         super.applyFixedRotationTransform(info, displayFrames, config);
-        ensureActivityConfiguration(0 /* globalChanges */, false /* preserveWindow */);
+        ensureActivityConfiguration();
     }
 
     /**
@@ -7989,7 +7966,7 @@
         startFreezingScreen(originalDisplayRotation);
         // This activity may relaunch or perform configuration change so once it has reported drawn,
         // the screen can be unfrozen.
-        ensureActivityConfiguration(0 /* globalChanges */, !PRESERVE_WINDOWS);
+        ensureActivityConfiguration();
         if (mTransitionController.isCollecting(this)) {
             // In case the task was changed from PiP but still keeps old transform.
             task.resetSurfaceControlTransforms();
@@ -8017,7 +7994,7 @@
         // the request is handled at task level with letterbox.
         if (!getMergedOverrideConfiguration().equals(
                 mLastReportedConfiguration.getMergedConfiguration())) {
-            ensureActivityConfiguration(0 /* globalChanges */, false /* preserveWindow */,
+            ensureActivityConfiguration(
                     false /* ignoreVisibility */, true /* isRequestedOrientationChanged */);
             if (mTransitionController.inPlayingTransition(this)) {
                 mTransitionController.mValidateActivityCompat.add(this);
@@ -8070,7 +8047,7 @@
      */
     @Override
     int getOrientation(int candidate) {
-        if (shouldIgnoreOrientationRequests()) {
+        if (finishing || shouldIgnoreOrientationRequests()) {
             return SCREEN_ORIENTATION_UNSET;
         }
 
@@ -8084,8 +8061,7 @@
         // The {@link ActivityRecord} should only specify an orientation when it is not closing.
         // Allowing closing {@link ActivityRecord} to participate can lead to an Activity in another
         // task being started in the wrong orientation during the transition.
-        if (!getDisplayContent().mClosingApps.contains(this)
-                && (isVisibleRequested() || getDisplayContent().mOpeningApps.contains(this))) {
+        if (isVisibleRequested()) {
             return getOverrideOrientation();
         }
 
@@ -8125,12 +8101,8 @@
      * Set the last reported configuration to the client. Should be called whenever
      * a new merged configuration is sent to the client for this activity.
      */
-    void setLastReportedConfiguration(@NonNull MergedConfiguration config) {
-        setLastReportedConfiguration(config.getGlobalConfiguration(),
-            config.getOverrideConfiguration());
-    }
-
-    private void setLastReportedConfiguration(Configuration global, Configuration override) {
+    void setLastReportedConfiguration(@NonNull Configuration global,
+            @NonNull Configuration override) {
         mLastReportedConfiguration.setConfiguration(global, override);
     }
 
@@ -9525,14 +9497,12 @@
         return mLastReportedDisplayId != getDisplayId();
     }
 
-    boolean ensureActivityConfiguration(int globalChanges, boolean preserveWindow) {
-        return ensureActivityConfiguration(globalChanges, preserveWindow,
-                false /* ignoreVisibility */, false /* isRequestedOrientationChanged */);
+    boolean ensureActivityConfiguration() {
+        return ensureActivityConfiguration(false /* ignoreVisibility */);
     }
 
-    boolean ensureActivityConfiguration(int globalChanges, boolean preserveWindow,
-            boolean ignoreVisibility) {
-        return ensureActivityConfiguration(globalChanges, preserveWindow, ignoreVisibility,
+    boolean ensureActivityConfiguration(boolean ignoreVisibility) {
+        return ensureActivityConfiguration(ignoreVisibility,
                 false /* isRequestedOrientationChanged */);
     }
 
@@ -9540,9 +9510,6 @@
      * Make sure the given activity matches the current configuration. Ensures the HistoryRecord
      * is updated with the correct configuration and all other bookkeeping is handled.
      *
-     * @param globalChanges The changes to the global configuration.
-     * @param preserveWindow If the activity window should be preserved on screen if the activity
-     *                       is relaunched.
      * @param ignoreVisibility If we should try to relaunch the activity even if it is invisible
      *                         (stopped state). This is useful for the case where we know the
      *                         activity will be visible soon and we want to ensure its configuration
@@ -9552,8 +9519,8 @@
      * @return False if the activity was relaunched and true if it wasn't relaunched because we
      *         can't or the app handles the specific configuration that is changing.
      */
-    boolean ensureActivityConfiguration(int globalChanges, boolean preserveWindow,
-            boolean ignoreVisibility, boolean isRequestedOrientationChanged) {
+    boolean ensureActivityConfiguration(boolean ignoreVisibility,
+            boolean isRequestedOrientationChanged) {
         final Task rootTask = getRootTask();
         if (rootTask.mConfigWillChange) {
             ProtoLog.v(WM_DEBUG_CONFIGURATION, "Skipping config check "
@@ -9667,10 +9634,21 @@
         if (shouldRelaunchLocked(changes, mTmpConfig)) {
             // Aha, the activity isn't handling the change, so DIE DIE DIE.
             configChangeFlags |= changes;
-            startFreezingScreenLocked(globalChanges);
+            if (mVisible && mAtmService.mTmpUpdateConfigurationResult.mIsUpdating
+                    && !mTransitionController.isShellTransitionsEnabled()) {
+                startFreezingScreenLocked(app, mAtmService.mTmpUpdateConfigurationResult.changes);
+            }
+            final boolean displayMayChange = mTmpConfig.windowConfiguration.getDisplayRotation()
+                    != getWindowConfiguration().getDisplayRotation()
+                    || !mTmpConfig.windowConfiguration.getMaxBounds().equals(
+                            getWindowConfiguration().getMaxBounds());
+            final boolean isAppResizeOnly = !displayMayChange
+                    && (changes & ~(CONFIG_SCREEN_SIZE | CONFIG_SMALLEST_SCREEN_SIZE
+                            | CONFIG_ORIENTATION | CONFIG_SCREEN_LAYOUT)) == 0;
             // Do not preserve window if it is freezing screen because the original window won't be
             // able to update drawn state that causes freeze timeout.
-            preserveWindow &= isResizeOnlyChange(changes) && !mFreezingScreen;
+            // TODO(b/258618073): Always preserve if possible.
+            final boolean preserveWindow = isAppResizeOnly && !mFreezingScreen;
             final boolean hasResizeChange = hasResizeChange(changes & ~info.getRealConfigChanged());
             if (hasResizeChange) {
                 final boolean isDragResizing = task.isDragResizing();
@@ -9834,11 +9812,6 @@
         return changes;
     }
 
-    private static boolean isResizeOnlyChange(int change) {
-        return (change & ~(CONFIG_SCREEN_SIZE | CONFIG_SMALLEST_SCREEN_SIZE | CONFIG_ORIENTATION
-                | CONFIG_SCREEN_LAYOUT)) == 0;
-    }
-
     private static boolean hasResizeChange(int change) {
         return (change & (CONFIG_SCREEN_SIZE | CONFIG_SMALLEST_SCREEN_SIZE | CONFIG_ORIENTATION
                 | CONFIG_SCREEN_LAYOUT)) != 0;
@@ -9882,8 +9855,6 @@
                     task.mTaskId, shortComponentName, Integer.toHexString(configChangeFlags));
         }
 
-        startFreezingScreenLocked(0);
-
         try {
             ProtoLog.i(WM_DEBUG_STATES, "Moving to %s Relaunching %s callers=%s" ,
                     (andResume ? "RESUMED" : "PAUSED"), this, Debug.getCallers(6));
@@ -10363,6 +10334,10 @@
                 mLetterboxUiController.shouldRefreshActivityViaPauseForCameraCompat());
         proto.write(SHOULD_OVERRIDE_MIN_ASPECT_RATIO,
                 mLetterboxUiController.shouldOverrideMinAspectRatio());
+        proto.write(SHOULD_IGNORE_ORIENTATION_REQUEST_LOOP,
+                mLetterboxUiController.shouldIgnoreOrientationRequestLoop());
+        proto.write(SHOULD_OVERRIDE_FORCE_RESIZE_APP,
+                mLetterboxUiController.shouldOverrideForceResizeApp());
     }
 
     @Override
@@ -10648,6 +10623,14 @@
 
     @Override
     boolean isSyncFinished(BLASTSyncEngine.SyncGroup group) {
+        if (task != null && task.mSharedStartingData != null) {
+            final WindowState startingWin = task.topStartingWindow();
+            if (startingWin != null && startingWin.mSyncState == SYNC_STATE_READY
+                    && mDisplayContent.mUnknownAppVisibilityController.allResolved()) {
+                // The sync is ready if a drawn starting window covered the task.
+                return true;
+            }
+        }
         if (!super.isSyncFinished(group)) return false;
         if (mDisplayContent != null && mDisplayContent.mUnknownAppVisibilityController
                 .isVisibilityUnknown(this)) {
diff --git a/services/core/java/com/android/server/wm/ActivitySnapshotController.java b/services/core/java/com/android/server/wm/ActivitySnapshotController.java
index 7af494c..a692167 100644
--- a/services/core/java/com/android/server/wm/ActivitySnapshotController.java
+++ b/services/core/java/com/android/server/wm/ActivitySnapshotController.java
@@ -25,6 +25,7 @@
 import android.os.SystemProperties;
 import android.os.Trace;
 import android.util.ArraySet;
+import android.util.IntArray;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.window.TaskSnapshot;
@@ -36,6 +37,7 @@
 import com.android.window.flags.Flags;
 
 import java.io.File;
+import java.io.PrintWriter;
 import java.util.ArrayList;
 
 /**
@@ -136,11 +138,26 @@
                 false /* enableLowResSnapshots */, 0 /* lowResScaleFactor */, use16BitFormat);
     }
 
-    /** Retrieves a snapshot for an activity from cache. */
+    /**
+     * Retrieves a snapshot for a set of activities from cache.
+     * This will only return the snapshot IFF input activities exist entirely in the snapshot.
+     * Sample: If the snapshot was captured with activity A and B, here will return null if the
+     * input activity is only [A] or [B], it must be [A, B]
+     */
     @Nullable
-    TaskSnapshot getSnapshot(ActivityRecord ar) {
-        final int code = getSystemHashCode(ar);
-        return mCache.getSnapshot(code);
+    TaskSnapshot getSnapshot(@NonNull ActivityRecord[] activities) {
+        if (activities.length == 0) {
+            return null;
+        }
+        final UserSavedFile tmpUsf = findSavedFile(activities[0]);
+        if (tmpUsf == null || tmpUsf.mActivityIds.size() != activities.length) {
+            return null;
+        }
+        int fileId = 0;
+        for (int i = activities.length - 1; i >= 0; --i) {
+            fileId ^= getSystemHashCode(activities[i]);
+        }
+        return tmpUsf.mFileId == fileId ? mCache.getSnapshot(tmpUsf.mActivityIds.get(0)) : null;
     }
 
     private void cleanUpUserFiles(int userId) {
@@ -229,33 +246,16 @@
                     + " load " + mPendingLoadActivity);
         }
         // load snapshot to cache
-        for (int i = mPendingLoadActivity.size() - 1; i >= 0; i--) {
-            final ActivityRecord ar = mPendingLoadActivity.valueAt(i);
-            final int code = getSystemHashCode(ar);
-            final int userId = ar.mUserId;
-            if (mCache.getSnapshot(code) != null) {
-                // already in cache, skip
-                continue;
-            }
-            if (containsFile(code, userId)) {
-                synchronized (mSnapshotPersistQueue.getLock()) {
-                    mSnapshotPersistQueue.insertQueueAtFirstLocked(
-                            new LoadActivitySnapshotItem(ar, code, userId, mPersistInfoProvider));
-                }
-            }
-        }
+        loadActivitySnapshot();
         // clear mTmpRemoveActivity from cache
         for (int i = mPendingRemoveActivity.size() - 1; i >= 0; i--) {
             final ActivityRecord ar = mPendingRemoveActivity.valueAt(i);
-            final int code = getSystemHashCode(ar);
-            mCache.onIdRemoved(code);
+            removeCachedFiles(ar);
         }
         // clear snapshot on cache and delete files
         for (int i = mPendingDeleteActivity.size() - 1; i >= 0; i--) {
             final ActivityRecord ar = mPendingDeleteActivity.valueAt(i);
-            final int code = getSystemHashCode(ar);
-            mCache.onIdRemoved(code);
-            removeIfUserSavedFileExist(code, ar.mUserId);
+            removeIfUserSavedFileExist(ar);
         }
         // don't keep any reference
         resetTmpFields();
@@ -264,28 +264,38 @@
     class LoadActivitySnapshotItem extends SnapshotPersistQueue.WriteQueueItem {
         private final int mCode;
         private final int mUserId;
-        private final ActivityRecord mActivityRecord;
+        private final ActivityRecord[] mActivities;
 
-        LoadActivitySnapshotItem(@NonNull ActivityRecord ar, int code, int userId,
+        LoadActivitySnapshotItem(@NonNull ActivityRecord[] activities, int code, int userId,
                 @NonNull PersistInfoProvider persistInfoProvider) {
             super(persistInfoProvider);
-            mActivityRecord = ar;
+            mActivities = activities;
             mCode = code;
             mUserId = userId;
         }
 
         @Override
         void write() {
-            Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER,
-                    "load_activity_snapshot");
-            final TaskSnapshot snapshot = mSnapshotLoader.loadTask(mCode,
-                    mUserId, false /* loadLowResolutionBitmap */);
-            synchronized (mService.getWindowManagerLock()) {
-                if (snapshot != null && !mActivityRecord.finishing) {
-                    mCache.putSnapshot(mActivityRecord, snapshot);
+            try {
+                Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER,
+                        "load_activity_snapshot");
+                final TaskSnapshot snapshot = mSnapshotLoader.loadTask(mCode,
+                        mUserId, false /* loadLowResolutionBitmap */);
+                if (snapshot == null) {
+                    return;
                 }
+                synchronized (mService.getWindowManagerLock()) {
+                    // Verify the snapshot is still needed, and the activity is not finishing
+                    if (!hasRecord(mActivities[0])) {
+                        return;
+                    }
+                    for (ActivityRecord ar : mActivities) {
+                        mCache.putSnapshot(ar, snapshot);
+                    }
+                }
+            } finally {
+                Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
             }
-            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
         }
 
         @Override
@@ -297,18 +307,81 @@
         }
     }
 
-    void recordSnapshot(ActivityRecord activity) {
-        if (shouldDisableSnapshots()) {
+    void loadActivitySnapshot() {
+        if (mPendingLoadActivity.isEmpty()) {
+            return;
+        }
+        // Only load if saved file exists.
+        final ArraySet<UserSavedFile> loadingFiles = new ArraySet<>();
+        for (int i = mPendingLoadActivity.size() - 1; i >= 0; i--) {
+            final ActivityRecord ar = mPendingLoadActivity.valueAt(i);
+            final UserSavedFile usf = findSavedFile(ar);
+            if (usf != null) {
+                loadingFiles.add(usf);
+            }
+        }
+        // Filter out the activity if the snapshot was removed.
+        for (int i = loadingFiles.size() - 1; i >= 0; i--) {
+            final UserSavedFile usf = loadingFiles.valueAt(i);
+            final ActivityRecord[] activities = usf.filterExistActivities(mPendingLoadActivity);
+            if (activities == null) {
+                continue;
+            }
+            if (getSnapshot(activities) != null) {
+                // Found the cache in memory, so skip loading from file.
+                continue;
+            }
+            loadSnapshotInner(activities, usf);
+        }
+    }
+
+    @VisibleForTesting
+    void loadSnapshotInner(ActivityRecord[] activities, UserSavedFile usf) {
+        synchronized (mSnapshotPersistQueue.getLock()) {
+            mSnapshotPersistQueue.insertQueueAtFirstLocked(new LoadActivitySnapshotItem(
+                    activities, usf.mFileId, usf.mUserId, mPersistInfoProvider));
+        }
+    }
+
+    /**
+     * Record one or multiple activities within a snapshot where those activities must belong to
+     * the same task.
+     * @param activity If the request activity is more than one, try to record those activities
+     *                 as a single snapshot, so those activities should belong to the same task.
+     */
+    void recordSnapshot(@NonNull ArrayList<ActivityRecord> activity) {
+        if (shouldDisableSnapshots() || activity.isEmpty()) {
             return;
         }
         if (DEBUG) {
             Slog.d(TAG, "ActivitySnapshotController#recordSnapshot " + activity);
         }
-        final TaskSnapshot snapshot = recordSnapshotInner(activity);
-        if (snapshot != null) {
-            final int code = getSystemHashCode(activity);
-            addUserSavedFile(code, activity.mUserId, snapshot);
+        final int size = activity.size();
+        final int[] mixedCode = new int[size];
+        if (size == 1) {
+            final ActivityRecord singleActivity = activity.get(0);
+            final TaskSnapshot snapshot = recordSnapshotInner(singleActivity);
+            if (snapshot != null) {
+                mixedCode[0] = getSystemHashCode(singleActivity);
+                addUserSavedFile(singleActivity.mUserId, snapshot, mixedCode);
+            }
+            return;
         }
+
+        final Task mainTask = activity.get(0).getTask();
+        // Snapshot by task controller with activity's scale.
+        final TaskSnapshot snapshot = mService.mTaskSnapshotController
+                .snapshot(mainTask, mHighResSnapshotScale);
+        if (snapshot == null) {
+            return;
+        }
+
+        for (int i = 0; i < activity.size(); ++i) {
+            final ActivityRecord next = activity.get(i);
+            mCache.putSnapshot(next, snapshot);
+            mixedCode[i] = getSystemHashCode(next);
+        }
+        addUserSavedFile(mainTask.mUserId, snapshot, mixedCode);
     }
 
     /**
@@ -331,7 +404,8 @@
         }
     }
 
-    private static int getSystemHashCode(ActivityRecord activity) {
+    @VisibleForTesting
+    static int getSystemHashCode(ActivityRecord activity) {
         return System.identityHashCode(activity);
     }
 
@@ -362,7 +436,13 @@
         if (ar.isVisibleRequested()) {
             mPendingDeleteActivity.add(ar);
             // load next one if exists.
-            addBelowActivityIfExist(ar, mPendingLoadActivity, true, "load-snapshot");
+            // Note if this transition is happen between two TaskFragment, the next N - 1 activity
+            // may not participant in this transition.
+            // Sample:
+            //   [TF1] close
+            //   [TF2] open
+            //   Bottom Activity <- Able to load this even it didn't participant the transition.
+            addBelowActivityIfExist(ar, mPendingLoadActivity, false, "load-snapshot");
         } else {
             // remove the snapshot for the one below close
             addBelowActivityIfExist(ar, mPendingRemoveActivity, true, "remove-snapshot");
@@ -478,10 +558,8 @@
     }
 
     private void adjustSavedFileOrder(Task nextTopTask) {
-        final int userId = nextTopTask.mUserId;
         nextTopTask.forAllActivities(ar -> {
-            final int code = getSystemHashCode(ar);
-            final UserSavedFile usf = getUserFiles(userId).get(code);
+            final UserSavedFile usf = findSavedFile(ar);
             if (usf != null) {
                 mSavedFilesInOrder.remove(usf);
                 mSavedFilesInOrder.add(usf);
@@ -494,9 +572,7 @@
         if (shouldDisableSnapshots()) {
             return;
         }
-        super.onAppRemoved(activity);
-        final int code = getSystemHashCode(activity);
-        removeIfUserSavedFileExist(code, activity.mUserId);
+        removeIfUserSavedFileExist(activity);
         if (DEBUG) {
             Slog.d(TAG, "ActivitySnapshotController#onAppRemoved delete snapshot " + activity);
         }
@@ -507,9 +583,7 @@
         if (shouldDisableSnapshots()) {
             return;
         }
-        super.onAppDied(activity);
-        final int code = getSystemHashCode(activity);
-        removeIfUserSavedFileExist(code, activity.mUserId);
+        removeIfUserSavedFileExist(activity);
         if (DEBUG) {
             Slog.d(TAG, "ActivitySnapshotController#onAppDied delete snapshot " + activity);
         }
@@ -558,55 +632,92 @@
         return mUserSavedFiles.get(userId);
     }
 
-    private void removeIfUserSavedFileExist(int code, int userId) {
-        final UserSavedFile usf = getUserFiles(userId).get(code);
+    UserSavedFile findSavedFile(@NonNull ActivityRecord ar) {
+        final int code = getSystemHashCode(ar);
+        return findSavedFile(ar.mUserId, code);
+    }
+
+    UserSavedFile findSavedFile(int userId, int code) {
+        final SparseArray<UserSavedFile> usfs = getUserFiles(userId);
+        return usfs.get(code);
+    }
+
+    private void removeCachedFiles(ActivityRecord ar) {
+        final UserSavedFile usf = findSavedFile(ar);
         if (usf != null) {
-            mUserSavedFiles.get(userId).remove(code);
-            mSavedFilesInOrder.remove(usf);
-            mPersister.removeSnapshot(code, userId);
+            for (int i = usf.mActivityIds.size() - 1; i >= 0; --i) {
+                final int activityId = usf.mActivityIds.get(i);
+                mCache.onIdRemoved(activityId);
+            }
         }
     }
 
-    private boolean containsFile(int code, int userId) {
-        return getUserFiles(userId).get(code) != null;
+    private void removeIfUserSavedFileExist(ActivityRecord ar) {
+        final UserSavedFile usf = findSavedFile(ar);
+        if (usf != null) {
+            final SparseArray<UserSavedFile> usfs = getUserFiles(ar.mUserId);
+            for (int i = usf.mActivityIds.size() - 1; i >= 0; --i) {
+                final int activityId = usf.mActivityIds.get(i);
+                usf.remove(activityId);
+                mCache.onIdRemoved(activityId);
+                usfs.remove(activityId);
+            }
+            mSavedFilesInOrder.remove(usf);
+            mPersister.removeSnapshot(usf.mFileId, ar.mUserId);
+        }
     }
 
-    private void addUserSavedFile(int code, int userId, TaskSnapshot snapshot) {
-        final SparseArray<UserSavedFile> savedFiles = getUserFiles(userId);
-        final UserSavedFile savedFile = savedFiles.get(code);
-        if (savedFile == null) {
-            final UserSavedFile usf = new UserSavedFile(code, userId);
-            savedFiles.put(code, usf);
-            mSavedFilesInOrder.add(usf);
-            mPersister.persistSnapshot(code, userId, snapshot);
+    @VisibleForTesting
+    boolean hasRecord(@NonNull ActivityRecord ar) {
+        return findSavedFile(ar) != null;
+    }
 
-            if (mSavedFilesInOrder.size() > MAX_PERSIST_SNAPSHOT_COUNT * 2) {
-                purgeSavedFile();
-            }
+    @VisibleForTesting
+    void addUserSavedFile(int userId, TaskSnapshot snapshot, @NonNull int[] code) {
+        final UserSavedFile savedFile = findSavedFile(userId, code[0]);
+        if (savedFile != null) {
+            Slog.w(TAG, "Duplicate request for recording activity snapshot " + savedFile);
+            return;
+        }
+        int fileId = 0;
+        for (int i = code.length - 1; i >= 0; --i) {
+            fileId ^= code[i];
+        }
+        final UserSavedFile usf = new UserSavedFile(fileId, userId);
+        SparseArray<UserSavedFile> usfs = getUserFiles(userId);
+        for (int i = code.length - 1; i >= 0; --i) {
+            usfs.put(code[i], usf);
+        }
+        usf.mActivityIds.addAll(code);
+        mSavedFilesInOrder.add(usf);
+        mPersister.persistSnapshot(fileId, userId, snapshot);
+
+        if (mSavedFilesInOrder.size() > MAX_PERSIST_SNAPSHOT_COUNT * 2) {
+            purgeSavedFile();
         }
     }
 
     private void purgeSavedFile() {
         final int savedFileCount = mSavedFilesInOrder.size();
         final int removeCount = savedFileCount - MAX_PERSIST_SNAPSHOT_COUNT;
-        final ArrayList<UserSavedFile> usfs = new ArrayList<>();
-        if (removeCount > 0) {
-            final int removeTillIndex = savedFileCount - removeCount;
-            for (int i = savedFileCount - 1; i > removeTillIndex; --i) {
-                final UserSavedFile usf = mSavedFilesInOrder.remove(i);
-                if (usf != null) {
-                    final SparseArray<UserSavedFile> records = getUserFiles(usf.mUserId);
-                    records.remove(usf.mFileId);
-                    usfs.add(usf);
-                }
+        if (removeCount < 1) {
+            return;
+        }
+
+        final ArrayList<UserSavedFile> removeTargets = new ArrayList<>();
+        for (int i = removeCount - 1; i >= 0; --i) {
+            final UserSavedFile usf = mSavedFilesInOrder.remove(i);
+            final SparseArray<UserSavedFile> files = mUserSavedFiles.get(usf.mUserId);
+            for (int j = usf.mActivityIds.size() - 1; j >= 0; --j) {
+                mCache.removeRunningEntry(usf.mActivityIds.get(j));
+                files.remove(usf.mActivityIds.get(j));
             }
+            removeTargets.add(usf);
         }
-        if (usfs.size() > 0) {
-            removeSnapshotFiles(usfs);
-        }
+        removeSnapshotFiles(removeTargets);
     }
 
-    private void removeSnapshotFiles(ArrayList<UserSavedFile> files) {
+    private void removeSnapshotFiles(@NonNull ArrayList<UserSavedFile> files) {
         synchronized (mSnapshotPersistQueue.getLock()) {
             mSnapshotPersistQueue.sendToQueueLocked(
                     new SnapshotPersistQueue.WriteQueueItem(mPersistInfoProvider) {
@@ -624,12 +735,85 @@
         }
     }
 
+    @Override
+    void dump(PrintWriter pw, String prefix) {
+        super.dump(pw, prefix);
+        final String doublePrefix = prefix + "  ";
+        final String triplePrefix = doublePrefix + "  ";
+        for (int i = mUserSavedFiles.size() - 1; i >= 0; --i) {
+            final SparseArray<UserSavedFile> usfs = mUserSavedFiles.valueAt(i);
+            pw.println(doublePrefix + "UserSavedFile userId=" + mUserSavedFiles.keyAt(i));
+            final ArraySet<UserSavedFile> sets = new ArraySet<>();
+            for (int j = usfs.size() - 1; j >= 0; --j) {
+                sets.add(usfs.valueAt(j));
+            }
+            for (int j = sets.size() - 1; j >= 0; --j) {
+                pw.println(triplePrefix + "SavedFile=" + sets.valueAt(j));
+            }
+        }
+    }
+
     static class UserSavedFile {
-        int mFileId;
-        int mUserId;
+        // The unique id as filename.
+        final int mFileId;
+        final int mUserId;
+
+        /**
+         * The Id of all activities which are includes in the snapshot.
+         */
+        final IntArray mActivityIds = new IntArray();
+
         UserSavedFile(int fileId, int userId) {
             mFileId = fileId;
             mUserId = userId;
         }
+
+        boolean contains(int code) {
+            return mActivityIds.contains(code);
+        }
+
+        void remove(int code) {
+            final int index = mActivityIds.indexOf(code);
+            if (index >= 0) {
+                mActivityIds.remove(index);
+            }
+        }
+
+        ActivityRecord[] filterExistActivities(
+                @NonNull ArraySet<ActivityRecord> pendingLoadActivity) {
+            ArrayList<ActivityRecord> matchedActivities = null;
+            for (int i = pendingLoadActivity.size() - 1; i >= 0; --i) {
+                final ActivityRecord ar = pendingLoadActivity.valueAt(i);
+                if (contains(getSystemHashCode(ar))) {
+                    if (matchedActivities == null) {
+                        matchedActivities = new ArrayList<>();
+                    }
+                    matchedActivities.add(ar);
+                }
+            }
+            if (matchedActivities == null || matchedActivities.size() != mActivityIds.size()) {
+                return null;
+            }
+            return matchedActivities.toArray(new ActivityRecord[0]);
+        }
+
+        @Override
+        public String toString() {
+            StringBuilder sb = new StringBuilder(128);
+            sb.append("UserSavedFile{");
+            sb.append(Integer.toHexString(System.identityHashCode(this)));
+            sb.append(" fileId=");
+            sb.append(Integer.toHexString(mFileId));
+            sb.append(", activityIds=[");
+            for (int i = mActivityIds.size() - 1; i >= 0; --i) {
+                sb.append(Integer.toHexString(mActivityIds.get(i)));
+                if (i > 0) {
+                    sb.append(',');
+                }
+            }
+            sb.append("]");
+            sb.append("}");
+            return sb.toString();
+        }
     }
 }
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index cb2adbc..13f7152 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -73,7 +73,6 @@
 import static com.android.server.wm.ActivityTaskManagerService.ANIMATE;
 import static com.android.server.wm.ActivityTaskSupervisor.DEFER_RESUME;
 import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP;
-import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
 import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_DEFAULT;
 import static com.android.server.wm.BackgroundActivityStartController.BAL_BLOCK;
 import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.PHASE_BOUNDS;
@@ -994,17 +993,6 @@
             }
         }
 
-        if (Flags.archiving()) {
-            PackageArchiver packageArchiver = mService
-                    .getPackageManagerInternalLocked()
-                    .getPackageArchiver();
-            if (packageArchiver.isIntentResolvedToArchivedApp(intent, mRequest.userId)) {
-                return packageArchiver
-                        .requestUnarchiveOnActivityStart(
-                                intent, callingPackage, mRequest.userId, realCallingUid);
-            }
-        }
-
         final int launchFlags = intent.getFlags();
         if ((launchFlags & Intent.FLAG_ACTIVITY_FORWARD_RESULT) != 0 && sourceRecord != null) {
             // Transfer the result target from the source activity to the new one being started,
@@ -1046,6 +1034,17 @@
         }
 
         if (err == ActivityManager.START_SUCCESS && aInfo == null) {
+            if (Flags.archiving()) {
+                PackageArchiver packageArchiver = mService
+                        .getPackageManagerInternalLocked()
+                        .getPackageArchiver();
+                if (packageArchiver.isIntentResolvedToArchivedApp(intent, mRequest.userId)) {
+                    return packageArchiver
+                            .requestUnarchiveOnActivityStart(
+                                    intent, callingPackage, mRequest.userId, realCallingUid);
+                }
+            }
+
             // We couldn't find the specific class specified in the Intent.
             // Also the end of the line.
             err = ActivityManager.START_CLASS_NOT_FOUND;
@@ -1733,6 +1732,7 @@
             // So disallow the transient hide activity to move itself to front, e.g. trampoline.
             if (!avoidMoveToFront() && (mService.mHomeProcess == null
                     || mService.mHomeProcess.mUid != realCallingUid)
+                    && (prevTopTask != null && prevTopTask.isActivityTypeHomeOrRecents())
                     && r.mTransitionController.isTransientHide(targetTask)) {
                 mCanMoveToFrontCode = MOVE_TO_FRONT_AVOID_LEGACY;
             }
@@ -1859,8 +1859,7 @@
                 // over is removed.
                 // Passing {@code null} as the start parameter ensures all activities are made
                 // visible.
-                mTargetRootTask.ensureActivitiesVisible(null /* starting */,
-                        0 /* configChanges */, !PRESERVE_WINDOWS);
+                mTargetRootTask.ensureActivitiesVisible(null /* starting */);
                 // Go ahead and tell window manager to execute app transition for this activity
                 // since the app transition will not be triggered through the resume channel.
                 mTargetRootTask.mDisplayContent.executeAppTransition();
@@ -2867,7 +2866,7 @@
                 mRootWindowContainer.resumeFocusedTasksTopActivities(mTargetRootTask, null,
                         mOptions, mTransientLaunch);
             } else {
-                mRootWindowContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
+                mRootWindowContainer.ensureActivitiesVisible();
             }
         } else {
             ActivityOptions.abort(mOptions);
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index dbae29b..3959a5e 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -118,7 +118,6 @@
 import static com.android.server.wm.ActivityTaskManagerService.UiHandler.DISMISS_DIALOG_UI_MSG;
 import static com.android.server.wm.ActivityTaskSupervisor.DEFER_RESUME;
 import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP;
-import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
 import static com.android.server.wm.ActivityTaskSupervisor.REMOVE_FROM_RECENTS;
 import static com.android.server.wm.BackgroundActivityStartController.BalVerdict;
 import static com.android.server.wm.LockTaskController.LOCK_TASK_AUTH_DONT_LOCK;
@@ -496,16 +495,13 @@
     final UpdateConfigurationResult mTmpUpdateConfigurationResult =
             new UpdateConfigurationResult();
 
+    // TODO(b/258618073): Remove this and make the related methods return whether config is changed.
     static final class UpdateConfigurationResult {
         // Configuration changes that were updated.
         int changes;
         // If the activity was relaunched to match the new configuration.
         boolean activityRelaunched;
-
-        void reset() {
-            changes = 0;
-            activityRelaunched = false;
-        }
+        boolean mIsUpdating;
     }
 
     /** Current sequencing integer of the configuration, for skipping old configurations. */
@@ -3695,19 +3691,13 @@
             return false;
         }
 
-        // If the app is using legacy-entry (not auto-enter), then we will get a client-request
-        // that was actually a server-request (via pause(userLeaving=true)). This happens when
-        // the app is PAUSING, so detect that case here.
-        boolean originallyFromClient = fromClient
-                && (!r.isState(PAUSING) || params.isAutoEnterEnabled());
-
-        // If PiP2 flag is on and client-request to enter PiP came via onUserLeaveHint(),
-        // we request a direct transition from Shell to TRANSIT_PIP_LEGACY to get the startWct
-        // with the right entry bounds.
-        if (isPip2ExperimentEnabled() && !originallyFromClient && !params.isAutoEnterEnabled()) {
+        // If PiP2 flag is on and client-request to enter PiP comes in,
+        // we request a direct transition from Shell to TRANSIT_PIP to get the startWct
+        // with the right entry bounds. So PiP activity isn't moved to a pinned task until after
+        // Shell calls back into Core with the entry bounds passed through.
+        if (isPip2ExperimentEnabled()) {
             final Transition legacyEnterPipTransition = new Transition(TRANSIT_PIP,
-                    0 /* flags */, getTransitionController(),
-                    mWindowManager.mSyncEngine);
+                    0 /* flags */, getTransitionController(), mWindowManager.mSyncEngine);
             legacyEnterPipTransition.setPipActivity(r);
             getTransitionController().startCollectOrQueue(legacyEnterPipTransition, (deferred) -> {
                 getTransitionController().requestStartTransition(legacyEnterPipTransition,
@@ -3716,6 +3706,12 @@
             return true;
         }
 
+        // If the app is using legacy-entry (not auto-enter), then we will get a client-request
+        // that was actually a server-request (via pause(userLeaving=true)). This happens when
+        // the app is PAUSING, so detect that case here.
+        boolean originallyFromClient = fromClient
+                && (!r.isState(PAUSING) || params.isAutoEnterEnabled());
+
         // Create a transition only for this pip entry if it is coming from the app without the
         // system requesting that the app enter-pip. If the system requested it, that means it
         // should be part of that transition if possible.
@@ -3834,8 +3830,7 @@
                     Settings.System.clearConfiguration(values);
                 }
                 updateConfigurationLocked(values, null, false, false /* persistent */,
-                        UserHandle.USER_NULL, false /* deferResume */,
-                        mTmpUpdateConfigurationResult);
+                        UserHandle.USER_NULL, false /* deferResume */);
                 return mTmpUpdateConfigurationResult.changes != 0;
             } finally {
                 Binder.restoreCallingIdentity(origId);
@@ -4507,12 +4502,6 @@
         }
     }
 
-    private boolean updateConfigurationLocked(Configuration values, ActivityRecord starting,
-            boolean initLocale, boolean persistent, int userId, boolean deferResume) {
-        return updateConfigurationLocked(values, starting, initLocale, persistent, userId,
-                deferResume, null /* result */);
-    }
-
     /**
      * Do either or both things: (1) change the current configuration, and (2)
      * make sure the given activity is running with the (now) current
@@ -4524,8 +4513,7 @@
      *               for that particular user
      */
     boolean updateConfigurationLocked(Configuration values, ActivityRecord starting,
-            boolean initLocale, boolean persistent, int userId, boolean deferResume,
-            ActivityTaskManagerService.UpdateConfigurationResult result) {
+            boolean initLocale, boolean persistent, int userId, boolean deferResume) {
         int changes = 0;
         boolean kept = true;
 
@@ -4533,19 +4521,18 @@
         try {
             if (values != null) {
                 changes = updateGlobalConfigurationLocked(values, initLocale, persistent, userId);
+                mTmpUpdateConfigurationResult.changes = changes;
+                mTmpUpdateConfigurationResult.mIsUpdating = true;
             }
 
             if (!deferResume) {
                 kept = ensureConfigAndVisibilityAfterUpdate(starting, changes);
             }
         } finally {
+            mTmpUpdateConfigurationResult.mIsUpdating = false;
             continueWindowLayout();
         }
-
-        if (result != null) {
-            result.changes = changes;
-            result.activityRelaunched = !kept;
-        }
+        mTmpUpdateConfigurationResult.activityRelaunched = !kept;
         return kept;
     }
 
@@ -5325,12 +5312,10 @@
             }
 
             if (starting != null) {
-                kept = starting.ensureActivityConfiguration(changes,
-                        false /* preserveWindow */);
+                kept = starting.ensureActivityConfiguration();
                 // And we need to make sure at this point that all other activities
                 // are made visible with the correct configuration.
-                mRootWindowContainer.ensureActivitiesVisible(starting, changes,
-                        !PRESERVE_WINDOWS);
+                mRootWindowContainer.ensureActivitiesVisible(starting);
             }
         }
 
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index 10efb94..ec0e3e7 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -134,7 +134,6 @@
 import android.os.WorkSource;
 import android.provider.MediaStore;
 import android.util.ArrayMap;
-import android.util.MergedConfiguration;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseIntArray;
@@ -820,8 +819,6 @@
         proc.pauseConfigurationDispatch();
 
         try {
-            r.startFreezingScreenLocked(proc, 0);
-
             // schedule launch ticks to collect information about slow apps.
             r.startLaunchTickingLocked();
             r.lastLaunchTime = SystemClock.uptimeMillis();
@@ -908,13 +905,9 @@
                         r.intent.getComponent().getPackageName(), NOTIFY_PACKAGE_USE_ACTIVITY);
                 mService.getAppWarningsLocked().onStartActivity(r);
 
-                // Because we could be starting an Activity in the system process this may not go
-                // across a Binder interface which would create a new Configuration. Consequently
-                // we have to always create a new Configuration here.
                 final Configuration procConfig = proc.prepareConfigurationForLaunchingActivity();
-                final MergedConfiguration mergedConfiguration = new MergedConfiguration(
-                        procConfig, r.getMergedOverrideConfiguration());
-                r.setLastReportedConfiguration(mergedConfiguration);
+                final Configuration overrideConfig = r.getMergedOverrideConfiguration();
+                r.setLastReportedConfiguration(procConfig, overrideConfig);
 
                 logIfTransactionTooLarge(r.intent, r.getSavedState());
 
@@ -932,13 +925,10 @@
                 final int deviceId = getDeviceIdForDisplayId(r.getDisplayId());
                 final LaunchActivityItem launchActivityItem = LaunchActivityItem.obtain(r.token,
                         r.intent, System.identityHashCode(r), r.info,
-                        // TODO: Have this take the merged configuration instead of separate global
-                        // and override configs.
-                        mergedConfiguration.getGlobalConfiguration(),
-                        mergedConfiguration.getOverrideConfiguration(), deviceId,
+                        procConfig, overrideConfig, deviceId,
                         r.getFilteredReferrer(r.launchedFromPackage), task.voiceInteractor,
                         proc.getReportedProcState(), r.getSavedState(), r.getPersistentSavedState(),
-                        results, newIntents, r.takeOptions(), isTransitionForward,
+                        results, newIntents, r.takeSceneTransitionInfo(), isTransitionForward,
                         proc.createProfilerInfoIfNeeded(), r.assistToken, activityClientController,
                         r.shareableActivityToken, r.getLaunchedFromBubble(), fragmentToken);
 
@@ -1462,7 +1452,7 @@
                 }
                 mLaunchingActivityWakeLock.release();
             }
-            mRootWindowContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
+            mRootWindowContainer.ensureActivitiesVisible();
         }
 
         // Atomically retrieve all of the other things to do.
@@ -1603,7 +1593,7 @@
          */
         rootTask.cancelAnimation();
         rootTask.setForceHidden(FLAG_FORCE_HIDDEN_FOR_PINNED_TASK, true /* set */);
-        rootTask.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);
+        rootTask.ensureActivitiesVisible(null /* starting */);
         activityIdleInternal(null /* idleActivity */, false /* fromTimeout */,
                 true /* processPausingActivities */, null /* configuration */);
 
@@ -1622,7 +1612,7 @@
             // Follow on the workaround: activities are kept force hidden till the new windowing
             // mode is set.
             rootTask.setForceHidden(FLAG_FORCE_HIDDEN_FOR_PINNED_TASK, false /* set */);
-            mRootWindowContainer.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);
+            mRootWindowContainer.ensureActivitiesVisible();
             mRootWindowContainer.resumeFocusedTasksTopActivities();
         } finally {
             mService.continueWindowLayout();
@@ -2026,7 +2016,7 @@
 
         final Task rootTask = r.getRootTask();
         if (rootTask.getDisplayArea().allResumedActivitiesComplete()) {
-            mRootWindowContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
+            mRootWindowContainer.ensureActivitiesVisible();
             // Make sure activity & window visibility should be identical
             // for all displays in this stage.
             mRootWindowContainer.executeAppTransitionForAllDisplay();
@@ -2042,7 +2032,7 @@
 
         mRecentTasks.add(task);
         mService.getTaskChangeNotificationController().notifyTaskStackChanged();
-        rootTask.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
+        rootTask.ensureActivitiesVisible(null /* starting */);
 
         // When launching tasks behind, update the last active time of the top task after the new
         // task has been shown briefly
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index 05087f8..939babc 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -1176,7 +1176,6 @@
                 mDisplayContent.mNoAnimationNotifyOnTransitionFinished.add(app.token);
             }
             app.updateReportedVisibilityLocked();
-            app.waitingToShow = false;
             app.showAllWindowsLocked();
 
             if (mDisplayContent.mAppTransition.isNextAppTransitionThumbnailUp()) {
diff --git a/services/core/java/com/android/server/wm/AsyncRotationController.java b/services/core/java/com/android/server/wm/AsyncRotationController.java
index 68d13cd..6ed8967 100644
--- a/services/core/java/com/android/server/wm/AsyncRotationController.java
+++ b/services/core/java/com/android/server/wm/AsyncRotationController.java
@@ -91,6 +91,13 @@
     /** Non-zero if this controller is triggered by shell transition. */
     private final @TransitionOp int mTransitionOp;
 
+    /**
+     * Whether {@link #setupStartTransaction} is called when the transition is ready.
+     * If this is never set for {@link #OP_CHANGE}, the display may be changed to original state
+     * before the transition is ready, then this controller should be finished.
+     */
+    private boolean mIsStartTransactionPrepared;
+
     /** Whether the start transaction of the transition is committed (by shell). */
     private boolean mIsStartTransactionCommitted;
 
@@ -226,7 +233,8 @@
     void updateTargetWindows() {
         if (mTransitionOp == OP_LEGACY) return;
         if (!mIsStartTransactionCommitted) {
-            if (mTimeoutRunnable == null && !mDisplayContent.hasTopFixedRotationLaunchingApp()
+            if ((mTimeoutRunnable == null || !mIsStartTransactionPrepared)
+                    && !mDisplayContent.hasTopFixedRotationLaunchingApp()
                     && !mDisplayContent.isRotationChanging() && !mDisplayContent.inTransition()) {
                 Slog.d(TAG, "Cancel for no change");
                 mDisplayContent.finishAsyncRotationIfPossible();
@@ -401,9 +409,18 @@
         if (mTimeoutRunnable == null) {
             mTimeoutRunnable = () -> {
                 synchronized (mService.mGlobalLock) {
-                    Slog.i(TAG, "Async rotation timeout: " + (!mIsStartTransactionCommitted
-                            ? " start transaction is not committed" : mTargetWindowTokens));
+                    final String reason;
                     if (!mIsStartTransactionCommitted) {
+                        if (!mIsStartTransactionPrepared) {
+                            reason = "setupStartTransaction is not called";
+                        } else {
+                            reason = "start transaction is not committed";
+                        }
+                    } else {
+                        reason = "unfinished windows " + mTargetWindowTokens;
+                    }
+                    Slog.i(TAG, "Async rotation timeout: " + reason);
+                    if (!mIsStartTransactionCommitted && mIsStartTransactionPrepared) {
                         // The transaction commit timeout will be handled by:
                         // 1. BLASTSyncEngine will notify onTransactionCommitTimeout() and then
                         //    apply the start transaction of transition.
@@ -558,6 +575,7 @@
                 }
             }
         });
+        mIsStartTransactionPrepared = true;
     }
 
     /** Called when the start transition is ready, but it is not applied in time. */
@@ -577,6 +595,10 @@
     /** Called when the transition by shell is done. */
     void onTransitionFinished() {
         if (mTransitionOp == OP_CHANGE) {
+            if (mTargetWindowTokens.isEmpty()) {
+                // If nothing was handled, then complete with the transition.
+                mDisplayContent.finishAsyncRotationIfPossible();
+            }
             // With screen rotation animation, the windows are always faded in when they are drawn.
             // Because if they are drawn fast enough, the fade animation should not be observable.
             return;
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index c3f1e41..8aaf76a 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -1311,7 +1311,7 @@
                 Rect insets;
                 if (mainWindow != null) {
                     insets = mainWindow.getInsetsStateWithVisibilityOverride().calculateInsets(
-                            mBounds, WindowInsets.Type.systemBars(),
+                            mBounds, WindowInsets.Type.tappableElement(),
                             false /* ignoreVisibility */).toRect();
                     InsetUtils.addInsets(insets, mainWindow.mActivityRecord.getLetterboxInsets());
                 } else {
@@ -1327,7 +1327,8 @@
                 return mAnimationTarget;
             }
 
-            void createStartingSurface(@NonNull WindowContainer closeWindow) {
+            void createStartingSurface(@NonNull WindowContainer closeWindow,
+                    @NonNull ActivityRecord[] visibleOpenActivities) {
                 if (!mIsOpen) {
                     return;
                 }
@@ -1346,7 +1347,7 @@
                 if (mainActivity == null) {
                     return;
                 }
-                final TaskSnapshot snapshot = getSnapshot(mTarget);
+                final TaskSnapshot snapshot = getSnapshot(mTarget, visibleOpenActivities);
                 mRequestedStartingSurfaceId = openTask.mAtmService.mTaskOrganizerController
                         .addWindowlessStartingSurface(openTask, mainActivity,
                                 // Choose configuration from closeWindow, because the configuration
@@ -1489,7 +1490,8 @@
                         // Try to draw two snapshot within a WindowlessStartingWindow, or find
                         // another key for StartingWindowRecordManager.
                         && openAnimationAdaptor.length == 1) {
-                    openAnimationAdaptor[0].createStartingSurface(closeWindow);
+                    openAnimationAdaptor[0].createStartingSurface(closeWindow,
+                            visibleOpenActivities);
                 } else {
                     for (int i = visibleOpenActivities.length - 1; i >= 0; --i) {
                         setLaunchBehind(visibleOpenActivities[i]);
@@ -1594,7 +1596,9 @@
             // skip commitVisibility call in setVisibility cause the activity won't visible here.
             // Call it again to make sure the activity could be visible while handling the pending
             // animation.
-            activity.commitVisibility(true, true);
+            // Do not performLayout during prepare animation, because it could cause focus window
+            // change. Let that happen after the BackNavigationInfo has returned to shell.
+            activity.commitVisibility(true, false /* performLayout */);
             activity.mTransitionController.mSnapshotController
                     .mActivitySnapshotController.addOnBackPressedActivity(activity);
         }
@@ -1614,7 +1618,7 @@
                 "Setting Activity.mLauncherTaskBehind to true. Activity=%s", activity);
         activity.mTaskSupervisor.mStoppingActivities.remove(activity);
         activity.getDisplayContent().ensureActivitiesVisible(null /* starting */,
-                0 /* configChanges */, false /* preserveWindows */, true);
+                true /* notifyClients */);
     }
 
     private static void restoreLaunchBehind(@NonNull ActivityRecord activity) {
@@ -1671,7 +1675,8 @@
         mPendingAnimationBuilder = null;
     }
 
-    static TaskSnapshot getSnapshot(@NonNull WindowContainer w) {
+    static TaskSnapshot getSnapshot(@NonNull WindowContainer w,
+            ActivityRecord[] visibleOpenActivities) {
         if (w.asTask() != null) {
             final Task task = w.asTask();
             return task.mRootWindowContainer.mWindowManager.mTaskSnapshotController.getSnapshot(
@@ -1681,7 +1686,8 @@
 
         if (w.asActivityRecord() != null) {
             final ActivityRecord ar = w.asActivityRecord();
-            return ar.mWmService.mSnapshotController.mActivitySnapshotController.getSnapshot(ar);
+            return ar.mWmService.mSnapshotController.mActivitySnapshotController
+                    .getSnapshot(visibleOpenActivities);
         }
         return null;
     }
diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
index eed46fe..9c9cf04 100644
--- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
+++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
@@ -61,6 +61,7 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.FrameworkStatsLog;
+import com.android.internal.util.Preconditions;
 import com.android.server.UiThread;
 import com.android.server.am.PendingIntentRecord;
 import com.android.window.flags.Flags;
@@ -119,38 +120,56 @@
 
     static final int BAL_BLOCK = 0;
 
-    static final int BAL_ALLOW_DEFAULT = 1;
+    static final int BAL_ALLOW_DEFAULT =
+            FrameworkStatsLog.BAL_ALLOWED__ALLOWED_REASON__BAL_ALLOW_DEFAULT;
 
     // Following codes are in order of precedence
 
     /** Important UIDs which should be always allowed to launch activities */
-    static final int BAL_ALLOW_ALLOWLISTED_UID = 2;
+    static final int BAL_ALLOW_ALLOWLISTED_UID =
+            FrameworkStatsLog.BAL_ALLOWED__ALLOWED_REASON__BAL_ALLOW_ALLOWLISTED_UID;
 
     /** Apps that fulfill a certain role that can can always launch new tasks */
-    static final int BAL_ALLOW_ALLOWLISTED_COMPONENT = 3;
+    static final int BAL_ALLOW_ALLOWLISTED_COMPONENT =
+            FrameworkStatsLog.BAL_ALLOWED__ALLOWED_REASON__BAL_ALLOW_ALLOWLISTED_COMPONENT;
 
-    /** Apps which currently have a visible window or are bound by a service with a visible
-     * window */
-    static final int BAL_ALLOW_VISIBLE_WINDOW = 4;
+    /**
+     * Apps which currently have a visible window or are bound by a service with a visible
+     * window
+     */
+    static final int BAL_ALLOW_VISIBLE_WINDOW =
+            FrameworkStatsLog.BAL_ALLOWED__ALLOWED_REASON__BAL_ALLOW_VISIBLE_WINDOW;
 
     /** Allowed due to the PendingIntent sender */
-    static final int BAL_ALLOW_PENDING_INTENT = 5;
+    static final int BAL_ALLOW_PENDING_INTENT =
+            FrameworkStatsLog.BAL_ALLOWED__ALLOWED_REASON__BAL_ALLOW_PENDING_INTENT;
 
-    /** App has START_ACTIVITIES_FROM_BACKGROUND permission or BAL instrumentation privileges
-     * granted to it */
-    static final int BAL_ALLOW_PERMISSION = 6;
+    /**
+     * App has START_ACTIVITIES_FROM_BACKGROUND permission or BAL instrumentation privileges
+     * granted to it
+     */
+    static final int BAL_ALLOW_PERMISSION =
+            FrameworkStatsLog.BAL_ALLOWED__ALLOWED_REASON__BAL_ALLOW_BAL_PERMISSION;
 
     /** Process has SYSTEM_ALERT_WINDOW permission granted to it */
-    static final int BAL_ALLOW_SAW_PERMISSION = 7;
+    static final int BAL_ALLOW_SAW_PERMISSION =
+            FrameworkStatsLog.BAL_ALLOWED__ALLOWED_REASON__BAL_ALLOW_SAW_PERMISSION;
 
     /** App is in grace period after an activity was started or finished */
-    static final int BAL_ALLOW_GRACE_PERIOD = 8;
+    static final int BAL_ALLOW_GRACE_PERIOD =
+            FrameworkStatsLog.BAL_ALLOWED__ALLOWED_REASON__BAL_ALLOW_GRACE_PERIOD;
 
     /** App is in a foreground task or bound to a foreground service (but not itself visible) */
-    static final int BAL_ALLOW_FOREGROUND = 9;
+    static final int BAL_ALLOW_FOREGROUND =
+            FrameworkStatsLog.BAL_ALLOWED__ALLOWED_REASON__BAL_ALLOW_FOREGROUND;
 
     /** Process belongs to a SDK sandbox */
-    static final int BAL_ALLOW_SDK_SANDBOX = 10;
+    static final int BAL_ALLOW_SDK_SANDBOX =
+            FrameworkStatsLog.BAL_ALLOWED__ALLOWED_REASON__BAL_ALLOW_SDK_SANDBOX;
+
+    /** Process belongs to a SDK sandbox */
+    static final int BAL_ALLOW_NON_APP_VISIBLE_WINDOW =
+            FrameworkStatsLog.BAL_ALLOWED__ALLOWED_REASON__BAL_ALLOW_NON_APP_VISIBLE_WINDOW;
 
     static String balCodeToString(@BalCode int balCode) {
         return switch (balCode) {
@@ -159,6 +178,7 @@
             case BAL_ALLOW_DEFAULT -> "BAL_ALLOW_DEFAULT";
             case BAL_ALLOW_FOREGROUND -> "BAL_ALLOW_FOREGROUND";
             case BAL_ALLOW_GRACE_PERIOD -> "BAL_ALLOW_GRACE_PERIOD";
+            case BAL_ALLOW_NON_APP_VISIBLE_WINDOW -> "BAL_ALLOW_NON_APP_VISIBLE_WINDOW";
             case BAL_ALLOW_PENDING_INTENT -> "BAL_ALLOW_PENDING_INTENT";
             case BAL_ALLOW_PERMISSION -> "BAL_ALLOW_PERMISSION";
             case BAL_ALLOW_SAW_PERMISSION -> "BAL_ALLOW_SAW_PERMISSION";
@@ -219,6 +239,9 @@
         private final WindowProcessController mCallerApp;
         private final WindowProcessController mRealCallerApp;
         private final boolean mIsCallForResult;
+        private final ActivityOptions mCheckedOptions;
+        private BalVerdict mResultForCaller;
+        private BalVerdict mResultForRealCaller;
 
         private BalState(int callingUid, int callingPid, final String callingPackage,
                  int realCallingUid, int realCallingPid,
@@ -239,6 +262,7 @@
             mIntent = intent;
             mRealCallingPackage = mService.getPackageNameIfUnique(realCallingUid, realCallingPid);
             mIsCallForResult = resultRecord != null;
+            mCheckedOptions = checkedOptions;
             if (balRequireOptInByPendingIntentCreator() // auto-opt in introduced with this feature
                     && (originatingPendingIntent == null // not a PendingIntent
                     || mIsCallForResult) // sent for result
@@ -369,8 +393,19 @@
             return mCallingUid == mRealCallingUid;
         }
 
-        private String dump(BalVerdict resultIfPiCreatorAllowsBal,
-                            BalVerdict resultIfPiSenderAllowsBal) {
+        public void setResultForCaller(BalVerdict resultForCaller) {
+            Preconditions.checkState(mResultForCaller == null,
+                    "mResultForCaller can only be set once");
+            this.mResultForCaller = resultForCaller;
+        }
+
+        public void setResultForRealCaller(BalVerdict resultForRealCaller) {
+            Preconditions.checkState(mResultForRealCaller == null,
+                    "mResultForRealCaller can only be set once");
+            this.mResultForRealCaller = resultForRealCaller;
+        }
+
+        private String dump() {
             StringBuilder sb = new StringBuilder(2048);
             sb.append("[callingPackage: ")
                     .append(getDebugPackageName(mCallingPackage, mCallingUid));
@@ -392,7 +427,7 @@
             sb.append("; balAllowedByPiCreator: ").append(mBalAllowedByPiCreator);
             sb.append("; balAllowedByPiCreatorWithHardening: ")
                     .append(mBalAllowedByPiCreatorWithHardening);
-            sb.append("; resultIfPiCreatorAllowsBal: ").append(resultIfPiCreatorAllowsBal);
+            sb.append("; resultIfPiCreatorAllowsBal: ").append(mResultForCaller);
             sb.append("; hasRealCaller: ").append(hasRealCaller());
             sb.append("; isCallForResult: ").append(mIsCallForResult);
             sb.append("; isPendingIntent: ").append(isPendingIntent());
@@ -416,7 +451,7 @@
                             .append(mRealCallerApp.hasActivityInVisibleTask());
                 }
                 sb.append("; balAllowedByPiSender: ").append(mBalAllowedByPiSender);
-                sb.append("; resultIfPiSenderAllowsBal: ").append(resultIfPiSenderAllowsBal);
+                sb.append("; resultIfPiSenderAllowsBal: ").append(mResultForRealCaller);
             }
             sb.append("]");
             return sb.toString();
@@ -559,23 +594,25 @@
             // realCallingSdkSandboxUidToAppUid should probably just be used instead (or in addition
             // to realCallingUid when calculating resultForRealCaller below.
             if (mService.hasActiveVisibleWindow(realCallingSdkSandboxUidToAppUid)) {
-                BalVerdict balVerdict = new BalVerdict(BAL_ALLOW_SDK_SANDBOX, /*background*/ false,
-                        "uid in SDK sandbox has visible (non-toast) window");
-                return statsLog(balVerdict, state);
+                state.setResultForRealCaller(new BalVerdict(BAL_ALLOW_SDK_SANDBOX,
+                        /*background*/ false,
+                        "uid in SDK sandbox has visible (non-toast) window"));
+                return allowBasedOnRealCaller(state);
             }
         }
 
         BalVerdict resultForCaller = checkBackgroundActivityStartAllowedByCaller(state);
+        state.setResultForCaller(resultForCaller);
 
         if (!state.hasRealCaller()) {
             if (resultForCaller.allows()) {
                 if (DEBUG_ACTIVITY_STARTS) {
                     Slog.d(TAG, "Background activity start allowed. "
-                            + state.dump(resultForCaller, resultForCaller));
+                            + state.dump());
                 }
-                return statsLog(resultForCaller, state);
+                return allowBasedOnCaller(state);
             }
-            return abortLaunch(state, resultForCaller, resultForCaller);
+            return abortLaunch(state);
         }
 
         // The realCaller result is only calculated for PendingIntents (indicated by a valid
@@ -589,6 +626,8 @@
                 ? resultForCaller
                 : checkBackgroundActivityStartAllowedBySender(state, checkedOptions)
                         .setBasedOnRealCaller();
+        state.setResultForRealCaller(resultForRealCaller);
+
         if (state.isPendingIntent()) {
             resultForCaller.setOnlyCreatorAllows(
                     resultForCaller.allows() && resultForRealCaller.blocks());
@@ -600,18 +639,18 @@
                 == ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED) {
             if (DEBUG_ACTIVITY_STARTS) {
                 Slog.d(TAG, "Activity start explicitly allowed by caller. "
-                        + state.dump(resultForCaller, resultForRealCaller));
+                        + state.dump());
             }
-            return statsLog(resultForCaller, state);
+            return allowBasedOnCaller(state);
         }
         if (resultForRealCaller.allows()
                 && checkedOptions.getPendingIntentBackgroundActivityStartMode()
                 == ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED) {
             if (DEBUG_ACTIVITY_STARTS) {
                 Slog.d(TAG, "Activity start explicitly allowed by real caller. "
-                        + state.dump(resultForCaller, resultForRealCaller));
+                        + state.dump());
             }
-            return statsLog(resultForRealCaller, state);
+            return allowBasedOnRealCaller(state);
         }
         // Handle PendingIntent cases with default behavior next
         boolean callerCanAllow = resultForCaller.allows()
@@ -626,26 +665,24 @@
                 // Will be allowed even with BAL hardening.
                 if (DEBUG_ACTIVITY_STARTS) {
                     Slog.d(TAG, "Activity start allowed by caller. "
-                            + state.dump(resultForCaller, resultForRealCaller));
+                            + state.dump());
                 }
-                // return the realCaller result for backwards compatibility
-                return statsLog(resultForRealCaller, state);
+                return allowBasedOnCaller(state);
             }
             if (state.mBalAllowedByPiCreator.allowsBackgroundActivityStarts()) {
                 Slog.wtf(TAG,
                         "With Android 15 BAL hardening this activity start may be blocked"
                                 + " if the PI creator upgrades target_sdk to 35+"
                                 + " AND the PI sender upgrades target_sdk to 34+! "
-                                + state.dump(resultForCaller, resultForRealCaller));
+                                + state.dump());
                 showBalRiskToast();
-                // return the realCaller result for backwards compatibility
-                return statsLog(resultForRealCaller, state);
+                return allowBasedOnCaller(state);
             }
             Slog.wtf(TAG,
                     "Without Android 15 BAL hardening this activity start would be allowed"
                             + " (missing opt in by PI creator or sender)! "
-                            + state.dump(resultForCaller, resultForRealCaller));
-            return abortLaunch(state, resultForCaller, resultForRealCaller);
+                            + state.dump());
+            return abortLaunch(state);
         }
         if (callerCanAllow) {
             // Allowed before V by creator
@@ -653,24 +690,24 @@
                 // Will be allowed even with BAL hardening.
                 if (DEBUG_ACTIVITY_STARTS) {
                     Slog.d(TAG, "Activity start allowed by caller. "
-                            + state.dump(resultForCaller, resultForRealCaller));
+                            + state.dump());
                 }
-                return statsLog(resultForCaller, state);
+                return allowBasedOnCaller(state);
             }
             if (state.mBalAllowedByPiCreator.allowsBackgroundActivityStarts()) {
                 Slog.wtf(TAG,
                         "With Android 15 BAL hardening this activity start may be blocked"
                                 + " if the PI creator upgrades target_sdk to 35+! "
                                 + " (missing opt in by PI creator)! "
-                                + state.dump(resultForCaller, resultForRealCaller));
+                                + state.dump());
                 showBalRiskToast();
-                return statsLog(resultForCaller, state);
+                return allowBasedOnCaller(state);
             }
             Slog.wtf(TAG,
                     "Without Android 15 BAL hardening this activity start would be allowed"
                             + " (missing opt in by PI creator)! "
-                            + state.dump(resultForCaller, resultForRealCaller));
-            return abortLaunch(state, resultForCaller, resultForRealCaller);
+                            + state.dump());
+            return abortLaunch(state);
         }
         if (realCallerCanAllow) {
             // Allowed before U by sender
@@ -679,23 +716,38 @@
                         "With Android 14 BAL hardening this activity start will be blocked"
                                 + " if the PI sender upgrades target_sdk to 34+! "
                                 + " (missing opt in by PI sender)! "
-                                + state.dump(resultForCaller, resultForRealCaller));
+                                + state.dump());
                 showBalRiskToast();
-                return statsLog(resultForRealCaller, state);
+                return allowBasedOnRealCaller(state);
             }
             Slog.wtf(TAG, "Without Android 14 BAL hardening this activity start would be allowed"
                     + " (missing opt in by PI sender)! "
-                    + state.dump(resultForCaller, resultForRealCaller));
-            return abortLaunch(state, resultForCaller, resultForRealCaller);
+                    + state.dump());
+            return abortLaunch(state);
         }
         // neither the caller not the realCaller can allow or have explicitly opted out
-        return abortLaunch(state, resultForCaller, resultForRealCaller);
+        return abortLaunch(state);
     }
 
-    private BalVerdict abortLaunch(BalState state, BalVerdict resultForCaller,
-            BalVerdict resultForRealCaller) {
+    private BalVerdict allowBasedOnCaller(BalState state) {
+        if (DEBUG_ACTIVITY_STARTS) {
+            Slog.d(TAG, "Background activity launch allowed based on caller. "
+                    + state.dump());
+        }
+        return statsLog(state.mResultForCaller, state);
+    }
+
+    private BalVerdict allowBasedOnRealCaller(BalState state) {
+        if (DEBUG_ACTIVITY_STARTS) {
+            Slog.d(TAG, "Background activity launch allowed based on real caller. "
+                    + state.dump());
+        }
+        return statsLog(state.mResultForRealCaller, state);
+    }
+
+    private BalVerdict abortLaunch(BalState state) {
         Slog.w(TAG, "Background activity launch blocked! "
-                + state.dump(resultForCaller, resultForRealCaller));
+                + state.dump());
         showBalBlockedToast();
         return statsLog(BalVerdict.BLOCK, state);
     }
@@ -755,7 +807,7 @@
                     /*background*/ false, "callingUid has visible window");
         }
         if (mService.mActiveUids.hasNonAppVisibleWindow(callingUid)) {
-            return new BalVerdict(BAL_ALLOW_VISIBLE_WINDOW,
+            return new BalVerdict(BAL_ALLOW_NON_APP_VISIBLE_WINDOW,
                     /*background*/ false, "callingUid has non-app visible window");
         }
 
@@ -851,7 +903,7 @@
                         /*background*/ false, "realCallingUid has visible window");
             }
             if (mService.mActiveUids.hasNonAppVisibleWindow(state.mRealCallingUid)) {
-                return new BalVerdict(BAL_ALLOW_VISIBLE_WINDOW,
+                return new BalVerdict(BAL_ALLOW_NON_APP_VISIBLE_WINDOW,
                         /*background*/ false, "realCallingUid has non-app visible window");
             }
         } else {
@@ -956,7 +1008,8 @@
                     || balCode == BAL_ALLOW_PERMISSION
                     || balCode == BAL_ALLOW_PENDING_INTENT
                     || balCode == BAL_ALLOW_SAW_PERMISSION
-                    || balCode == BAL_ALLOW_VISIBLE_WINDOW) {
+                    || balCode == BAL_ALLOW_VISIBLE_WINDOW
+                    || balCode == BAL_ALLOW_NON_APP_VISIBLE_WINDOW) {
                 return true;
             }
         }
@@ -1468,27 +1521,40 @@
         Intent intent = state.mIntent;
 
         if (code == BAL_ALLOW_PENDING_INTENT
-                && (callingUid == Process.SYSTEM_UID || realCallingUid == Process.SYSTEM_UID)) {
+                && (callingUid < Process.FIRST_APPLICATION_UID
+                || realCallingUid < Process.FIRST_APPLICATION_UID)) {
             String activityName = intent != null
                     ? requireNonNull(intent.getComponent()).flattenToShortString() : "";
-            FrameworkStatsLog.write(FrameworkStatsLog.BAL_ALLOWED,
-                    activityName,
-                    BAL_ALLOW_PENDING_INTENT,
-                    callingUid,
-                    realCallingUid);
+            writeBalAllowedLog(activityName, BAL_ALLOW_PENDING_INTENT,
+                    state);
         }
         if (code == BAL_ALLOW_PERMISSION || code == BAL_ALLOW_FOREGROUND
-                    || code == BAL_ALLOW_SAW_PERMISSION) {
+                || code == BAL_ALLOW_SAW_PERMISSION) {
             // We don't need to know which activity in this case.
-            FrameworkStatsLog.write(FrameworkStatsLog.BAL_ALLOWED,
-                    /*activityName*/ "",
-                    code,
-                    callingUid,
-                    realCallingUid);
+            writeBalAllowedLog("", code, state);
+
         }
         return finalVerdict;
     }
 
+    private static void writeBalAllowedLog(String activityName, int code, BalState state) {
+        FrameworkStatsLog.write(FrameworkStatsLog.BAL_ALLOWED,
+                activityName,
+                code,
+                state.mCallingUid,
+                state.mRealCallingUid,
+                state.mResultForCaller == null ? BAL_BLOCK : state.mResultForCaller.getRawCode(),
+                state.mBalAllowedByPiCreator.allowsBackgroundActivityStarts(),
+                state.mCheckedOptions.getPendingIntentCreatorBackgroundActivityStartMode()
+                        != ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED,
+                state.mResultForRealCaller == null ? BAL_BLOCK
+                        : state.mResultForRealCaller.getRawCode(),
+                state.mBalAllowedByPiSender.allowsBackgroundActivityStarts(),
+                state.mCheckedOptions.getPendingIntentBackgroundActivityStartMode()
+                        != ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED
+        );
+    }
+
     /**
      * Called whenever an activity finishes. Stores the record, so it can be used by ASM grace
      * period checks.
diff --git a/services/core/java/com/android/server/wm/ClientLifecycleManager.java b/services/core/java/com/android/server/wm/ClientLifecycleManager.java
index 2e47677..c7df83a 100644
--- a/services/core/java/com/android/server/wm/ClientLifecycleManager.java
+++ b/services/core/java/com/android/server/wm/ClientLifecycleManager.java
@@ -24,6 +24,7 @@
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.os.Trace;
 import android.util.ArrayMap;
 import android.util.Slog;
 
@@ -74,13 +75,13 @@
     }
 
     /**
-     * Similar to {@link #scheduleTransactionItem}, but is called without WM lock.
+     * Similar to {@link #scheduleTransactionItem}, but it sends the transaction immediately and
+     * it can be called without WM lock.
      *
      * @see WindowProcessController#setReportedProcState(int)
      */
-    void scheduleTransactionItemUnlocked(@NonNull IApplicationThread client,
+    void scheduleTransactionItemNow(@NonNull IApplicationThread client,
             @NonNull ClientTransactionItem transactionItem) throws RemoteException {
-        // Immediately dispatching to client, and must not access WMS.
         final ClientTransaction clientTransaction = ClientTransaction.obtain(client);
         if (transactionItem.isActivityLifecycleItem()) {
             clientTransaction.setLifecycleStateRequest((ActivityLifecycleItem) transactionItem);
@@ -146,9 +147,10 @@
 
     /** Executes all the pending transactions. */
     void dispatchPendingTransactions() {
-        if (!Flags.bundleClientTransactionFlag()) {
+        if (!Flags.bundleClientTransactionFlag() || mPendingTransactions.isEmpty()) {
             return;
         }
+        Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "clientTransactionsDispatched");
         final int size = mPendingTransactions.size();
         for (int i = 0; i < size; i++) {
             final ClientTransaction transaction = mPendingTransactions.valueAt(i);
@@ -159,6 +161,7 @@
             }
         }
         mPendingTransactions.clear();
+        Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java b/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java
index 975fdc0..0f9e5b0 100644
--- a/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java
+++ b/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java
@@ -179,6 +179,12 @@
             if (physicalDisplayUpdated) {
                 onDisplayUpdated(transition, fromRotation, startBounds);
             } else {
+                final TransitionRequestInfo.DisplayChange displayChange =
+                        getCurrentDisplayChange(fromRotation, startBounds);
+                mDisplayContent.mTransitionController.requestStartTransition(transition,
+                        /* startTask= */ null, /* remoteTransition= */ null, displayChange);
+                mDisplayContent.mTransitionController.setDisplaySyncMethod(displayChange,
+                        mDisplayContent);
                 transition.setAllReady();
             }
         });
@@ -204,15 +210,8 @@
         return new DisplayInfo(mNonOverrideDisplayInfo);
     }
 
-    /**
-     * Called when physical display is updated, this could happen e.g. on foldable
-     * devices when the physical underlying display is replaced. This method should be called
-     * when the new display info is already applied to the WM hierarchy.
-     *
-     * @param fromRotation rotation before the display change
-     * @param startBounds  display bounds before the display change
-     */
-    private void onDisplayUpdated(@NonNull Transition transition, int fromRotation,
+    @NonNull
+    private TransitionRequestInfo.DisplayChange getCurrentDisplayChange(int fromRotation,
             @NonNull Rect startBounds) {
         final Rect endBounds = new Rect(0, 0, mDisplayContent.mInitialDisplayWidth,
                 mDisplayContent.mInitialDisplayHeight);
@@ -224,6 +223,23 @@
         displayChange.setEndAbsBounds(endBounds);
         displayChange.setStartRotation(fromRotation);
         displayChange.setEndRotation(toRotation);
+        return displayChange;
+    }
+
+    /**
+     * Called when physical display is updated, this could happen e.g. on foldable
+     * devices when the physical underlying display is replaced. This method should be called
+     * when the new display info is already applied to the WM hierarchy.
+     *
+     * @param fromRotation rotation before the display change
+     * @param startBounds  display bounds before the display change
+     */
+    private void onDisplayUpdated(@NonNull Transition transition, int fromRotation,
+            @NonNull Rect startBounds) {
+        final int toRotation = mDisplayContent.getRotation();
+
+        final TransitionRequestInfo.DisplayChange displayChange =
+                getCurrentDisplayChange(fromRotation, startBounds);
         displayChange.setPhysicalDisplayChanged(true);
 
         mDisplayContent.mTransitionController.requestStartTransition(transition,
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index f8dc9c7..e7ecf52 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -779,7 +779,7 @@
 
     /**
      * Used to prevent recursions when calling
-     * {@link #ensureActivitiesVisible(ActivityRecord, int, boolean, boolean)}
+     * {@link #ensureActivitiesVisible(ActivityRecord, boolean)}
      */
     private boolean mInEnsureActivitiesVisible = false;
 
@@ -1713,7 +1713,7 @@
         if (handled && requestingContainer instanceof ActivityRecord) {
             final ActivityRecord activityRecord = (ActivityRecord) requestingContainer;
             final boolean kept = updateDisplayOverrideConfigurationLocked(config, activityRecord,
-                    false /* deferResume */, null /* result */);
+                    false /* deferResume */);
             if (!kept) {
                 mRootWindowContainer.resumeFocusedTasksTopActivities();
             }
@@ -1721,7 +1721,7 @@
             // We have a new configuration to push so we need to update ATMS for now.
             // TODO: Clean up display configuration push between ATMS and WMS after unification.
             updateDisplayOverrideConfigurationLocked(config, null /* starting */,
-                    false /* deferResume */, null);
+                    false /* deferResume */);
         }
         return handled;
     }
@@ -6333,7 +6333,7 @@
 
         Settings.System.clearConfiguration(values);
         updateDisplayOverrideConfigurationLocked(values, null /* starting */,
-                false /* deferResume */, mAtmService.mTmpUpdateConfigurationResult);
+                false /* deferResume */);
         return mAtmService.mTmpUpdateConfigurationResult.changes != 0;
     }
 
@@ -6342,8 +6342,7 @@
      * new one will be computed in WM based on current display info.
      */
     boolean updateDisplayOverrideConfigurationLocked(Configuration values,
-            ActivityRecord starting, boolean deferResume,
-            ActivityTaskManagerService.UpdateConfigurationResult result) {
+            ActivityRecord starting, boolean deferResume) {
 
         int changes = 0;
         boolean kept = true;
@@ -6361,19 +6360,19 @@
                 } else {
                     changes = performDisplayOverrideConfigUpdate(values);
                 }
+                mAtmService.mTmpUpdateConfigurationResult.changes = changes;
+                mAtmService.mTmpUpdateConfigurationResult.mIsUpdating = true;
             }
 
             if (!deferResume) {
                 kept = mAtmService.ensureConfigAndVisibilityAfterUpdate(starting, changes);
             }
         } finally {
+            mAtmService.mTmpUpdateConfigurationResult.mIsUpdating = false;
             mAtmService.continueWindowLayout();
         }
 
-        if (result != null) {
-            result.changes = changes;
-            result.activityRelaunched = !kept;
-        }
+        mAtmService.mTmpUpdateConfigurationResult.activityRelaunched = !kept;
         return kept;
     }
 
@@ -6569,8 +6568,7 @@
     }
 
 
-    void ensureActivitiesVisible(ActivityRecord starting, int configChanges,
-            boolean preserveWindows, boolean notifyClients) {
+    void ensureActivitiesVisible(ActivityRecord starting, boolean notifyClients) {
         if (mInEnsureActivitiesVisible) {
             // Don't do recursive work.
             return;
@@ -6579,8 +6577,7 @@
         try {
             mInEnsureActivitiesVisible = true;
             forAllRootTasks(rootTask -> {
-                rootTask.ensureActivitiesVisible(starting, configChanges, preserveWindows,
-                        notifyClients);
+                rootTask.ensureActivitiesVisible(starting, notifyClients);
             });
             if (mTransitionController.useShellTransitionsRotation()
                     && mTransitionController.isCollecting()
@@ -6619,7 +6616,7 @@
         if (!wasTransitionSet) {
             prepareAppTransition(TRANSIT_NONE);
         }
-        mRootWindowContainer.ensureActivitiesVisible(null, 0, false /* preserveWindows */);
+        mRootWindowContainer.ensureActivitiesVisible();
 
         // If there was a transition set already we don't want to interfere with it as we might be
         // starting it too early.
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 460a68f..63ca592 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -40,6 +40,7 @@
 import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_CONSUME_IME_INSETS;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_EDGE_TO_EDGE_ENFORCED;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IMMERSIVE_CONFIRMATION_WINDOW;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_INTERCEPT_GLOBAL_DRAG_AND_DROP;
@@ -959,15 +960,17 @@
 
             case TYPE_BASE_APPLICATION:
 
-                // A non-translucent main app window isn't allowed to fit insets, as it would create
-                // a hole on the display!
+                // A non-translucent main app window isn't allowed to fit insets or display cutouts,
+                // as it would create a hole on the display!
                 if (attrs.isFullscreen() && win.mActivityRecord != null
                         && win.mActivityRecord.fillsParent()
-                        && (win.mAttrs.privateFlags & PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS) != 0
-                        && attrs.getFitInsetsTypes() != 0) {
+                        && (attrs.privateFlags & PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS) != 0
+                        && (attrs.getFitInsetsTypes() != 0
+                                || (attrs.privateFlags & PRIVATE_FLAG_EDGE_TO_EDGE_ENFORCED) != 0
+                                        && attrs.layoutInDisplayCutoutMode
+                                                != LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS)) {
                     throw new IllegalArgumentException("Illegal attributes: Main activity window"
-                            + " that isn't translucent trying to fit insets: "
-                            + attrs.getFitInsetsTypes()
+                            + " that isn't translucent trying to fit insets or display cutouts."
                             + " attrs=" + attrs);
                 }
                 break;
diff --git a/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
index 9cc311d..f40eb24 100644
--- a/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
+++ b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
@@ -33,8 +33,6 @@
     private boolean mAboveTop;
     private boolean mContainerShouldBeVisible;
     private boolean mBehindFullyOccludedContainer;
-    private int mConfigChanges;
-    private boolean mPreserveWindows;
     private boolean mNotifyClients;
 
     EnsureActivitiesVisibleHelper(TaskFragment container) {
@@ -45,14 +43,10 @@
      * Update all attributes except {@link mTaskFragment} to use in subsequent calculations.
      *
      * @param starting The activity that is being started
-     * @param configChanges Parts of the configuration that changed for this activity for evaluating
-     *                      if the screen should be frozen.
-     * @param preserveWindows Flag indicating whether windows should be preserved when updating.
      * @param notifyClients Flag indicating whether the configuration and visibility changes shoulc
      *                      be sent to the clients.
      */
-    void reset(ActivityRecord starting, int configChanges, boolean preserveWindows,
-            boolean notifyClients) {
+    void reset(ActivityRecord starting, boolean notifyClients) {
         mStarting = starting;
         mTopRunningActivity = mTaskFragment.topRunningActivity();
         // If the top activity is not fullscreen, then we need to make sure any activities under it
@@ -60,33 +54,26 @@
         mAboveTop = mTopRunningActivity != null;
         mContainerShouldBeVisible = mTaskFragment.shouldBeVisible(mStarting);
         mBehindFullyOccludedContainer = !mContainerShouldBeVisible;
-        mConfigChanges = configChanges;
-        mPreserveWindows = preserveWindows;
         mNotifyClients = notifyClients;
     }
 
     /**
      * Update and commit visibility with an option to also update the configuration of visible
      * activities.
-     * @see Task#ensureActivitiesVisible(ActivityRecord, int, boolean)
-     * @see RootWindowContainer#ensureActivitiesVisible(ActivityRecord, int, boolean)
+     * @see Task#ensureActivitiesVisible(ActivityRecord)
+     * @see RootWindowContainer#ensureActivitiesVisible()
      * @param starting The top most activity in the task.
      *                 The activity is either starting or resuming.
      *                 Caller should ensure starting activity is visible.
      *
-     * @param configChanges Parts of the configuration that changed for this activity for evaluating
-     *                      if the screen should be frozen.
-     * @param preserveWindows Flag indicating whether windows should be preserved when updating.
      * @param notifyClients Flag indicating whether the configuration and visibility changes shoulc
      *                      be sent to the clients.
      */
-    void process(@Nullable ActivityRecord starting, int configChanges, boolean preserveWindows,
-            boolean notifyClients) {
-        reset(starting, configChanges, preserveWindows, notifyClients);
+    void process(@Nullable ActivityRecord starting, boolean notifyClients) {
+        reset(starting, notifyClients);
 
         if (DEBUG_VISIBILITY) {
-            Slog.v(TAG_VISIBILITY, "ensureActivitiesVisible behind " + mTopRunningActivity
-                    + " configChanges=0x" + Integer.toHexString(configChanges));
+            Slog.v(TAG_VISIBILITY, "ensureActivitiesVisible behind " + mTopRunningActivity);
         }
         if (mTopRunningActivity != null && mTaskFragment.asTask() != null) {
             // TODO(14709632): Check if this needed to be implemented in TaskFragment.
@@ -107,8 +94,7 @@
             final TaskFragment childTaskFragment = child.asTaskFragment();
             if (childTaskFragment != null
                     && childTaskFragment.getTopNonFinishingActivity() != null) {
-                childTaskFragment.updateActivityVisibilities(starting, configChanges,
-                        preserveWindows, notifyClients);
+                childTaskFragment.updateActivityVisibilities(starting, notifyClients);
                 // The TaskFragment should fully occlude the activities below if the bounds
                 // equals to its parent task, unless it is translucent.
                 mBehindFullyOccludedContainer |=
@@ -188,13 +174,11 @@
             // First: if this is not the current activity being started, make
             // sure it matches the current configuration.
             if (r != mStarting && mNotifyClients) {
-                r.ensureActivityConfiguration(0 /* globalChanges */, mPreserveWindows,
-                        true /* ignoreVisibility */);
+                r.ensureActivityConfiguration(true /* ignoreVisibility */);
             }
 
             if (!r.attachedToProcess()) {
-                makeVisibleAndRestartIfNeeded(mStarting, mConfigChanges,
-                        resumeTopActivity && isTop, r);
+                makeVisibleAndRestartIfNeeded(mStarting, resumeTopActivity && isTop, r);
             } else if (r.isVisibleRequested()) {
                 // If this activity is already visible, then there is nothing to do here.
                 if (DEBUG_VISIBILITY) {
@@ -213,8 +197,6 @@
             } else {
                 r.makeVisibleIfNeeded(mStarting, mNotifyClients);
             }
-            // Aggregate current change flags.
-            mConfigChanges |= r.configChangeFlags;
         } else {
             if (DEBUG_VISIBILITY) {
                 Slog.v(TAG_VISIBILITY, "Make invisible? " + r
@@ -242,16 +224,13 @@
         }
     }
 
-    private void makeVisibleAndRestartIfNeeded(ActivityRecord starting, int configChanges,
+    private void makeVisibleAndRestartIfNeeded(ActivityRecord starting,
             boolean andResume, ActivityRecord r) {
         // This activity needs to be visible, but isn't even running...
         // get it started and resume if no other root task in this root task is resumed.
         if (DEBUG_VISIBILITY) {
             Slog.v(TAG_VISIBILITY, "Start and freeze screen for " + r);
         }
-        if (r != starting) {
-            r.startFreezingScreenLocked(configChanges);
-        }
         if (!r.isVisibleRequested() || r.mLaunchTaskBehind) {
             if (DEBUG_VISIBILITY) {
                 Slog.v(TAG_VISIBILITY, "Starting and making visible: " + r);
diff --git a/services/core/java/com/android/server/wm/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java
index 8cf4713..a84ebd9 100644
--- a/services/core/java/com/android/server/wm/InputManagerCallback.java
+++ b/services/core/java/com/android/server/wm/InputManagerCallback.java
@@ -167,10 +167,10 @@
 
     /** {@inheritDoc} */
     @Override
-    public int interceptMotionBeforeQueueingNonInteractive(int displayId, long whenNanos,
-            int policyFlags) {
+    public int interceptMotionBeforeQueueingNonInteractive(int displayId, int source, int action,
+            long whenNanos, int policyFlags) {
         return mService.mPolicy.interceptMotionBeforeQueueingNonInteractive(
-                displayId, whenNanos, policyFlags);
+                displayId, source, action, whenNanos, policyFlags);
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index bf30af3..8035a29 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -112,7 +112,7 @@
      * z-layering reference so that we can place the recents input consumer above it.
      */
     private WeakReference<ActivityRecord> mActiveRecentsActivity = null;
-    private WeakReference<ActivityRecord> mActiveRecentsLayerRef = null;
+    private WeakReference<Task> mActiveRecentsLayerRef = null;
 
     private class UpdateInputWindows implements Runnable {
         @Override
@@ -389,9 +389,9 @@
     /**
      * Inform InputMonitor when recents is active so it can enable the recents input consumer.
      * @param activity The active recents activity. {@code null} means recents is not active.
-     * @param layer An activity whose Z-layer is used as a reference for how to sort the consumer.
+     * @param layer A task whose Z-layer is used as a reference for how to sort the consumer.
      */
-    void setActiveRecents(@Nullable ActivityRecord activity, @Nullable ActivityRecord layer) {
+    void setActiveRecents(@Nullable ActivityRecord activity, @Nullable Task layer) {
         final boolean clear = activity == null;
         final boolean wasActive = mActiveRecentsActivity != null && mActiveRecentsLayerRef != null;
         mActiveRecentsActivity = clear ? null : new WeakReference<>(activity);
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index cbc7b83..6d11804 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -43,7 +43,6 @@
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
 import static com.android.server.wm.KeyguardControllerProto.AOD_SHOWING;
 import static com.android.server.wm.KeyguardControllerProto.KEYGUARD_GOING_AWAY;
 import static com.android.server.wm.KeyguardControllerProto.KEYGUARD_PER_DISPLAY;
@@ -239,7 +238,7 @@
         // Update the sleep token first such that ensureActivitiesVisible has correct sleep token
         // state when evaluating visibilities.
         updateKeyguardSleepToken();
-        mRootWindowContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
+        mRootWindowContainer.ensureActivitiesVisible();
         InputMethodManagerInternal.get().updateImeWindowStatus(false /* disableImeIcon */,
                 displayId);
         setWakeTransitionReady();
@@ -291,7 +290,7 @@
 
             // Some stack visibility might change (e.g. docked stack)
             mRootWindowContainer.resumeFocusedTasksTopActivities();
-            mRootWindowContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
+            mRootWindowContainer.ensureActivitiesVisible();
             mRootWindowContainer.addStartingWindowsForVisibleActivities();
             mWindowManager.executeAppTransition();
         } finally {
diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java
index 9305396..47972b3 100644
--- a/services/core/java/com/android/server/wm/LetterboxUiController.java
+++ b/services/core/java/com/android/server/wm/LetterboxUiController.java
@@ -46,6 +46,7 @@
 import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_16_9;
 import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_3_2;
 import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_4_3;
+import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_APP_DEFAULT;
 import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_DISPLAY_SIZE;
 import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_FULLSCREEN;
 import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_SPLIT_SCREEN;
@@ -172,7 +173,7 @@
     // Corresponds to OVERRIDE_ANY_ORIENTATION
     private final boolean mIsOverrideAnyOrientationEnabled;
     // Corresponds to OVERRIDE_ANY_ORIENTATION_TO_USER
-    private final boolean mIsOverrideToUserOrientationEnabled;
+    private final boolean mIsSystemOverrideToFullscreenEnabled;
     // Corresponds to OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT
     private final boolean mIsOverrideToPortraitOrientationEnabled;
     // Corresponds to OVERRIDE_UNDEFINED_ORIENTATION_TO_NOSENSOR
@@ -357,7 +358,7 @@
                         PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE);
 
         mIsOverrideAnyOrientationEnabled = isCompatChangeEnabled(OVERRIDE_ANY_ORIENTATION);
-        mIsOverrideToUserOrientationEnabled =
+        mIsSystemOverrideToFullscreenEnabled =
                 isCompatChangeEnabled(OVERRIDE_ANY_ORIENTATION_TO_USER);
         mIsOverrideToPortraitOrientationEnabled =
                 isCompatChangeEnabled(OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT);
@@ -512,7 +513,6 @@
      *     timer and activity is not letterboxed for fixed orientation
      * </ul>
      */
-    @VisibleForTesting
     boolean shouldIgnoreOrientationRequestLoop() {
         if (!shouldEnableWithOptInOverrideAndOptOutProperty(
                 /* gatingCondition */ mLetterboxConfiguration
@@ -670,8 +670,7 @@
         final DisplayContent displayContent = mActivityRecord.mDisplayContent;
         final boolean isIgnoreOrientationRequestEnabled = displayContent != null
                 && displayContent.getIgnoreOrientationRequest();
-        if (shouldApplyUserFullscreenOverride()
-                && isIgnoreOrientationRequestEnabled) {
+        if (shouldApplyUserFullscreenOverride() && isIgnoreOrientationRequestEnabled) {
             Slog.v(TAG, "Requested orientation " + screenOrientationToString(candidate) + " for "
                     + mActivityRecord + " is overridden to "
                     + screenOrientationToString(SCREEN_ORIENTATION_USER)
@@ -706,8 +705,7 @@
         // mUserAspectRatio is always initialized first in shouldApplyUserFullscreenOverride(),
         // which will always come first before this check as user override > device
         // manufacturer override.
-        if (mUserAspectRatio == PackageManager.USER_MIN_ASPECT_RATIO_UNSET
-                && mIsOverrideToUserOrientationEnabled && isIgnoreOrientationRequestEnabled) {
+        if (isSystemOverrideToFullscreenEnabled() && isIgnoreOrientationRequestEnabled) {
             Slog.v(TAG, "Requested orientation  " + screenOrientationToString(candidate) + " for "
                     + mActivityRecord + " is overridden to "
                     + screenOrientationToString(SCREEN_ORIENTATION_USER));
@@ -1185,6 +1183,7 @@
         mUserAspectRatio = getUserMinAspectRatioOverrideCode();
 
         return mUserAspectRatio != USER_MIN_ASPECT_RATIO_UNSET
+                && mUserAspectRatio != USER_MIN_ASPECT_RATIO_APP_DEFAULT
                 && mUserAspectRatio != USER_MIN_ASPECT_RATIO_FULLSCREEN;
     }
 
@@ -1200,6 +1199,13 @@
         return mUserAspectRatio == USER_MIN_ASPECT_RATIO_FULLSCREEN;
     }
 
+    boolean isSystemOverrideToFullscreenEnabled() {
+        return mIsSystemOverrideToFullscreenEnabled
+                && !FALSE.equals(mBooleanPropertyAllowOrientationOverride)
+                && (mUserAspectRatio == USER_MIN_ASPECT_RATIO_UNSET
+                    || mUserAspectRatio == USER_MIN_ASPECT_RATIO_FULLSCREEN);
+    }
+
     float getUserMinAspectRatio() {
         switch (mUserAspectRatio) {
             case USER_MIN_ASPECT_RATIO_DISPLAY_SIZE:
diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java
index 5269d35..7b23004 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimation.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimation.java
@@ -28,7 +28,6 @@
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_RECENTS_ANIMATIONS;
 import static com.android.server.wm.ActivityRecord.State.STOPPED;
 import static com.android.server.wm.ActivityRecord.State.STOPPING;
-import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
 import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE;
 import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_ORIGINAL_POSITION;
 import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_TOP;
@@ -126,8 +125,7 @@
                 // The activity may be relaunched if it cannot handle the current configuration
                 // changes. The activity will be paused state if it is relaunched, otherwise it
                 // keeps the original stopped state.
-                targetActivity.ensureActivityConfiguration(0 /* globalChanges */,
-                        false /* preserveWindow */, true /* ignoreVisibility */);
+                targetActivity.ensureActivityConfiguration(true /* ignoreVisibility */);
                 ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "Updated config=%s",
                         targetActivity.getConfiguration());
             }
@@ -261,7 +259,7 @@
 
             // If we updated the launch-behind state, update the visibility of the activities after
             // we fetch the visible tasks to be controlled by the animation
-            mService.mRootWindowContainer.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);
+            mService.mRootWindowContainer.ensureActivitiesVisible();
 
             ActivityOptions options = null;
             if (eventTime > 0) {
@@ -380,8 +378,7 @@
                         // transition (the target activity will be one of closing apps).
                         if (!controller.shouldDeferCancelWithScreenshot()
                                 && !targetRootTask.isFocusedRootTaskOnDisplay()) {
-                            targetRootTask.ensureActivitiesVisible(null /* starting */,
-                                    0 /* starting */, false /* preserveWindows */);
+                            targetRootTask.ensureActivitiesVisible(null /* starting */);
                         }
                         // Keep target root task in place, nothing changes, so ignore the transition
                         // logic below
@@ -389,7 +386,7 @@
                     }
 
                     mWindowManager.prepareAppTransitionNone();
-                    mService.mRootWindowContainer.ensureActivitiesVisible(null, 0, false);
+                    mService.mRootWindowContainer.ensureActivitiesVisible();
                     mService.mRootWindowContainer.resumeFocusedTasksTopActivities();
 
                     // No reason to wait for the pausing activity in this case, as the hiding of
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 9a75dae..02b3f15 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -63,7 +63,6 @@
 import static com.android.server.wm.ActivityTaskManagerService.isPip2ExperimentEnabled;
 import static com.android.server.wm.ActivityTaskSupervisor.DEFER_RESUME;
 import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP;
-import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
 import static com.android.server.wm.ActivityTaskSupervisor.dumpHistoryList;
 import static com.android.server.wm.ActivityTaskSupervisor.printThisActivity;
 import static com.android.server.wm.KeyguardController.KEYGUARD_SLEEP_TOKEN_TAG;
@@ -153,6 +152,7 @@
 import com.android.server.policy.PermissionPolicyInternal;
 import com.android.server.policy.WindowManagerPolicy;
 import com.android.server.utils.Slogf;
+import com.android.window.flags.Flags;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -796,11 +796,18 @@
             Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
         }
 
+        if (Flags.bundleClientTransactionFlag()) {
+            // mWmService.mResizingWindows is populated in #applySurfaceChangesTransaction()
+            handleResizingWindows();
+
+            // Called after #handleResizingWindows to include WindowStateResizeItem if any.
+            mWmService.mAtmService.getLifecycleManager().dispatchPendingTransactions();
+        }
+
         // Send any pending task-info changes that were queued-up during a layout deferment
         mWmService.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
         mWmService.mAtmService.mTaskFragmentOrganizerController.dispatchPendingEvents();
         mWmService.mSyncEngine.onSurfacePlacement();
-        mWmService.mAnimator.executeAfterPrepareSurfacesRunnables();
 
         checkAppTransitionReady(surfacePlacer);
 
@@ -839,12 +846,11 @@
             }
         }
 
-        handleResizingWindows();
+        if (!Flags.bundleClientTransactionFlag()) {
+            handleResizingWindows();
+        }
         clearFrameChangingWindows();
 
-        // Called after #handleResizingWindows to include WindowStateResizeItem if any.
-        mWmService.mAtmService.getLifecycleManager().dispatchPendingTransactions();
-
         if (mWmService.mDisplayFrozen) {
             ProtoLog.v(WM_DEBUG_ORIENTATION,
                     "With display frozen, orientationChangeComplete=%b",
@@ -1753,8 +1759,7 @@
         // activities are affecting configuration now.
         // Passing null here for 'starting' param value, so that visibility of actual starting
         // activity will be properly updated.
-        ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
-                false /* preserveWindows */, false /* notifyClients */);
+        ensureActivitiesVisible(null /* starting */, false /* notifyClients */);
 
         if (displayId == INVALID_DISPLAY) {
             // The caller didn't provide a valid display id, skip updating config.
@@ -1778,7 +1783,7 @@
         if (displayContent != null) {
             // Update the configuration of the activities on the display.
             return displayContent.updateDisplayOverrideConfigurationLocked(config, starting,
-                    deferResume, null /* result */);
+                    deferResume);
         } else {
             return true;
         }
@@ -1865,16 +1870,18 @@
      * Make sure that all activities that need to be visible in the system actually are and update
      * their configuration.
      */
-    void ensureActivitiesVisible(ActivityRecord starting, int configChanges,
-            boolean preserveWindows) {
-        ensureActivitiesVisible(starting, configChanges, preserveWindows, true /* notifyClients */);
+    void ensureActivitiesVisible() {
+        ensureActivitiesVisible(null /* starting */);
+    }
+
+    void ensureActivitiesVisible(ActivityRecord starting) {
+        ensureActivitiesVisible(starting, true /* notifyClients */);
     }
 
     /**
-     * @see #ensureActivitiesVisible(ActivityRecord, int, boolean)
+     * @see #ensureActivitiesVisible()
      */
-    void ensureActivitiesVisible(ActivityRecord starting, int configChanges,
-            boolean preserveWindows, boolean notifyClients) {
+    void ensureActivitiesVisible(ActivityRecord starting, boolean notifyClients) {
         if (mTaskSupervisor.inActivityVisibilityUpdate()
                 || mTaskSupervisor.isRootVisibilityUpdateDeferred()) {
             // Don't do recursive work.
@@ -1885,8 +1892,7 @@
             // First the front root tasks. In case any are not fullscreen and are in front of home.
             for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {
                 final DisplayContent display = getChildAt(displayNdx);
-                display.ensureActivitiesVisible(starting, configChanges, preserveWindows,
-                        notifyClients);
+                display.ensureActivitiesVisible(starting, notifyClients);
             }
         } finally {
             mTaskSupervisor.endActivityVisibilityUpdate();
@@ -2237,7 +2243,7 @@
             try {
                 if (localVisibilityDeferred) {
                     mTaskSupervisor.setDeferRootVisibilityUpdate(false);
-                    ensureActivitiesVisible(null, 0, false /* preserveWindows */);
+                    ensureActivitiesVisible();
                 }
             } finally {
                 transitionController.continueTransitionReady();
@@ -2370,7 +2376,7 @@
             // It may be nothing to resume because there are pausing activities or all the top
             // activities are resumed. Then it still needs to make sure all visible activities are
             // running in case the tasks were reordered or there are non-top visible activities.
-            ensureActivitiesVisible(null /* starting */, 0 /* configChanges */, !PRESERVE_WINDOWS);
+            ensureActivitiesVisible();
         }
     }
 
@@ -2542,8 +2548,7 @@
                     // display orientation can be updated first if needed. Otherwise there may
                     // have redundant configuration changes due to apply outdated display
                     // orientation (from keyguard) to activity.
-                    rootTask.ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
-                            false /* preserveWindows */);
+                    rootTask.ensureActivitiesVisible(null /* starting */);
                 }
             });
         }
@@ -2885,8 +2890,7 @@
             if (allowDelay) {
                 result[0] &= task.goToSleepIfPossible(shuttingDown);
             } else {
-                task.ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
-                        !PRESERVE_WINDOWS);
+                task.ensureActivitiesVisible(null /* starting */);
             }
         });
         return result[0];
@@ -3774,8 +3778,7 @@
                 }
             }
             if (!mHasActivityStarted) {
-                ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
-                        false /* preserveWindows */);
+                ensureActivitiesVisible();
             }
             return mHasActivityStarted;
         }
diff --git a/services/core/java/com/android/server/wm/SensitiveContentPackages.java b/services/core/java/com/android/server/wm/SensitiveContentPackages.java
new file mode 100644
index 0000000..3862b82
--- /dev/null
+++ b/services/core/java/com/android/server/wm/SensitiveContentPackages.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import android.annotation.NonNull;
+import android.util.ArraySet;
+
+import java.io.PrintWriter;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Cache of distinct package/uid pairs that require being blocked from screen capture. This class is
+ * not threadsafe and any call site should hold {@link WindowManagerGlobalLock}
+ */
+public class SensitiveContentPackages {
+    private final ArraySet<PackageInfo> mProtectedPackages = new ArraySet<>();
+
+    /** Returns {@code true} if package/uid pair should be blocked from screen capture */
+    public boolean shouldBlockScreenCaptureForApp(String pkg, int uid) {
+        for (int i = 0; i < mProtectedPackages.size(); i++) {
+            PackageInfo info = mProtectedPackages.valueAt(i);
+            if (info != null && info.mPkg.equals(pkg) && info.mUid == uid) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /** Replaces the set of package/uid pairs to set that should be blocked from screen capture */
+    public void setShouldBlockScreenCaptureForApp(@NonNull Set<PackageInfo> packageInfos) {
+        mProtectedPackages.clear();
+        mProtectedPackages.addAll(packageInfos);
+    }
+
+    void dump(PrintWriter pw) {
+        final String innerPrefix = "  ";
+        pw.println("SensitiveContentPackages:");
+        pw.println(innerPrefix + "Packages that should block screen capture ("
+                + mProtectedPackages.size() + "):");
+        for (PackageInfo info : mProtectedPackages) {
+            pw.println(innerPrefix + "  package=" + info.mPkg + "  uid=" + info.mUid);
+        }
+    }
+
+    /** Helper class that represents a package/uid pair */
+    public static class PackageInfo {
+        private String mPkg;
+        private int mUid;
+
+        public PackageInfo(String pkg, int uid) {
+            this.mPkg = pkg;
+            this.mUid = uid;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (!(o instanceof PackageInfo)) return false;
+            PackageInfo that = (PackageInfo) o;
+            return mUid == that.mUid && Objects.equals(mPkg, that.mPkg);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mPkg, mUid);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 7995028..f10a733 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -75,6 +75,7 @@
 import android.view.SurfaceControl;
 import android.view.SurfaceSession;
 import android.view.View;
+import android.view.View.FocusDirection;
 import android.view.WindowInsets;
 import android.view.WindowInsets.Type.InsetsType;
 import android.view.WindowManager;
@@ -771,6 +772,7 @@
             if (mLastReportedAnimatorScale != mService.getCurrentAnimatorScale()) {
                 mService.dispatchNewAnimatorScaleLocked(this);
             }
+            mProcess.mWindowSession = this;
         }
         mAddedWindows.add(w);
     }
@@ -782,6 +784,9 @@
         }
     }
 
+    boolean hasWindow() {
+        return !mAddedWindows.isEmpty();
+    }
 
     void onWindowSurfaceVisibilityChanged(WindowSurfaceController surfaceController,
             boolean visible, int type) {
@@ -996,6 +1001,17 @@
         }
         return didTransfer;
     }
+
+    @Override
+    public boolean moveFocusToAdjacentWindow(IWindow fromWindow, @FocusDirection int direction) {
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            return mService.moveFocusToAdjacentWindow(this, fromWindow, direction);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
     @Override
     public void generateDisplayHash(IWindow window, Rect boundsInWindow, String hashAlgorithm,
             RemoteCallback callback) {
diff --git a/services/core/java/com/android/server/wm/SnapshotController.java b/services/core/java/com/android/server/wm/SnapshotController.java
index b6f040a..3014f97 100644
--- a/services/core/java/com/android/server/wm/SnapshotController.java
+++ b/services/core/java/com/android/server/wm/SnapshotController.java
@@ -160,9 +160,7 @@
                 if (!allOpensOptInOnBackInvoked() || mCloseActivities.isEmpty()) {
                     return;
                 }
-                for (int i = mCloseActivities.size() - 1; i >= 0; --i) {
-                    controller.recordSnapshot(mCloseActivities.get(i));
-                }
+                controller.recordSnapshot(mCloseActivities);
             }
         }
     }
diff --git a/services/core/java/com/android/server/wm/SnapshotPersistQueue.java b/services/core/java/com/android/server/wm/SnapshotPersistQueue.java
index bffdf54..e4379b5 100644
--- a/services/core/java/com/android/server/wm/SnapshotPersistQueue.java
+++ b/services/core/java/com/android/server/wm/SnapshotPersistQueue.java
@@ -274,7 +274,9 @@
 
         @Override
         void write() {
-            Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "StoreWriteQueueItem");
+            if (Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER)) {
+                Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "StoreWriteQueueItem#" + mId);
+            }
             if (!mPersistInfoProvider.createDirectory(mUserId)) {
                 Slog.e(TAG, "Unable to create snapshot directory for user dir="
                         + mPersistInfoProvider.getDirectory(mUserId));
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index dbfcc22..a7a6bf2 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -90,7 +90,6 @@
 import static com.android.server.wm.ActivityTaskManagerService.H.FIRST_ACTIVITY_TASK_MSG;
 import static com.android.server.wm.ActivityTaskSupervisor.DEFER_RESUME;
 import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP;
-import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
 import static com.android.server.wm.ActivityTaskSupervisor.REMOVE_FROM_RECENTS;
 import static com.android.server.wm.ActivityTaskSupervisor.printThisActivity;
 import static com.android.server.wm.IdentifierProto.HASH_CODE;
@@ -760,7 +759,7 @@
             return;
         }
         mResizeMode = resizeMode;
-        mRootWindowContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
+        mRootWindowContainer.ensureActivitiesVisible();
         mRootWindowContainer.resumeFocusedTasksTopActivities();
         updateTaskDescription();
     }
@@ -801,15 +800,14 @@
             if (setBounds(bounds, forced) != BOUNDS_CHANGE_NONE) {
                 final ActivityRecord r = topRunningActivityLocked();
                 if (r != null) {
-                    kept = r.ensureActivityConfiguration(0 /* globalChanges */,
-                            preserveWindow);
+                    kept = r.ensureActivityConfiguration();
                     // Preserve other windows for resizing because if resizing happens when there
                     // is a dialog activity in the front, the activity that still shows some
                     // content to the user will become black and cause flickers. Note in most cases
                     // this won't cause tons of irrelevant windows being preserved because only
                     // activities in this task may experience a bounds change. Configs for other
                     // activities stay the same.
-                    mRootWindowContainer.ensureActivitiesVisible(r, 0, preserveWindow);
+                    mRootWindowContainer.ensureActivitiesVisible(r);
                     if (!kept) {
                         mRootWindowContainer.resumeFocusedTasksTopActivities();
                     }
@@ -915,7 +913,7 @@
         if (!deferResume) {
             // The task might have already been running and its visibility needs to be synchronized
             // with the visibility of the root task / windows.
-            root.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);
+            root.ensureActivitiesVisible();
             root.resumeFocusedTasksTopActivities();
         }
 
@@ -1413,12 +1411,13 @@
         return isUidPresent;
     }
 
+    WindowState topStartingWindow() {
+        return getWindow(w -> w.mAttrs.type == TYPE_APPLICATION_STARTING);
+    }
+
     ActivityRecord topActivityContainsStartingWindow() {
-        if (getParent() == null) {
-            return null;
-        }
-        return getActivity((r) -> r.getWindow(window ->
-                window.getBaseType() == TYPE_APPLICATION_STARTING) != null);
+        final WindowState startingWindow = topStartingWindow();
+        return startingWindow != null ? startingWindow.mActivityRecord : null;
     }
 
     /**
@@ -3508,6 +3507,8 @@
         appCompatTaskInfo.topActivityLetterboxHeight = TaskInfo.PROPERTY_VALUE_UNSET;
         appCompatTaskInfo.isUserFullscreenOverrideEnabled = top != null
                 && top.mLetterboxUiController.shouldApplyUserFullscreenOverride();
+        appCompatTaskInfo.isSystemFullscreenOverrideEnabled = top != null
+                && top.mLetterboxUiController.isSystemOverrideToFullscreenEnabled();
         appCompatTaskInfo.isFromLetterboxDoubleTap = top != null
                 && top.mLetterboxUiController.isFromDoubleTap();
         if (appCompatTaskInfo.isLetterboxDoubleTapEnabled) {
@@ -3698,6 +3699,16 @@
                 }
                 wc.assignLayer(t, layer++);
 
+                // Boost the adjacent TaskFragment for dimmer if needed.
+                final TaskFragment taskFragment = wc.asTaskFragment();
+                if (taskFragment != null && taskFragment.isEmbedded()
+                        && taskFragment.isVisibleRequested()) {
+                    final TaskFragment adjacentTf = taskFragment.getAdjacentTaskFragment();
+                    if (adjacentTf != null && adjacentTf.shouldBoostDimmer()) {
+                        adjacentTf.assignLayer(t, layer++);
+                    }
+                }
+
                 // Place the decor surface just above the owner TaskFragment.
                 if (mDecorSurfaceContainer != null && !decorSurfacePlaced
                         && wc == mDecorSurfaceContainer.mOwnerTaskFragment) {
@@ -4752,7 +4763,7 @@
         }
 
         if (!mTaskSupervisor.isRootVisibilityUpdateDeferred()) {
-            mRootWindowContainer.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);
+            mRootWindowContainer.ensureActivitiesVisible();
             mRootWindowContainer.resumeFocusedTasksTopActivities();
         }
     }
@@ -4784,6 +4795,7 @@
         }
         if (top.isAttached()) {
             top.setWindowingMode(WINDOWING_MODE_UNDEFINED);
+            top.mWaitForEnteringPinnedMode = false;
         }
     }
 
@@ -4793,8 +4805,7 @@
         mRootWindowContainer.resumeFocusedTasksTopActivities();
         // Update visibility of activities before notifying WM. This way it won't try to resize
         // windows that are no longer visible.
-        mRootWindowContainer.ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
-                !PRESERVE_WINDOWS);
+        mRootWindowContainer.ensureActivitiesVisible();
     }
 
     final boolean isOnHomeDisplay() {
@@ -4938,41 +4949,27 @@
      * @param starting The top most activity in the task.
      *                 The activity is either starting or resuming.
      *                 Caller should ensure starting activity is visible.
-     * @param preserveWindows Flag indicating whether windows should be preserved when updating
-     *                        configuration in {@link EnsureActivitiesVisibleHelper}.
-     * @param configChanges Parts of the configuration that changed for this activity for evaluating
-     *                      if the screen should be frozen as part of
-     *                      {@link EnsureActivitiesVisibleHelper}.
-     *
      */
-    void ensureActivitiesVisible(@Nullable ActivityRecord starting, int configChanges,
-            boolean preserveWindows) {
-        ensureActivitiesVisible(starting, configChanges, preserveWindows, true /* notifyClients */);
+    void ensureActivitiesVisible(@Nullable ActivityRecord starting) {
+        ensureActivitiesVisible(starting, true /* notifyClients */);
     }
 
     /**
      * Ensure visibility with an option to also update the configuration of visible activities.
-     * @see #ensureActivitiesVisible(ActivityRecord, int, boolean)
-     * @see RootWindowContainer#ensureActivitiesVisible(ActivityRecord, int, boolean)
+     * @see #ensureActivitiesVisible(ActivityRecord)
+     * @see RootWindowContainer#ensureActivitiesVisible()
      * @param starting The top most activity in the task.
      *                 The activity is either starting or resuming.
      *                 Caller should ensure starting activity is visible.
      * @param notifyClients Flag indicating whether the visibility updates should be sent to the
      *                      clients in {@link EnsureActivitiesVisibleHelper}.
-     * @param preserveWindows Flag indicating whether windows should be preserved when updating
-     *                        configuration in {@link EnsureActivitiesVisibleHelper}.
-     * @param configChanges Parts of the configuration that changed for this activity for evaluating
-     *                      if the screen should be frozen as part of
-     *                      {@link EnsureActivitiesVisibleHelper}.
      */
     // TODO: Should be re-worked based on the fact that each task as a root task in most cases.
-    void ensureActivitiesVisible(@Nullable ActivityRecord starting, int configChanges,
-            boolean preserveWindows, boolean notifyClients) {
+    void ensureActivitiesVisible(@Nullable ActivityRecord starting, boolean notifyClients) {
         mTaskSupervisor.beginActivityVisibilityUpdate();
         try {
             forAllLeafTasks(task -> {
-                task.updateActivityVisibilities(starting, configChanges, preserveWindows,
-                        notifyClients);
+                task.updateActivityVisibilities(starting, notifyClients);
             }, true /* traverseTopToBottom */);
 
             if (mTranslucentActivityWaiting != null &&
@@ -5273,7 +5270,7 @@
             // tell WindowManager that r is visible even though it is at the back of the root
             // task.
             r.setVisibility(true);
-            ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
+            ensureActivitiesVisible(null /* starting */);
             // If launching behind, the app will start regardless of what's above it, so mark it
             // as unknown even before prior `pause`. This also prevents a race between set-ready
             // and activityPause. Launch-behind is basically only used for dream now.
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index c57983c..90a3b253 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -1777,13 +1777,11 @@
         void onRootTaskOrderChanged(Task rootTask);
     }
 
-    void ensureActivitiesVisible(ActivityRecord starting, int configChanges,
-            boolean preserveWindows, boolean notifyClients) {
+    void ensureActivitiesVisible(ActivityRecord starting, boolean notifyClients) {
         mAtmService.mTaskSupervisor.beginActivityVisibilityUpdate();
         try {
             forAllRootTasks(rootTask -> {
-                rootTask.ensureActivitiesVisible(starting, configChanges, preserveWindows,
-                        notifyClients);
+                rootTask.ensureActivitiesVisible(starting, notifyClients);
             });
         } finally {
             mAtmService.mTaskSupervisor.endActivityVisibilityUpdate();
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 5d01912..f56759f 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -16,6 +16,7 @@
 
 package com.android.server.wm;
 
+import static android.Manifest.permission.MANAGE_ACTIVITY_TASKS;
 import static android.app.ActivityTaskManager.INVALID_TASK_ID;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
@@ -38,6 +39,7 @@
 import static android.os.Process.SYSTEM_UID;
 import static android.os.UserHandle.USER_NULL;
 import static android.view.Display.INVALID_DISPLAY;
+import static android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND;
 import static android.view.WindowManager.TRANSIT_CLOSE;
 import static android.view.WindowManager.TRANSIT_FLAG_OPEN_BEHIND;
 import static android.view.WindowManager.TRANSIT_NONE;
@@ -57,7 +59,7 @@
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_TRANSITION;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
+import static com.android.server.wm.ActivityTaskManagerService.checkPermission;
 import static com.android.server.wm.ActivityTaskSupervisor.printThisActivity;
 import static com.android.server.wm.IdentifierProto.HASH_CODE;
 import static com.android.server.wm.IdentifierProto.TITLE;
@@ -81,7 +83,9 @@
 import android.app.servertransaction.NewIntentItem;
 import android.app.servertransaction.PauseActivityItem;
 import android.app.servertransaction.ResumeActivityItem;
+import android.content.PermissionChecker;
 import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
 import android.content.res.Configuration;
 import android.graphics.Point;
 import android.graphics.Rect;
@@ -745,7 +749,17 @@
         // The system is trusted to embed other apps securely and for all users.
         return UserHandle.getAppId(uid) == SYSTEM_UID
                 // Activities from the same UID can be embedded freely by the host.
-                || a.isUid(uid);
+                || a.isUid(uid)
+                // Apps which have the signature MANAGE_ACTIVITY_TASK permission are trusted.
+                || hasManageTaskPermission(uid);
+    }
+
+    /**
+     * Checks if a particular app uid has the {@link MANAGE_ACTIVITY_TASKS} permission.
+     */
+    private static boolean hasManageTaskPermission(int uid) {
+        return checkPermission(MANAGE_ACTIVITY_TASKS, PermissionChecker.PID_UNKNOWN, uid)
+                == PackageManager.PERMISSION_GRANTED;
     }
 
     /**
@@ -927,10 +941,14 @@
     boolean sleepIfPossible(boolean shuttingDown) {
         boolean shouldSleep = true;
         if (mResumedActivity != null) {
-            // Still have something resumed; can't sleep until it is paused.
-            ProtoLog.v(WM_DEBUG_STATES, "Sleep needs to pause %s", mResumedActivity);
-            startPausing(false /* userLeaving */, true /* uiSleeping */, null /* resuming */,
-                    "sleep");
+            if (!shuttingDown && mResumedActivity.canTurnScreenOn()) {
+                ProtoLog.v(WM_DEBUG_STATES, "Waiting for screen on due to %s", mResumedActivity);
+            } else {
+                // Still have something resumed; can't sleep until it is paused.
+                ProtoLog.v(WM_DEBUG_STATES, "Sleep needs to pause %s", mResumedActivity);
+                startPausing(false /* userLeaving */, true /* uiSleeping */, null /* resuming */,
+                        "sleep");
+            }
             shouldSleep = false;
         } else if (mPausingActivity != null) {
             // Still waiting for something to pause; can't sleep yet.
@@ -950,8 +968,7 @@
         }
 
         if (shouldSleep) {
-            updateActivityVisibilities(null /* starting */, 0 /* configChanges */,
-                    !PRESERVE_WINDOWS, true /* notifyClients */);
+            updateActivityVisibilities(null /* starting */, true /* notifyClients */);
         }
 
         return shouldSleep;
@@ -1218,12 +1235,11 @@
         return top != null && top.mLaunchTaskBehind;
     }
 
-    final void updateActivityVisibilities(@Nullable ActivityRecord starting, int configChanges,
-            boolean preserveWindows, boolean notifyClients) {
+    final void updateActivityVisibilities(@Nullable ActivityRecord starting,
+            boolean notifyClients) {
         mTaskSupervisor.beginActivityVisibilityUpdate();
         try {
-            mEnsureActivitiesVisibleHelper.process(
-                    starting, configChanges, preserveWindows, notifyClients);
+            mEnsureActivitiesVisibleHelper.process(starting, notifyClients);
         } finally {
             mTaskSupervisor.endActivityVisibilityUpdate();
         }
@@ -1249,8 +1265,7 @@
         if (mResumedActivity == next && next.isState(RESUMED)
                 && taskDisplayArea.allResumedActivitiesComplete()) {
             // Ensure the visibility gets updated before execute app transition.
-            taskDisplayArea.ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
-                    false /* preserveWindows */, true /* notifyClients */);
+            taskDisplayArea.ensureActivitiesVisible(null /* starting */, true /* notifyClients */);
             // Make sure we have executed any pending transitions, since there
             // should be nothing left to do at this point.
             executeAppTransition(options);
@@ -1907,7 +1922,7 @@
             prev.resumeKeyDispatchingLocked();
         }
 
-        mRootWindowContainer.ensureActivitiesVisible(resuming, 0, !PRESERVE_WINDOWS);
+        mRootWindowContainer.ensureActivitiesVisible(resuming);
 
         // Notify when the task stack has changed, but only if visibilities changed (not just
         // focus). Also if there is an active root pinned task - we always want to notify it about
@@ -2981,10 +2996,34 @@
         }, false /* traverseTopToBottom */);
     }
 
+    boolean shouldBoostDimmer() {
+        if (asTask() != null || !isDimmingOnParentTask()) {
+            // early return if not embedded or should not dim on parent Task.
+            return false;
+        }
+
+        final TaskFragment adjacentTf = getAdjacentTaskFragment();
+        if (adjacentTf == null) {
+            // early return if no adjacent TF.
+            return false;
+        }
+
+        if (getParent().mChildren.indexOf(adjacentTf) < getParent().mChildren.indexOf(this)) {
+            // early return if this TF already has higher z-ordering.
+            return false;
+        }
+
+        // boost if there's an Activity window that has FLAG_DIM_BEHIND flag.
+        return forAllWindows(
+                (w) -> (w.mAttrs.flags & FLAG_DIM_BEHIND) != 0 && w.mActivityRecord != null
+                        && w.mActivityRecord.isEmbedded() && (w.mActivityRecord.isVisibleRequested()
+                        || w.mActivityRecord.isVisible()), true);
+    }
+
     @Override
     Dimmer getDimmer() {
         // If this is in an embedded TaskFragment and we want the dim applies on the TaskFragment.
-        if (mIsEmbedded && mEmbeddedDimArea == EMBEDDED_DIM_AREA_TASK_FRAGMENT) {
+        if (mIsEmbedded && !isDimmingOnParentTask()) {
             return mDimmer;
         }
 
@@ -2993,7 +3032,9 @@
 
     /** Bounds to be used for dimming, as well as touch related tests. */
     void getDimBounds(@NonNull Rect out) {
-        if (mIsEmbedded && mEmbeddedDimArea == EMBEDDED_DIM_AREA_PARENT_TASK) {
+        if (mIsEmbedded && isDimmingOnParentTask() && getDimmer().getDimBounds() != null) {
+            // Return the task bounds if the dimmer is showing and should cover on the Task (not
+            // just on this embedded TaskFragment).
             out.set(getTask().getBounds());
         } else {
             out.set(getBounds());
@@ -3004,6 +3045,11 @@
         mEmbeddedDimArea = embeddedDimArea;
     }
 
+    @VisibleForTesting
+    boolean isDimmingOnParentTask() {
+        return mEmbeddedDimArea == EMBEDDED_DIM_AREA_PARENT_TASK;
+    }
+
     @Override
     void prepareSurfaces() {
         if (asTask() != null) {
diff --git a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
index 707f9fc..81fe453 100644
--- a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
@@ -35,6 +35,7 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.IApplicationThread;
 import android.content.Intent;
 import android.content.res.Configuration;
 import android.os.Binder;
@@ -106,6 +107,7 @@
      */
     private class TaskFragmentOrganizerState implements IBinder.DeathRecipient {
         private final ArrayList<TaskFragment> mOrganizedTaskFragments = new ArrayList<>();
+        private final IApplicationThread mAppThread;
         private final ITaskFragmentOrganizer mOrganizer;
         private final int mOrganizerPid;
         private final int mOrganizerUid;
@@ -169,6 +171,11 @@
 
         TaskFragmentOrganizerState(@NonNull ITaskFragmentOrganizer organizer, int pid, int uid,
                 boolean isSystemOrganizer) {
+            if (Flags.bundleClientTransactionFlag()) {
+                mAppThread = getAppThread(pid, uid);
+            } else {
+                mAppThread = null;
+            }
             mOrganizer = organizer;
             mOrganizerPid = pid;
             mOrganizerUid = uid;
@@ -407,7 +414,13 @@
                 return;
             }
             try {
-                mOrganizer.onTransactionReady(transaction);
+                if (Flags.bundleClientTransactionFlag()) {
+                    // Dispatch through IApplicationThread to ensure the binder call is in order
+                    // with ClientTransaction.
+                    mAppThread.scheduleTaskFragmentTransaction(mOrganizer, transaction);
+                } else {
+                    mOrganizer.onTransactionReady(transaction);
+                }
             } catch (RemoteException e) {
                 Slog.d(TAG, "Exception sending TaskFragmentTransaction", e);
                 return;
@@ -464,11 +477,6 @@
                 : null;
     }
 
-    @VisibleForTesting
-    void registerOrganizer(@NonNull ITaskFragmentOrganizer organizer) {
-        registerOrganizerInternal(organizer, false /* isSystemOrganizer */);
-    }
-
     @Override
     public void registerOrganizer(
             @NonNull ITaskFragmentOrganizer organizer, boolean isSystemOrganizer) {
@@ -477,8 +485,7 @@
                 Flags.taskFragmentSystemOrganizerFlag() && isSystemOrganizer);
     }
 
-    @VisibleForTesting
-    void registerOrganizerInternal(
+    private void registerOrganizerInternal(
             @NonNull ITaskFragmentOrganizer organizer, boolean isSystemOrganizer) {
         if (isSystemOrganizer) {
             enforceTaskPermission("registerSystemOrganizer()");
@@ -1198,6 +1205,20 @@
         }
     }
 
+    @VisibleForTesting
+    @NonNull
+    IApplicationThread getAppThread(int pid, int uid) {
+        final WindowProcessController wpc = mAtmService.mProcessMap.getProcess(pid);
+        final IApplicationThread appThread = wpc != null && wpc.mUid == uid
+                ? wpc.getThread()
+                : null;
+        if (appThread == null) {
+            throw new IllegalArgumentException("Cannot find process for pid=" + pid
+                    + " uid=" + uid);
+        }
+        return appThread;
+    }
+
     /**
      * Trims the given Intent to only those that are needed to for embedding rules. This helps to
      * make it safer for cross-uid embedding even if we only send the Intent for trusted embedding.
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 3117db5..f620a97 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -1136,8 +1136,7 @@
             // The transient hide tasks could be occluded now, e.g. returning to home. So trigger
             // the update to make the activities in the tasks invisible-requested, then the next
             // step can continue to commit the visibility.
-            mController.mAtm.mRootWindowContainer.ensureActivitiesVisible(null /* starting */,
-                    0 /* configChanges */, true /* preserveWindows */);
+            mController.mAtm.mRootWindowContainer.ensureActivitiesVisible();
             // Record all the now-hiding activities so that they are committed. Just use
             // mParticipants because we can avoid a new list this way.
             for (int i = 0; i < mTransientHideTasks.size(); ++i) {
@@ -1907,7 +1906,6 @@
         for (int i = mParticipants.size() - 1; i >= 0; --i) {
             final WallpaperWindowToken wallpaper = mParticipants.valueAt(i).asWallpaperToken();
             if (wallpaper != null) {
-                wallpaper.waitingToShow = false;
                 if (!wallpaper.isVisible() && wallpaper.isVisibleRequested()) {
                     wallpaper.commitVisibility(showWallpaper);
                 }
@@ -1935,29 +1933,29 @@
                 dc.getInputMonitor().getInputConsumer(INPUT_CONSUMER_RECENTS_ANIMATION);
         ActivityRecord recentsActivity = null;
         if (recentsAnimationInputConsumer != null) {
-            // find the top-most going-away activity and the recents activity. The top-most
+            // Find the top-most going-away task and the recents activity. The top-most
             // is used as layer reference while the recents is used for registering the consumer
             // override.
-            ActivityRecord topActivity = null;
+            Task topNonRecentsTask = null;
             for (int i = 0; i < info.getChanges().size(); ++i) {
-                final TransitionInfo.Change change = info.getChanges().get(i);
-                if (change.getTaskInfo() == null) continue;
-                final Task task = Task.fromWindowContainerToken(
-                        info.getChanges().get(i).getTaskInfo().token);
+                final ActivityManager.RunningTaskInfo taskInfo =
+                        info.getChanges().get(i).getTaskInfo();
+                if (taskInfo == null) continue;
+                final Task task = Task.fromWindowContainerToken(taskInfo.token);
                 if (task == null) continue;
-                final int activityType = change.getTaskInfo().topActivityType;
+                final int activityType = taskInfo.topActivityType;
                 final boolean isRecents = activityType == ACTIVITY_TYPE_HOME
                         || activityType == ACTIVITY_TYPE_RECENTS;
                 if (isRecents && recentsActivity == null) {
                     recentsActivity = task.getTopVisibleActivity();
-                } else if (!isRecents && topActivity == null) {
-                    topActivity = task.getTopNonFinishingActivity();
+                } else if (!isRecents && topNonRecentsTask == null) {
+                    topNonRecentsTask = task;
                 }
             }
-            if (recentsActivity != null && topActivity != null) {
+            if (recentsActivity != null && topNonRecentsTask != null) {
                 recentsAnimationInputConsumer.mWindowHandle.touchableRegion.set(
-                        topActivity.getBounds());
-                dc.getInputMonitor().setActiveRecents(recentsActivity, topActivity);
+                        topNonRecentsTask.getBounds());
+                dc.getInputMonitor().setActiveRecents(recentsActivity, topNonRecentsTask);
             }
         }
 
@@ -2022,16 +2020,17 @@
         }
         mController.mNavigationBarAttachedToApp = false;
 
-        if (mRecentsDisplayId == INVALID_DISPLAY) {
-            Slog.e(TAG, "Reparented navigation bar without a valid display");
-            mRecentsDisplayId = DEFAULT_DISPLAY;
+        int recentsDisplayId = mRecentsDisplayId;
+        if (recentsDisplayId == INVALID_DISPLAY) {
+            Slog.i(TAG, "Restore parent surface of navigation bar by another transition");
+            recentsDisplayId = DEFAULT_DISPLAY;
         }
 
         final DisplayContent dc =
-                mController.mAtm.mRootWindowContainer.getDisplayContent(mRecentsDisplayId);
+                mController.mAtm.mRootWindowContainer.getDisplayContent(recentsDisplayId);
         final StatusBarManagerInternal bar = dc.getDisplayPolicy().getStatusBarManagerInternal();
         if (bar != null) {
-            bar.setNavigationBarLumaSamplingEnabled(mRecentsDisplayId, true);
+            bar.setNavigationBarLumaSamplingEnabled(recentsDisplayId, true);
         }
         final WindowState navWindow = dc.getDisplayPolicy().getNavigationBar();
         if (navWindow == null) return;
@@ -2598,6 +2597,10 @@
                 change.setBackgroundColor(ColorUtils.setAlphaComponent(backgroundColor, 255));
             }
 
+            if (activityRecord != null) {
+                change.setActivityComponent(activityRecord.mActivityComponent);
+            }
+
             change.setRotation(info.mRotation, endRotation);
             if (info.mSnapshot != null) {
                 change.setSnapshot(info.mSnapshot, info.mSnapshotLuma);
@@ -2863,8 +2866,7 @@
      * check whether to deliver the new configuration to clients.
      */
     @Nullable
-    ArrayList<ActivityRecord> applyDisplayChangeIfNeeded() {
-        ArrayList<ActivityRecord> activitiesMayChange = null;
+    void applyDisplayChangeIfNeeded(@NonNull ArraySet<WindowContainer<?>> activitiesMayChange) {
         for (int i = mParticipants.size() - 1; i >= 0; --i) {
             final WindowContainer<?> wc = mParticipants.valueAt(i);
             final DisplayContent dc = wc.asDisplayContent();
@@ -2881,18 +2883,13 @@
             // If the update is deferred, sendNewConfiguration won't deliver new configuration to
             // clients, then it is the caller's responsibility to deliver the changes.
             if (mController.mAtm.mTaskSupervisor.isRootVisibilityUpdateDeferred()) {
-                if (activitiesMayChange == null) {
-                    activitiesMayChange = new ArrayList<>();
-                }
-                final ArrayList<ActivityRecord> visibleActivities = activitiesMayChange;
                 dc.forAllActivities(r -> {
                     if (r.isVisibleRequested()) {
-                        visibleActivities.add(r);
+                        activitiesMayChange.add(r);
                     }
                 });
             }
         }
-        return activitiesMayChange;
     }
 
     boolean getLegacyIsReady() {
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index c3aca6f..708d63e 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -634,8 +634,8 @@
     }
 
     /** Sets the sync method for the display change. */
-    private void setDisplaySyncMethod(@NonNull TransitionRequestInfo.DisplayChange displayChange,
-            @NonNull Transition displayTransition, @NonNull DisplayContent displayContent) {
+    void setDisplaySyncMethod(@NonNull TransitionRequestInfo.DisplayChange displayChange,
+            @NonNull DisplayContent displayContent) {
         final Rect startBounds = displayChange.getStartAbsBounds();
         final Rect endBounds = displayChange.getEndAbsBounds();
         if (startBounds == null || endBounds == null) return;
@@ -686,7 +686,7 @@
                     trigger != null ? trigger.asTask() : null, remoteTransition, displayChange);
             if (newTransition != null && displayChange != null && trigger != null
                     && trigger.asDisplayContent() != null) {
-                setDisplaySyncMethod(displayChange, newTransition, trigger.asDisplayContent());
+                setDisplaySyncMethod(displayChange, trigger.asDisplayContent());
             }
         }
         if (trigger != null) {
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index 33ef3c5..a9f0554 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -183,18 +183,21 @@
                 && (mWallpaperTarget == w || w.isDrawFinishedLw())) {
             if (DEBUG_WALLPAPER) Slog.v(TAG, "Found wallpaper target: " + w);
             mFindResults.setWallpaperTarget(w);
+            mFindResults.setIsWallpaperTargetForLetterbox(w.hasWallpaperForLetterboxBackground());
             if (w == mWallpaperTarget && w.isAnimating(TRANSITION | PARENTS)) {
                 // The current wallpaper target is animating, so we'll look behind it for
                 // another possible target and figure out what is going on later.
                 if (DEBUG_WALLPAPER) Slog.v(TAG,
                         "Win " + w + ": token animating, looking behind.");
             }
-            mFindResults.setIsWallpaperTargetForLetterbox(w.hasWallpaperForLetterboxBackground());
             // While the keyguard is going away, both notification shade and a normal activity such
             // as a launcher can satisfy criteria for a wallpaper target. In this case, we should
             // chose the normal activity, otherwise wallpaper becomes invisible when a new animation
             // starts before the keyguard going away animation finishes.
-            return w.mActivityRecord != null;
+            if (w.mActivityRecord == null && mDisplayContent.isKeyguardGoingAway()) {
+                return false;
+            }
+            return true;
         }
         return false;
     };
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index e28262d..bdea1bc 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -3938,7 +3938,13 @@
     @Nullable
     BLASTSyncEngine.SyncGroup getSyncGroup() {
         if (mSyncGroup != null) return mSyncGroup;
-        if (mParent != null) return mParent.getSyncGroup();
+        WindowContainer<?> parent = mParent;
+        while (parent != null) {
+            if (parent.mSyncGroup != null) {
+                return parent.mSyncGroup;
+            }
+            parent = parent.mParent;
+        }
         return null;
     }
 
@@ -3972,7 +3978,7 @@
      * @param cancel If true, this is being finished because it is leaving the sync group rather
      *               than due to the sync group completing.
      */
-    void finishSync(Transaction outMergedTransaction, BLASTSyncEngine.SyncGroup group,
+    void finishSync(Transaction outMergedTransaction, @Nullable BLASTSyncEngine.SyncGroup group,
             boolean cancel) {
         if (mSyncState == SYNC_STATE_NONE) return;
         final BLASTSyncEngine.SyncGroup syncGroup = getSyncGroup();
@@ -4000,7 +4006,8 @@
         if (!isVisibleRequested()) {
             return true;
         }
-        if (mSyncState == SYNC_STATE_NONE) {
+        if (mSyncState == SYNC_STATE_NONE && getSyncGroup() != null) {
+            Slog.i(TAG, "prepareSync in isSyncFinished: " + this);
             prepareSync();
         }
         if (mSyncState == SYNC_STATE_WAITING_FOR_DRAW) {
@@ -4059,16 +4066,18 @@
             }
             if (newParent == null) {
                 // This is getting removed.
+                final BLASTSyncEngine.SyncGroup syncGroup = getSyncGroup();
                 if (oldParent.mSyncState != SYNC_STATE_NONE) {
                     // In order to keep the transaction in sync, merge it into the parent.
-                    finishSync(oldParent.mSyncTransaction, getSyncGroup(), true /* cancel */);
-                } else if (mSyncGroup != null) {
-                    // This is watched directly by the sync-group, so merge this transaction into
-                    // into the sync-group so it isn't lost
-                    finishSync(mSyncGroup.getOrphanTransaction(), mSyncGroup, true /* cancel */);
+                    finishSync(oldParent.mSyncTransaction, syncGroup, true /* cancel */);
+                } else if (syncGroup != null) {
+                    // This is watched by the sync-group, so merge this transaction into the
+                    // sync-group for not losing the operations in the transaction.
+                    finishSync(syncGroup.getOrphanTransaction(), syncGroup, true /* cancel */);
                 } else {
-                    throw new IllegalStateException("This container is in sync mode without a sync"
-                            + " group: " + this);
+                    Slog.wtf(TAG, this + " is in sync mode without a sync group");
+                    // Make sure the removal transaction take effect.
+                    finishSync(getPendingTransaction(), null /* group */, true /* cancel */);
                 }
                 return;
             } else if (mSyncGroup == null) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerFlags.java b/services/core/java/com/android/server/wm/WindowManagerFlags.java
index 89a70e5..7b0d931 100644
--- a/services/core/java/com/android/server/wm/WindowManagerFlags.java
+++ b/services/core/java/com/android/server/wm/WindowManagerFlags.java
@@ -43,8 +43,6 @@
 
     /* Start Available Flags */
 
-    final boolean mWindowStateResizeItemFlag = Flags.windowStateResizeItemFlag();
-
     final boolean mWallpaperOffsetAsync = Flags.wallpaperOffsetAsync();
 
     final boolean mAllowsScreenSizeDecoupledFromStatusBarAndCutout =
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 516d37c..22b690e 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -51,6 +51,7 @@
 import com.android.internal.policy.KeyInterceptionInfo;
 import com.android.server.input.InputManagerService;
 import com.android.server.policy.WindowManagerPolicy;
+import com.android.server.wm.SensitiveContentPackages.PackageInfo;
 
 import java.lang.annotation.Retention;
 import java.util.List;
@@ -1012,4 +1013,12 @@
      */
     public abstract void setOrientationRequestPolicy(boolean respected,
             int[] fromOrientations, int[] toOrientations);
+
+    /**
+     * Set whether screen capture should be disabled for all windows of a specific app windows based
+     * on sensitive content protections.
+     *
+     * @param packageInfos set of {@link PackageInfo} whose windows should be blocked from capture
+     */
+    public abstract void setShouldBlockScreenCaptureForApp(@NonNull Set<PackageInfo> packageInfos);
 }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index c1310a6..9544835 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -123,6 +123,7 @@
 import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_SOLID_COLOR;
 import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_WALLPAPER;
 import static com.android.server.wm.RootWindowContainer.MATCH_ATTACHED_TASK_OR_RECENT_TASKS;
+import static com.android.server.wm.SensitiveContentPackages.PackageInfo;
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_ALL;
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
@@ -286,6 +287,7 @@
 import android.view.SurfaceSession;
 import android.view.TaskTransitionSpec;
 import android.view.View;
+import android.view.View.FocusDirection;
 import android.view.ViewDebug;
 import android.view.WindowContentFrameStats;
 import android.view.WindowInsets;
@@ -312,6 +314,7 @@
 import android.window.WindowContextInfo;
 
 import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.os.IResultReceiver;
 import com.android.internal.policy.IKeyguardDismissCallback;
@@ -366,6 +369,7 @@
 import java.util.NoSuchElementException;
 import java.util.Objects;
 import java.util.Optional;
+import java.util.Set;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 import java.util.function.Function;
@@ -1053,6 +1057,9 @@
 
     SystemPerformanceHinter mSystemPerformanceHinter;
 
+    @GuardedBy("mGlobalLock")
+    final SensitiveContentPackages mSensitiveContentPackages = new SensitiveContentPackages();
+
     /** Listener to notify activity manager about app transitions. */
     final WindowManagerInternal.AppTransitionListener mActivityManagerAppTransitionNotifier
             = new WindowManagerInternal.AppTransitionListener() {
@@ -1797,7 +1804,12 @@
 
             // Don't do layout here, the window must call
             // relayout to be displayed, so we'll do it there.
-            win.getParent().assignChildLayers();
+            if (win.mActivityRecord != null && win.mActivityRecord.isEmbedded()) {
+                // Assign child layers from the parent Task if the Activity is embedded.
+                win.getTask().assignChildLayers();
+            } else {
+                win.getParent().assignChildLayers();
+            }
 
             if (focusChanged) {
                 displayContent.getInputMonitor().setInputFocusLw(displayContent.mCurrentFocus,
@@ -1931,12 +1943,13 @@
 
     /**
      * Set whether screen capture is disabled for all windows of a specific user from
-     * the device policy cache.
+     * the device policy cache, or specific windows based on sensitive content protections.
      */
     @Override
     public void refreshScreenCaptureDisabled() {
         int callingUid = Binder.getCallingUid();
-        if (callingUid != SYSTEM_UID) {
+        // MY_UID (Process.myUid()) should always be SYSTEM_UID here, but using MY_UID for tests
+        if (callingUid != MY_UID) {
             throw new SecurityException("Only system can call refreshScreenCaptureDisabled.");
         }
 
@@ -3105,7 +3118,7 @@
         try {
             synchronized (mGlobalLock) {
                 if (mAtmService.mKeyguardController.isKeyguardShowing(DEFAULT_DISPLAY)) {
-                    mRoot.ensureActivitiesVisible(null, 0, false /* preserveWindows */);
+                    mRoot.ensureActivitiesVisible();
                 }
             }
         } finally {
@@ -5748,7 +5761,6 @@
                 case INSETS_CHANGED: {
                     synchronized (mGlobalLock) {
                         if (mWindowsInsetsChanged > 0) {
-                            mWindowsInsetsChanged = 0;
                             // We need to update resizing windows and dispatch the new insets state
                             // to them.
                             mWindowPlacerLocked.performSurfacePlacement();
@@ -6848,6 +6860,7 @@
                     pw.println(defaultDisplayContent.getLastOrientation());
             pw.print("  mWaitingForConfig=");
                     pw.println(defaultDisplayContent.mWaitingForConfig);
+            pw.print("  mWindowsInsetsChanged="); pw.println(mWindowsInsetsChanged);
             mRotationWatcherController.dump(pw);
 
             pw.print("  Animation settings: disabled="); pw.print(mAnimationsDisabled);
@@ -7169,6 +7182,7 @@
             }
             mSystemPerformanceHinter.dump(pw, "");
             mTrustedPresentationListenerController.dump(pw);
+            mSensitiveContentPackages.dump(pw);
         }
     }
 
@@ -8550,6 +8564,14 @@
             InputTarget inputTarget = WindowManagerService.this.getInputTargetFromToken(inputToken);
             return inputTarget == null ? null : inputTarget.getWindowToken();
         }
+
+        @Override
+        public void setShouldBlockScreenCaptureForApp(Set<PackageInfo> packageInfos) {
+            synchronized (mGlobalLock) {
+                mSensitiveContentPackages.setShouldBlockScreenCaptureForApp(packageInfos);
+                WindowManagerService.this.refreshScreenCaptureDisabled();
+            }
+        }
     }
 
     private final class ImeTargetVisibilityPolicyImpl extends ImeTargetVisibilityPolicy {
@@ -9083,6 +9105,66 @@
                 win.mClient);
     }
 
+    boolean moveFocusToAdjacentWindow(Session session, IWindow fromWindow,
+            @FocusDirection int direction) {
+        synchronized (mGlobalLock) {
+            final WindowState fromWin = windowForClientLocked(session, fromWindow, false);
+            if (fromWin == null || !fromWin.isFocused()) {
+                return false;
+            }
+            final TaskFragment fromFragment = fromWin.getTaskFragment();
+            if (fromFragment == null) {
+                return false;
+            }
+            final TaskFragment adjacentFragment = fromFragment.getAdjacentTaskFragment();
+            if (adjacentFragment == null || adjacentFragment.asTask() != null) {
+                // Don't move the focus to another task.
+                return false;
+            }
+            final Rect fromBounds = fromFragment.getBounds();
+            final Rect adjacentBounds = adjacentFragment.getBounds();
+            switch (direction) {
+                case View.FOCUS_LEFT:
+                    if (adjacentBounds.left >= fromBounds.left) {
+                        return false;
+                    }
+                    break;
+                case View.FOCUS_UP:
+                    if (adjacentBounds.top >= fromBounds.top) {
+                        return false;
+                    }
+                    break;
+                case View.FOCUS_RIGHT:
+                    if (adjacentBounds.right <= fromBounds.right) {
+                        return false;
+                    }
+                    break;
+                case View.FOCUS_DOWN:
+                    if (adjacentBounds.bottom <= fromBounds.bottom) {
+                        return false;
+                    }
+                    break;
+                case View.FOCUS_BACKWARD:
+                case View.FOCUS_FORWARD:
+                    // These are not absolute directions. Skip checking the bounds.
+                    break;
+                default:
+                    return false;
+            }
+            final ActivityRecord topRunningActivity = adjacentFragment.topRunningActivity(
+                    true /* focusableOnly */);
+            if (topRunningActivity == null) {
+                return false;
+            }
+            moveDisplayToTopInternal(topRunningActivity.getDisplayId());
+            handleTaskFocusChange(topRunningActivity.getTask(), topRunningActivity);
+            if (fromWin.isFocused()) {
+                return false;
+            }
+        }
+        return true;
+    }
+
     /** Return whether layer tracing is enabled */
     public boolean isLayerTracing() {
         if (!checkCallingPermission(
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 4b99432..59d0210 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -34,6 +34,7 @@
 import static android.window.TaskFragmentOperation.OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS;
 import static android.window.TaskFragmentOperation.OP_TYPE_SET_ANIMATION_PARAMS;
 import static android.window.TaskFragmentOperation.OP_TYPE_SET_COMPANION_TASK_FRAGMENT;
+import static android.window.TaskFragmentOperation.OP_TYPE_SET_DIM_ON_TASK;
 import static android.window.TaskFragmentOperation.OP_TYPE_SET_ISOLATED_NAVIGATION;
 import static android.window.TaskFragmentOperation.OP_TYPE_SET_RELATIVE_BOUNDS;
 import static android.window.TaskFragmentOperation.OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT;
@@ -65,10 +66,11 @@
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER;
 import static com.android.server.wm.ActivityRecord.State.PAUSING;
 import static com.android.server.wm.ActivityTaskManagerService.enforceTaskPermission;
-import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
 import static com.android.server.wm.ActivityTaskSupervisor.REMOVE_FROM_RECENTS;
 import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_PINNED_TASK;
 import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_TASK_ORG;
+import static com.android.server.wm.TaskFragment.EMBEDDED_DIM_AREA_PARENT_TASK;
+import static com.android.server.wm.TaskFragment.EMBEDDED_DIM_AREA_TASK_FRAGMENT;
 import static com.android.server.wm.TaskFragment.EMBEDDING_ALLOWED;
 import static com.android.server.wm.TaskFragment.FLAG_FORCE_HIDDEN_FOR_TASK_FRAGMENT_ORG;
 import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
@@ -571,14 +573,15 @@
         mService.deferWindowLayout();
         mService.mTaskSupervisor.setDeferRootVisibilityUpdate(true /* deferUpdate */);
         try {
-            final ArrayList<ActivityRecord> activitiesMayChange =
-                    transition != null ? transition.applyDisplayChangeIfNeeded() : null;
-            if (activitiesMayChange != null) {
-                effects |= TRANSACT_EFFECTS_CLIENT_CONFIG;
+            final ArraySet<WindowContainer<?>> haveConfigChanges = new ArraySet<>();
+            if (transition != null) {
+                transition.applyDisplayChangeIfNeeded(haveConfigChanges);
+                if (!haveConfigChanges.isEmpty()) {
+                    effects |= TRANSACT_EFFECTS_CLIENT_CONFIG;
+                }
             }
             final List<WindowContainerTransaction.HierarchyOp> hops = t.getHierarchyOps();
             final int hopSize = hops.size();
-            final ArraySet<WindowContainer<?>> haveConfigChanges = new ArraySet<>();
             Iterator<Map.Entry<IBinder, WindowContainerTransaction.Change>> entries =
                     t.getChanges().entrySet().iterator();
             while (entries.hasNext()) {
@@ -626,7 +629,7 @@
                     // When removing pip, make sure that onStop is sent to the app ahead of
                     // onPictureInPictureModeChanged.
                     // See also PinnedStackTests#testStopBeforeMultiWindowCallbacksOnDismiss
-                    wc.asTask().ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);
+                    wc.asTask().ensureActivitiesVisible(null /* starting */);
                     wc.asTask().mTaskSupervisor.processStoppingAndFinishingActivities(
                             null /* launchedActivity */, false /* processPausingActivities */,
                             "force-stop-on-removing-pip");
@@ -692,29 +695,16 @@
             if ((effects & TRANSACT_EFFECTS_LIFECYCLE) != 0) {
                 mService.mTaskSupervisor.setDeferRootVisibilityUpdate(false /* deferUpdate */);
                 // Already calls ensureActivityConfig
-                mService.mRootWindowContainer.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);
+                mService.mRootWindowContainer.ensureActivitiesVisible();
                 mService.mRootWindowContainer.resumeFocusedTasksTopActivities();
             } else if ((effects & TRANSACT_EFFECTS_CLIENT_CONFIG) != 0) {
                 for (int i = haveConfigChanges.size() - 1; i >= 0; --i) {
                     haveConfigChanges.valueAt(i).forAllActivities(r -> {
-                        r.ensureActivityConfiguration(0, PRESERVE_WINDOWS);
-                        if (activitiesMayChange != null) {
-                            activitiesMayChange.remove(r);
+                        if (r.isVisibleRequested()) {
+                            r.ensureActivityConfiguration(true /* ignoreVisibility */);
                         }
                     });
                 }
-                // TODO(b/258618073): Combine with haveConfigChanges after confirming that there
-                //  is no problem to always preserve window. Currently this uses the parameters
-                //  as ATMS#ensureConfigAndVisibilityAfterUpdate.
-                if (activitiesMayChange != null) {
-                    for (int i = activitiesMayChange.size() - 1; i >= 0; --i) {
-                        final ActivityRecord ar = activitiesMayChange.get(i);
-                        if (!ar.isVisibleRequested()) continue;
-                        ar.ensureActivityConfiguration(0 /* globalChanges */,
-                                !PRESERVE_WINDOWS, true /* ignoreVisibility */,
-                                false /* isRequestedOrientationChanged */);
-                    }
-                }
             }
 
             if (effects != 0) {
@@ -1506,6 +1496,12 @@
                 task.removeDecorSurface();
                 break;
             }
+            case OP_TYPE_SET_DIM_ON_TASK: {
+                final boolean dimOnTask = operation.isDimOnTask();
+                taskFragment.setEmbeddedDimArea(dimOnTask ? EMBEDDED_DIM_AREA_PARENT_TASK
+                        : EMBEDDED_DIM_AREA_TASK_FRAGMENT);
+                break;
+            }
         }
         return effects;
     }
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 5721750..b8fa5e5 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -188,6 +188,10 @@
     // Set to true when process was launched with a wrapper attached
     private volatile boolean mUsingWrapper;
 
+    /** Non-null if this process may have a window. */
+    @Nullable
+    Session mWindowSession;
+
     // Thread currently set for VR scheduling
     int mVrThreadTid;
 
@@ -399,7 +403,7 @@
             // the latest configuration in their lifecycle callbacks (e.g. onReceive, onCreate).
             try {
                 // No WM lock here.
-                mAtm.getLifecycleManager().scheduleTransactionItemUnlocked(
+                mAtm.getLifecycleManager().scheduleTransactionItemNow(
                         thread, configurationChangeItem);
             } catch (Exception e) {
                 Slog.e(TAG_CONFIGURATION, "Failed to schedule ConfigurationChangeItem="
@@ -989,7 +993,7 @@
             if (packageName.equals(r.packageName)
                     && r.applyAppSpecificConfig(nightMode, localesOverride, gender)
                     && r.isVisibleRequested()) {
-                r.ensureActivityConfiguration(0 /* globalChanges */, true /* preserveWindow */);
+                r.ensureActivityConfiguration();
             }
         }
     }
@@ -1675,7 +1679,12 @@
     private void scheduleClientTransactionItem(@NonNull IApplicationThread thread,
             @NonNull ClientTransactionItem transactionItem) {
         try {
-            mAtm.getLifecycleManager().scheduleTransactionItem(thread, transactionItem);
+            if (mWindowSession != null && mWindowSession.hasWindow()) {
+                mAtm.getLifecycleManager().scheduleTransactionItem(thread, transactionItem);
+            } else {
+                // Non-UI process can handle the change directly.
+                mAtm.getLifecycleManager().scheduleTransactionItemNow(thread, transactionItem);
+            }
         } catch (DeadObjectException e) {
             // Expected if the process has been killed.
             Slog.w(TAG_CONFIGURATION, "Failed for dead process. ClientTransactionItem="
@@ -1723,7 +1732,7 @@
             overrideConfig.assetsSeq = assetSeq;
             r.onRequestedOverrideConfigurationChanged(overrideConfig);
             if (r.isVisibleRequested()) {
-                r.ensureActivityConfiguration(0, true);
+                r.ensureActivityConfiguration();
             }
         }
     }
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 32638e0..56f2bc3 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -252,6 +252,7 @@
 import com.android.server.wm.LocalAnimationAdapter.AnimationSpec;
 import com.android.server.wm.RefreshRatePolicy.FrameRateVote;
 import com.android.server.wm.SurfaceAnimator.AnimationType;
+import com.android.window.flags.Flags;
 
 import dalvik.annotation.optimization.NeverCompile;
 
@@ -1442,16 +1443,7 @@
                         this, mWindowFrames.getInsetsChangedInfo(),
                         configChanged, didFrameInsetsChange);
 
-            if (insetsChanged) {
-                mWindowFrames.setInsetsChanged(false);
-                if (mWmService.mWindowsInsetsChanged > 0) {
-                    mWmService.mWindowsInsetsChanged--;
-                }
-                if (mWmService.mWindowsInsetsChanged == 0) {
-                    mWmService.mH.removeMessages(WindowManagerService.H.INSETS_CHANGED);
-                }
-            }
-
+            consumeInsetsChange();
             onResizeHandled();
             mWmService.makeWindowFreezingScreenIfNeededLocked(this);
 
@@ -1904,6 +1896,14 @@
         if ((mAttrs.flags & WindowManager.LayoutParams.FLAG_SECURE) != 0) {
             return true;
         }
+
+        if (com.android.server.notification.Flags.sensitiveNotificationAppProtection()) {
+            if (mWmService.mSensitiveContentPackages
+                    .shouldBlockScreenCaptureForApp(getOwningPackage(), getOwningUid())) {
+                return true;
+            }
+        }
+
         return !DevicePolicyCache.getInstance().isScreenCaptureAllowed(mShowUserId);
     }
 
@@ -1937,9 +1937,6 @@
      * of a transition that has not yet been started.
      */
     boolean isReadyForDisplay() {
-        if (mToken.waitingToShow && getDisplayContent().mAppTransition.isTransitionSet()) {
-            return false;
-        }
         final boolean parentAndClientVisible = !isParentWindowHidden()
                 && mViewVisibility == View.VISIBLE && mToken.isVisible();
         return mHasSurface && isVisibleByPolicy() && !mDestroying
@@ -2348,6 +2345,8 @@
 
         mWmService.mTrustedPresentationListenerController.removeIgnoredWindowTokens(
                 getWindowToken());
+
+        consumeInsetsChange();
     }
 
     @Override
@@ -3675,7 +3674,7 @@
 
         markRedrawForSyncReported();
 
-        if (mWmService.mFlags.mWindowStateResizeItemFlag) {
+        if (Flags.bundleClientTransactionFlag()) {
             getProcess().scheduleClientTransactionItem(
                     WindowStateResizeItem.obtain(mClient, mClientWindowFrames, reportDraw,
                             mLastReportedConfiguration, getCompatInsetsState(), forceRelayout,
@@ -3721,6 +3720,16 @@
         return mClient instanceof IWindow.Stub;
     }
 
+    private void consumeInsetsChange() {
+        if (mWindowFrames.hasInsetsChanged()) {
+            mWindowFrames.setInsetsChanged(false);
+            mWmService.mWindowsInsetsChanged--;
+            if (mWmService.mWindowsInsetsChanged == 0) {
+                mWmService.mH.removeMessages(WindowManagerService.H.INSETS_CHANGED);
+            }
+        }
+    }
+
     /**
      * Called when the insets state changed.
      */
@@ -3728,10 +3737,10 @@
         ProtoLog.d(WM_DEBUG_WINDOW_INSETS, "notifyInsetsChanged for %s ", this);
         if (!mWindowFrames.hasInsetsChanged()) {
             mWindowFrames.setInsetsChanged(true);
+            mWmService.mWindowsInsetsChanged++;
 
             // If the new InsetsState won't be dispatched before releasing WM lock, the following
             // message will be executed.
-            mWmService.mWindowsInsetsChanged++;
             mWmService.mH.removeMessages(WindowManagerService.H.INSETS_CHANGED);
             mWmService.mH.sendEmptyMessage(WindowManagerService.H.INSETS_CHANGED);
         }
@@ -5637,12 +5646,6 @@
             // Skip sync for invisible app windows which are not managed by activity lifecycle.
             return false;
         }
-        if (mActivityRecord != null && mViewVisibility != View.VISIBLE
-                && mWinAnimator.mAttrType != TYPE_BASE_APPLICATION
-                && mWinAnimator.mAttrType != TYPE_APPLICATION_STARTING) {
-            // Skip sync for invisible app windows which are not managed by activity lifecycle.
-            return false;
-        }
         // In the WindowContainer implementation we immediately mark ready
         // since a generic WindowContainer only needs to wait for its
         // children to finish and is immediately ready from its own
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index 7d21dbf..5048cef 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -28,7 +28,6 @@
 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
 import static com.android.server.wm.WindowTokenProto.HASH_CODE;
 import static com.android.server.wm.WindowTokenProto.PAUSED;
-import static com.android.server.wm.WindowTokenProto.WAITING_TO_SHOW;
 import static com.android.server.wm.WindowTokenProto.WINDOW_CONTAINER;
 
 import android.annotation.CallSuper;
@@ -91,10 +90,6 @@
     // Is key dispatching paused for this token?
     boolean paused = false;
 
-    // Set to true when this token is in a pending transaction where it
-    // will be shown.
-    boolean waitingToShow;
-
     /** The owner has {@link android.Manifest.permission#MANAGE_APP_TOKENS} */
     final boolean mOwnerCanManageAppTokens;
 
@@ -702,7 +697,6 @@
         final long token = proto.start(fieldId);
         super.dumpDebug(proto, WINDOW_CONTAINER, logLevel);
         proto.write(HASH_CODE, System.identityHashCode(this));
-        proto.write(WAITING_TO_SHOW, waitingToShow);
         proto.write(PAUSED, paused);
         proto.end(token);
     }
@@ -716,9 +710,6 @@
         super.dump(pw, prefix, dumpAll);
         pw.print(prefix); pw.print("windows="); pw.println(mChildren);
         pw.print(prefix); pw.print("windowType="); pw.print(windowType);
-        if (waitingToShow) {
-            pw.print(" waitingToShow=true");
-        }
         pw.println();
         if (hasFixedRotationTransform()) {
             pw.print(prefix);
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index b19f3d8..775570c 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -37,6 +37,7 @@
         "com_android_server_adb_AdbDebuggingManager.cpp",
         "com_android_server_am_BatteryStatsService.cpp",
         "com_android_server_biometrics_SurfaceToNativeHandleConverter.cpp",
+        "com_android_server_BootReceiver.cpp",
         "com_android_server_ConsumerIrService.cpp",
         "com_android_server_companion_virtual_InputController.cpp",
         "com_android_server_devicepolicy_CryptoTestHelper.cpp",
@@ -79,6 +80,7 @@
         ":lib_cachedAppOptimizer_native",
         ":lib_gameManagerService_native",
         ":lib_oomConnection_native",
+        ":lib_anrTimer_native",
     ],
 
     include_dirs: [
@@ -93,6 +95,16 @@
     header_libs: [
         "bionic_libc_platform_headers",
     ],
+
+    static_libs: [
+        "libunwindstack",
+    ],
+
+    whole_static_libs: [
+        "libdebuggerd_tombstone_proto_to_text",
+    ],
+
+    runtime_libs: ["libdexfile"],
 }
 
 cc_defaults {
@@ -246,3 +258,10 @@
     name: "lib_oomConnection_native",
     srcs: ["com_android_server_am_OomConnection.cpp"],
 }
+
+filegroup {
+    name: "lib_anrTimer_native",
+    srcs: [
+        "com_android_server_utils_AnrTimer.cpp",
+    ],
+}
diff --git a/services/core/jni/OWNERS b/services/core/jni/OWNERS
index cc08488..15eb7c6 100644
--- a/services/core/jni/OWNERS
+++ b/services/core/jni/OWNERS
@@ -33,3 +33,7 @@
 
 # Bug component : 158088 = per-file *AnrTimer*
 per-file *AnrTimer* = file:/PERFORMANCE_OWNERS
+
+# Bug component : 158088 = per-file com_android_server_utils_AnrTimer*.java
+per-file com_android_server_utils_AnrTimer*.java = file:/PERFORMANCE_OWNERS
+per-file com_android_server_BootReceiver.cpp = file:/STABILITY_OWNERS
diff --git a/services/core/jni/com_android_server_BootReceiver.cpp b/services/core/jni/com_android_server_BootReceiver.cpp
new file mode 100644
index 0000000..3892d28
--- /dev/null
+++ b/services/core/jni/com_android_server_BootReceiver.cpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <libdebuggerd/tombstone.h>
+#include <nativehelper/JNIHelp.h>
+
+#include <sstream>
+
+#include "jni.h"
+#include "tombstone.pb.h"
+
+namespace android {
+
+static void writeToString(std::stringstream& ss, const std::string& line, bool should_log) {
+    ss << line << std::endl;
+}
+
+static jstring com_android_server_BootReceiver_getTombstoneText(JNIEnv* env, jobject,
+                                                                jbyteArray tombstoneBytes) {
+    Tombstone tombstone;
+    tombstone.ParseFromArray(env->GetByteArrayElements(tombstoneBytes, 0),
+                             env->GetArrayLength(tombstoneBytes));
+
+    std::stringstream tombstoneString;
+
+    tombstone_proto_to_text(tombstone,
+                            std::bind(&writeToString, std::ref(tombstoneString),
+                                      std::placeholders::_1, std::placeholders::_2));
+
+    return env->NewStringUTF(tombstoneString.str().c_str());
+}
+
+static const JNINativeMethod sMethods[] = {
+        /* name, signature, funcPtr */
+        {"getTombstoneText", "([B)Ljava/lang/String;",
+         (jstring*)com_android_server_BootReceiver_getTombstoneText},
+};
+
+int register_com_android_server_BootReceiver(JNIEnv* env) {
+    return jniRegisterNativeMethods(env, "com/android/server/BootReceiver", sMethods,
+                                    NELEM(sMethods));
+}
+
+} // namespace android
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 0dd0564..414339d 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -114,6 +114,7 @@
     jmethodID notifyFocusChanged;
     jmethodID notifySensorEvent;
     jmethodID notifySensorAccuracy;
+    jmethodID notifyStickyModifierStateChanged;
     jmethodID notifyStylusGestureStarted;
     jmethodID isInputMethodConnectionActive;
     jmethodID notifyVibratorState;
@@ -270,7 +271,8 @@
 class NativeInputManager : public virtual InputReaderPolicyInterface,
                            public virtual InputDispatcherPolicyInterface,
                            public virtual PointerControllerPolicyInterface,
-                           public virtual PointerChoreographerPolicyInterface {
+                           public virtual PointerChoreographerPolicyInterface,
+                           public virtual InputFilterPolicyInterface {
 protected:
     virtual ~NativeInputManager();
 
@@ -297,7 +299,7 @@
     void setSystemUiLightsOut(bool lightsOut);
     void setPointerDisplayId(int32_t displayId);
     void setPointerSpeed(int32_t speed);
-    void setPointerAcceleration(float acceleration);
+    void setMousePointerAccelerationEnabled(bool enabled);
     void setTouchpadPointerSpeed(int32_t speed);
     void setTouchpadNaturalScrollingEnabled(bool enabled);
     void setTouchpadTapToClickEnabled(bool enabled);
@@ -358,8 +360,8 @@
     void notifyVibratorState(int32_t deviceId, bool isOn) override;
     bool filterInputEvent(const InputEvent& inputEvent, uint32_t policyFlags) override;
     void interceptKeyBeforeQueueing(const KeyEvent& keyEvent, uint32_t& policyFlags) override;
-    void interceptMotionBeforeQueueing(int32_t displayId, nsecs_t when,
-                                       uint32_t& policyFlags) override;
+    void interceptMotionBeforeQueueing(int32_t displayId, uint32_t source, int32_t action,
+                                       nsecs_t when, uint32_t& policyFlags) override;
     nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>& token, const KeyEvent& keyEvent,
                                           uint32_t policyFlags) override;
     std::optional<KeyEvent> dispatchUnhandledKey(const sp<IBinder>& token, const KeyEvent& keyEvent,
@@ -388,6 +390,10 @@
             PointerControllerInterface::ControllerType type) override;
     void notifyPointerDisplayIdChanged(int32_t displayId, const FloatPoint& position) override;
 
+    /* --- InputFilterPolicyInterface implementation --- */
+    void notifyStickyModifierStateChanged(uint32_t modifierState,
+                                          uint32_t lockedModifierState) override;
+
 private:
     sp<InputManagerInterface> mInputManager;
 
@@ -405,8 +411,8 @@
         // Pointer speed.
         int32_t pointerSpeed{0};
 
-        // Pointer acceleration.
-        float pointerAcceleration{android::os::IInputConstants::DEFAULT_POINTER_ACCELERATION};
+        // True if pointer acceleration is enabled for mice.
+        bool mousePointerAccelerationEnabled{true};
 
         // True if pointer gestures are enabled.
         bool pointerGesturesEnabled{true};
@@ -477,7 +483,7 @@
 
     mServiceObj = env->NewGlobalRef(serviceObj);
 
-    InputManager* im = new InputManager(this, *this, *this);
+    InputManager* im = new InputManager(this, *this, *this, *this);
     mInputManager = im;
     defaultServiceManager()->addService(String16("inputflinger"), im);
 }
@@ -496,7 +502,8 @@
         dump += StringPrintf(INDENT "System UI Lights Out: %s\n",
                              toString(mLocked.systemUiLightsOut));
         dump += StringPrintf(INDENT "Pointer Speed: %" PRId32 "\n", mLocked.pointerSpeed);
-        dump += StringPrintf(INDENT "Pointer Acceleration: %0.3f\n", mLocked.pointerAcceleration);
+        dump += StringPrintf(INDENT "Mouse Pointer Acceleration: %s\n",
+                             mLocked.mousePointerAccelerationEnabled ? "Enabled" : "Disabled");
         dump += StringPrintf(INDENT "Pointer Gestures Enabled: %s\n",
                 toString(mLocked.pointerGesturesEnabled));
         dump += StringPrintf(INDENT "Show Touches: %s\n", toString(mLocked.showTouches));
@@ -680,7 +687,10 @@
 
         outConfig->pointerVelocityControlParameters.scale = exp2f(mLocked.pointerSpeed
                 * POINTER_SPEED_EXPONENT);
-        outConfig->pointerVelocityControlParameters.acceleration = mLocked.pointerAcceleration;
+        outConfig->pointerVelocityControlParameters.acceleration =
+                mLocked.mousePointerAccelerationEnabled
+                ? android::os::IInputConstants::DEFAULT_POINTER_ACCELERATION
+                : 1;
         outConfig->pointerGesturesEnabled = mLocked.pointerGesturesEnabled;
 
         outConfig->showTouches = mLocked.showTouches;
@@ -806,6 +816,14 @@
     checkAndClearExceptionFromCallback(env, "onPointerDisplayIdChanged");
 }
 
+void NativeInputManager::notifyStickyModifierStateChanged(uint32_t modifierState,
+                                                          uint32_t lockedModifierState) {
+    JNIEnv* env = jniEnv();
+    env->CallVoidMethod(mServiceObj, gServiceClassInfo.notifyStickyModifierStateChanged,
+                        modifierState, lockedModifierState);
+    checkAndClearExceptionFromCallback(env, "notifyStickyModifierStateChanged");
+}
+
 sp<SurfaceControl> NativeInputManager::getParentSurfaceForPointers(int displayId) {
     JNIEnv* env = jniEnv();
     jlong nativeSurfaceControlPtr =
@@ -1193,16 +1211,16 @@
             InputReaderConfiguration::Change::POINTER_SPEED);
 }
 
-void NativeInputManager::setPointerAcceleration(float acceleration) {
+void NativeInputManager::setMousePointerAccelerationEnabled(bool enabled) {
     { // acquire lock
         std::scoped_lock _l(mLock);
 
-        if (mLocked.pointerAcceleration == acceleration) {
+        if (mLocked.mousePointerAccelerationEnabled == enabled) {
             return;
         }
 
-        ALOGI("Setting pointer acceleration to %0.3f", acceleration);
-        mLocked.pointerAcceleration = acceleration;
+        ALOGI("Setting mouse pointer acceleration to %s", toString(enabled));
+        mLocked.mousePointerAccelerationEnabled = enabled;
     } // release lock
 
     mInputManager->getReader().requestRefreshConfiguration(
@@ -1496,7 +1514,8 @@
     handleInterceptActions(wmActions, when, /*byref*/ policyFlags);
 }
 
-void NativeInputManager::interceptMotionBeforeQueueing(int32_t displayId, nsecs_t when,
+void NativeInputManager::interceptMotionBeforeQueueing(int32_t displayId, uint32_t source,
+                                                       int32_t action, nsecs_t when,
                                                        uint32_t& policyFlags) {
     ATRACE_CALL();
     // Policy:
@@ -1525,7 +1544,7 @@
     const jint wmActions =
             env->CallIntMethod(mServiceObj,
                                gServiceClassInfo.interceptMotionBeforeQueueingNonInteractive,
-                               displayId, when, policyFlags);
+                               displayId, source, action, when, policyFlags);
     if (checkAndClearExceptionFromCallback(env, "interceptMotionBeforeQueueingNonInteractive")) {
         return;
     }
@@ -2159,10 +2178,11 @@
     im->setPointerSpeed(speed);
 }
 
-static void nativeSetPointerAcceleration(JNIEnv* env, jobject nativeImplObj, jfloat acceleration) {
+static void nativeSetMousePointerAccelerationEnabled(JNIEnv* env, jobject nativeImplObj,
+                                                     jboolean enabled) {
     NativeInputManager* im = getNativeInputManager(env, nativeImplObj);
 
-    im->setPointerAcceleration(acceleration);
+    im->setMousePointerAccelerationEnabled(enabled);
 }
 
 static void nativeSetTouchpadPointerSpeed(JNIEnv* env, jobject nativeImplObj, jint speed) {
@@ -2797,7 +2817,8 @@
          (void*)nativeTransferTouchFocus},
         {"transferTouch", "(Landroid/os/IBinder;I)Z", (void*)nativeTransferTouch},
         {"setPointerSpeed", "(I)V", (void*)nativeSetPointerSpeed},
-        {"setPointerAcceleration", "(F)V", (void*)nativeSetPointerAcceleration},
+        {"setMousePointerAccelerationEnabled", "(Z)V",
+         (void*)nativeSetMousePointerAccelerationEnabled},
         {"setTouchpadPointerSpeed", "(I)V", (void*)nativeSetTouchpadPointerSpeed},
         {"setTouchpadNaturalScrollingEnabled", "(Z)V",
          (void*)nativeSetTouchpadNaturalScrollingEnabled},
@@ -2943,7 +2964,7 @@
             "interceptKeyBeforeQueueing", "(Landroid/view/KeyEvent;I)I");
 
     GET_METHOD_ID(gServiceClassInfo.interceptMotionBeforeQueueingNonInteractive, clazz,
-            "interceptMotionBeforeQueueingNonInteractive", "(IJI)I");
+            "interceptMotionBeforeQueueingNonInteractive", "(IIIJI)I");
 
     GET_METHOD_ID(gServiceClassInfo.interceptKeyBeforeDispatching, clazz,
             "interceptKeyBeforeDispatching",
@@ -2956,6 +2977,9 @@
     GET_METHOD_ID(gServiceClassInfo.onPointerDisplayIdChanged, clazz, "onPointerDisplayIdChanged",
                   "(IFF)V");
 
+    GET_METHOD_ID(gServiceClassInfo.notifyStickyModifierStateChanged, clazz,
+                  "notifyStickyModifierStateChanged", "(II)V");
+
     GET_METHOD_ID(gServiceClassInfo.onPointerDownOutsideFocus, clazz,
             "onPointerDownOutsideFocus", "(Landroid/os/IBinder;)V");
 
diff --git a/services/core/jni/com_android_server_utils_AnrTimer.cpp b/services/core/jni/com_android_server_utils_AnrTimer.cpp
new file mode 100644
index 0000000..1e48ace
--- /dev/null
+++ b/services/core/jni/com_android_server_utils_AnrTimer.cpp
@@ -0,0 +1,920 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <time.h>
+#include <pthread.h>
+#include <sys/timerfd.h>
+#include <inttypes.h>
+
+#include <algorithm>
+#include <list>
+#include <memory>
+#include <set>
+#include <string>
+#include <vector>
+
+#define LOG_TAG "AnrTimerService"
+
+#include <jni.h>
+#include <nativehelper/JNIHelp.h>
+#include "android_runtime/AndroidRuntime.h"
+#include "core_jni_helpers.h"
+
+#include <utils/Mutex.h>
+#include <utils/Timers.h>
+
+#include <utils/Log.h>
+#include <utils/Timers.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
+
+using ::android::base::StringPrintf;
+
+
+// Native support is unavailable on WIN32 platforms.  This macro preemptively disables it.
+#ifdef _WIN32
+#define NATIVE_SUPPORT 0
+#else
+#define NATIVE_SUPPORT 1
+#endif
+
+namespace android {
+
+// using namespace android;
+
+// Almost nothing in this module needs to be in the android namespace.
+namespace {
+
+// If not on a Posix system, create stub timerfd methods.  These are defined to allow
+// compilation.  They are not functional.  Also, they do not leak outside this compilation unit.
+#ifdef _WIN32
+int timer_create() {
+  return -1;
+}
+int timer_settime(int, int, void const *, void *) {
+  return -1;
+}
+#else
+int timer_create() {
+  return timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC);
+}
+int timer_settime(int fd, int flags, const struct itimerspec *new_value,
+                  struct itimerspec *_Nullable old_value) {
+  return timerfd_settime(fd, flags, new_value, old_value);
+}
+#endif
+
+// A local debug flag that gates a set of log messages for debug only.  This is normally const
+// false so the debug statements are not included in the image.  The flag can be set true in a
+// unit test image to debug test failures.
+const bool DEBUG = false;
+
+// Return the current time in nanoseconds.  This time is relative to system boot.
+nsecs_t now() {
+    return systemTime(SYSTEM_TIME_MONOTONIC);
+}
+
+/**
+ * This class encapsulates the anr timer service.  The service manages a list of individual
+ * timers.  A timer is either Running or Expired.  Once started, a timer may be canceled or
+ * accepted.  Both actions collect statistics about the timer and then delete it.  An expired
+ * timer may also be discarded, which deletes the timer without collecting any statistics.
+ *
+ * All public methods in this class are thread-safe.
+ */
+class AnrTimerService {
+  private:
+    class ProcessStats;
+    class Timer;
+
+  public:
+
+    // The class that actually runs the clock.
+    class Ticker;
+
+    // A timer is identified by a timer_id_t.  Timer IDs are unique in the moment.
+    using timer_id_t = uint32_t;
+
+    // A manifest constant.  No timer is ever created with this ID.
+    static const timer_id_t NOTIMER = 0;
+
+    // A notifier is called with a timer ID, the timer's tag, and the client's cookie.  The pid
+    // and uid that were originally assigned to the timer are passed as well.
+    using notifier_t = bool (*)(timer_id_t, int pid, int uid, void* cookie, jweak object);
+
+    enum Status {
+        Invalid,
+        Running,
+        Expired,
+        Canceled
+    };
+
+    /**
+     * Create a timer service.  The service is initialized with a name used for logging.  The
+     * constructor is also given the notifier callback, and two cookies for the callback: the
+     * traditional void* and an int.
+     */
+    AnrTimerService(char const* label, notifier_t notifier, void* cookie, jweak jtimer, Ticker*);
+
+    // Delete the service and clean up memory.
+    ~AnrTimerService();
+
+    // Start a timer and return the associated timer ID.  It does not matter if the same pid/uid
+    // are already in the running list.  Once start() is called, one of cancel(), accept(), or
+    // discard() must be called to clean up the internal data structures.
+    timer_id_t start(int pid, int uid, nsecs_t timeout, bool extend);
+
+    // Cancel a timer and remove it from all lists.  This is called when the event being timed
+    // has occurred.  If the timer was Running, the function returns true.  The other
+    // possibilities are that the timer was Expired or non-existent; in both cases, the function
+    // returns false.
+    bool cancel(timer_id_t timerId);
+
+    // Accept a timer and remove it from all lists.  This is called when the upper layers accept
+    // that a timer has expired.  If the timer was Expired, the function returns true.  The
+    // other possibilities are tha the timer was Running or non-existing; in both cases, the
+    // function returns false.
+    bool accept(timer_id_t timerId);
+
+    // Discard a timer without collecting any statistics.  This is called when the upper layers
+    // recognize that a timer expired but decide the expiration is not significant.  If the
+    // timer was Expired, the function returns true.  The other possibilities are tha the timer
+    // was Running or non-existing; in both cases, the function returns false.
+    bool discard(timer_id_t timerId);
+
+    // A timer has expired.
+    void expire(timer_id_t);
+
+    // Dump a small amount of state to the log file.
+    void dump(bool verbose) const;
+
+    // Return the Java object associated with this instance.
+    jweak jtimer() const {
+        return notifierObject_;
+    }
+
+  private:
+    // The service cannot be copied.
+    AnrTimerService(AnrTimerService const &) = delete;
+
+    // Insert a timer into the running list.  The lock must be held by the caller.
+    void insert(const Timer&);
+
+    // Remove a timer from the lists and return it. The lock must be held by the caller.
+    Timer remove(timer_id_t timerId);
+
+    // Return a string representation of a status value.
+    static char const *statusString(Status);
+
+    // The name of this service, for logging.
+    std::string const label_;
+
+    // The callback that is invoked when a timer expires.
+    notifier_t const notifier_;
+
+    // The two cookies passed to the notifier.
+    void* notifierCookie_;
+    jweak notifierObject_;
+
+    // The global lock
+    mutable Mutex lock_;
+
+    // The list of all timers that are still running.  This is sorted by ID for fast lookup.
+    std::set<Timer> running_;
+
+    // The maximum number of active timers.
+    size_t maxActive_;
+
+    // Simple counters
+    struct Counters {
+        // The number of timers started, canceled, accepted, discarded, and expired.
+        size_t started;
+        size_t canceled;
+        size_t accepted;
+        size_t discarded;
+        size_t expired;
+
+        // The number of times there were zero active timers.
+        size_t drained;
+
+        // The number of times a protocol error was seen.
+        size_t error;
+    };
+
+    Counters counters_;
+
+    // The clock used by this AnrTimerService.
+    Ticker *ticker_;
+};
+
+class AnrTimerService::ProcessStats {
+  public:
+    nsecs_t cpu_time;
+    nsecs_t cpu_delay;
+
+    ProcessStats() :
+            cpu_time(0),
+            cpu_delay(0) {
+    }
+
+    // Collect all statistics for a process.  Return true if the fill succeeded and false if it
+    // did not.  If there is any problem, the statistics are zeroed.
+    bool fill(int pid) {
+        cpu_time = 0;
+        cpu_delay = 0;
+
+        char path[PATH_MAX];
+        snprintf(path, sizeof(path), "/proc/%u/schedstat", pid);
+        ::android::base::unique_fd fd(open(path, O_RDONLY | O_CLOEXEC));
+        if (!fd.ok()) {
+            return false;
+        }
+        char buffer[128];
+        ssize_t len = read(fd, buffer, sizeof(buffer));
+        if (len <= 0) {
+            return false;
+        }
+        if (len >= sizeof(buffer)) {
+            ALOGE("proc file too big: %s", path);
+            return false;
+        }
+        buffer[len] = 0;
+        unsigned long t1;
+        unsigned long t2;
+        if (sscanf(buffer, "%lu %lu", &t1, &t2) != 2) {
+            return false;
+        }
+        cpu_time = t1;
+        cpu_delay = t2;
+        return true;
+    }
+};
+
+class AnrTimerService::Timer {
+  public:
+    // A unique ID assigned when the Timer is created.
+    timer_id_t const id;
+
+    // The creation parameters.  The timeout is the original, relative timeout.
+    int const pid;
+    int const uid;
+    nsecs_t const timeout;
+    bool const extend;
+
+    // The state of this timer.
+    Status status;
+
+    // The scheduled timeout.  This is an absolute time.  It may be extended.
+    nsecs_t scheduled;
+
+    // True if this timer has been extended.
+    bool extended;
+
+    // Bookkeeping for extensions.  The initial state of the process.  This is collected only if
+    // the timer is extensible.
+    ProcessStats initial;
+
+    // The default constructor is used to create timers that are Invalid, representing the "not
+    // found" condition when a collection is searched.
+    Timer() :
+            id(NOTIMER),
+            pid(0),
+            uid(0),
+            timeout(0),
+            extend(false),
+            status(Invalid),
+            scheduled(0),
+            extended(false) {
+    }
+
+    // This constructor creates a timer with the specified id.  This can be used as the argument
+    // to find().
+    Timer(timer_id_t id) :
+            id(id),
+            pid(0),
+            uid(0),
+            timeout(0),
+            extend(false),
+            status(Invalid),
+            scheduled(0),
+            extended(false) {
+    }
+
+    // Create a new timer.  This starts the timer.
+    Timer(int pid, int uid, nsecs_t timeout, bool extend) :
+            id(nextId()),
+            pid(pid),
+            uid(uid),
+            timeout(timeout),
+            extend(extend),
+            status(Running),
+            scheduled(now() + timeout),
+            extended(false) {
+        if (extend && pid != 0) {
+            initial.fill(pid);
+        }
+    }
+
+    // Cancel a timer.  Return the headroom (which may be negative).  This does not, as yet,
+    // account for extensions.
+    void cancel() {
+        ALOGW_IF(DEBUG && status != Running, "cancel %s", toString().c_str());
+        status = Canceled;
+    }
+
+    // Expire a timer. Return true if the timer is expired and false otherwise.  The function
+    // returns false if the timer is eligible for extension.  If the function returns false, the
+    // scheduled time is updated.
+    bool expire() {
+        ALOGI_IF(DEBUG, "expire %s", toString().c_str());
+        nsecs_t extension = 0;
+        if (extend && !extended) {
+            // Only one extension is permitted.
+            extended = true;
+            ProcessStats current;
+            current.fill(pid);
+            extension = current.cpu_delay - initial.cpu_delay;
+            if (extension < 0) extension = 0;
+            if (extension > timeout) extension = timeout;
+        }
+        if (extension == 0) {
+            status = Expired;
+        } else {
+            scheduled += extension;
+        }
+        return status == Expired;
+    }
+
+    // Accept a timeout.
+    void accept() {
+    }
+
+    // Discard a timeout.
+    void discard() {
+    }
+
+    // Timers are sorted by id, which is unique.  This provides fast lookups.
+    bool operator<(Timer const &r) const {
+        return id < r.id;
+    }
+
+    bool operator==(timer_id_t r) const {
+        return id == r;
+    }
+
+    std::string toString() const {
+        return StringPrintf("timer id=%d pid=%d status=%s", id, pid, statusString(status));
+    }
+
+    std::string toString(nsecs_t now) const {
+        uint32_t ms = nanoseconds_to_milliseconds(now - scheduled);
+        return StringPrintf("timer id=%d pid=%d status=%s scheduled=%ums",
+                            id, pid, statusString(status), -ms);
+    }
+
+    static int maxId() {
+        return idGen;
+    }
+
+  private:
+    // Get the next free ID.  NOTIMER is never returned.
+    static timer_id_t nextId() {
+        timer_id_t id = idGen.fetch_add(1);
+        while (id == NOTIMER) {
+            id = idGen.fetch_add(1);
+        }
+        return id;
+    }
+
+    // IDs start at 1.  A zero ID is invalid.
+    static std::atomic<timer_id_t> idGen;
+};
+
+// IDs start at 1.
+std::atomic<AnrTimerService::timer_id_t> AnrTimerService::Timer::idGen(1);
+
+/**
+ * Manage a set of timers and notify clients when there is a timeout.
+ */
+class AnrTimerService::Ticker {
+  private:
+    struct Entry {
+        const nsecs_t scheduled;
+        const timer_id_t id;
+        AnrTimerService* const service;
+
+        Entry(nsecs_t scheduled, timer_id_t id, AnrTimerService* service) :
+                scheduled(scheduled), id(id), service(service) {};
+
+        bool operator<(const Entry &r) const {
+            return scheduled == r.scheduled ? id < r.id : scheduled < r.scheduled;
+        }
+    };
+
+  public:
+
+    // Construct the ticker.  This creates the timerfd file descriptor and starts the monitor
+    // thread.  The monitor thread is given a unique name.
+    Ticker() {
+        timerFd_ = timer_create();
+        if (timerFd_ < 0) {
+            ALOGE("failed to create timerFd: %s", strerror(errno));
+            return;
+        }
+
+        if (pthread_create(&watcher_, 0, run, this) != 0) {
+            ALOGE("failed to start thread: %s", strerror(errno));
+            watcher_ = 0;
+            ::close(timerFd_);
+            return;
+        }
+
+        // 16 is a magic number from the kernel.  Thread names may not be longer than this many
+        // bytes, including the terminating null.  The snprintf() method will truncate properly.
+        char name[16];
+        snprintf(name, sizeof(name), "AnrTimerService");
+        pthread_setname_np(watcher_, name);
+
+        ready_ = true;
+    }
+
+    ~Ticker() {
+        // Closing the file descriptor will close the monitor process, if any.
+        if (timerFd_ >= 0) ::close(timerFd_);
+        timerFd_ = -1;
+        watcher_ = 0;
+    }
+
+    // Insert a timer.  Unless canceled, the timer will expire at the scheduled time.  If it
+    // expires, the service will be notified with the id.
+    void insert(nsecs_t scheduled, timer_id_t id, AnrTimerService *service) {
+        Entry e(scheduled, id, service);
+        AutoMutex _l(lock_);
+        timer_id_t front = headTimerId();
+        running_.insert(e);
+        if (front != headTimerId()) restartLocked();
+        maxRunning_ = std::max(maxRunning_, running_.size());
+    }
+
+    // Remove a timer.  The timer is identified by its scheduled timeout and id.  Technically,
+    // the id is sufficient (because timer IDs are unique) but using the timeout is more
+    // efficient.
+    void remove(nsecs_t scheduled, timer_id_t id) {
+        Entry key(scheduled, id, 0);
+        AutoMutex _l(lock_);
+        timer_id_t front = headTimerId();
+        auto found = running_.find(key);
+        if (found != running_.end()) running_.erase(found);
+        if (front != headTimerId()) restartLocked();
+    }
+
+    // Remove every timer associated with the service.
+    void remove(AnrTimerService const* service) {
+        AutoMutex _l(lock_);
+        timer_id_t front = headTimerId();
+        for (auto i = running_.begin(); i != running_.end(); ) {
+            if (i->service == service) {
+                i = running_.erase(i);
+            } else {
+                i++;
+            }
+        }
+        if (front != headTimerId()) restartLocked();
+    }
+
+    // Return the number of timers still running.
+    size_t running() const {
+        AutoMutex _l(lock_);
+        return running_.size();
+    }
+
+    // Return the high-water mark of timers running.
+    size_t maxRunning() const {
+        AutoMutex _l(lock_);
+        return maxRunning_;
+    }
+
+  private:
+
+    // Return the head of the running list.  The lock must be held by the caller.
+    timer_id_t headTimerId() {
+        return running_.empty() ? NOTIMER : running_.cbegin()->id;
+    }
+
+    // A simple wrapper that meets the requirements of pthread_create.
+    static void* run(void* arg) {
+        reinterpret_cast<Ticker*>(arg)->monitor();
+        ALOGI("monitor exited");
+        return 0;
+    }
+
+    // Loop (almost) forever.  Whenever the timerfd expires, expire as many entries as
+    // possible.  The loop terminates when the read fails; this generally indicates that the
+    // file descriptor has been closed and the thread can exit.
+    void monitor() {
+        uint64_t token = 0;
+        while (read(timerFd_, &token, sizeof(token)) == sizeof(token)) {
+            // Move expired timers into the local ready list.  This is done inside
+            // the lock.  Then, outside the lock, expire them.
+            nsecs_t current = now();
+            std::vector<Entry> ready;
+            {
+                AutoMutex _l(lock_);
+                while (!running_.empty()) {
+                    Entry timer = *(running_.begin());
+                    if (timer.scheduled <= current) {
+                        ready.push_back(timer);
+                        running_.erase(running_.cbegin());
+                    } else {
+                        break;
+                    }
+                }
+                restartLocked();
+            }
+            // Call the notifiers outside the lock.  Calling the notifiers with the lock held
+            // can lead to deadlock, if the Java-side handler also takes a lock.  Note that the
+            // timerfd is already running.
+            for (auto i = ready.begin(); i != ready.end(); i++) {
+                Entry e = *i;
+                e.service->expire(e.id);
+            }
+        }
+    }
+
+    // Restart the ticker.  The caller must be holding the lock.  This method updates the
+    // timerFd_ to expire at the time of the first Entry in the running list.  This method does
+    // not check to see if the currently programmed expiration time is different from the
+    // scheduled expiration time of the first entry.
+    void restartLocked() {
+        if (!running_.empty()) {
+            Entry const x = *(running_.cbegin());
+            nsecs_t delay = x.scheduled - now();
+            // Force a minimum timeout of 10ns.
+            if (delay < 10) delay = 10;
+            time_t sec = nanoseconds_to_seconds(delay);
+            time_t ns = delay - seconds_to_nanoseconds(sec);
+            struct itimerspec setting = {
+                .it_interval = { 0, 0 },
+                .it_value = { sec, ns },
+            };
+            timer_settime(timerFd_, 0, &setting, nullptr);
+            restarted_++;
+            ALOGI_IF(DEBUG, "restarted timerfd for %ld.%09ld", sec, ns);
+        } else {
+            const struct itimerspec setting = {
+                .it_interval = { 0, 0 },
+                .it_value = { 0, 0 },
+            };
+            timer_settime(timerFd_, 0, &setting, nullptr);
+            drained_++;
+            ALOGI_IF(DEBUG, "drained timer list");
+        }
+    }
+
+    // The usual lock.
+    mutable Mutex lock_;
+
+    // True if the object was initialized properly.  Android does not support throwing C++
+    // exceptions, so clients should check this flag after constructing the object.  This is
+    // effectively const after the instance has been created.
+    bool ready_ = false;
+
+    // The file descriptor of the timer.
+    int timerFd_ = -1;
+
+    // The thread that monitors the timer.
+    pthread_t watcher_ = 0;
+
+    // The number of times the timer was restarted.
+    size_t restarted_ = 0;
+
+    // The number of times the timer list was exhausted.
+    size_t drained_ = 0;
+
+    // The highwater mark of timers that are running.
+    size_t maxRunning_ = 0;
+
+    // The list of timers that are scheduled.  This set is sorted by timeout and then by timer
+    // ID.  A set is sufficient (as opposed to a multiset) because timer IDs are unique.
+    std::set<Entry> running_;
+};
+
+
+AnrTimerService::AnrTimerService(char const* label,
+            notifier_t notifier, void* cookie, jweak jtimer, Ticker* ticker) :
+        label_(label),
+        notifier_(notifier),
+        notifierCookie_(cookie),
+        notifierObject_(jtimer),
+        ticker_(ticker) {
+
+    // Zero the statistics
+    maxActive_ = 0;
+    memset(&counters_, 0, sizeof(counters_));
+
+    ALOGI_IF(DEBUG, "initialized %s", label);
+}
+
+AnrTimerService::~AnrTimerService() {
+    AutoMutex _l(lock_);
+    ticker_->remove(this);
+}
+
+char const *AnrTimerService::statusString(Status s) {
+    switch (s) {
+        case Invalid: return "invalid";
+        case Running: return "running";
+        case Expired: return "expired";
+        case Canceled: return "canceled";
+    }
+    return "unknown";
+}
+
+AnrTimerService::timer_id_t AnrTimerService::start(int pid, int uid,
+        nsecs_t timeout, bool extend) {
+    ALOGI_IF(DEBUG, "starting");
+    AutoMutex _l(lock_);
+    Timer t(pid, uid, timeout, extend);
+    insert(t);
+    counters_.started++;
+
+    ALOGI_IF(DEBUG, "started timer %u timeout=%zu", t.id, static_cast<size_t>(timeout));
+    return t.id;
+}
+
+bool AnrTimerService::cancel(timer_id_t timerId) {
+    ALOGI_IF(DEBUG, "canceling %u", timerId);
+    if (timerId == NOTIMER) return false;
+    AutoMutex _l(lock_);
+    Timer timer = remove(timerId);
+
+    bool result = timer.status == Running;
+    if (timer.status != Invalid) {
+        timer.cancel();
+    } else {
+        counters_.error++;
+    }
+    counters_.canceled++;
+    ALOGI_IF(DEBUG, "canceled timer %u", timerId);
+    return result;
+}
+
+bool AnrTimerService::accept(timer_id_t timerId) {
+    ALOGI_IF(DEBUG, "accepting %u", timerId);
+    if (timerId == NOTIMER) return false;
+    AutoMutex _l(lock_);
+    Timer timer = remove(timerId);
+
+    bool result = timer.status == Expired;
+    if (timer.status == Expired) {
+        timer.accept();
+    } else {
+        counters_.error++;
+    }
+    counters_.accepted++;
+    ALOGI_IF(DEBUG, "accepted timer %u", timerId);
+    return result;
+}
+
+bool AnrTimerService::discard(timer_id_t timerId) {
+    ALOGI_IF(DEBUG, "discarding %u", timerId);
+    if (timerId == NOTIMER) return false;
+    AutoMutex _l(lock_);
+    Timer timer = remove(timerId);
+
+    bool result = timer.status == Expired;
+    if (timer.status == Expired) {
+        timer.discard();
+    } else {
+        counters_.error++;
+    }
+    counters_.discarded++;
+    ALOGI_IF(DEBUG, "discarded timer %u", timerId);
+    return result;
+}
+
+// Hold the lock in order to manage the running list.
+// the listener.
+void AnrTimerService::expire(timer_id_t timerId) {
+    ALOGI_IF(DEBUG, "expiring %u", timerId);
+    // Save the timer attributes for the notification
+    int pid = 0;
+    int uid = 0;
+    bool expired = false;
+    {
+        AutoMutex _l(lock_);
+        Timer t = remove(timerId);
+        expired = t.expire();
+        if (t.status == Invalid) {
+            ALOGW_IF(DEBUG, "error: expired invalid timer %u", timerId);
+            return;
+        } else {
+            // The timer is either Running (because it was extended) or expired (and is awaiting an
+            // accept or discard).
+            insert(t);
+        }
+    }
+
+    // Deliver the notification outside of the lock.
+    if (expired) {
+        if (!notifier_(timerId, pid, uid, notifierCookie_, notifierObject_)) {
+            AutoMutex _l(lock_);
+            // Notification failed, which means the listener will never call accept() or
+            // discard().  Do not reinsert the timer.
+            remove(timerId);
+        }
+    }
+    ALOGI_IF(DEBUG, "expired timer %u", timerId);
+}
+
+void AnrTimerService::insert(const Timer& t) {
+    running_.insert(t);
+    if (t.status == Running) {
+        // Only forward running timers to the ticker.  Expired timers are handled separately.
+        ticker_->insert(t.scheduled, t.id, this);
+        maxActive_ = std::max(maxActive_, running_.size());
+    }
+}
+
+AnrTimerService::Timer AnrTimerService::remove(timer_id_t timerId) {
+    Timer key(timerId);
+    auto found = running_.find(key);
+    if (found != running_.end()) {
+        Timer result = *found;
+        running_.erase(found);
+        ticker_->remove(result.scheduled, result.id);
+        return result;
+    }
+    return Timer();
+}
+
+void AnrTimerService::dump(bool verbose) const {
+    AutoMutex _l(lock_);
+    ALOGI("timer %s ops started=%zu canceled=%zu accepted=%zu discarded=%zu expired=%zu",
+          label_.c_str(),
+          counters_.started, counters_.canceled, counters_.accepted,
+          counters_.discarded, counters_.expired);
+    ALOGI("timer %s stats max-active=%zu/%zu running=%zu/%zu errors=%zu",
+          label_.c_str(),
+          maxActive_, ticker_->maxRunning(), running_.size(), ticker_->running(),
+          counters_.error);
+
+    if (verbose) {
+        nsecs_t time = now();
+        for (auto i = running_.begin(); i != running_.end(); i++) {
+            Timer t = *i;
+            ALOGI("   running %s", t.toString(time).c_str());
+        }
+    }
+}
+
+/**
+ * True if the native methods are supported in this process.  Native methods are supported only
+ * if the initialization succeeds.
+ */
+bool nativeSupportEnabled = false;
+
+/**
+ * Singleton/globals for the anr timer.  Among other things, this includes a Ticker* and a use
+ * count.  The JNI layer creates a single Ticker for all operational AnrTimers.  The Ticker is
+ * created when the first AnrTimer is created, and is deleted when the last AnrTimer is closed.
+ */
+static Mutex gAnrLock;
+struct AnrArgs {
+    jclass clazz = NULL;
+    jmethodID func = NULL;
+    JavaVM* vm = NULL;
+    AnrTimerService::Ticker* ticker = nullptr;
+    int tickerUseCount = 0;;
+};
+static AnrArgs gAnrArgs;
+
+// The cookie is the address of the AnrArgs object to which the notification should be sent.
+static bool anrNotify(AnrTimerService::timer_id_t timerId, int pid, int uid,
+                      void* cookie, jweak jtimer) {
+    AutoMutex _l(gAnrLock);
+    AnrArgs* target = reinterpret_cast<AnrArgs* >(cookie);
+    JNIEnv *env;
+    if (target->vm->AttachCurrentThread(&env, 0) != JNI_OK) {
+        ALOGE("failed to attach thread to JavaVM");
+        return false;
+    }
+    jboolean r = false;
+    jobject timer = env->NewGlobalRef(jtimer);
+    if (timer != nullptr) {
+        r = env->CallBooleanMethod(timer, target->func, timerId, pid, uid);
+        env->DeleteGlobalRef(timer);
+    }
+    target->vm->DetachCurrentThread();
+    return r;
+}
+
+jboolean anrTimerSupported(JNIEnv* env, jclass) {
+    return nativeSupportEnabled;
+}
+
+jlong anrTimerCreate(JNIEnv* env, jobject jtimer, jstring jname) {
+    if (!nativeSupportEnabled) return 0;
+    AutoMutex _l(gAnrLock);
+    if (!gAnrArgs.ticker) {
+        gAnrArgs.ticker = new AnrTimerService::Ticker();
+    }
+    gAnrArgs.tickerUseCount++;
+
+    ScopedUtfChars name(env, jname);
+    jobject timer = env->NewWeakGlobalRef(jtimer);
+    AnrTimerService* service =
+            new AnrTimerService(name.c_str(), anrNotify, &gAnrArgs, timer, gAnrArgs.ticker);
+    return reinterpret_cast<jlong>(service);
+}
+
+AnrTimerService *toService(jlong pointer) {
+    return reinterpret_cast<AnrTimerService*>(pointer);
+}
+
+jint anrTimerClose(JNIEnv* env, jclass, jlong ptr) {
+    if (!nativeSupportEnabled) return -1;
+    if (ptr == 0) return -1;
+    AutoMutex _l(gAnrLock);
+    AnrTimerService *s = toService(ptr);
+    env->DeleteWeakGlobalRef(s->jtimer());
+    delete s;
+    if (--gAnrArgs.tickerUseCount <= 0) {
+        delete gAnrArgs.ticker;
+        gAnrArgs.ticker = nullptr;
+    }
+    return 0;
+}
+
+jint anrTimerStart(JNIEnv* env, jclass, jlong ptr,
+        jint pid, jint uid, jlong timeout, jboolean extend) {
+    if (!nativeSupportEnabled) return 0;
+    // On the Java side, timeouts are expressed in milliseconds and must be converted to
+    // nanoseconds before being passed to the library code.
+    return toService(ptr)->start(pid, uid, milliseconds_to_nanoseconds(timeout), extend);
+}
+
+jboolean anrTimerCancel(JNIEnv* env, jclass, jlong ptr, jint timerId) {
+    if (!nativeSupportEnabled) return false;
+    return toService(ptr)->cancel(timerId);
+}
+
+jboolean anrTimerAccept(JNIEnv* env, jclass, jlong ptr, jint timerId) {
+    if (!nativeSupportEnabled) return false;
+    return toService(ptr)->accept(timerId);
+}
+
+jboolean anrTimerDiscard(JNIEnv* env, jclass, jlong ptr, jint timerId) {
+    if (!nativeSupportEnabled) return false;
+    return toService(ptr)->discard(timerId);
+}
+
+jint anrTimerDump(JNIEnv *env, jclass, jlong ptr, jboolean verbose) {
+    if (!nativeSupportEnabled) return -1;
+    toService(ptr)->dump(verbose);
+    return 0;
+}
+
+static const JNINativeMethod methods[] = {
+    {"nativeAnrTimerSupported", "()Z",  (void*) anrTimerSupported},
+    {"nativeAnrTimerCreate", "(Ljava/lang/String;)J", (void*) anrTimerCreate},
+    {"nativeAnrTimerClose", "(J)I",     (void*) anrTimerClose},
+    {"nativeAnrTimerStart", "(JIIJZ)I", (void*) anrTimerStart},
+    {"nativeAnrTimerCancel", "(JI)Z",   (void*) anrTimerCancel},
+    {"nativeAnrTimerAccept", "(JI)Z",   (void*) anrTimerAccept},
+    {"nativeAnrTimerDiscard", "(JI)Z",  (void*) anrTimerDiscard},
+    {"nativeAnrTimerDump", "(JZ)V",     (void*) anrTimerDump},
+};
+
+} // anonymous namespace
+
+int register_android_server_utils_AnrTimer(JNIEnv* env)
+{
+    static const char *className = "com/android/server/utils/AnrTimer";
+    jniRegisterNativeMethods(env, className, methods, NELEM(methods));
+
+    jclass service = FindClassOrDie(env, className);
+    gAnrArgs.clazz = MakeGlobalRefOrDie(env, service);
+    gAnrArgs.func = env->GetMethodID(gAnrArgs.clazz, "expire", "(III)Z");
+    env->GetJavaVM(&gAnrArgs.vm);
+
+    nativeSupportEnabled = NATIVE_SUPPORT;
+
+    return 0;
+}
+
+} // namespace android
diff --git a/services/core/jni/gnss/Android.bp b/services/core/jni/gnss/Android.bp
index f3ba484f62..e72259f 100644
--- a/services/core/jni/gnss/Android.bp
+++ b/services/core/jni/gnss/Android.bp
@@ -68,5 +68,6 @@
         "android.hardware.gnss@2.1",
         "android.hardware.gnss.measurement_corrections@1.0",
         "android.hardware.gnss.visibility_control@1.0",
+        "android_location_flags_c_lib",
     ],
 }
diff --git a/services/core/jni/gnss/Utils.cpp b/services/core/jni/gnss/Utils.cpp
index 571534f..274e3b7 100644
--- a/services/core/jni/gnss/Utils.cpp
+++ b/services/core/jni/gnss/Utils.cpp
@@ -20,6 +20,7 @@
 
 #include <android/hardware/gnss/1.0/IGnss.h>
 #include <android/hardware/gnss/2.0/IGnss.h>
+#include <android_location_flags.h>
 #include <utils/SystemClock.h>
 /*
  * Save a pointer to JavaVm to attach/detach threads executing
@@ -27,6 +28,8 @@
  */
 JavaVM* android::ScopedJniThreadAttach::sJvm;
 
+namespace location_flags = android::location::flags;
+
 namespace android {
 
 namespace {
@@ -194,7 +197,12 @@
 
     flags = static_cast<uint32_t>(location.elapsedRealtime.flags);
     if (flags & android::hardware::gnss::ElapsedRealtime::HAS_TIMESTAMP_NS) {
-        SET(ElapsedRealtimeNanos, location.elapsedRealtime.timestampNs);
+        if (location_flags::replace_future_elapsed_realtime_jni() &&
+            location.elapsedRealtime.timestampNs > android::elapsedRealtimeNano()) {
+            SET(ElapsedRealtimeNanos, android::elapsedRealtimeNano());
+        } else {
+            SET(ElapsedRealtimeNanos, location.elapsedRealtime.timestampNs);
+        }
     } else {
         SET(ElapsedRealtimeNanos, android::elapsedRealtimeNano());
     }
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index 11734da..a4b1f84 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -55,6 +55,7 @@
 int register_android_server_am_OomConnection(JNIEnv* env);
 int register_android_server_am_CachedAppOptimizer(JNIEnv* env);
 int register_android_server_am_LowMemDetector(JNIEnv* env);
+int register_android_server_utils_AnrTimer(JNIEnv *env);
 int register_com_android_server_soundtrigger_middleware_AudioSessionProviderImpl(JNIEnv* env);
 int register_com_android_server_soundtrigger_middleware_ExternalCaptureStateTracker(JNIEnv* env);
 int register_android_server_com_android_server_pm_PackageManagerShellCommandDataLoader(JNIEnv* env);
@@ -65,6 +66,7 @@
 int register_android_server_sensor_SensorService(JavaVM* vm, JNIEnv* env);
 int register_android_server_companion_virtual_InputController(JNIEnv* env);
 int register_android_server_app_GameManagerService(JNIEnv* env);
+int register_com_android_server_BootReceiver(JNIEnv* env);
 int register_com_android_server_wm_TaskFpsCallbackController(JNIEnv* env);
 int register_com_android_server_display_DisplayControl(JNIEnv* env);
 int register_com_android_server_SystemClockTime(JNIEnv* env);
@@ -116,6 +118,7 @@
     register_android_server_am_OomConnection(env);
     register_android_server_am_CachedAppOptimizer(env);
     register_android_server_am_LowMemDetector(env);
+    register_android_server_utils_AnrTimer(env);
     register_com_android_server_soundtrigger_middleware_AudioSessionProviderImpl(env);
     register_com_android_server_soundtrigger_middleware_ExternalCaptureStateTracker(env);
     register_android_server_com_android_server_pm_PackageManagerShellCommandDataLoader(env);
@@ -126,6 +129,7 @@
     register_android_server_sensor_SensorService(vm, env);
     register_android_server_companion_virtual_InputController(env);
     register_android_server_app_GameManagerService(env);
+    register_com_android_server_BootReceiver(env);
     register_com_android_server_wm_TaskFpsCallbackController(env);
     register_com_android_server_display_DisplayControl(env);
     register_com_android_server_SystemClockTime(env);
diff --git a/services/core/lint-baseline.xml b/services/core/lint-baseline.xml
index 070bd4b..2ccd1e4 100644
--- a/services/core/lint-baseline.xml
+++ b/services/core/lint-baseline.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="5" by="lint 7.2.0-dev">
+<issues format="6" by="lint 8.4.0-alpha01" type="baseline" client="" dependencies="true" name="" variant="all" version="8.4.0-alpha01">
 
     <issue
         id="NonUserGetterCalled"
@@ -145,4 +145,4 @@
             line="7158"/>
     </issue>
 
-</issues>
+</issues>
\ No newline at end of file
diff --git a/services/core/xsd/display-layout-config/display-layout-config.xsd b/services/core/xsd/display-layout-config/display-layout-config.xsd
index 4e95465..73c13ce 100644
--- a/services/core/xsd/display-layout-config/display-layout-config.xsd
+++ b/services/core/xsd/display-layout-config/display-layout-config.xsd
@@ -49,7 +49,7 @@
 
     <xs:complexType name="display">
         <xs:sequence>
-            <xs:element name="address" type="xs:nonNegativeInteger"/>
+            <xs:group ref="displayReference" minOccurs="1" maxOccurs="1"/>
             <xs:element name="position" type="xs:string" minOccurs="0" maxOccurs="1" />
             <xs:element name="brightnessThrottlingMapId" type="xs:string" minOccurs="0" maxOccurs="1" />
             <xs:element name="powerThrottlingMapId" type="xs:string" minOccurs="0" maxOccurs="1" />
@@ -67,4 +67,11 @@
             </xs:simpleType>
         </xs:attribute>
     </xs:complexType>
+
+    <xs:group name="displayReference">
+        <xs:choice>
+            <xs:element name="address" type="xs:nonNegativeInteger" minOccurs="0"/>
+            <xs:element name="port" type="xs:nonNegativeInteger" minOccurs="0"/>
+        </xs:choice>
+    </xs:group>
 </xs:schema>
diff --git a/services/core/xsd/display-layout-config/schema/current.txt b/services/core/xsd/display-layout-config/schema/current.txt
index 195cae5..0d2f6a8 100644
--- a/services/core/xsd/display-layout-config/schema/current.txt
+++ b/services/core/xsd/display-layout-config/schema/current.txt
@@ -3,22 +3,24 @@
 
   public class Display {
     ctor public Display();
-    method public java.math.BigInteger getAddress();
+    method public java.math.BigInteger getAddress_optional();
     method public String getBrightnessThrottlingMapId();
     method public String getDisplayGroup();
     method public java.math.BigInteger getLeadDisplayAddress();
+    method public java.math.BigInteger getPort_optional();
     method public String getPosition();
     method public String getPowerThrottlingMapId();
     method public String getRefreshRateThermalThrottlingMapId();
     method public String getRefreshRateZoneId();
     method public boolean isDefaultDisplay();
     method public boolean isEnabled();
-    method public void setAddress(java.math.BigInteger);
+    method public void setAddress_optional(java.math.BigInteger);
     method public void setBrightnessThrottlingMapId(String);
     method public void setDefaultDisplay(boolean);
     method public void setDisplayGroup(String);
     method public void setEnabled(boolean);
     method public void setLeadDisplayAddress(java.math.BigInteger);
+    method public void setPort_optional(java.math.BigInteger);
     method public void setPosition(String);
     method public void setPowerThrottlingMapId(String);
     method public void setRefreshRateThermalThrottlingMapId(String);
diff --git a/services/credentials/java/com/android/server/credentials/CreateRequestSession.java b/services/credentials/java/com/android/server/credentials/CreateRequestSession.java
index 6899ad4..31409ab 100644
--- a/services/credentials/java/com/android/server/credentials/CreateRequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/CreateRequestSession.java
@@ -109,7 +109,7 @@
                             PermissionUtils.hasPermission(mContext, mClientAppInfo.getPackageName(),
                                     Manifest.permission.CREDENTIAL_MANAGER_SET_ALLOWED_PROVIDERS),
                             /*defaultProviderId=*/flattenedPrimaryProviders),
-                    providerDataList);
+                    providerDataList, /*isRequestForAllOptions=*/ false);
             mClientCallback.onPendingIntent(mPendingIntent);
         } catch (RemoteException e) {
             mRequestSessionMetric.collectUiReturnedFinalPhase(/*uiReturned=*/ false);
diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
index 4a2e1cb..fb0729f 100644
--- a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
+++ b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
@@ -63,9 +63,9 @@
 import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.view.autofill.IAutoFillManagerClient;
 
 import com.android.internal.annotations.GuardedBy;
-import com.android.internal.content.PackageMonitor;
 import com.android.server.credentials.metrics.ApiName;
 import com.android.server.credentials.metrics.ApiStatus;
 import com.android.server.infra.AbstractMasterSystemService;
@@ -484,6 +484,7 @@
         public ICancellationSignal getCandidateCredentials(
                 GetCredentialRequest request,
                 IGetCandidateCredentialsCallback callback,
+                IAutoFillManagerClient clientCallback,
                 final String callingPackage) {
             Slog.i(TAG, "starting getCandidateCredentials with callingPackage: "
                     + callingPackage);
@@ -504,7 +505,8 @@
                             request,
                             constructCallingAppInfo(callingPackage, userId, request.getOrigin()),
                             getEnabledProvidersForUser(userId),
-                            CancellationSignal.fromTransport(cancelTransport)
+                            CancellationSignal.fromTransport(cancelTransport),
+                            clientCallback
                     );
             addSessionLocked(userId, session);
 
@@ -515,6 +517,8 @@
                                     .map(CredentialOption::getType)
                                     .collect(Collectors.toList()));
 
+            finalizeAndEmitInitialPhaseMetric(session);
+
             if (providerSessions.isEmpty()) {
                 try {
                     callback.onError(
@@ -774,6 +778,13 @@
             providerSessions.forEach(ProviderSession::invokeSession);
         }
 
+        private void finalizeAndEmitInitialPhaseMetric(GetCandidateRequestSession session) {
+            var initMetric = session.mRequestSessionMetric.getInitialPhaseMetric();
+            initMetric.setAutofillSessionId(session.getAutofillSessionId());
+            initMetric.setAutofillRequestId(session.getAutofillRequestId());
+            finalizeAndEmitInitialPhaseMetric((RequestSession) session);
+        }
+
         private void finalizeAndEmitInitialPhaseMetric(RequestSession session) {
             try {
                 var initMetric = session.mRequestSessionMetric.getInitialPhaseMetric();
@@ -1204,6 +1215,9 @@
         // If the app being removed matches any of the package names from
         // this list then don't add it in the output.
         Set<String> providers = new HashSet<>();
+        if (rawProviders == null || packageName == null) {
+            return providers;
+        }
         for (String rawComponentName : rawProviders.split(":")) {
             if (TextUtils.isEmpty(rawComponentName)
                     || rawComponentName.equals("null")) {
diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java b/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java
index 3c190bf..f092dcc 100644
--- a/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java
+++ b/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java
@@ -150,9 +150,12 @@
      *
      * @param requestInfo      the information about the request
      * @param providerDataList the list of provider data from remote providers
+     * @param isRequestForAllOptions whether the bottom sheet should directly navigate to the
+     *                               all options page
      */
     public PendingIntent createPendingIntent(
-            RequestInfo requestInfo, ArrayList<ProviderData> providerDataList) {
+            RequestInfo requestInfo, ArrayList<ProviderData> providerDataList,
+            boolean isRequestForAllOptions) {
         List<CredentialProviderInfo> allProviders =
                 CredentialProviderInfoFactory.getCredentialProviderServices(
                         mContext,
@@ -168,7 +171,8 @@
                         disabledProvider.getComponentName().flattenToString())).toList();
 
         Intent intent = IntentFactory.createCredentialSelectorIntent(requestInfo, providerDataList,
-                        new ArrayList<>(disabledProviderDataList), mResultReceiver)
+                        new ArrayList<>(disabledProviderDataList), mResultReceiver,
+                        isRequestForAllOptions)
                 .setAction(UUID.randomUUID().toString());
         //TODO: Create unique pending intent using request code and cancel any pre-existing pending
         // intents
diff --git a/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java b/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java
index 6d9b7e8..0f914c3 100644
--- a/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java
@@ -16,6 +16,7 @@
 
 package com.android.server.credentials;
 
+import android.Manifest;
 import android.annotation.Nullable;
 import android.content.ComponentName;
 import android.content.Context;
@@ -23,6 +24,7 @@
 import android.credentials.GetCandidateCredentialsException;
 import android.credentials.GetCandidateCredentialsResponse;
 import android.credentials.GetCredentialRequest;
+import android.credentials.GetCredentialResponse;
 import android.credentials.IGetCandidateCredentialsCallback;
 import android.credentials.ui.GetCredentialProviderData;
 import android.credentials.ui.ProviderData;
@@ -30,7 +32,9 @@
 import android.os.CancellationSignal;
 import android.os.RemoteException;
 import android.service.credentials.CallingAppInfo;
+import android.service.credentials.PermissionUtils;
 import android.util.Slog;
+import android.view.autofill.IAutoFillManagerClient;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -42,18 +46,29 @@
  */
 public class GetCandidateRequestSession extends RequestSession<GetCredentialRequest,
         IGetCandidateCredentialsCallback, GetCandidateCredentialsResponse>
-        implements ProviderSession.ProviderInternalCallback<GetCandidateCredentialsResponse> {
+        implements ProviderSession.ProviderInternalCallback<GetCredentialResponse> {
     private static final String TAG = "GetCandidateRequestSession";
 
+    private static final String SESSION_ID_KEY = "autofill_session_id";
+    private static final String REQUEST_ID_KEY = "autofill_request_id";
+
+    private final IAutoFillManagerClient mAutoFillCallback;
+    private final int mAutofillSessionId;
+    private final int mAutofillRequestId;
+
     public GetCandidateRequestSession(
             Context context, SessionLifetime sessionCallback,
             Object lock, int userId, int callingUid,
             IGetCandidateCredentialsCallback callback, GetCredentialRequest request,
             CallingAppInfo callingAppInfo, Set<ComponentName> enabledProviders,
-            CancellationSignal cancellationSignal) {
+            CancellationSignal cancellationSignal,
+            IAutoFillManagerClient autoFillCallback) {
         super(context, sessionCallback, lock, userId, callingUid, request, callback,
                 RequestInfo.TYPE_GET, callingAppInfo, enabledProviders,
                 cancellationSignal, 0L);
+        mAutoFillCallback = autoFillCallback;
+        mAutofillSessionId = request.getData().getInt(SESSION_ID_KEY, -1);
+        mAutofillRequestId = request.getData().getInt(REQUEST_ID_KEY, -1);
     }
 
     /**
@@ -92,12 +107,26 @@
             return;
         }
 
+        cancelExistingPendingIntent();
+        mPendingIntent = mCredentialManagerUi.createPendingIntent(
+                RequestInfo.newGetRequestInfo(
+                        mRequestId, mClientRequest, mClientAppInfo.getPackageName(),
+                        PermissionUtils.hasPermission(mContext, mClientAppInfo.getPackageName(),
+                                Manifest.permission.CREDENTIAL_MANAGER_SET_ALLOWED_PROVIDERS)),
+                providerDataList,
+                /*isRequestForAllOptions=*/ true);
+
         List<GetCredentialProviderData> candidateProviderDataList = new ArrayList<>();
         for (ProviderData providerData : providerDataList) {
             candidateProviderDataList.add((GetCredentialProviderData) (providerData));
         }
-        respondToClientWithResponseAndFinish(new GetCandidateCredentialsResponse(
-                candidateProviderDataList));
+
+        try {
+            invokeClientCallbackSuccess(new GetCandidateCredentialsResponse(
+                    candidateProviderDataList, mPendingIntent));
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Issue while responding to client with error : " + e);
+        }
     }
 
     @Override
@@ -151,7 +180,23 @@
 
     @Override
     public void onFinalResponseReceived(ComponentName componentName,
-            GetCandidateCredentialsResponse response) {
-        // Not applicable for session without UI
+            GetCredentialResponse response) {
+        Slog.d(TAG, "onFinalResponseReceived");
+        respondToClientWithResponseAndFinish(new GetCandidateCredentialsResponse(response));
+    }
+
+    /**
+     * Returns autofill session id. Returns -1 if unavailable.
+     */
+    public int getAutofillSessionId() {
+        return mAutofillSessionId;
+    }
+
+    /**
+     * Returns autofill request id. Returns -1 if unavailable.
+     */
+    public int getAutofillRequestId() {
+        return mAutofillRequestId;
+
     }
 }
diff --git a/services/credentials/java/com/android/server/credentials/GetRequestSession.java b/services/credentials/java/com/android/server/credentials/GetRequestSession.java
index c9e691e..3f57c80 100644
--- a/services/credentials/java/com/android/server/credentials/GetRequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/GetRequestSession.java
@@ -99,21 +99,24 @@
     protected void launchUiWithProviderData(ArrayList<ProviderData> providerDataList) {
         mRequestSessionMetric.collectUiCallStartTime(System.nanoTime());
         mCredentialManagerUi.setStatus(CredentialManagerUi.UiStatus.USER_INTERACTION);
-        Binder.withCleanCallingIdentity(()-> {
-        try {
+        Binder.withCleanCallingIdentity(() -> {
+            try {
                 cancelExistingPendingIntent();
-            mPendingIntent = mCredentialManagerUi.createPendingIntent(
-                    RequestInfo.newGetRequestInfo(
-                            mRequestId, mClientRequest, mClientAppInfo.getPackageName(),
-                            PermissionUtils.hasPermission(mContext, mClientAppInfo.getPackageName(),
-                                    Manifest.permission.CREDENTIAL_MANAGER_SET_ALLOWED_PROVIDERS)),
-                    providerDataList);
-            mClientCallback.onPendingIntent(mPendingIntent);
-        } catch (RemoteException e) {
-            mRequestSessionMetric.collectUiReturnedFinalPhase(/*uiReturned=*/ false);
-            mCredentialManagerUi.setStatus(CredentialManagerUi.UiStatus.TERMINATED);
-            String exception = GetCredentialException.TYPE_UNKNOWN;
-            mRequestSessionMetric.collectFrameworkException(exception);
+                mPendingIntent = mCredentialManagerUi.createPendingIntent(
+                        RequestInfo.newGetRequestInfo(
+                                mRequestId, mClientRequest, mClientAppInfo.getPackageName(),
+                                PermissionUtils.hasPermission(mContext,
+                                        mClientAppInfo.getPackageName(),
+                                        Manifest.permission
+                                                .CREDENTIAL_MANAGER_SET_ALLOWED_PROVIDERS)),
+                        providerDataList,
+                        /*isRequestForAllOptions=*/ false);
+                mClientCallback.onPendingIntent(mPendingIntent);
+            } catch (RemoteException e) {
+                mRequestSessionMetric.collectUiReturnedFinalPhase(/*uiReturned=*/ false);
+                mCredentialManagerUi.setStatus(CredentialManagerUi.UiStatus.TERMINATED);
+                String exception = GetCredentialException.TYPE_UNKNOWN;
+                mRequestSessionMetric.collectFrameworkException(exception);
                 respondToClientWithErrorAndFinish(exception, "Unable to instantiate selector");
             }
         });
diff --git a/services/credentials/java/com/android/server/credentials/MetricUtilities.java b/services/credentials/java/com/android/server/credentials/MetricUtilities.java
index b36de0b..23aa374 100644
--- a/services/credentials/java/com/android/server/credentials/MetricUtilities.java
+++ b/services/credentials/java/com/android/server/credentials/MetricUtilities.java
@@ -426,7 +426,11 @@
                     /* per_classtype_counts */
                     initialPhaseMetric.getUniqueRequestCounts(),
                     /* origin_specified */
-                    initialPhaseMetric.isOriginSpecified()
+                    initialPhaseMetric.isOriginSpecified(),
+                    /* autofill_session_id */
+                    initialPhaseMetric.getAutofillSessionId(),
+                    /* autofill_request_id */
+                    initialPhaseMetric.getAutofillRequestId()
             );
         } catch (Exception e) {
             Slog.w(TAG, "Unexpected error during initial metric emit: " + e);
diff --git a/services/credentials/java/com/android/server/credentials/PrepareGetRequestSession.java b/services/credentials/java/com/android/server/credentials/PrepareGetRequestSession.java
index f447c1f..fbfc9ca 100644
--- a/services/credentials/java/com/android/server/credentials/PrepareGetRequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/PrepareGetRequestSession.java
@@ -192,7 +192,7 @@
                             mRequestId, mClientRequest, mClientAppInfo.getPackageName(),
                             PermissionUtils.hasPermission(mContext, mClientAppInfo.getPackageName(),
                                     Manifest.permission.CREDENTIAL_MANAGER_SET_ALLOWED_PROVIDERS)),
-                    providerDataList);
+                    providerDataList, /*isRequestForAllOptions=*/ false);
         } else {
             return null;
         }
diff --git a/services/credentials/java/com/android/server/credentials/metrics/InitialPhaseMetric.java b/services/credentials/java/com/android/server/credentials/metrics/InitialPhaseMetric.java
index 8e965e3..8a4e86c 100644
--- a/services/credentials/java/com/android/server/credentials/metrics/InitialPhaseMetric.java
+++ b/services/credentials/java/com/android/server/credentials/metrics/InitialPhaseMetric.java
@@ -49,6 +49,12 @@
     // Stores the deduped request information, particularly {"req":5}
     private Map<String, Integer> mRequestCounts = new LinkedHashMap<>();
 
+    // The session id of autofill if the request is from autofill, defaults to -1
+    private int mAutofillSessionId = -1;
+
+    // The request id of autofill if the request is from autofill, defaults to -1
+    private int mAutofillRequestId = -1;
+
 
     public InitialPhaseMetric(int sessionIdTrackOne) {
         mSessionIdCaller = sessionIdTrackOne;
@@ -126,6 +132,24 @@
         return mOriginSpecified;
     }
 
+    /* ------ Autofill Integration ------ */
+
+    public void setAutofillSessionId(int autofillSessionId) {
+        mAutofillSessionId = autofillSessionId;
+    }
+
+    public int getAutofillSessionId() {
+        return mAutofillSessionId;
+    }
+
+    public void setAutofillRequestId(int autofillRequestId) {
+        mAutofillRequestId = autofillRequestId;
+    }
+
+    public int getAutofillRequestId() {
+        return mAutofillRequestId;
+    }
+
     /* ------ Unique Request Counts Map Information ------ */
 
     public void setRequestCounts(Map<String, Integer> requestCounts) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
index e0232b1..14dc0eb 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
@@ -32,6 +32,7 @@
 import android.annotation.Nullable;
 import android.app.AppGlobals;
 import android.app.BroadcastOptions;
+import android.app.admin.BooleanPolicyValue;
 import android.app.admin.DevicePolicyIdentifiers;
 import android.app.admin.DevicePolicyManager;
 import android.app.admin.DevicePolicyState;
@@ -142,6 +143,67 @@
         mAdminPolicySize = new SparseArray<>();
     }
 
+    private void maybeForceEnforcementRefreshLocked(@NonNull PolicyDefinition<?> policyDefinition) {
+        try {
+            if (shouldForceEnforcementRefresh(policyDefinition)) {
+                // This is okay because it's only true for user restrictions which are all <Boolean>
+                forceEnforcementRefreshLocked((PolicyDefinition<Boolean>) policyDefinition);
+            }
+        } catch (Throwable e) {
+            // Catch any possible exceptions just to be on the safe side
+            Log.e(TAG, "Exception throw during maybeForceEnforcementRefreshLocked", e);
+        }
+    }
+
+    private boolean shouldForceEnforcementRefresh(@NonNull PolicyDefinition<?> policyDefinition) {
+        // These are all "not nullable" but for the purposes of maximum safety for a lightly tested
+        // change we check here
+        if (policyDefinition == null) {
+            return false;
+        }
+        PolicyKey policyKey = policyDefinition.getPolicyKey();
+        if (policyKey == null) {
+            return false;
+        }
+
+        if (policyKey instanceof UserRestrictionPolicyKey) {
+            // b/307481299 We must force all user restrictions to re-sync local
+            // + global on each set/clear
+            return true;
+        }
+
+        return false;
+    }
+
+    private void forceEnforcementRefreshLocked(PolicyDefinition<Boolean> policyDefinition) {
+        Binder.withCleanCallingIdentity(() -> {
+            // Sync global state
+            PolicyValue<Boolean> globalValue = new BooleanPolicyValue(false);
+            try {
+                PolicyState<Boolean> policyState = getGlobalPolicyStateLocked(policyDefinition);
+                globalValue = policyState.getCurrentResolvedPolicy();
+            } catch (IllegalArgumentException e) {
+                // Expected for local-only policies
+            }
+
+            enforcePolicy(policyDefinition, globalValue, UserHandle.USER_ALL);
+
+            // Loop through each user and sync that user's state
+            for (UserInfo user : mUserManager.getUsers()) {
+                PolicyValue<Boolean> localValue = new BooleanPolicyValue(false);
+                try {
+                    PolicyState<Boolean> localPolicyState = getLocalPolicyStateLocked(
+                            policyDefinition, user.id);
+                    localValue = localPolicyState.getCurrentResolvedPolicy();
+                } catch (IllegalArgumentException e) {
+                    // Expected for global-only policies
+                }
+
+                enforcePolicy(policyDefinition, localValue, user.id);
+            }
+        });
+    }
+
     /**
      * Set the policy for the provided {@code policyDefinition} (see {@link PolicyDefinition}) and
      * {@code enforcingAdmin} to the provided {@code value}.
@@ -188,6 +250,7 @@
             // No need to notify admins as no new policy is actually enforced, we're just filling in
             // the data structures.
             if (!skipEnforcePolicy) {
+                maybeForceEnforcementRefreshLocked(policyDefinition);
                 if (policyChanged) {
                     onLocalPolicyChangedLocked(policyDefinition, enforcingAdmin, userId);
                 }
@@ -278,6 +341,7 @@
         Objects.requireNonNull(enforcingAdmin);
 
         synchronized (mLock) {
+            maybeForceEnforcementRefreshLocked(policyDefinition);
             if (!hasLocalPolicyLocked(policyDefinition, userId)) {
                 return;
             }
@@ -451,6 +515,7 @@
             // No need to notify admins as no new policy is actually enforced, we're just filling in
             // the data structures.
             if (!skipEnforcePolicy) {
+                maybeForceEnforcementRefreshLocked(policyDefinition);
                 if (policyChanged) {
                     onGlobalPolicyChangedLocked(policyDefinition, enforcingAdmin);
                 }
@@ -506,6 +571,7 @@
 
             boolean policyChanged = policyState.removePolicy(enforcingAdmin);
 
+            maybeForceEnforcementRefreshLocked(policyDefinition);
             if (policyChanged) {
                 onGlobalPolicyChangedLocked(policyDefinition, enforcingAdmin);
             }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index a490013..f288103 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -6243,9 +6243,8 @@
 
         final long id = mInjector.binderClearCallingIdentity();
         try {
-            final KeyChainConnection keyChainConnection =
-                    KeyChain.bindAsUser(mContext, caller.getUserHandle());
-            try {
+            try (KeyChainConnection keyChainConnection =
+                         KeyChain.bindAsUser(mContext, caller.getUserHandle())) {
                 IKeyChainService keyChain = keyChainConnection.getService();
                 if (!keyChain.installKeyPair(privKey, cert, chain, alias, KeyStore.UID_SELF)) {
                     logInstallKeyPairFailure(caller, isCredentialManagementApp);
@@ -6263,10 +6262,8 @@
                                 ? CREDENTIAL_MANAGEMENT_APP : NOT_CREDENTIAL_MANAGEMENT_APP)
                         .write();
                 return true;
-            } catch (RemoteException e) {
+            } catch (RemoteException | AssertionError e) {
                 Slogf.e(LOG_TAG, "Installing certificate", e);
-            } finally {
-                keyChainConnection.close();
             }
         } catch (InterruptedException e) {
             Slogf.w(LOG_TAG, "Interrupted while installing certificate", e);
@@ -6313,9 +6310,8 @@
 
         final long id = Binder.clearCallingIdentity();
         try {
-            final KeyChainConnection keyChainConnection =
-                    KeyChain.bindAsUser(mContext, caller.getUserHandle());
-            try {
+            try (KeyChainConnection keyChainConnection =
+                         KeyChain.bindAsUser(mContext, caller.getUserHandle())) {
                 IKeyChainService keyChain = keyChainConnection.getService();
                 DevicePolicyEventLogger
                         .createEvent(DevicePolicyEnums.REMOVE_KEY_PAIR)
@@ -6325,10 +6321,8 @@
                                 ? CREDENTIAL_MANAGEMENT_APP : NOT_CREDENTIAL_MANAGEMENT_APP)
                         .write();
                 return keyChain.removeKeyPair(alias);
-            } catch (RemoteException e) {
+            } catch (RemoteException | AssertionError e) {
                 Slogf.e(LOG_TAG, "Removing keypair", e);
-            } finally {
-                keyChainConnection.close();
             }
         } catch (InterruptedException e) {
             Slogf.w(LOG_TAG, "Interrupted while removing keypair", e);
@@ -6355,7 +6349,7 @@
             try (KeyChainConnection keyChainConnection =
                          KeyChain.bindAsUser(mContext, caller.getUserHandle())) {
                 return keyChainConnection.getService().containsKeyPair(alias);
-            } catch (RemoteException e) {
+            } catch (RemoteException | AssertionError e) {
                 Slogf.e(LOG_TAG, "Querying keypair", e);
             } catch (InterruptedException e) {
                 Slogf.w(LOG_TAG, "Interrupted while querying keypair", e);
@@ -6417,7 +6411,7 @@
                     }
                 }
                 return false;
-            } catch (RemoteException e) {
+            } catch (RemoteException | AssertionError e) {
                 Slogf.e(LOG_TAG, "Querying grant to wifi auth.", e);
                 return false;
             }
@@ -6497,7 +6491,7 @@
                     }
                     result.put(uid, new ArraySet<String>(packages));
                 }
-            } catch (RemoteException e) {
+            } catch (RemoteException | AssertionError e) {
                 Slogf.e(LOG_TAG, "Querying keypair grants", e);
             } catch (InterruptedException e) {
                 Slogf.w(LOG_TAG, "Interrupted while querying keypair grants", e);
@@ -6667,7 +6661,7 @@
                         .write();
                 return true;
             }
-        } catch (RemoteException e) {
+        } catch (RemoteException | AssertionError e) {
             Slogf.e(LOG_TAG, "KeyChain error while generating a keypair", e);
         } catch (InterruptedException e) {
             Slogf.w(LOG_TAG, "Interrupted while generating keypair", e);
@@ -6742,7 +6736,7 @@
         } catch (InterruptedException e) {
             Slogf.w(LOG_TAG, "Interrupted while setting keypair certificate", e);
             Thread.currentThread().interrupt();
-        } catch (RemoteException e) {
+        } catch (RemoteException | AssertionError e) {
             Slogf.e(LOG_TAG, "Failed setting keypair certificate", e);
         } finally {
             mInjector.binderRestoreCallingIdentity(id);
@@ -7227,7 +7221,7 @@
                         connection.getService().getCredentialManagementAppPolicy();
                 return policy != null && !policy.getAppAndUriMappings().isEmpty()
                         && containsAlias(policy, alias);
-            } catch (RemoteException | InterruptedException e) {
+            } catch (RemoteException | InterruptedException | AssertionError e) {
                 return false;
             }
         });
diff --git a/services/java/com/android/server/SystemConfigService.java b/services/java/com/android/server/SystemConfigService.java
index 6e82907..fd21a32 100644
--- a/services/java/com/android/server/SystemConfigService.java
+++ b/services/java/com/android/server/SystemConfigService.java
@@ -21,6 +21,8 @@
 import android.Manifest;
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.pm.PackageManagerInternal;
+import android.os.Binder;
 import android.os.ISystemConfig;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -108,6 +110,15 @@
                     "Caller must hold " + Manifest.permission.QUERY_ALL_PACKAGES);
             return new ArrayList<>(SystemConfig.getInstance().getDefaultVrComponents());
         }
+
+        @Override
+        public List<String> getPreventUserDisablePackages() {
+            PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
+            return SystemConfig.getInstance().getPreventUserDisablePackages().stream()
+                    .filter(preventUserDisablePackage ->
+                            pmi.canQueryPackage(Binder.getCallingUid(), preventUserDisablePackage))
+                    .collect(toList());
+        }
     };
 
     public SystemConfigService(Context context) {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 59e95e7..86ad494 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -2734,11 +2734,7 @@
 
         // AdServicesManagerService (PP API service)
         t.traceBegin("StartAdServicesManagerService");
-        SystemService adServices = mSystemServiceManager
-                .startService(AD_SERVICES_MANAGER_SERVICE_CLASS);
-        if (adServices instanceof Dumpable) {
-            mDumper.addDumpable((Dumpable) adServices);
-        }
+        mSystemServiceManager.startService(AD_SERVICES_MANAGER_SERVICE_CLASS);
         t.traceEnd();
 
         // OnDevicePersonalizationSystemService
@@ -2969,6 +2965,12 @@
             t.traceEnd();
         }
 
+        if (com.android.server.notification.Flags.sensitiveNotificationAppProtection()) {
+            t.traceBegin("StartSensitiveContentProtectionManager");
+            mSystemServiceManager.startService(SensitiveContentProtectionManagerService.class);
+            t.traceEnd();
+        }
+
         // These are needed to propagate to the runnable below.
         final NetworkManagementService networkManagementF = networkManagement;
         final NetworkPolicyManagerService networkPolicyF = networkPolicy;
diff --git a/services/lint-baseline.xml b/services/lint-baseline.xml
index 8489c17..a311d07 100644
--- a/services/lint-baseline.xml
+++ b/services/lint-baseline.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.1.0-dev" type="baseline" client="" dependencies="true" name="" variant="all" version="8.1.0-dev">
+<issues format="6" by="lint 8.4.0-alpha01" type="baseline" client="" dependencies="true" name="" variant="all" version="8.4.0-alpha01">
 
     <issue
         id="SimpleManualPermissionEnforcement"
@@ -56,4 +56,4 @@
             column="13"/>
     </issue>
 
-</issues>
+</issues>
\ No newline at end of file
diff --git a/services/print/Android.bp b/services/print/Android.bp
index 5b4349a..0dfceaa 100644
--- a/services/print/Android.bp
+++ b/services/print/Android.bp
@@ -19,4 +19,7 @@
     defaults: ["platform_service_defaults"],
     srcs: [":services.print-sources"],
     libs: ["services.core"],
+    lint: {
+        baseline_filename: "lint-baseline.xml",
+    },
 }
diff --git a/services/print/lint-baseline.xml b/services/print/lint-baseline.xml
index 1bf031a..11c0cc8e 100644
--- a/services/print/lint-baseline.xml
+++ b/services/print/lint-baseline.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.1.0-dev" type="baseline" client="" dependencies="true" name="" variant="all" version="8.1.0-dev">
+<issues format="6" by="lint 8.4.0-alpha01" type="baseline" client="" dependencies="true" name="" variant="all" version="8.4.0-alpha01">
 
     <issue
         id="SimpleManualPermissionEnforcement"
@@ -12,4 +12,4 @@
             column="13"/>
     </issue>
 
-</issues>
+</issues>
\ No newline at end of file
diff --git a/services/robotests/Android.bp b/services/robotests/Android.bp
index 52eae21..a70802a 100644
--- a/services/robotests/Android.bp
+++ b/services/robotests/Android.bp
@@ -57,9 +57,13 @@
     ],
     static_libs: [
         "androidx.test.ext.truth",
+        "Settings-robo-testutils",
+        "SettingsLib-robo-testutils",
     ],
 
     instrumentation_for: "FrameworksServicesLib",
+
+    upstream: true,
 }
 
 filegroup {
diff --git a/services/robotests/backup/Android.bp b/services/robotests/backup/Android.bp
index 8b9efb3..569786b 100644
--- a/services/robotests/backup/Android.bp
+++ b/services/robotests/backup/Android.bp
@@ -57,6 +57,8 @@
     // Include the testing libraries
     libs: [
         "mockito-robolectric-prebuilt",
+        "Settings-robo-testutils",
+        "SettingsLib-robo-testutils",
         "platform-test-annotations",
         "testng",
         "truth",
@@ -64,4 +66,6 @@
 
     instrumentation_for: "BackupFrameworksServicesLib",
 
+    upstream: true,
+
 }
diff --git a/services/robotests/backup/config/robolectric.properties b/services/robotests/backup/config/robolectric.properties
index 850557a..1ebf6d4 100644
--- a/services/robotests/backup/config/robolectric.properties
+++ b/services/robotests/backup/config/robolectric.properties
@@ -1 +1,3 @@
-sdk=NEWEST_SDK
\ No newline at end of file
+sdk=NEWEST_SDK
+looperMode=LEGACY
+shadows=com.android.server.testing.shadows.FrameworkShadowLooper
diff --git a/services/robotests/backup/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java b/services/robotests/backup/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java
index ee5a534..6839a06 100644
--- a/services/robotests/backup/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java
@@ -57,6 +57,7 @@
             ShadowBackupDataOutput.class,
             ShadowEnvironment.class,
             ShadowFullBackup.class,
+            ShadowSigningInfo.class,
         })
 public class AppMetadataBackupWriterTest {
     private static final String TEST_PACKAGE = "com.test.package";
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileUser.kt b/services/robotests/backup/src/com/android/server/backup/fullbackup/ShadowSigningInfo.java
similarity index 63%
copy from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileUser.kt
copy to services/robotests/backup/src/com/android/server/backup/fullbackup/ShadowSigningInfo.java
index efc7431..53d807c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileUser.kt
+++ b/services/robotests/backup/src/com/android/server/backup/fullbackup/ShadowSigningInfo.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * 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.
@@ -14,12 +14,14 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.custom.di.bound
+package com.android.server.backup.fullbackup;
 
-import javax.inject.Qualifier
+import static android.os.Build.VERSION_CODES.P;
 
-/** User associated with current custom tile binding. */
-@Qualifier
-@MustBeDocumented
-@Retention(AnnotationRetention.RUNTIME)
-annotation class CustomTileUser
+import android.content.pm.SigningInfo;
+
+import org.robolectric.annotation.Implements;
+
+@Implements(value = SigningInfo.class, minSdk = P)
+public class ShadowSigningInfo {
+}
diff --git a/services/robotests/src/com/android/server/location/gnss/NtpNetworkTimeHelperTest.java b/services/robotests/src/com/android/server/location/gnss/NtpNetworkTimeHelperTest.java
index 4949091..0092763 100644
--- a/services/robotests/src/com/android/server/location/gnss/NtpNetworkTimeHelperTest.java
+++ b/services/robotests/src/com/android/server/location/gnss/NtpNetworkTimeHelperTest.java
@@ -35,6 +35,7 @@
 import org.mockito.MockitoAnnotations;
 import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.LooperMode;
 import org.robolectric.shadows.ShadowLooper;
 
 import java.util.concurrent.CountDownLatch;
@@ -45,6 +46,7 @@
  */
 @RunWith(RobolectricTestRunner.class)
 @Presubmit
+@LooperMode(LooperMode.Mode.LEGACY)
 public class NtpNetworkTimeHelperTest {
 
     private static final long MOCK_NTP_TIME = 1519930775453L;
diff --git a/services/robotests/src/com/android/server/testing/shadows/FrameworkShadowLooper.java b/services/robotests/src/com/android/server/testing/shadows/FrameworkShadowLooper.java
index 16d16cd..3681bd4 100644
--- a/services/robotests/src/com/android/server/testing/shadows/FrameworkShadowLooper.java
+++ b/services/robotests/src/com/android/server/testing/shadows/FrameworkShadowLooper.java
@@ -21,12 +21,15 @@
 import org.robolectric.annotation.Implementation;
 import org.robolectric.annotation.Implements;
 import org.robolectric.annotation.RealObject;
+import org.robolectric.shadows.LooperShadowPicker;
+import org.robolectric.shadows.ShadowLegacyLooper;
 import org.robolectric.shadows.ShadowLooper;
+import org.robolectric.shadows.ShadowPausedLooper;
 
 import java.util.Optional;
 
-@Implements(value = Looper.class)
-public class FrameworkShadowLooper extends ShadowLooper {
+@Implements(value = Looper.class, shadowPicker = FrameworkShadowLooper.Picker.class)
+public class FrameworkShadowLooper extends ShadowLegacyLooper {
     @RealObject private Looper mLooper;
     private Optional<Boolean> mIsCurrentThread = Optional.empty();
 
@@ -45,4 +48,10 @@
         }
         return Thread.currentThread() == mLooper.getThread();
     }
+
+    public static class Picker extends LooperShadowPicker<ShadowLooper> {
+        public Picker() {
+            super(FrameworkShadowLooper.class, ShadowPausedLooper.class);
+        }
+    }
 }
diff --git a/services/robotests/src/com/android/server/testing/shadows/ShadowApplicationPackageManager.java b/services/robotests/src/com/android/server/testing/shadows/ShadowApplicationPackageManager.java
index 4a99486..1da6759 100644
--- a/services/robotests/src/com/android/server/testing/shadows/ShadowApplicationPackageManager.java
+++ b/services/robotests/src/com/android/server/testing/shadows/ShadowApplicationPackageManager.java
@@ -95,7 +95,6 @@
         sPackageAppEnabledStates.put(packageName, Integer.valueOf(newState));  // flags unused here.
     }
 
-    @Override
     protected PackageInfo getPackageInfoAsUser(String packageName, int flags, int userId)
             throws NameNotFoundException {
         if (!sPackageInfos.containsKey(packageName)) {
diff --git a/services/tests/InputMethodSystemServerTests/Android.bp b/services/tests/InputMethodSystemServerTests/Android.bp
index ffe6dc5..56423b9 100644
--- a/services/tests/InputMethodSystemServerTests/Android.bp
+++ b/services/tests/InputMethodSystemServerTests/Android.bp
@@ -40,6 +40,7 @@
         "frameworks-base-testutils",
         "mockito-target-extended-minus-junit4",
         "platform-test-annotations",
+        "ravenwood-junit",
         "services.core",
         "service-permission.stubs.system_server",
         "servicestests-core-utils",
@@ -66,6 +67,28 @@
     },
 }
 
+android_ravenwood_test {
+    name: "FrameworksInputMethodSystemServerTests_host",
+    static_libs: [
+        "androidx.annotation_annotation",
+        "androidx.test.rules",
+        "framework",
+        "mockito_ravenwood",
+        "ravenwood-runtime",
+        "ravenwood-utils",
+        "services",
+    ],
+    libs: [
+        "android.test.base",
+        "android.test.runner",
+    ],
+    srcs: [
+        "src/com/android/server/inputmethod/**/ClientControllerTest.java",
+    ],
+    sdk_version: "test_current",
+    auto_gen_config: true,
+}
+
 android_test {
     name: "FrameworksImeTests",
     defaults: [
@@ -88,6 +111,7 @@
         "frameworks-base-testutils",
         "mockito-target-extended-minus-junit4",
         "platform-test-annotations",
+        "ravenwood-junit",
         "services.core",
         "service-permission.stubs.system_server",
         "servicestests-core-utils",
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ClientControllerTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ClientControllerTest.java
new file mode 100644
index 0000000..3c8f5c9
--- /dev/null
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ClientControllerTest.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.inputmethod;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+import static org.mockito.Mockito.when;
+
+import android.content.pm.PackageManagerInternal;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.ravenwood.RavenwoodRule;
+import android.view.Display;
+import android.view.inputmethod.InputBinding;
+
+import com.android.internal.inputmethod.IInputMethodClient;
+import com.android.internal.inputmethod.IRemoteInputConnection;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+
+// This test is designed to run on both device and host (Ravenwood) side.
+public final class ClientControllerTest {
+    private static final int ANY_DISPLAY_ID = Display.DEFAULT_DISPLAY;
+    private static final int ANY_CALLER_UID = 1;
+    private static final int ANY_CALLER_PID = 1;
+
+    @Rule
+    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
+            .setProvideMainThread(true).build();
+
+    @Mock
+    private PackageManagerInternal mMockPackageManagerInternal;
+
+    @Mock(extraInterfaces = IBinder.class)
+    private IInputMethodClient mClient;
+
+    @Mock
+    private IRemoteInputConnection mConnection;
+
+    @Mock
+    private IBinder.DeathRecipient mDeathRecipient;
+
+    private Handler mHandler;
+
+    private ClientController mController;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mHandler = new Handler(Looper.getMainLooper());
+        mController = new ClientController(mMockPackageManagerInternal);
+        when(mClient.asBinder()).thenReturn((IBinder) mClient);
+    }
+
+    @Test
+    // TODO(b/314150112): Enable host side mode for this test once b/315544364 is fixed.
+    @IgnoreUnderRavenwood(blockedBy = {InputBinding.class, IInputMethodClientInvoker.class})
+    public void testAddClient_cannotAddTheSameClientTwice() {
+        var invoker = IInputMethodClientInvoker.create(mClient, mHandler);
+
+        synchronized (ImfLock.class) {
+            mController.addClient(invoker, mConnection, ANY_DISPLAY_ID, mDeathRecipient,
+                    ANY_CALLER_UID, ANY_CALLER_PID);
+
+            SecurityException thrown = assertThrows(SecurityException.class,
+                    () -> {
+                        synchronized (ImfLock.class) {
+                            mController.addClient(invoker, mConnection, ANY_DISPLAY_ID,
+                                    mDeathRecipient, ANY_CALLER_UID, ANY_CALLER_PID);
+                        }
+                    });
+            assertThat(thrown.getMessage()).isEqualTo(
+                    "uid=1/pid=1/displayId=0 is already registered");
+        }
+    }
+}
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java
index 3199e06..438bea4 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java
@@ -22,6 +22,7 @@
 import static com.android.internal.inputmethod.SoftInputShowHideReason.HIDE_SOFT_INPUT;
 import static com.android.internal.inputmethod.SoftInputShowHideReason.HIDE_SWITCH_USER;
 import static com.android.internal.inputmethod.SoftInputShowHideReason.SHOW_SOFT_INPUT;
+import static com.android.server.inputmethod.ClientController.ClientState;
 import static com.android.server.inputmethod.ImeVisibilityStateComputer.STATE_HIDE_IME;
 import static com.android.server.inputmethod.ImeVisibilityStateComputer.STATE_HIDE_IME_EXPLICIT;
 import static com.android.server.inputmethod.ImeVisibilityStateComputer.STATE_HIDE_IME_NOT_ALWAYS;
@@ -68,8 +69,7 @@
         super.setUp();
         mVisibilityApplier =
                 (DefaultImeVisibilityApplier) mInputMethodManagerService.getVisibilityApplier();
-        mInputMethodManagerService.setAttachedClientForTesting(
-                mock(InputMethodManagerService.ClientState.class));
+        mInputMethodManagerService.setAttachedClientForTesting(mock(ClientState.class));
     }
 
     @Test
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/UserDataPreparerTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/UserDataPreparerTest.java
index e5be4d9..9e11fa2 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/UserDataPreparerTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/UserDataPreparerTest.java
@@ -50,7 +50,7 @@
 import java.util.Arrays;
 import java.util.Collections;
 
-// atest PackageManagerServiceTest:com.android.server.pm.UserDataPreparerTest
+// atest PackageManagerServiceServerTests:com.android.server.pm.UserDataPreparerTest
 @RunWith(AndroidJUnit4.class)
 @Presubmit
 @SmallTest
@@ -99,7 +99,7 @@
         systemDeDir.mkdirs();
         mUserDataPreparer.prepareUserData(TEST_USER, StorageManager.FLAG_STORAGE_DE);
         verify(mStorageManagerMock).prepareUserStorage(isNull(String.class), eq(TEST_USER_ID),
-                eq(TEST_USER_SERIAL), eq(StorageManager.FLAG_STORAGE_DE));
+                eq(StorageManager.FLAG_STORAGE_DE));
         verify(mInstaller).createUserData(isNull(String.class), eq(TEST_USER_ID),
                 eq(TEST_USER_SERIAL), eq(StorageManager.FLAG_STORAGE_DE));
         int serialNumber = UserDataPreparer.getSerialNumber(userDeDir);
@@ -116,7 +116,7 @@
         systemCeDir.mkdirs();
         mUserDataPreparer.prepareUserData(TEST_USER, StorageManager.FLAG_STORAGE_CE);
         verify(mStorageManagerMock).prepareUserStorage(isNull(String.class), eq(TEST_USER_ID),
-                eq(TEST_USER_SERIAL), eq(StorageManager.FLAG_STORAGE_CE));
+                eq(StorageManager.FLAG_STORAGE_CE));
         verify(mInstaller).createUserData(isNull(String.class), eq(TEST_USER_ID),
                 eq(TEST_USER_SERIAL), eq(StorageManager.FLAG_STORAGE_CE));
         int serialNumber = UserDataPreparer.getSerialNumber(userCeDir);
@@ -129,7 +129,7 @@
     public void testPrepareUserData_forNewUser_destroysOnFailure() throws Exception {
         TEST_USER.lastLoggedInTime = 0;
         doThrow(new IllegalStateException("expected exception for test")).when(mStorageManagerMock)
-                .prepareUserStorage(isNull(String.class), eq(TEST_USER_ID), eq(TEST_USER_SERIAL),
+                .prepareUserStorage(isNull(String.class), eq(TEST_USER_ID),
                         eq(StorageManager.FLAG_STORAGE_CE));
         mUserDataPreparer.prepareUserData(TEST_USER, StorageManager.FLAG_STORAGE_CE);
         verify(mStorageManagerMock).destroyUserStorage(isNull(String.class), eq(TEST_USER_ID),
@@ -140,7 +140,7 @@
     public void testPrepareUserData_forExistingUser_doesNotDestroyOnFailure() throws Exception {
         TEST_USER.lastLoggedInTime = System.currentTimeMillis();
         doThrow(new IllegalStateException("expected exception for test")).when(mStorageManagerMock)
-                .prepareUserStorage(isNull(String.class), eq(TEST_USER_ID), eq(TEST_USER_SERIAL),
+                .prepareUserStorage(isNull(String.class), eq(TEST_USER_ID),
                         eq(StorageManager.FLAG_STORAGE_CE));
         mUserDataPreparer.prepareUserData(TEST_USER, StorageManager.FLAG_STORAGE_CE);
         verify(mStorageManagerMock, never()).destroyUserStorage(isNull(String.class),
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DeviceStateToLayoutMapTest.java b/services/tests/displayservicetests/src/com/android/server/display/DeviceStateToLayoutMapTest.java
index 8cc3408..567792e 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DeviceStateToLayoutMapTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DeviceStateToLayoutMapTest.java
@@ -25,6 +25,7 @@
 
 import androidx.test.filters.SmallTest;
 
+import com.android.server.display.feature.DisplayManagerFlags;
 import com.android.server.display.layout.DisplayIdProducer;
 import com.android.server.display.layout.Layout;
 
@@ -45,6 +46,7 @@
     private DeviceStateToLayoutMap mDeviceStateToLayoutMap;
 
     @Mock DisplayIdProducer mDisplayIdProducerMock;
+    @Mock DisplayManagerFlags mMockFlags;
 
     @Before
     public void setUp() throws IOException {
@@ -52,7 +54,7 @@
 
         Mockito.when(mDisplayIdProducerMock.getId(false)).thenReturn(1);
 
-        setupDeviceStateToLayoutMap();
+        setupDeviceStateToLayoutMap(getContent());
     }
 
     //////////////////
@@ -268,6 +270,41 @@
                 IllegalArgumentException.class, () -> layout.postProcessLocked());
     }
 
+    @Test
+    public void testPortInLayout_disabledFlag() {
+        Mockito.when(mMockFlags.isPortInDisplayLayoutEnabled()).thenReturn(false);
+        assertThrows("Expected IllegalArgumentException when using <port>",
+                IllegalArgumentException.class,
+                () -> setupDeviceStateToLayoutMap(getPortContent()));
+    }
+
+    @Test
+    public void testPortInLayout_readLayout() throws Exception {
+        Mockito.when(mMockFlags.isPortInDisplayLayoutEnabled()).thenReturn(true);
+        setupDeviceStateToLayoutMap(getPortContent());
+
+        Layout configLayout = mDeviceStateToLayoutMap.get(0);
+
+        Layout testLayout = new Layout();
+        testLayout.createDisplayLocked(DisplayAddress.fromPortAndModel(123, null),
+                /* isDefault= */ true, /* isEnabled= */ true, /* displayGroupName= */ null,
+                mDisplayIdProducerMock,  Layout.Display.POSITION_UNKNOWN,
+                /* leadDisplayAddress= */ null, /* brightnessThrottlingMapId= */ null,
+                /* refreshRateZoneId= */ null,
+                /* refreshRateThermalThrottlingMapId= */ null,
+                /* powerThrottlingMapId= */ null);
+        testLayout.createDisplayLocked(DisplayAddress.fromPhysicalDisplayId(78910L),
+                /* isDefault= */ false, /* isEnabled= */ false, /* displayGroupName= */ null,
+                mDisplayIdProducerMock, Layout.Display.POSITION_UNKNOWN,
+                /* leadDisplayAddress= */ null, /* brightnessThrottlingMapId= */ null,
+                /* refreshRateZoneId= */ null,
+                /* refreshRateThermalThrottlingMapId= */ null,
+                /* powerThrottlingMapId= */ null);
+        testLayout.postProcessLocked();
+
+        assertEquals(testLayout, configLayout);
+    }
+
     ////////////////////
     // Helper Methods //
     ////////////////////
@@ -287,13 +324,28 @@
                 /* powerThrottlingMapId= */ null);
     }
 
-    private void setupDeviceStateToLayoutMap() throws IOException {
+    private void setupDeviceStateToLayoutMap(String content) throws IOException {
         Path tempFile = Files.createTempFile("device_state_layout_map", ".tmp");
-        Files.write(tempFile, getContent().getBytes(StandardCharsets.UTF_8));
-        mDeviceStateToLayoutMap = new DeviceStateToLayoutMap(mDisplayIdProducerMock,
+        Files.write(tempFile, content.getBytes(StandardCharsets.UTF_8));
+        mDeviceStateToLayoutMap = new DeviceStateToLayoutMap(mDisplayIdProducerMock, mMockFlags,
                 tempFile.toFile());
     }
 
+    private String getPortContent() {
+        return "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
+                +  "<layouts>\n"
+                +    "<layout>\n"
+                +      "<state>0</state> \n"
+                +      "<display enabled=\"true\" defaultDisplay=\"true\">\n"
+                +        "<port>123</port>\n"
+                +      "</display>\n"
+                +      "<display enabled=\"false\">\n"
+                +        "<address>78910</address>\n"
+                +      "</display>\n"
+                +    "</layout>\n"
+                +  "</layouts>\n";
+    }
+
     private String getContent() {
         return "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
                 +  "<layouts>\n"
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayBrightnessStateTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayBrightnessStateTest.java
index eb6e8b4..ad4d91f 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayBrightnessStateTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayBrightnessStateTest.java
@@ -96,6 +96,8 @@
                 .append(displayBrightnessState.isSlowChange())
                 .append("\n    maxBrightness:")
                 .append(displayBrightnessState.getMaxBrightness())
+                .append("\n    minBrightness:")
+                .append(displayBrightnessState.getMinBrightness())
                 .append("\n    customAnimationRate:")
                 .append(displayBrightnessState.getCustomAnimationRate())
                 .append("\n    shouldUpdateScreenBrightnessSetting:")
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
index 7a84406..c67e7c5 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
@@ -41,6 +41,7 @@
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.hardware.display.DisplayManagerInternal;
+import android.os.PowerManager;
 import android.os.Temperature;
 import android.provider.Settings;
 import android.util.SparseArray;
@@ -109,6 +110,43 @@
     }
 
     @Test
+    public void testDefaultValues() {
+        when(mResources.getString(com.android.internal.R.string.config_displayLightSensorType))
+                .thenReturn("test_light_sensor");
+        when(mResources.getBoolean(R.bool.config_automatic_brightness_available)).thenReturn(true);
+
+        mDisplayDeviceConfig = DisplayDeviceConfig.create(mContext, /* useConfigXml= */ false,
+                mFlags);
+
+        assertEquals(DisplayDeviceConfig.BRIGHTNESS_DEFAULT,
+                mDisplayDeviceConfig.getBrightnessDefault(), ZERO_DELTA);
+        assertEquals(PowerManager.BRIGHTNESS_MAX,
+                mDisplayDeviceConfig.getBrightnessRampFastDecrease(), ZERO_DELTA);
+        assertEquals(PowerManager.BRIGHTNESS_MAX,
+                mDisplayDeviceConfig.getBrightnessRampFastIncrease(), ZERO_DELTA);
+        assertEquals(PowerManager.BRIGHTNESS_MAX,
+                mDisplayDeviceConfig.getBrightnessRampSlowDecrease(), ZERO_DELTA);
+        assertEquals(PowerManager.BRIGHTNESS_MAX,
+                mDisplayDeviceConfig.getBrightnessRampSlowIncrease(), ZERO_DELTA);
+        assertEquals(PowerManager.BRIGHTNESS_MAX,
+                mDisplayDeviceConfig.getBrightnessRampSlowDecreaseIdle(), ZERO_DELTA);
+        assertEquals(PowerManager.BRIGHTNESS_MAX,
+                mDisplayDeviceConfig.getBrightnessRampSlowIncreaseIdle(), ZERO_DELTA);
+        assertEquals(0, mDisplayDeviceConfig.getBrightnessRampDecreaseMaxMillis());
+        assertEquals(0, mDisplayDeviceConfig.getBrightnessRampIncreaseMaxMillis());
+        assertEquals(0, mDisplayDeviceConfig.getBrightnessRampDecreaseMaxIdleMillis());
+        assertEquals(0, mDisplayDeviceConfig.getBrightnessRampIncreaseMaxIdleMillis());
+        assertNull(mDisplayDeviceConfig.getNits());
+        assertNull(mDisplayDeviceConfig.getBacklight());
+        assertEquals(0.3f, mDisplayDeviceConfig.getBacklightFromBrightness(0.3f), ZERO_DELTA);
+        assertEquals("test_light_sensor", mDisplayDeviceConfig.getAmbientLightSensor().type);
+        assertEquals("", mDisplayDeviceConfig.getAmbientLightSensor().name);
+        assertNull(mDisplayDeviceConfig.getProximitySensor().type);
+        assertNull(mDisplayDeviceConfig.getProximitySensor().name);
+        assertTrue(mDisplayDeviceConfig.isAutoBrightnessAvailable());
+    }
+
+    @Test
     public void testConfigValuesFromDisplayConfig() throws IOException {
         setupDisplayDeviceConfigFromDisplayConfigFile();
 
@@ -681,6 +719,7 @@
 
         assertEquals("test_light_sensor", mDisplayDeviceConfig.getAmbientLightSensor().type);
         assertEquals("", mDisplayDeviceConfig.getAmbientLightSensor().name);
+        assertTrue(mDisplayDeviceConfig.isAutoBrightnessAvailable());
 
         assertEquals(brightnessIntToFloat(35),
                 mDisplayDeviceConfig.getBrightnessCapForWearBedtimeMode(), ZERO_DELTA);
@@ -759,6 +798,15 @@
                         AUTO_BRIGHTNESS_MODE_DEFAULT,
                         Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_DIM), SMALL_DELTA);
 
+        assertArrayEquals(new float[]{0.0f, 80},
+                mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsLux(
+                        AUTO_BRIGHTNESS_MODE_DEFAULT,
+                        Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_BRIGHT), ZERO_DELTA);
+        assertArrayEquals(new float[]{0.6f, 0.7f},
+                mDisplayDeviceConfig.getAutoBrightnessBrighteningLevels(
+                        AUTO_BRIGHTNESS_MODE_DEFAULT,
+                        Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_BRIGHT), SMALL_DELTA);
+
         assertArrayEquals(new float[]{0.0f, 95},
                 mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsLux(
                         AUTO_BRIGHTNESS_MODE_DOZE,
@@ -798,6 +846,24 @@
                 mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsNits(), SMALL_DELTA);
     }
 
+    @Test
+    public void testIsAutoBrightnessAvailable_EnabledInConfigResource() throws IOException {
+        when(mResources.getBoolean(R.bool.config_automatic_brightness_available)).thenReturn(true);
+
+        setupDisplayDeviceConfigFromDisplayConfigFile();
+
+        assertTrue(mDisplayDeviceConfig.isAutoBrightnessAvailable());
+    }
+
+    @Test
+    public void testIsAutoBrightnessAvailable_DisabledInConfigResource() throws IOException {
+        when(mResources.getBoolean(R.bool.config_automatic_brightness_available)).thenReturn(false);
+
+        setupDisplayDeviceConfigFromDisplayConfigFile();
+
+        assertFalse(mDisplayDeviceConfig.isAutoBrightnessAvailable());
+    }
+
     private String getValidLuxThrottling() {
         return "<luxThrottling>\n"
                 + "    <brightnessLimitMap>\n"
@@ -1167,7 +1233,7 @@
                 +           "<nits>" + NITS[2] + "</nits>\n"
                 +       "</point>\n"
                 +   "</screenBrightnessMap>\n"
-                +   "<autoBrightness>\n"
+                +   "<autoBrightness enabled=\"true\">\n"
                 +       "<brighteningLightDebounceMillis>2000</brighteningLightDebounceMillis>\n"
                 +       "<darkeningLightDebounceMillis>1000</darkeningLightDebounceMillis>\n"
                 + (includeIdleMode ? getRampSpeedsIdle() : "")
@@ -1197,6 +1263,20 @@
                 +           "</map>\n"
                 +       "</luxToBrightnessMapping>\n"
                 +       "<luxToBrightnessMapping>\n"
+                +           "<mode>default</mode>\n"
+                +           "<setting>bright</setting>\n"
+                +           "<map>\n"
+                +               "<point>\n"
+                +                   "<first>0</first>\n"
+                +                   "<second>0.6</second>\n"
+                +               "</point>\n"
+                +               "<point>\n"
+                +                   "<first>80</first>\n"
+                +                   "<second>0.7</second>\n"
+                +               "</point>\n"
+                +           "</map>\n"
+                +       "</luxToBrightnessMapping>\n"
+                +       "<luxToBrightnessMapping>\n"
                 +           "<mode>doze</mode>\n"
                 +           "<map>\n"
                 +               "<point>\n"
@@ -1570,6 +1650,7 @@
 
         when(mResources.getString(com.android.internal.R.string.config_displayLightSensorType))
                 .thenReturn("test_light_sensor");
+        when(mResources.getBoolean(R.bool.config_automatic_brightness_available)).thenReturn(true);
 
         when(mResources.getInteger(
                 R.integer.config_autoBrightnessBrighteningLightDebounce))
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerController2Test.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerController2Test.java
deleted file mode 100644
index 4cc68cf..0000000
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerController2Test.java
+++ /dev/null
@@ -1,1971 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.display;
-
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
-import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DEFAULT;
-import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DOZE;
-import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_IDLE;
-
-import static org.junit.Assert.assertNotNull;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyFloat;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyLong;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.ArgumentMatchers.isA;
-import static org.mockito.Mockito.atLeastOnce;
-import static org.mockito.Mockito.clearInvocations;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.when;
-
-import android.app.ActivityManager;
-import android.content.Context;
-import android.content.res.Resources;
-import android.hardware.Sensor;
-import android.hardware.SensorEventListener;
-import android.hardware.SensorManager;
-import android.hardware.display.BrightnessInfo;
-import android.hardware.display.DisplayManagerInternal;
-import android.hardware.display.DisplayManagerInternal.DisplayPowerCallbacks;
-import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.PowerManager;
-import android.os.SystemProperties;
-import android.os.UserHandle;
-import android.os.test.TestLooper;
-import android.platform.test.annotations.RequiresFlagsDisabled;
-import android.platform.test.annotations.RequiresFlagsEnabled;
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.DeviceFlagsValueProvider;
-import android.provider.Settings;
-import android.testing.TestableContext;
-import android.util.FloatProperty;
-import android.util.SparseArray;
-import android.view.Display;
-import android.view.DisplayInfo;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-import androidx.test.platform.app.InstrumentationRegistry;
-
-import com.android.modules.utils.testing.ExtendedMockitoRule;
-import com.android.server.LocalServices;
-import com.android.server.am.BatteryStatsService;
-import com.android.server.display.RampAnimator.DualRampAnimator;
-import com.android.server.display.brightness.BrightnessEvent;
-import com.android.server.display.brightness.clamper.BrightnessClamperController;
-import com.android.server.display.brightness.clamper.HdrClamper;
-import com.android.server.display.color.ColorDisplayService;
-import com.android.server.display.config.SensorData;
-import com.android.server.display.feature.DisplayManagerFlags;
-import com.android.server.display.feature.flags.Flags;
-import com.android.server.display.layout.Layout;
-import com.android.server.display.whitebalance.DisplayWhiteBalanceController;
-import com.android.server.policy.WindowManagerPolicy;
-import com.android.server.testutils.OffsettableClock;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
-import org.mockito.Mock;
-import org.mockito.quality.Strictness;
-import org.mockito.stubbing.Answer;
-
-import java.util.List;
-
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public final class DisplayPowerController2Test {
-    private static final int DISPLAY_ID = Display.DEFAULT_DISPLAY;
-    private static final String UNIQUE_ID = "unique_id_test123";
-    private static final int FOLLOWER_DISPLAY_ID = DISPLAY_ID + 1;
-    private static final String FOLLOWER_UNIQUE_ID = "unique_id_456";
-    private static final int SECOND_FOLLOWER_DISPLAY_ID = FOLLOWER_DISPLAY_ID + 1;
-    private static final String SECOND_FOLLOWER_UNIQUE_DISPLAY_ID = "unique_id_789";
-    private static final float PROX_SENSOR_MAX_RANGE = 5;
-    private static final float BRIGHTNESS_RAMP_RATE_MINIMUM = 0.0f;
-    private static final float BRIGHTNESS_RAMP_RATE_FAST_DECREASE = 0.3f;
-    private static final float BRIGHTNESS_RAMP_RATE_FAST_INCREASE = 0.4f;
-    private static final float BRIGHTNESS_RAMP_RATE_SLOW_DECREASE = 0.1f;
-    private static final float BRIGHTNESS_RAMP_RATE_SLOW_INCREASE = 0.2f;
-    private static final float BRIGHTNESS_RAMP_RATE_SLOW_INCREASE_IDLE = 0.5f;
-    private static final float BRIGHTNESS_RAMP_RATE_SLOW_DECREASE_IDLE = 0.6f;
-
-    private static final long BRIGHTNESS_RAMP_INCREASE_MAX = 1000;
-    private static final long BRIGHTNESS_RAMP_DECREASE_MAX = 2000;
-    private static final long BRIGHTNESS_RAMP_INCREASE_MAX_IDLE = 3000;
-    private static final long BRIGHTNESS_RAMP_DECREASE_MAX_IDLE = 4000;
-
-    private OffsettableClock mClock;
-    private TestLooper mTestLooper;
-    private Handler mHandler;
-    private DisplayPowerControllerHolder mHolder;
-    private Sensor mProxSensor;
-
-    @Mock
-    private DisplayPowerCallbacks mDisplayPowerCallbacksMock;
-    @Mock
-    private SensorManager mSensorManagerMock;
-    @Mock
-    private DisplayBlanker mDisplayBlankerMock;
-    @Mock
-    private BrightnessTracker mBrightnessTrackerMock;
-    @Mock
-    private WindowManagerPolicy mWindowManagerPolicyMock;
-    @Mock
-    private PowerManager mPowerManagerMock;
-    @Mock
-    private ColorDisplayService.ColorDisplayServiceInternal mCdsiMock;
-    @Mock
-    private DisplayWhiteBalanceController mDisplayWhiteBalanceControllerMock;
-    @Mock
-    private DisplayManagerFlags mDisplayManagerFlagsMock;
-    @Mock
-    private DisplayManagerInternal.DisplayOffloadSession mDisplayOffloadSession;
-    @Captor
-    private ArgumentCaptor<SensorEventListener> mSensorEventListenerCaptor;
-
-    @Rule
-    public final TestableContext mContext = new TestableContext(
-            InstrumentationRegistry.getInstrumentation().getContext());
-
-    @Rule
-    public final ExtendedMockitoRule mExtendedMockitoRule =
-            new ExtendedMockitoRule.Builder(this)
-                    .setStrictness(Strictness.LENIENT)
-                    .spyStatic(SystemProperties.class)
-                    .spyStatic(BatteryStatsService.class)
-                    .spyStatic(ActivityManager.class)
-                    .build();
-
-    @Rule
-    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
-
-    @Before
-    public void setUp() throws Exception {
-        mClock = new OffsettableClock.Stopped();
-        mTestLooper = new TestLooper(mClock::now);
-        mHandler = new Handler(mTestLooper.getLooper());
-
-        // Set some settings to minimize unexpected events and have a consistent starting state
-        Settings.System.putInt(mContext.getContentResolver(),
-                Settings.System.SCREEN_BRIGHTNESS_MODE,
-                Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL);
-        Settings.System.putFloatForUser(mContext.getContentResolver(),
-                Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, 0, UserHandle.USER_CURRENT);
-
-        addLocalServiceMock(WindowManagerPolicy.class, mWindowManagerPolicyMock);
-        addLocalServiceMock(ColorDisplayService.ColorDisplayServiceInternal.class,
-                mCdsiMock);
-
-        mContext.addMockSystemService(PowerManager.class, mPowerManagerMock);
-
-        mContext.getOrCreateTestableResources().addOverride(
-                com.android.internal.R.bool.config_displayColorFadeDisabled, false);
-
-        doAnswer((Answer<Void>) invocationOnMock -> null).when(() ->
-                SystemProperties.set(anyString(), any()));
-        doAnswer((Answer<Void>) invocationOnMock -> null).when(BatteryStatsService::getService);
-        doAnswer((Answer<Boolean>) invocationOnMock -> false)
-                .when(ActivityManager::isLowRamDeviceStatic);
-
-        setUpSensors();
-        mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID);
-    }
-
-    @After
-    public void tearDown() {
-        LocalServices.removeServiceForTest(WindowManagerPolicy.class);
-        LocalServices.removeServiceForTest(ColorDisplayService.ColorDisplayServiceInternal.class);
-    }
-
-    @Test
-    public void testReleaseProxSuspendBlockersOnExit() throws Exception {
-        when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON);
-        // Send a display power request
-        DisplayPowerRequest dpr = new DisplayPowerRequest();
-        dpr.policy = DisplayPowerRequest.POLICY_BRIGHT;
-        dpr.useProximitySensor = true;
-        mHolder.dpc.requestPowerState(dpr, false /* waitForNegativeProximity */);
-
-        // Run updatePowerState to start listener for the prox sensor
-        advanceTime(1);
-
-        SensorEventListener listener = getSensorEventListener(mProxSensor);
-        assertNotNull(listener);
-
-        listener.onSensorChanged(TestUtils.createSensorEvent(mProxSensor, /* value= */ 5));
-        advanceTime(1);
-
-        // two times, one for unfinished business and one for proximity
-        verify(mHolder.wakelockController, times(2)).acquireWakelock(
-                WakelockController.WAKE_LOCK_UNFINISHED_BUSINESS);
-        verify(mHolder.wakelockController).acquireWakelock(
-                WakelockController.WAKE_LOCK_PROXIMITY_DEBOUNCE);
-
-        mHolder.dpc.stop();
-        advanceTime(1);
-        // two times, one for unfinished business and one for proximity
-        verify(mHolder.wakelockController, times(2)).acquireWakelock(
-                WakelockController.WAKE_LOCK_UNFINISHED_BUSINESS);
-        verify(mHolder.wakelockController).acquireWakelock(
-                WakelockController.WAKE_LOCK_PROXIMITY_DEBOUNCE);
-    }
-
-    @Test
-    public void testScreenOffBecauseOfProximity() throws Exception {
-        when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON);
-        // Send a display power request
-        DisplayPowerRequest dpr = new DisplayPowerRequest();
-        dpr.policy = DisplayPowerRequest.POLICY_BRIGHT;
-        dpr.useProximitySensor = true;
-        mHolder.dpc.requestPowerState(dpr, false /* waitForNegativeProximity */);
-
-        // Run updatePowerState to start listener for the prox sensor
-        advanceTime(1);
-
-        SensorEventListener listener = getSensorEventListener(mProxSensor);
-        assertNotNull(listener);
-
-        // Send a positive proximity event
-        listener.onSensorChanged(TestUtils.createSensorEvent(mProxSensor, /* value= */ 1));
-        advanceTime(1);
-
-        // The display should have been turned off
-        verify(mHolder.displayPowerState).setScreenState(Display.STATE_OFF);
-
-        clearInvocations(mHolder.displayPowerState);
-        when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_OFF);
-        // Send a negative proximity event
-        listener.onSensorChanged(TestUtils.createSensorEvent(mProxSensor,
-                (int) PROX_SENSOR_MAX_RANGE + 1));
-        // Advance time by less than PROXIMITY_SENSOR_NEGATIVE_DEBOUNCE_DELAY
-        advanceTime(1);
-
-        // The prox sensor is debounced so the display should not have been turned back on yet
-        verify(mHolder.displayPowerState, never()).setScreenState(Display.STATE_ON);
-
-        // Advance time by more than PROXIMITY_SENSOR_NEGATIVE_DEBOUNCE_DELAY
-        advanceTime(1000);
-
-        // The display should have been turned back on
-        verify(mHolder.displayPowerState).setScreenState(Display.STATE_ON);
-    }
-
-    @Test
-    public void testScreenOffBecauseOfProximity_ProxSensorGone() throws Exception {
-        when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON);
-        // Send a display power request
-        DisplayPowerRequest dpr = new DisplayPowerRequest();
-        dpr.policy = DisplayPowerRequest.POLICY_BRIGHT;
-        dpr.useProximitySensor = true;
-        mHolder.dpc.requestPowerState(dpr, false /* waitForNegativeProximity */);
-
-        // Run updatePowerState to start listener for the prox sensor
-        advanceTime(1);
-
-        SensorEventListener listener = getSensorEventListener(mProxSensor);
-        assertNotNull(listener);
-
-        // Send a positive proximity event
-        listener.onSensorChanged(TestUtils.createSensorEvent(mProxSensor, /* value= */ 1));
-        advanceTime(1);
-
-        // The display should have been turned off
-        verify(mHolder.displayPowerState).setScreenState(Display.STATE_OFF);
-
-        when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_OFF);
-        // The display device changes and we no longer have a prox sensor
-        reset(mSensorManagerMock);
-        setUpDisplay(DISPLAY_ID, "new_unique_id", mHolder.display, mock(DisplayDevice.class),
-                mock(DisplayDeviceConfig.class), /* isEnabled= */ true);
-        mHolder.dpc.onDisplayChanged(mHolder.hbmMetadata, Layout.NO_LEAD_DISPLAY);
-
-        advanceTime(1); // Run updatePowerState
-
-        // The display should have been turned back on and the listener should have been
-        // unregistered
-        verify(mHolder.displayPowerState).setScreenState(Display.STATE_ON);
-        verify(mSensorManagerMock).unregisterListener(listener);
-    }
-
-    @Test
-    public void testProximitySensorListenerNotRegisteredForNonDefaultDisplay() {
-        when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON);
-        // send a display power request
-        DisplayPowerRequest dpr = new DisplayPowerRequest();
-        dpr.policy = DisplayPowerRequest.POLICY_BRIGHT;
-        dpr.useProximitySensor = true;
-        final DisplayPowerControllerHolder followerDpc =
-                createDisplayPowerController(FOLLOWER_DISPLAY_ID, FOLLOWER_UNIQUE_ID);
-        followerDpc.dpc.requestPowerState(dpr, false /* waitForNegativeProximity */);
-
-        // Run updatePowerState
-        advanceTime(1);
-
-        verify(mSensorManagerMock, never()).registerListener(any(SensorEventListener.class),
-                eq(mProxSensor), anyInt(), any(Handler.class));
-    }
-
-    @Test
-    public void testDisplayBrightnessFollowers_BothDpcsSupportNits() {
-        DisplayPowerControllerHolder followerDpc =
-                createDisplayPowerController(FOLLOWER_DISPLAY_ID, FOLLOWER_UNIQUE_ID);
-        when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
-        when(followerDpc.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
-
-        DisplayPowerRequest dpr = new DisplayPowerRequest();
-        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
-        followerDpc.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
-        advanceTime(1); // Run updatePowerState
-
-        ArgumentCaptor<BrightnessSetting.BrightnessSettingListener> listenerCaptor =
-                ArgumentCaptor.forClass(BrightnessSetting.BrightnessSettingListener.class);
-        verify(mHolder.brightnessSetting).registerListener(listenerCaptor.capture());
-        BrightnessSetting.BrightnessSettingListener listener = listenerCaptor.getValue();
-
-        mHolder.dpc.addDisplayBrightnessFollower(followerDpc.dpc);
-
-        // Test different float scale values
-        float leadBrightness = 0.3f;
-        float followerBrightness = 0.4f;
-        float nits = 300;
-        when(mHolder.automaticBrightnessController.convertToNits(leadBrightness)).thenReturn(nits);
-        when(followerDpc.automaticBrightnessController.getBrightnessFromNits(nits))
-                .thenReturn(followerBrightness);
-        when(mHolder.brightnessSetting.getBrightness()).thenReturn(leadBrightness);
-        listener.onBrightnessChanged(leadBrightness);
-        advanceTime(1); // Send messages, run updatePowerState
-        verify(mHolder.animator).animateTo(eq(leadBrightness), anyFloat(), anyFloat(), eq(false));
-        verify(followerDpc.animator).animateTo(eq(followerBrightness), anyFloat(),
-                anyFloat(), eq(false));
-
-        clearInvocations(mHolder.animator, followerDpc.animator);
-
-        // Test the same float scale value
-        float brightness = 0.6f;
-        nits = 600;
-        when(mHolder.automaticBrightnessController.convertToNits(brightness)).thenReturn(nits);
-        when(followerDpc.automaticBrightnessController.getBrightnessFromNits(nits))
-                .thenReturn(brightness);
-        when(mHolder.brightnessSetting.getBrightness()).thenReturn(brightness);
-        listener.onBrightnessChanged(brightness);
-        advanceTime(1); // Send messages, run updatePowerState
-        verify(mHolder.animator).animateTo(eq(brightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
-        verify(followerDpc.animator).animateTo(eq(brightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
-    }
-
-    @Test
-    public void testDisplayBrightnessFollowers_FollowerDoesNotSupportNits() {
-        DisplayPowerControllerHolder followerDpc =
-                createDisplayPowerController(FOLLOWER_DISPLAY_ID, FOLLOWER_UNIQUE_ID);
-        when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
-        when(followerDpc.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
-
-        DisplayPowerRequest dpr = new DisplayPowerRequest();
-        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
-        followerDpc.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
-        advanceTime(1); // Run updatePowerState
-
-        ArgumentCaptor<BrightnessSetting.BrightnessSettingListener> listenerCaptor =
-                ArgumentCaptor.forClass(BrightnessSetting.BrightnessSettingListener.class);
-        verify(mHolder.brightnessSetting).registerListener(listenerCaptor.capture());
-        BrightnessSetting.BrightnessSettingListener listener = listenerCaptor.getValue();
-
-        mHolder.dpc.addDisplayBrightnessFollower(followerDpc.dpc);
-
-        float brightness = 0.3f;
-        when(mHolder.automaticBrightnessController.convertToNits(brightness)).thenReturn(300f);
-        when(followerDpc.automaticBrightnessController.getBrightnessFromNits(anyFloat()))
-                .thenReturn(PowerManager.BRIGHTNESS_INVALID_FLOAT);
-        when(mHolder.brightnessSetting.getBrightness()).thenReturn(brightness);
-        listener.onBrightnessChanged(brightness);
-        advanceTime(1); // Send messages, run updatePowerState
-        verify(mHolder.animator).animateTo(eq(brightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
-        verify(followerDpc.animator).animateTo(eq(brightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
-    }
-
-    @Test
-    public void testDisplayBrightnessFollowers_LeadDpcDoesNotSupportNits() {
-        DisplayPowerControllerHolder followerDpc =
-                createDisplayPowerController(FOLLOWER_DISPLAY_ID, FOLLOWER_UNIQUE_ID);
-        when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
-        when(followerDpc.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
-
-        DisplayPowerRequest dpr = new DisplayPowerRequest();
-        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
-        followerDpc.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
-        advanceTime(1); // Run updatePowerState
-
-        ArgumentCaptor<BrightnessSetting.BrightnessSettingListener> listenerCaptor =
-                ArgumentCaptor.forClass(BrightnessSetting.BrightnessSettingListener.class);
-        verify(mHolder.brightnessSetting).registerListener(listenerCaptor.capture());
-        BrightnessSetting.BrightnessSettingListener listener = listenerCaptor.getValue();
-
-        mHolder.dpc.addDisplayBrightnessFollower(followerDpc.dpc);
-
-        float brightness = 0.3f;
-        when(mHolder.automaticBrightnessController.convertToNits(anyFloat())).thenReturn(-1f);
-        when(mHolder.brightnessSetting.getBrightness()).thenReturn(brightness);
-        listener.onBrightnessChanged(brightness);
-        advanceTime(1); // Send messages, run updatePowerState
-        verify(mHolder.animator).animateTo(eq(brightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
-        verify(followerDpc.animator).animateTo(eq(brightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
-    }
-
-    @Test
-    public void testDisplayBrightnessFollowers_NeitherDpcSupportsNits() {
-        DisplayPowerControllerHolder followerDpc =
-                createDisplayPowerController(FOLLOWER_DISPLAY_ID, FOLLOWER_UNIQUE_ID);
-        when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
-        when(followerDpc.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
-
-        DisplayPowerRequest dpr = new DisplayPowerRequest();
-        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
-        followerDpc.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
-        advanceTime(1); // Run updatePowerState
-
-        ArgumentCaptor<BrightnessSetting.BrightnessSettingListener> listenerCaptor =
-                ArgumentCaptor.forClass(BrightnessSetting.BrightnessSettingListener.class);
-        verify(mHolder.brightnessSetting).registerListener(listenerCaptor.capture());
-        BrightnessSetting.BrightnessSettingListener listener = listenerCaptor.getValue();
-
-        mHolder.dpc.addDisplayBrightnessFollower(followerDpc.dpc);
-
-        float brightness = 0.3f;
-        when(mHolder.automaticBrightnessController.convertToNits(anyFloat())).thenReturn(-1f);
-        when(followerDpc.automaticBrightnessController.getBrightnessFromNits(anyFloat()))
-                .thenReturn(PowerManager.BRIGHTNESS_INVALID_FLOAT);
-        when(mHolder.brightnessSetting.getBrightness()).thenReturn(brightness);
-        listener.onBrightnessChanged(brightness);
-        advanceTime(1); // Send messages, run updatePowerState
-        verify(mHolder.animator).animateTo(eq(brightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
-        verify(followerDpc.animator).animateTo(eq(brightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
-    }
-
-    @Test
-    public void testDisplayBrightnessFollowers_AutomaticBrightness() {
-        Settings.System.putInt(mContext.getContentResolver(),
-                Settings.System.SCREEN_BRIGHTNESS_MODE,
-                Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
-        DisplayPowerControllerHolder followerDpc =
-                createDisplayPowerController(FOLLOWER_DISPLAY_ID, FOLLOWER_UNIQUE_ID);
-        when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON);
-        when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
-        when(followerDpc.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
-        float leadBrightness = 0.1f;
-        float rawLeadBrightness = 0.3f;
-        float followerBrightness = 0.4f;
-        float nits = 300;
-        float ambientLux = 3000;
-        when(mHolder.automaticBrightnessController.getRawAutomaticScreenBrightness())
-                .thenReturn(rawLeadBrightness);
-        when(mHolder.automaticBrightnessController
-                .getAutomaticScreenBrightness(any(BrightnessEvent.class)))
-                .thenReturn(leadBrightness);
-        when(mHolder.automaticBrightnessController.convertToNits(rawLeadBrightness))
-                .thenReturn(nits);
-        when(mHolder.automaticBrightnessController.getAmbientLux()).thenReturn(ambientLux);
-        when(followerDpc.automaticBrightnessController.getBrightnessFromNits(nits))
-                .thenReturn(followerBrightness);
-
-        mHolder.dpc.addDisplayBrightnessFollower(followerDpc.dpc);
-
-        DisplayPowerRequest dpr = new DisplayPowerRequest();
-        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
-        followerDpc.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
-        advanceTime(1); // Run updatePowerState
-
-        verify(mHolder.animator).animateTo(eq(leadBrightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
-        // One triggered by handleBrightnessModeChange, another triggered by setBrightnessToFollow
-        verify(followerDpc.hbmController, times(2)).onAmbientLuxChange(ambientLux);
-        verify(followerDpc.animator, times(2)).animateTo(eq(followerBrightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
-
-        when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(leadBrightness);
-        when(followerDpc.displayPowerState.getScreenBrightness()).thenReturn(followerBrightness);
-        clearInvocations(mHolder.animator, followerDpc.animator);
-
-        leadBrightness = 0.05f;
-        rawLeadBrightness = 0.2f;
-        followerBrightness = 0.3f;
-        nits = 200;
-        ambientLux = 2000;
-        when(mHolder.automaticBrightnessController.getRawAutomaticScreenBrightness())
-                .thenReturn(rawLeadBrightness);
-        when(mHolder.automaticBrightnessController
-                .getAutomaticScreenBrightness(any(BrightnessEvent.class)))
-                .thenReturn(leadBrightness);
-        when(mHolder.automaticBrightnessController.convertToNits(rawLeadBrightness))
-                .thenReturn(nits);
-        when(mHolder.automaticBrightnessController.getAmbientLux()).thenReturn(ambientLux);
-        when(followerDpc.automaticBrightnessController.getBrightnessFromNits(nits))
-                .thenReturn(followerBrightness);
-
-        mHolder.dpc.updateBrightness();
-        advanceTime(1); // Run updatePowerState
-
-        // The second time, the animation rate should be slow
-        verify(mHolder.animator).animateTo(eq(leadBrightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_SLOW_DECREASE), eq(false));
-        verify(followerDpc.hbmController).onAmbientLuxChange(ambientLux);
-        verify(followerDpc.animator).animateTo(eq(followerBrightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_SLOW_DECREASE), eq(false));
-    }
-
-    @Test
-    public void testDisplayBrightnessFollowersRemoval_RemoveSingleFollower() {
-        DisplayPowerControllerHolder followerDpc = createDisplayPowerController(FOLLOWER_DISPLAY_ID,
-                FOLLOWER_UNIQUE_ID);
-        DisplayPowerControllerHolder secondFollowerDpc = createDisplayPowerController(
-                SECOND_FOLLOWER_DISPLAY_ID, SECOND_FOLLOWER_UNIQUE_DISPLAY_ID);
-        when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
-        when(followerDpc.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
-        when(secondFollowerDpc.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
-
-        DisplayPowerRequest dpr = new DisplayPowerRequest();
-        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
-        followerDpc.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
-        secondFollowerDpc.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
-        advanceTime(1); // Run updatePowerState
-
-        ArgumentCaptor<BrightnessSetting.BrightnessSettingListener> listenerCaptor =
-                ArgumentCaptor.forClass(BrightnessSetting.BrightnessSettingListener.class);
-        verify(mHolder.brightnessSetting).registerListener(listenerCaptor.capture());
-        BrightnessSetting.BrightnessSettingListener listener = listenerCaptor.getValue();
-
-        // Set the initial brightness on the DPC we're going to remove so we have a fixed value for
-        // it to return to.
-        listenerCaptor = ArgumentCaptor.forClass(BrightnessSetting.BrightnessSettingListener.class);
-        verify(followerDpc.brightnessSetting).registerListener(listenerCaptor.capture());
-        BrightnessSetting.BrightnessSettingListener followerListener = listenerCaptor.getValue();
-        final float initialFollowerBrightness = 0.3f;
-        when(followerDpc.brightnessSetting.getBrightness()).thenReturn(initialFollowerBrightness);
-        followerListener.onBrightnessChanged(initialFollowerBrightness);
-        advanceTime(1);
-        verify(followerDpc.animator).animateTo(eq(initialFollowerBrightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
-
-        when(followerDpc.displayPowerState.getScreenBrightness())
-                .thenReturn(initialFollowerBrightness);
-
-        mHolder.dpc.addDisplayBrightnessFollower(followerDpc.dpc);
-        mHolder.dpc.addDisplayBrightnessFollower(secondFollowerDpc.dpc);
-        clearInvocations(followerDpc.animator);
-
-        // Validate both followers are correctly registered and receiving brightness updates
-        float brightness = 0.6f;
-        float nits = 600;
-        when(mHolder.automaticBrightnessController.convertToNits(brightness)).thenReturn(nits);
-        when(followerDpc.automaticBrightnessController.getBrightnessFromNits(nits))
-                .thenReturn(brightness);
-        when(secondFollowerDpc.automaticBrightnessController.getBrightnessFromNits(nits))
-                .thenReturn(brightness);
-        when(mHolder.brightnessSetting.getBrightness()).thenReturn(brightness);
-        listener.onBrightnessChanged(brightness);
-        advanceTime(1); // Send messages, run updatePowerState
-        verify(mHolder.animator).animateTo(eq(brightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
-        verify(followerDpc.animator).animateTo(eq(brightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
-        verify(secondFollowerDpc.animator).animateTo(eq(brightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
-
-        when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(brightness);
-        when(followerDpc.displayPowerState.getScreenBrightness()).thenReturn(brightness);
-        when(secondFollowerDpc.displayPowerState.getScreenBrightness()).thenReturn(brightness);
-        clearInvocations(mHolder.animator, followerDpc.animator, secondFollowerDpc.animator);
-
-        // Remove the first follower and validate it goes back to its original brightness.
-        mHolder.dpc.removeDisplayBrightnessFollower(followerDpc.dpc);
-        advanceTime(1);
-        verify(followerDpc.animator).animateTo(eq(initialFollowerBrightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_FAST_DECREASE), eq(false));
-
-        when(followerDpc.displayPowerState.getScreenBrightness())
-                .thenReturn(initialFollowerBrightness);
-        clearInvocations(followerDpc.animator);
-
-        // Change the brightness of the lead display and validate only the second follower responds
-        brightness = 0.7f;
-        nits = 700;
-        when(mHolder.automaticBrightnessController.convertToNits(brightness)).thenReturn(nits);
-        when(followerDpc.automaticBrightnessController.getBrightnessFromNits(nits))
-                .thenReturn(brightness);
-        when(secondFollowerDpc.automaticBrightnessController.getBrightnessFromNits(nits))
-                .thenReturn(brightness);
-        when(mHolder.brightnessSetting.getBrightness()).thenReturn(brightness);
-        listener.onBrightnessChanged(brightness);
-        advanceTime(1); // Send messages, run updatePowerState
-        verify(mHolder.animator).animateTo(eq(brightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
-        verify(followerDpc.animator, never()).animateTo(anyFloat(), anyFloat(), anyFloat(),
-                anyBoolean());
-        verify(secondFollowerDpc.animator).animateTo(eq(brightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
-    }
-
-    @Test
-    public void testDisplayBrightnessFollowersRemoval_RemoveAllFollowers() {
-        DisplayPowerControllerHolder followerHolder =
-                createDisplayPowerController(FOLLOWER_DISPLAY_ID, FOLLOWER_UNIQUE_ID);
-        DisplayPowerControllerHolder secondFollowerHolder =
-                createDisplayPowerController(SECOND_FOLLOWER_DISPLAY_ID,
-                        SECOND_FOLLOWER_UNIQUE_DISPLAY_ID);
-        when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
-        when(followerHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
-        when(secondFollowerHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
-
-        DisplayPowerRequest dpr = new DisplayPowerRequest();
-        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
-        followerHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
-        secondFollowerHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
-        advanceTime(1); // Run updatePowerState
-
-        ArgumentCaptor<BrightnessSetting.BrightnessSettingListener> listenerCaptor =
-                ArgumentCaptor.forClass(BrightnessSetting.BrightnessSettingListener.class);
-        verify(mHolder.brightnessSetting).registerListener(listenerCaptor.capture());
-        BrightnessSetting.BrightnessSettingListener listener = listenerCaptor.getValue();
-
-        // Set the initial brightness on the DPCs we're going to remove so we have a fixed value for
-        // it to return to.
-        listenerCaptor = ArgumentCaptor.forClass(BrightnessSetting.BrightnessSettingListener.class);
-        verify(followerHolder.brightnessSetting).registerListener(listenerCaptor.capture());
-        BrightnessSetting.BrightnessSettingListener followerListener = listenerCaptor.getValue();
-        listenerCaptor = ArgumentCaptor.forClass(BrightnessSetting.BrightnessSettingListener.class);
-        verify(secondFollowerHolder.brightnessSetting).registerListener(listenerCaptor.capture());
-        BrightnessSetting.BrightnessSettingListener secondFollowerListener =
-                listenerCaptor.getValue();
-        final float initialFollowerBrightness = 0.3f;
-        when(followerHolder.brightnessSetting.getBrightness()).thenReturn(
-                initialFollowerBrightness);
-        when(secondFollowerHolder.brightnessSetting.getBrightness()).thenReturn(
-                initialFollowerBrightness);
-        followerListener.onBrightnessChanged(initialFollowerBrightness);
-        secondFollowerListener.onBrightnessChanged(initialFollowerBrightness);
-        advanceTime(1);
-        verify(followerHolder.animator).animateTo(eq(initialFollowerBrightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
-        verify(secondFollowerHolder.animator).animateTo(eq(initialFollowerBrightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
-
-        when(followerHolder.displayPowerState.getScreenBrightness())
-                .thenReturn(initialFollowerBrightness);
-        when(secondFollowerHolder.displayPowerState.getScreenBrightness())
-                .thenReturn(initialFollowerBrightness);
-
-        mHolder.dpc.addDisplayBrightnessFollower(followerHolder.dpc);
-        mHolder.dpc.addDisplayBrightnessFollower(secondFollowerHolder.dpc);
-        clearInvocations(followerHolder.animator, secondFollowerHolder.animator);
-
-        // Validate both followers are correctly registered and receiving brightness updates
-        float brightness = 0.6f;
-        float nits = 600;
-        when(mHolder.automaticBrightnessController.convertToNits(brightness)).thenReturn(nits);
-        when(followerHolder.automaticBrightnessController.getBrightnessFromNits(nits))
-                .thenReturn(brightness);
-        when(secondFollowerHolder.automaticBrightnessController.getBrightnessFromNits(nits))
-                .thenReturn(brightness);
-        when(mHolder.brightnessSetting.getBrightness()).thenReturn(brightness);
-        listener.onBrightnessChanged(brightness);
-        advanceTime(1); // Send messages, run updatePowerState
-        verify(mHolder.animator).animateTo(eq(brightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
-        verify(followerHolder.animator).animateTo(eq(brightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
-        verify(secondFollowerHolder.animator).animateTo(eq(brightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
-
-        when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(brightness);
-        when(followerHolder.displayPowerState.getScreenBrightness()).thenReturn(brightness);
-        when(secondFollowerHolder.displayPowerState.getScreenBrightness()).thenReturn(brightness);
-        clearInvocations(mHolder.animator, followerHolder.animator, secondFollowerHolder.animator);
-
-        // Stop the lead DPC and validate that the followers go back to their original brightness.
-        mHolder.dpc.stop();
-        advanceTime(1);
-        verify(followerHolder.animator).animateTo(eq(initialFollowerBrightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_FAST_DECREASE), eq(false));
-        verify(secondFollowerHolder.animator).animateTo(eq(initialFollowerBrightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_FAST_DECREASE), eq(false));
-        clearInvocations(followerHolder.animator, secondFollowerHolder.animator);
-    }
-
-    @Test
-    @RequiresFlagsEnabled(Flags.FLAG_FAST_HDR_TRANSITIONS)
-    public void testDisplayBrightnessHdr_SkipAnimationOnHdrAppearance() {
-        Settings.System.putInt(mContext.getContentResolver(),
-                Settings.System.SCREEN_BRIGHTNESS_MODE,
-                Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
-        final float sdrBrightness = 0.1f;
-        final float hdrBrightness = 0.3f;
-        when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON);
-        when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
-        when(mHolder.automaticBrightnessController.getAutomaticScreenBrightness(
-                any(BrightnessEvent.class))).thenReturn(sdrBrightness);
-        when(mHolder.hdrClamper.getMaxBrightness()).thenReturn(1.0f);
-
-        DisplayPowerRequest dpr = new DisplayPowerRequest();
-        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
-        advanceTime(1); // Run updatePowerState
-
-        verify(mHolder.animator).animateTo(eq(sdrBrightness), eq(sdrBrightness),
-                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
-
-        when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(sdrBrightness);
-        when(mHolder.displayPowerState.getSdrScreenBrightness()).thenReturn(sdrBrightness);
-
-        when(mHolder.hbmController.getHighBrightnessMode()).thenReturn(
-                BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR);
-        when(mHolder.hbmController.getHdrBrightnessValue()).thenReturn(hdrBrightness);
-        clearInvocations(mHolder.animator);
-
-        mHolder.dpc.updateBrightness();
-        advanceTime(1); // Run updatePowerState
-
-        verify(mHolder.animator).animateTo(eq(hdrBrightness), eq(sdrBrightness),
-                eq(BRIGHTNESS_RAMP_RATE_MINIMUM), eq(false));
-    }
-
-    @Test
-    @RequiresFlagsEnabled(Flags.FLAG_FAST_HDR_TRANSITIONS)
-    public void testDisplayBrightnessHdr_SkipAnimationOnHdrRemoval() {
-        Settings.System.putInt(mContext.getContentResolver(),
-                Settings.System.SCREEN_BRIGHTNESS_MODE,
-                Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
-        final float sdrBrightness = 0.1f;
-        final float hdrBrightness = 0.3f;
-        when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON);
-        when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
-        when(mHolder.automaticBrightnessController.isInIdleMode()).thenReturn(true);
-        when(mHolder.automaticBrightnessController.getAutomaticScreenBrightness(
-                any(BrightnessEvent.class))).thenReturn(sdrBrightness);
-        when(mHolder.hdrClamper.getMaxBrightness()).thenReturn(1.0f);
-
-        when(mHolder.hbmController.getHighBrightnessMode()).thenReturn(
-                BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR);
-        when(mHolder.hbmController.getHdrBrightnessValue()).thenReturn(hdrBrightness);
-
-        DisplayPowerRequest dpr = new DisplayPowerRequest();
-        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
-        advanceTime(1); // Run updatePowerState
-
-        verify(mHolder.animator).animateTo(eq(hdrBrightness), eq(sdrBrightness),
-                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
-
-        when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(hdrBrightness);
-        when(mHolder.displayPowerState.getSdrScreenBrightness()).thenReturn(sdrBrightness);
-        when(mHolder.hbmController.getHighBrightnessMode()).thenReturn(
-                BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF);
-
-        clearInvocations(mHolder.animator);
-
-        mHolder.dpc.updateBrightness();
-        advanceTime(1); // Run updatePowerState
-
-        verify(mHolder.animator).animateTo(eq(sdrBrightness), eq(sdrBrightness),
-                eq(BRIGHTNESS_RAMP_RATE_MINIMUM), eq(false));
-    }
-
-    @Test
-    public void testDoesNotSetScreenStateForNonDefaultDisplayUntilBootCompleted() {
-        // We should still set screen state for the default display
-        DisplayPowerRequest dpr = new DisplayPowerRequest();
-        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
-        advanceTime(1); // Run updatePowerState
-        verify(mHolder.displayPowerState, times(2)).setScreenState(anyInt());
-
-        mHolder = createDisplayPowerController(42, UNIQUE_ID);
-
-        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
-        advanceTime(1); // Run updatePowerState
-        verify(mHolder.displayPowerState, never()).setScreenState(anyInt());
-
-        mHolder.dpc.onBootCompleted();
-        advanceTime(1); // Run updatePowerState
-        verify(mHolder.displayPowerState).setScreenState(anyInt());
-    }
-
-    @Test
-    public void testSetScreenOffBrightnessSensorEnabled_DisplayIsOff() {
-        Settings.System.putInt(mContext.getContentResolver(),
-                Settings.System.SCREEN_BRIGHTNESS_MODE,
-                Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
-
-        when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_OFF);
-
-        DisplayPowerRequest dpr = new DisplayPowerRequest();
-        dpr.policy = DisplayPowerRequest.POLICY_OFF;
-        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
-        advanceTime(1); // Run updatePowerState
-
-        verify(mHolder.screenOffBrightnessSensorController, atLeastOnce())
-                .setLightSensorEnabled(true);
-
-        // The display turns on and we use the brightness value recommended by
-        // ScreenOffBrightnessSensorController
-        clearInvocations(mHolder.screenOffBrightnessSensorController);
-        float brightness = 0.14f;
-        when(mHolder.screenOffBrightnessSensorController.getAutomaticScreenBrightness())
-                .thenReturn(brightness);
-        dpr.policy = DisplayPowerRequest.POLICY_BRIGHT;
-        when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON);
-        when(mHolder.automaticBrightnessController.getAutomaticScreenBrightness(
-                any(BrightnessEvent.class))).thenReturn(PowerManager.BRIGHTNESS_INVALID_FLOAT);
-
-        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
-        advanceTime(1); // Run updatePowerState
-
-        verify(mHolder.screenOffBrightnessSensorController, atLeastOnce())
-                .getAutomaticScreenBrightness();
-        verify(mHolder.animator).animateTo(eq(brightness), anyFloat(), anyFloat(), eq(false));
-    }
-
-    @Test
-    public void testSetScreenOffBrightnessSensorEnabled_DisplayIsInDoze() {
-        mContext.getOrCreateTestableResources().addOverride(
-                com.android.internal.R.bool.config_allowAutoBrightnessWhileDozing, false);
-        mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID);
-
-        Settings.System.putInt(mContext.getContentResolver(),
-                Settings.System.SCREEN_BRIGHTNESS_MODE,
-                Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
-
-        when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_DOZE);
-        DisplayPowerRequest dpr = new DisplayPowerRequest();
-        dpr.policy = DisplayPowerRequest.POLICY_DOZE;
-        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
-        advanceTime(1); // Run updatePowerState
-
-        verify(mHolder.screenOffBrightnessSensorController, atLeastOnce())
-                .setLightSensorEnabled(true);
-
-        // The display turns on and we use the brightness value recommended by
-        // ScreenOffBrightnessSensorController
-        clearInvocations(mHolder.screenOffBrightnessSensorController);
-        float brightness = 0.14f;
-        when(mHolder.screenOffBrightnessSensorController.getAutomaticScreenBrightness())
-                .thenReturn(brightness);
-        dpr.policy = DisplayPowerRequest.POLICY_BRIGHT;
-        when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON);
-        when(mHolder.automaticBrightnessController.getAutomaticScreenBrightness(
-                any(BrightnessEvent.class))).thenReturn(PowerManager.BRIGHTNESS_INVALID_FLOAT);
-
-        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
-        advanceTime(1); // Run updatePowerState
-
-        verify(mHolder.screenOffBrightnessSensorController, atLeastOnce())
-                .getAutomaticScreenBrightness();
-        verify(mHolder.animator).animateTo(eq(brightness), anyFloat(), anyFloat(), eq(false));
-    }
-
-    @Test
-    public void testSetScreenOffBrightnessSensorDisabled_AutoBrightnessIsDisabled() {
-        // Tests are set up with manual brightness by default, so no need to set it here.
-        DisplayPowerRequest dpr = new DisplayPowerRequest();
-        dpr.policy = DisplayPowerRequest.POLICY_OFF;
-        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
-        advanceTime(1); // Run updatePowerState
-
-        verify(mHolder.screenOffBrightnessSensorController, atLeastOnce())
-                .setLightSensorEnabled(false);
-    }
-
-    @Test
-    public void testSetScreenOffBrightnessSensorDisabled_DisplayIsDisabled() {
-        Settings.System.putInt(mContext.getContentResolver(),
-                Settings.System.SCREEN_BRIGHTNESS_MODE,
-                Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
-        mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID, /* isEnabled= */ false);
-
-        DisplayPowerRequest dpr = new DisplayPowerRequest();
-        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
-        advanceTime(1); // Run updatePowerState
-
-        verify(mHolder.screenOffBrightnessSensorController, atLeastOnce())
-                .setLightSensorEnabled(false);
-    }
-
-    @Test
-    public void testSetScreenOffBrightnessSensorDisabled_DisplayIsOn() {
-        DisplayPowerRequest dpr = new DisplayPowerRequest();
-        dpr.policy = DisplayPowerRequest.POLICY_BRIGHT;
-
-        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
-        advanceTime(1); // Run updatePowerState
-
-        verify(mHolder.screenOffBrightnessSensorController, atLeastOnce())
-                .setLightSensorEnabled(false);
-    }
-
-    @Test
-    public void testSetScreenOffBrightnessSensorDisabled_DisplayIsAFollower() {
-        DisplayPowerRequest dpr = new DisplayPowerRequest();
-        dpr.policy = DisplayPowerRequest.POLICY_OFF;
-
-        mHolder.dpc.onDisplayChanged(mHolder.hbmMetadata, /* leadDisplayId= */ 42);
-        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
-        advanceTime(1); // Run updatePowerState
-
-        verify(mHolder.screenOffBrightnessSensorController, atLeastOnce())
-                .setLightSensorEnabled(false);
-    }
-
-    @Test
-    public void testStopScreenOffBrightnessSensorControllerWhenDisplayDeviceChanges() {
-        // New display device
-        setUpDisplay(DISPLAY_ID, "new_unique_id", mHolder.display, mock(DisplayDevice.class),
-                mock(DisplayDeviceConfig.class), /* isEnabled= */ true);
-
-        mHolder.dpc.onDisplayChanged(mHolder.hbmMetadata, Layout.NO_LEAD_DISPLAY);
-        DisplayPowerRequest dpr = new DisplayPowerRequest();
-        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
-        advanceTime(1); // Run updatePowerState
-
-        verify(mHolder.screenOffBrightnessSensorController).stop();
-    }
-
-    @Test
-    public void testAutoBrightnessEnabled_DisplayIsOn() {
-        Settings.System.putInt(mContext.getContentResolver(),
-                Settings.System.SCREEN_BRIGHTNESS_MODE,
-                Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
-
-        DisplayPowerRequest dpr = new DisplayPowerRequest();
-        dpr.policy = DisplayPowerRequest.POLICY_BRIGHT;
-        when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON);
-        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
-        advanceTime(1); // Run updatePowerState
-
-        verify(mHolder.automaticBrightnessController).configure(
-                AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED,
-                /* configuration= */ null, PowerManager.BRIGHTNESS_INVALID_FLOAT,
-                /* userChangedBrightness= */ false, /* adjustment= */ 0,
-                /* userChangedAutoBrightnessAdjustment= */ false, DisplayPowerRequest.POLICY_BRIGHT,
-                /* shouldResetShortTermModel= */ false
-        );
-        verify(mHolder.hbmController)
-                .setAutoBrightnessEnabled(AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED);
-    }
-
-    @Test
-    public void testAutoBrightnessEnabled_DisplayIsInDoze() {
-        Settings.System.putInt(mContext.getContentResolver(),
-                Settings.System.SCREEN_BRIGHTNESS_MODE,
-                Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
-        mContext.getOrCreateTestableResources().addOverride(
-                com.android.internal.R.bool.config_allowAutoBrightnessWhileDozing, true);
-        mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID);
-
-        DisplayPowerRequest dpr = new DisplayPowerRequest();
-        dpr.policy = DisplayPowerRequest.POLICY_DOZE;
-        when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_DOZE);
-        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
-        advanceTime(1); // Run updatePowerState
-
-        verify(mHolder.automaticBrightnessController).configure(
-                AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED,
-                /* configuration= */ null, PowerManager.BRIGHTNESS_INVALID_FLOAT,
-                /* userChangedBrightness= */ false, /* adjustment= */ 0,
-                /* userChangedAutoBrightnessAdjustment= */ false, DisplayPowerRequest.POLICY_DOZE,
-                /* shouldResetShortTermModel= */ false
-        );
-        verify(mHolder.hbmController)
-                .setAutoBrightnessEnabled(AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED);
-    }
-
-    @Test
-    public void testAutoBrightnessDisabled_ManualBrightnessMode() {
-        Settings.System.putInt(mContext.getContentResolver(),
-                Settings.System.SCREEN_BRIGHTNESS_MODE,
-                Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL);
-
-        DisplayPowerRequest dpr = new DisplayPowerRequest();
-        dpr.policy = DisplayPowerRequest.POLICY_BRIGHT;
-        when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON);
-        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
-        advanceTime(1); // Run updatePowerState
-
-        // One triggered by the test, the other by handleBrightnessModeChange
-        verify(mHolder.automaticBrightnessController, times(2)).configure(
-                AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED,
-                /* configuration= */ null, PowerManager.BRIGHTNESS_INVALID_FLOAT,
-                /* userChangedBrightness= */ false, /* adjustment= */ 0,
-                /* userChangedAutoBrightnessAdjustment= */ false, DisplayPowerRequest.POLICY_BRIGHT,
-                /* shouldResetShortTermModel= */ false
-        );
-        verify(mHolder.hbmController, times(2))
-                .setAutoBrightnessEnabled(AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED);
-    }
-
-    @Test
-    public void testAutoBrightnessDisabled_DisplayIsOff() {
-        Settings.System.putInt(mContext.getContentResolver(),
-                Settings.System.SCREEN_BRIGHTNESS_MODE,
-                Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
-
-        DisplayPowerRequest dpr = new DisplayPowerRequest();
-        dpr.policy = DisplayPowerRequest.POLICY_OFF;
-        when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_OFF);
-        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
-        advanceTime(1); // Run updatePowerState
-
-        verify(mHolder.automaticBrightnessController).configure(
-                AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE,
-                /* configuration= */ null, PowerManager.BRIGHTNESS_INVALID_FLOAT,
-                /* userChangedBrightness= */ false, /* adjustment= */ 0,
-                /* userChangedAutoBrightnessAdjustment= */ false, DisplayPowerRequest.POLICY_OFF,
-                /* shouldResetShortTermModel= */ false
-        );
-        verify(mHolder.hbmController).setAutoBrightnessEnabled(
-                AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE);
-    }
-
-    @Test
-    public void testAutoBrightnessDisabled_DisplayIsInDoze() {
-        Settings.System.putInt(mContext.getContentResolver(),
-                Settings.System.SCREEN_BRIGHTNESS_MODE,
-                Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
-        mContext.getOrCreateTestableResources().addOverride(
-                com.android.internal.R.bool.config_allowAutoBrightnessWhileDozing, false);
-        mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID);
-
-        DisplayPowerRequest dpr = new DisplayPowerRequest();
-        dpr.policy = DisplayPowerRequest.POLICY_DOZE;
-        when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_DOZE);
-        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
-        advanceTime(1); // Run updatePowerState
-
-        verify(mHolder.automaticBrightnessController).configure(
-                AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE,
-                /* configuration= */ null, PowerManager.BRIGHTNESS_INVALID_FLOAT,
-                /* userChangedBrightness= */ false, /* adjustment= */ 0,
-                /* userChangedAutoBrightnessAdjustment= */ false, DisplayPowerRequest.POLICY_DOZE,
-                /* shouldResetShortTermModel= */ false
-        );
-        verify(mHolder.hbmController).setAutoBrightnessEnabled(
-                AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE);
-    }
-
-    @Test
-    public void testAutoBrightnessDisabled_FollowerDisplay() {
-        Settings.System.putInt(mContext.getContentResolver(),
-                Settings.System.SCREEN_BRIGHTNESS_MODE,
-                Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
-        mHolder.dpc.setBrightnessToFollow(0.3f, -1, 0, /* slowChange= */ false);
-
-        DisplayPowerRequest dpr = new DisplayPowerRequest();
-        dpr.policy = DisplayPowerRequest.POLICY_BRIGHT;
-        when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON);
-        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
-        advanceTime(1); // Run updatePowerState
-
-        // One triggered by the test, the other by handleBrightnessModeChange
-        verify(mHolder.automaticBrightnessController, times(2)).configure(
-                AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED,
-                /* configuration= */ null, PowerManager.BRIGHTNESS_INVALID_FLOAT,
-                /* userChangedBrightness= */ false, /* adjustment= */ 0,
-                /* userChangedAutoBrightnessAdjustment= */ false, DisplayPowerRequest.POLICY_BRIGHT,
-                /* shouldResetShortTermModel= */ false
-        );
-
-        // HBM should be allowed for the follower display
-        verify(mHolder.hbmController)
-                .setAutoBrightnessEnabled(AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED);
-    }
-
-    @Test
-    public void testBrightnessNitsPersistWhenDisplayDeviceChanges() {
-        float brightness = 0.3f;
-        float nits = 500;
-        mContext.getOrCreateTestableResources().addOverride(
-                com.android.internal.R.bool.config_persistBrightnessNitsForDefaultDisplay,
-                true);
-        mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID);
-        when(mHolder.automaticBrightnessController.convertToNits(brightness)).thenReturn(nits);
-
-        mHolder.dpc.setBrightness(brightness);
-        verify(mHolder.brightnessSetting).setBrightnessNitsForDefaultDisplay(nits);
-
-        float newBrightness = 0.4f;
-        when(mHolder.brightnessSetting.getBrightnessNitsForDefaultDisplay()).thenReturn(nits);
-        when(mHolder.automaticBrightnessController.getBrightnessFromNits(nits))
-                .thenReturn(newBrightness);
-        // New display device
-        setUpDisplay(DISPLAY_ID, "new_unique_id", mHolder.display, mock(DisplayDevice.class),
-                mock(DisplayDeviceConfig.class), /* isEnabled= */ true);
-        mHolder.dpc.onDisplayChanged(mHolder.hbmMetadata, Layout.NO_LEAD_DISPLAY);
-        DisplayPowerRequest dpr = new DisplayPowerRequest();
-        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
-        advanceTime(1); // Run updatePowerState
-        // One triggered by handleBrightnessModeChange, another triggered by onDisplayChanged
-        verify(mHolder.animator, times(2)).animateTo(eq(newBrightness), anyFloat(), anyFloat(),
-                eq(false));
-    }
-
-    @Test
-    public void testShortTermModelPersistsWhenDisplayDeviceChanges() {
-        float lux = 2000;
-        float nits = 500;
-        when(mHolder.automaticBrightnessController.getUserLux()).thenReturn(lux);
-        when(mHolder.automaticBrightnessController.getUserNits()).thenReturn(nits);
-        DisplayPowerRequest dpr = new DisplayPowerRequest();
-        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
-        advanceTime(1);
-        clearInvocations(mHolder.injector);
-
-        // New display device
-        setUpDisplay(DISPLAY_ID, "new_unique_id", mHolder.display, mock(DisplayDevice.class),
-                mock(DisplayDeviceConfig.class), /* isEnabled= */ true);
-        mHolder.dpc.onDisplayChanged(mHolder.hbmMetadata, Layout.NO_LEAD_DISPLAY);
-        advanceTime(1);
-
-        verify(mHolder.injector).getAutomaticBrightnessController(
-                any(AutomaticBrightnessController.Callbacks.class),
-                any(Looper.class),
-                eq(mSensorManagerMock),
-                /* lightSensor= */ any(),
-                /* brightnessMappingStrategyMap= */ any(SparseArray.class),
-                /* lightSensorWarmUpTime= */ anyInt(),
-                /* brightnessMin= */ anyFloat(),
-                /* brightnessMax= */ anyFloat(),
-                /* dozeScaleFactor */ anyFloat(),
-                /* lightSensorRate= */ anyInt(),
-                /* initialLightSensorRate= */ anyInt(),
-                /* brighteningLightDebounceConfig */ anyLong(),
-                /* darkeningLightDebounceConfig */ anyLong(),
-                /* brighteningLightDebounceConfigIdle= */ anyLong(),
-                /* darkeningLightDebounceConfigIdle= */ anyLong(),
-                /* resetAmbientLuxAfterWarmUpConfig= */ anyBoolean(),
-                any(HysteresisLevels.class),
-                any(HysteresisLevels.class),
-                any(HysteresisLevels.class),
-                any(HysteresisLevels.class),
-                eq(mContext),
-                any(BrightnessRangeController.class),
-                any(BrightnessThrottler.class),
-                /* ambientLightHorizonShort= */ anyInt(),
-                /* ambientLightHorizonLong= */ anyInt(),
-                eq(lux),
-                eq(nits)
-        );
-    }
-
-    @Test
-    public void testUpdateBrightnessThrottlingDataId() {
-        mHolder.display.getDisplayInfoLocked().thermalBrightnessThrottlingDataId =
-                "throttling-data-id";
-        clearInvocations(mHolder.display.getPrimaryDisplayDeviceLocked().getDisplayDeviceConfig());
-
-        mHolder.dpc.onDisplayChanged(mHolder.hbmMetadata, Layout.NO_LEAD_DISPLAY);
-        DisplayPowerRequest dpr = new DisplayPowerRequest();
-        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
-        advanceTime(1); // Run updatePowerState
-
-        verify(mHolder.display.getPrimaryDisplayDeviceLocked().getDisplayDeviceConfig())
-                .getThermalBrightnessThrottlingDataMapByThrottlingId();
-    }
-
-    @Test
-    public void testSetBrightness_BrightnessShouldBeClamped() {
-        float clampedBrightness = 0.6f;
-        when(mHolder.hbmController.getCurrentBrightnessMax()).thenReturn(clampedBrightness);
-
-        mHolder.dpc.setBrightness(PowerManager.BRIGHTNESS_MAX);
-
-        verify(mHolder.brightnessSetting).setBrightness(clampedBrightness);
-    }
-
-    @Test
-    public void testDwbcCallsHappenOnHandler() {
-        mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID);
-
-        mHolder.dpc.setAutomaticScreenBrightnessMode(AUTO_BRIGHTNESS_MODE_IDLE);
-        verify(mDisplayWhiteBalanceControllerMock, never()).setStrongModeEnabled(true);
-
-        // dispatch handler looper
-        advanceTime(1);
-        verify(mDisplayWhiteBalanceControllerMock, times(1)).setStrongModeEnabled(true);
-    }
-
-    @Test
-    public void testRampRatesIdle() {
-        Settings.System.putInt(mContext.getContentResolver(),
-                Settings.System.SCREEN_BRIGHTNESS_MODE,
-                Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
-        float brightness = 0.6f;
-        when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON);
-        when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
-        when(mHolder.automaticBrightnessController.isInIdleMode()).thenReturn(true);
-        when(mHolder.automaticBrightnessController.getAutomaticScreenBrightness(
-                any(BrightnessEvent.class))).thenReturn(brightness);
-
-        DisplayPowerRequest dpr = new DisplayPowerRequest();
-        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
-        advanceTime(1); // Run updatePowerState
-
-        verify(mHolder.animator).animateTo(eq(brightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
-
-        when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(brightness);
-        brightness = 0.05f;
-        when(mHolder.automaticBrightnessController.getAutomaticScreenBrightness(
-                any(BrightnessEvent.class))).thenReturn(brightness);
-
-        mHolder.dpc.updateBrightness();
-        advanceTime(1); // Run updatePowerState
-
-        // The second time, the animation rate should be slow
-        verify(mHolder.animator).animateTo(eq(brightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_SLOW_DECREASE_IDLE), eq(false));
-
-        brightness = 0.9f;
-        when(mHolder.automaticBrightnessController.getAutomaticScreenBrightness(
-                any(BrightnessEvent.class))).thenReturn(brightness);
-
-        mHolder.dpc.updateBrightness();
-        advanceTime(1); // Run updatePowerState
-        // The third time, the animation rate should be slow
-        verify(mHolder.animator).animateTo(eq(brightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_SLOW_INCREASE_IDLE), eq(false));
-    }
-
-    @Test
-    public void testRampRateForHdrContent_HdrClamperOff() {
-        float hdrBrightness = 0.8f;
-        float clampedBrightness = 0.6f;
-        float transitionRate = 1.5f;
-
-        DisplayPowerRequest dpr = new DisplayPowerRequest();
-        when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
-        when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(.2f);
-        when(mHolder.displayPowerState.getSdrScreenBrightness()).thenReturn(.1f);
-        when(mHolder.hbmController.getHighBrightnessMode()).thenReturn(
-                BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR);
-        when(mHolder.hbmController.getHdrBrightnessValue()).thenReturn(hdrBrightness);
-        when(mHolder.hdrClamper.getMaxBrightness()).thenReturn(clampedBrightness);
-        when(mHolder.hdrClamper.getTransitionRate()).thenReturn(transitionRate);
-
-        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
-        advanceTime(1); // Run updatePowerState
-
-        verify(mHolder.animator, atLeastOnce()).animateTo(eq(hdrBrightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
-    }
-
-    @Test
-    public void testRampRateForHdrContent_HdrClamperOn() {
-        float clampedBrightness = 0.6f;
-        float transitionRate = 1.5f;
-        when(mDisplayManagerFlagsMock.isHdrClamperEnabled()).thenReturn(true);
-        mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID, /* isEnabled= */ true);
-
-        DisplayPowerRequest dpr = new DisplayPowerRequest();
-        when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
-        when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(.2f);
-        when(mHolder.displayPowerState.getSdrScreenBrightness()).thenReturn(.1f);
-        when(mHolder.hbmController.getHighBrightnessMode()).thenReturn(
-                BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR);
-        when(mHolder.hbmController.getHdrBrightnessValue()).thenReturn(PowerManager.BRIGHTNESS_MAX);
-        when(mHolder.hdrClamper.getMaxBrightness()).thenReturn(clampedBrightness);
-        when(mHolder.hdrClamper.getTransitionRate()).thenReturn(transitionRate);
-
-        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
-        advanceTime(1); // Run updatePowerState
-
-        verify(mHolder.animator, atLeastOnce()).animateTo(eq(clampedBrightness), anyFloat(),
-                eq(transitionRate), eq(true));
-    }
-
-    @Test
-    public void testRampRateForClampersControllerApplied() {
-        float transitionRate = 1.5f;
-        mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID);
-        DisplayPowerRequest dpr = new DisplayPowerRequest();
-        when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
-        when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(.2f);
-        when(mHolder.displayPowerState.getSdrScreenBrightness()).thenReturn(.1f);
-        when(mHolder.clamperController.clamp(any(), anyFloat(), anyBoolean())).thenAnswer(
-                invocation -> DisplayBrightnessState.builder()
-                        .setIsSlowChange(invocation.getArgument(2))
-                        .setBrightness(invocation.getArgument(1))
-                        .setMaxBrightness(PowerManager.BRIGHTNESS_MAX)
-                        .setCustomAnimationRate(transitionRate).build());
-
-        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
-        advanceTime(1); // Run updatePowerState
-
-        verify(mHolder.animator, atLeastOnce()).animateTo(anyFloat(), anyFloat(),
-                eq(transitionRate), anyBoolean());
-    }
-
-    @Test
-    public void testRampRateForClampersControllerNotApplied_ifDoze() {
-        float transitionRate = 1.5f;
-        mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID);
-        DisplayPowerRequest dpr = new DisplayPowerRequest();
-        dpr.policy = DisplayPowerRequest.POLICY_DOZE;
-        dpr.dozeScreenState = Display.STATE_UNKNOWN;
-        when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
-        when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(.2f);
-        when(mHolder.displayPowerState.getSdrScreenBrightness()).thenReturn(.1f);
-        when(mHolder.clamperController.clamp(any(), anyFloat(), anyBoolean())).thenAnswer(
-                invocation -> DisplayBrightnessState.builder()
-                        .setIsSlowChange(invocation.getArgument(2))
-                        .setBrightness(invocation.getArgument(1))
-                        .setMaxBrightness(PowerManager.BRIGHTNESS_MAX)
-                        .setCustomAnimationRate(transitionRate).build());
-
-        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
-        advanceTime(1); // Run updatePowerState
-
-        verify(mHolder.animator, atLeastOnce()).animateTo(anyFloat(), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_FAST_DECREASE), anyBoolean());
-        verify(mHolder.animator, never()).animateTo(anyFloat(), anyFloat(),
-                eq(transitionRate), anyBoolean());
-    }
-
-    @Test
-    @RequiresFlagsDisabled(Flags.FLAG_ENABLE_ADAPTIVE_TONE_IMPROVEMENTS_1)
-    public void testRampMaxTimeInteractiveThenIdle() {
-        // Send a display power request
-        DisplayPowerRequest dpr = new DisplayPowerRequest();
-        dpr.policy = DisplayPowerRequest.POLICY_BRIGHT;
-        dpr.useProximitySensor = true;
-        mHolder.dpc.requestPowerState(dpr, false /* waitForNegativeProximity */);
-
-        // Run updatePowerState
-        advanceTime(1);
-
-        setUpDisplay(DISPLAY_ID, "new_unique_id", mHolder.display, mock(DisplayDevice.class),
-                mHolder.config, /* isEnabled= */ true);
-        verify(mHolder.animator).setAnimationTimeLimits(BRIGHTNESS_RAMP_INCREASE_MAX,
-                BRIGHTNESS_RAMP_DECREASE_MAX);
-
-        // switch to idle mode
-        mHolder.dpc.setAutomaticScreenBrightnessMode(AUTO_BRIGHTNESS_MODE_IDLE);
-        advanceTime(1);
-
-        // A second time, when switching to idle mode.
-        verify(mHolder.animator, times(2)).setAnimationTimeLimits(BRIGHTNESS_RAMP_INCREASE_MAX,
-                BRIGHTNESS_RAMP_DECREASE_MAX);
-    }
-
-    @Test
-    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_ADAPTIVE_TONE_IMPROVEMENTS_1)
-    public void testRampMaxTimeInteractiveThenIdle_DifferentValues() {
-        when(mDisplayManagerFlagsMock.isAdaptiveTone1Enabled()).thenReturn(true);
-        mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID, /* isEnabled= */ true);
-
-        // Send a display power request
-        DisplayPowerRequest dpr = new DisplayPowerRequest();
-        dpr.policy = DisplayPowerRequest.POLICY_BRIGHT;
-        dpr.useProximitySensor = true;
-        mHolder.dpc.requestPowerState(dpr, false /* waitForNegativeProximity */);
-
-        // Run updatePowerState
-        advanceTime(1);
-
-        setUpDisplay(DISPLAY_ID, "new_unique_id", mHolder.display, mock(DisplayDevice.class),
-                mHolder.config, /* isEnabled= */ true);
-        verify(mHolder.animator).setAnimationTimeLimits(BRIGHTNESS_RAMP_INCREASE_MAX,
-                BRIGHTNESS_RAMP_DECREASE_MAX);
-
-        // switch to idle mode
-        mHolder.dpc.setAutomaticScreenBrightnessMode(AUTO_BRIGHTNESS_MODE_IDLE);
-        advanceTime(1);
-
-        // A second time, when switching to idle mode.
-        verify(mHolder.animator).setAnimationTimeLimits(BRIGHTNESS_RAMP_INCREASE_MAX_IDLE,
-                BRIGHTNESS_RAMP_DECREASE_MAX_IDLE);
-    }
-
-    @Test
-    @RequiresFlagsDisabled(Flags.FLAG_ENABLE_ADAPTIVE_TONE_IMPROVEMENTS_1)
-    public void testRampMaxTimeIdle() {
-        // Send a display power request
-        DisplayPowerRequest dpr = new DisplayPowerRequest();
-        dpr.policy = DisplayPowerRequest.POLICY_BRIGHT;
-        dpr.useProximitySensor = true;
-        mHolder.dpc.requestPowerState(dpr, false /* waitForNegativeProximity */);
-        // Run updatePowerState
-        advanceTime(1);
-        // Once on setup
-        verify(mHolder.animator).setAnimationTimeLimits(BRIGHTNESS_RAMP_INCREASE_MAX,
-                BRIGHTNESS_RAMP_DECREASE_MAX);
-
-        setUpDisplay(DISPLAY_ID, "new_unique_id", mHolder.display, mock(DisplayDevice.class),
-                mHolder.config, /* isEnabled= */ true);
-
-        // switch to idle mode
-        mHolder.dpc.setAutomaticScreenBrightnessMode(AUTO_BRIGHTNESS_MODE_IDLE);
-
-        // A second time when switching to idle mode.
-        verify(mHolder.animator, times(2)).setAnimationTimeLimits(BRIGHTNESS_RAMP_INCREASE_MAX,
-                BRIGHTNESS_RAMP_DECREASE_MAX);
-    }
-
-    @Test
-    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_ADAPTIVE_TONE_IMPROVEMENTS_1)
-    public void testRampMaxTimeIdle_DifferentValues() {
-        when(mDisplayManagerFlagsMock.isAdaptiveTone1Enabled()).thenReturn(true);
-        mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID, /* isEnabled= */ true);
-
-        // Send a display power request
-        DisplayPowerRequest dpr = new DisplayPowerRequest();
-        dpr.policy = DisplayPowerRequest.POLICY_BRIGHT;
-        dpr.useProximitySensor = true;
-        mHolder.dpc.requestPowerState(dpr, false /* waitForNegativeProximity */);
-
-        // Run updatePowerState
-        advanceTime(1);
-
-        setUpDisplay(DISPLAY_ID, "new_unique_id", mHolder.display, mock(DisplayDevice.class),
-                mHolder.config, /* isEnabled= */ true);
-
-        // switch to idle mode
-        mHolder.dpc.setAutomaticScreenBrightnessMode(AUTO_BRIGHTNESS_MODE_IDLE);
-
-        verify(mHolder.animator).setAnimationTimeLimits(BRIGHTNESS_RAMP_INCREASE_MAX_IDLE,
-                BRIGHTNESS_RAMP_DECREASE_MAX_IDLE);
-    }
-
-    @Test
-    public void testDozeScreenStateOverride_toSupportedOffloadStateFromDoze_DisplayStateChanges() {
-        // set up.
-        int initState = Display.STATE_DOZE;
-        int supportedTargetState = Display.STATE_DOZE_SUSPEND;
-        mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID);
-        when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
-        doAnswer(invocation -> {
-            when(mHolder.displayPowerState.getScreenState()).thenReturn(invocation.getArgument(0));
-            return null;
-        }).when(mHolder.displayPowerState).setScreenState(anyInt());
-        mHolder.dpc.setDisplayOffloadSession(mDisplayOffloadSession);
-
-        // start with DOZE.
-        when(mHolder.displayPowerState.getScreenState()).thenReturn(initState);
-        DisplayPowerRequest dpr = new DisplayPowerRequest();
-        dpr.policy = DisplayPowerRequest.POLICY_DOZE;
-        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
-        advanceTime(1); // Run updatePowerState
-
-        mHolder.dpc.overrideDozeScreenState(supportedTargetState);
-        advanceTime(1); // Run updatePowerState
-
-        verify(mHolder.displayPowerState).setScreenState(supportedTargetState);
-    }
-
-    @Test
-    public void testDozeScreenStateOverride_toUnSupportedOffloadStateFromDoze_stateRemains() {
-        // set up.
-        int initState = Display.STATE_DOZE;
-        int unSupportedTargetState = Display.STATE_ON;
-        mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID);
-        when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
-        doAnswer(invocation -> {
-            when(mHolder.displayPowerState.getScreenState()).thenReturn(invocation.getArgument(0));
-            return null;
-        }).when(mHolder.displayPowerState).setScreenState(anyInt());
-        mHolder.dpc.setDisplayOffloadSession(mDisplayOffloadSession);
-
-        // start with DOZE.
-        when(mHolder.displayPowerState.getScreenState()).thenReturn(initState);
-        DisplayPowerRequest dpr = new DisplayPowerRequest();
-        dpr.policy = DisplayPowerRequest.POLICY_DOZE;
-        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
-        advanceTime(1); // Run updatePowerState
-
-        mHolder.dpc.overrideDozeScreenState(unSupportedTargetState);
-        advanceTime(1); // Run updatePowerState
-
-        verify(mHolder.displayPowerState, never()).setScreenState(anyInt());
-    }
-
-    @Test
-    public void testDozeScreenStateOverride_toSupportedOffloadStateFromOFF_stateRemains() {
-        // set up.
-        int initState = Display.STATE_OFF;
-        int supportedTargetState = Display.STATE_DOZE_SUSPEND;
-        mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID);
-        doAnswer(invocation -> {
-            when(mHolder.displayPowerState.getScreenState()).thenReturn(invocation.getArgument(0));
-            return null;
-        }).when(mHolder.displayPowerState).setScreenState(anyInt());
-        mHolder.dpc.setDisplayOffloadSession(mDisplayOffloadSession);
-
-        // start with OFF.
-        when(mHolder.displayPowerState.getScreenState()).thenReturn(initState);
-        DisplayPowerRequest dpr = new DisplayPowerRequest();
-        dpr.policy = DisplayPowerRequest.POLICY_OFF;
-        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
-        advanceTime(1); // Run updatePowerState
-
-        mHolder.dpc.overrideDozeScreenState(supportedTargetState);
-        advanceTime(1); // Run updatePowerState
-
-        verify(mHolder.displayPowerState, never()).setScreenState(anyInt());
-    }
-
-    @Test
-    public void testBrightnessFromOffload() {
-        when(mDisplayManagerFlagsMock.isDisplayOffloadEnabled()).thenReturn(true);
-        mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID);
-        Settings.System.putInt(mContext.getContentResolver(),
-                Settings.System.SCREEN_BRIGHTNESS_MODE,
-                Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
-        float brightness = 0.34f;
-        when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
-        when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON);
-        when(mHolder.automaticBrightnessController.getAutomaticScreenBrightness(
-                any(BrightnessEvent.class))).thenReturn(PowerManager.BRIGHTNESS_INVALID_FLOAT);
-        mHolder.dpc.setDisplayOffloadSession(mDisplayOffloadSession);
-
-        mHolder.dpc.setBrightnessFromOffload(brightness);
-        DisplayPowerRequest dpr = new DisplayPowerRequest();
-        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
-        advanceTime(1); // Run updatePowerState
-
-        // One triggered by handleBrightnessModeChange, another triggered by
-        // setBrightnessFromOffload
-        verify(mHolder.animator, times(2)).animateTo(eq(brightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
-    }
-
-    @Test
-    public void testSwitchToDozeAutoBrightnessMode() {
-        when(mDisplayManagerFlagsMock.areAutoBrightnessModesEnabled()).thenReturn(true);
-        when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_DOZE);
-
-        DisplayPowerRequest dpr = new DisplayPowerRequest();
-        dpr.policy = DisplayPowerRequest.POLICY_DOZE;
-        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
-        advanceTime(1); // Run updatePowerState
-
-        // One triggered by handleBrightnessModeChange, another triggered by requestPowerState
-        verify(mHolder.automaticBrightnessController, times(2))
-                .switchMode(AUTO_BRIGHTNESS_MODE_DOZE);
-
-        // Back to default mode
-        when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON);
-        dpr.policy = DisplayPowerRequest.POLICY_BRIGHT;
-        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
-        advanceTime(1); // Run updatePowerState
-
-        verify(mHolder.automaticBrightnessController).switchMode(AUTO_BRIGHTNESS_MODE_DEFAULT);
-    }
-
-    @Test
-    public void testDoesNotSwitchFromIdleToDozeAutoBrightnessMode() {
-        when(mDisplayManagerFlagsMock.areAutoBrightnessModesEnabled()).thenReturn(true);
-        when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_DOZE);
-        when(mHolder.automaticBrightnessController.isInIdleMode()).thenReturn(true);
-
-        DisplayPowerRequest dpr = new DisplayPowerRequest();
-        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
-        advanceTime(1); // Run updatePowerState
-
-        verify(mHolder.automaticBrightnessController, never())
-                .switchMode(AUTO_BRIGHTNESS_MODE_DOZE);
-    }
-
-    @Test
-    public void testDoesNotSwitchDozeAutoBrightnessModeIfFeatureFlagOff() {
-        when(mDisplayManagerFlagsMock.areAutoBrightnessModesEnabled()).thenReturn(false);
-        when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_DOZE);
-
-        DisplayPowerRequest dpr = new DisplayPowerRequest();
-        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
-        advanceTime(1); // Run updatePowerState
-
-        verify(mHolder.automaticBrightnessController, never())
-                .switchMode(AUTO_BRIGHTNESS_MODE_DOZE);
-    }
-
-    /**
-     * Creates a mock and registers it to {@link LocalServices}.
-     */
-    private static <T> void addLocalServiceMock(Class<T> clazz, T mock) {
-        LocalServices.removeServiceForTest(clazz);
-        LocalServices.addService(clazz, mock);
-    }
-
-    private void advanceTime(long timeMs) {
-        mClock.fastForward(timeMs);
-        mTestLooper.dispatchAll();
-    }
-
-    private void setUpSensors() throws Exception {
-        mProxSensor = TestUtils.createSensor(Sensor.TYPE_PROXIMITY, Sensor.STRING_TYPE_PROXIMITY,
-                PROX_SENSOR_MAX_RANGE);
-        Sensor screenOffBrightnessSensor = TestUtils.createSensor(
-                Sensor.TYPE_LIGHT, Sensor.STRING_TYPE_LIGHT);
-        when(mSensorManagerMock.getSensorList(eq(Sensor.TYPE_ALL)))
-                .thenReturn(List.of(mProxSensor, screenOffBrightnessSensor));
-    }
-
-    private SensorEventListener getSensorEventListener(Sensor sensor) {
-        verify(mSensorManagerMock).registerListener(mSensorEventListenerCaptor.capture(),
-                eq(sensor), eq(SensorManager.SENSOR_DELAY_NORMAL), isA(Handler.class));
-        return mSensorEventListenerCaptor.getValue();
-    }
-
-    private void setUpDisplay(int displayId, String uniqueId, LogicalDisplay logicalDisplayMock,
-            DisplayDevice displayDeviceMock, DisplayDeviceConfig displayDeviceConfigMock,
-            boolean isEnabled) {
-        DisplayInfo info = new DisplayInfo();
-        DisplayDeviceInfo deviceInfo = new DisplayDeviceInfo();
-        deviceInfo.uniqueId = uniqueId;
-
-        when(logicalDisplayMock.getDisplayIdLocked()).thenReturn(displayId);
-        when(logicalDisplayMock.getPrimaryDisplayDeviceLocked()).thenReturn(displayDeviceMock);
-        when(logicalDisplayMock.getDisplayInfoLocked()).thenReturn(info);
-        when(logicalDisplayMock.isEnabledLocked()).thenReturn(isEnabled);
-        when(logicalDisplayMock.isInTransitionLocked()).thenReturn(false);
-        when(displayDeviceMock.getDisplayDeviceInfoLocked()).thenReturn(deviceInfo);
-        when(displayDeviceMock.getUniqueId()).thenReturn(uniqueId);
-        when(displayDeviceMock.getDisplayDeviceConfig()).thenReturn(displayDeviceConfigMock);
-        when(displayDeviceConfigMock.getProximitySensor()).thenReturn(
-                new SensorData(Sensor.STRING_TYPE_PROXIMITY, null));
-        when(displayDeviceConfigMock.getNits()).thenReturn(new float[]{2, 500});
-        when(displayDeviceConfigMock.isAutoBrightnessAvailable()).thenReturn(true);
-        when(displayDeviceConfigMock.getAmbientLightSensor()).thenReturn(
-                new SensorData());
-        when(displayDeviceConfigMock.getScreenOffBrightnessSensor()).thenReturn(
-                new SensorData(Sensor.STRING_TYPE_LIGHT, null));
-        when(displayDeviceConfigMock.getScreenOffBrightnessSensorValueToLux())
-                .thenReturn(new int[0]);
-
-        when(displayDeviceConfigMock.getBrightnessRampFastDecrease())
-                .thenReturn(BRIGHTNESS_RAMP_RATE_FAST_DECREASE);
-        when(displayDeviceConfigMock.getBrightnessRampFastIncrease())
-                .thenReturn(BRIGHTNESS_RAMP_RATE_FAST_INCREASE);
-        when(displayDeviceConfigMock.getBrightnessRampSlowDecrease())
-                .thenReturn(BRIGHTNESS_RAMP_RATE_SLOW_DECREASE);
-        when(displayDeviceConfigMock.getBrightnessRampSlowIncrease())
-                .thenReturn(BRIGHTNESS_RAMP_RATE_SLOW_INCREASE);
-        when(displayDeviceConfigMock.getBrightnessRampSlowIncreaseIdle())
-                .thenReturn(BRIGHTNESS_RAMP_RATE_SLOW_INCREASE_IDLE);
-        when(displayDeviceConfigMock.getBrightnessRampSlowDecreaseIdle())
-                .thenReturn(BRIGHTNESS_RAMP_RATE_SLOW_DECREASE_IDLE);
-
-        when(displayDeviceConfigMock.getBrightnessRampIncreaseMaxMillis())
-                .thenReturn(BRIGHTNESS_RAMP_INCREASE_MAX);
-        when(displayDeviceConfigMock.getBrightnessRampDecreaseMaxMillis())
-                .thenReturn(BRIGHTNESS_RAMP_DECREASE_MAX);
-        when(displayDeviceConfigMock.getBrightnessRampIncreaseMaxIdleMillis())
-                .thenReturn(BRIGHTNESS_RAMP_INCREASE_MAX_IDLE);
-        when(displayDeviceConfigMock.getBrightnessRampDecreaseMaxIdleMillis())
-                .thenReturn(BRIGHTNESS_RAMP_DECREASE_MAX_IDLE);
-    }
-
-    private DisplayPowerControllerHolder createDisplayPowerController(int displayId,
-            String uniqueId) {
-        return createDisplayPowerController(displayId, uniqueId, /* isEnabled= */ true);
-    }
-
-    private DisplayPowerControllerHolder createDisplayPowerController(int displayId,
-            String uniqueId, boolean isEnabled) {
-        final DisplayPowerState displayPowerState = mock(DisplayPowerState.class);
-        final DualRampAnimator<DisplayPowerState> animator = mock(DualRampAnimator.class);
-        final AutomaticBrightnessController automaticBrightnessController =
-                mock(AutomaticBrightnessController.class);
-        final WakelockController wakelockController = mock(WakelockController.class);
-        final BrightnessMappingStrategy brightnessMappingStrategy =
-                mock(BrightnessMappingStrategy.class);
-        final HysteresisLevels hysteresisLevels = mock(HysteresisLevels.class);
-        final ScreenOffBrightnessSensorController screenOffBrightnessSensorController =
-                mock(ScreenOffBrightnessSensorController.class);
-        final HighBrightnessModeController hbmController = mock(HighBrightnessModeController.class);
-        final HdrClamper hdrClamper = mock(HdrClamper.class);
-        BrightnessClamperController clamperController = mock(BrightnessClamperController.class);
-
-        when(hbmController.getCurrentBrightnessMax()).thenReturn(PowerManager.BRIGHTNESS_MAX);
-        when(clamperController.clamp(any(), anyFloat(), anyBoolean())).thenAnswer(
-                invocation -> DisplayBrightnessState.builder()
-                        .setIsSlowChange(invocation.getArgument(2))
-                        .setBrightness(invocation.getArgument(1))
-                        .setMaxBrightness(PowerManager.BRIGHTNESS_MAX)
-                        .setCustomAnimationRate(-1).build());
-
-        TestInjector injector = spy(new TestInjector(displayPowerState, animator,
-                automaticBrightnessController, wakelockController, brightnessMappingStrategy,
-                hysteresisLevels, screenOffBrightnessSensorController, hbmController, hdrClamper,
-                clamperController, mDisplayManagerFlagsMock));
-
-        final LogicalDisplay display = mock(LogicalDisplay.class);
-        final DisplayDevice device = mock(DisplayDevice.class);
-        final HighBrightnessModeMetadata hbmMetadata = mock(HighBrightnessModeMetadata.class);
-        final BrightnessSetting brightnessSetting = mock(BrightnessSetting.class);
-        final DisplayDeviceConfig config = mock(DisplayDeviceConfig.class);
-
-        setUpDisplay(displayId, uniqueId, display, device, config, isEnabled);
-
-        final DisplayPowerController2 dpc = new DisplayPowerController2(
-                mContext, injector, mDisplayPowerCallbacksMock, mHandler,
-                mSensorManagerMock, mDisplayBlankerMock, display,
-                mBrightnessTrackerMock, brightnessSetting, () -> {
-        },
-                hbmMetadata, /* bootCompleted= */ false, mDisplayManagerFlagsMock);
-
-        return new DisplayPowerControllerHolder(dpc, display, displayPowerState, brightnessSetting,
-                animator, automaticBrightnessController, wakelockController,
-                screenOffBrightnessSensorController, hbmController, hdrClamper, clamperController,
-                hbmMetadata, brightnessMappingStrategy, injector, config);
-    }
-
-    /**
-     * A class for holding a DisplayPowerController under test and all the mocks specifically
-     * related to it.
-     */
-    private static class DisplayPowerControllerHolder {
-        public final DisplayPowerController2 dpc;
-        public final LogicalDisplay display;
-        public final DisplayPowerState displayPowerState;
-        public final BrightnessSetting brightnessSetting;
-        public final DualRampAnimator<DisplayPowerState> animator;
-        public final AutomaticBrightnessController automaticBrightnessController;
-        public final WakelockController wakelockController;
-        public final ScreenOffBrightnessSensorController screenOffBrightnessSensorController;
-        public final HighBrightnessModeController hbmController;
-
-        public final HdrClamper hdrClamper;
-        public final BrightnessClamperController clamperController;
-        public final HighBrightnessModeMetadata hbmMetadata;
-        public final BrightnessMappingStrategy brightnessMappingStrategy;
-        public final DisplayPowerController2.Injector injector;
-        public final DisplayDeviceConfig config;
-
-        DisplayPowerControllerHolder(DisplayPowerController2 dpc, LogicalDisplay display,
-                DisplayPowerState displayPowerState, BrightnessSetting brightnessSetting,
-                DualRampAnimator<DisplayPowerState> animator,
-                AutomaticBrightnessController automaticBrightnessController,
-                WakelockController wakelockController,
-                ScreenOffBrightnessSensorController screenOffBrightnessSensorController,
-                HighBrightnessModeController hbmController,
-                HdrClamper hdrClamper,
-                BrightnessClamperController clamperController,
-                HighBrightnessModeMetadata hbmMetadata,
-                BrightnessMappingStrategy brightnessMappingStrategy,
-                DisplayPowerController2.Injector injector,
-                DisplayDeviceConfig config) {
-            this.dpc = dpc;
-            this.display = display;
-            this.displayPowerState = displayPowerState;
-            this.brightnessSetting = brightnessSetting;
-            this.animator = animator;
-            this.automaticBrightnessController = automaticBrightnessController;
-            this.wakelockController = wakelockController;
-            this.screenOffBrightnessSensorController = screenOffBrightnessSensorController;
-            this.hbmController = hbmController;
-            this.hdrClamper = hdrClamper;
-            this.clamperController = clamperController;
-            this.hbmMetadata = hbmMetadata;
-            this.brightnessMappingStrategy = brightnessMappingStrategy;
-            this.injector = injector;
-            this.config = config;
-        }
-    }
-
-    private class TestInjector extends DisplayPowerController2.Injector {
-        private final DisplayPowerState mDisplayPowerState;
-        private final DualRampAnimator<DisplayPowerState> mAnimator;
-        private final AutomaticBrightnessController mAutomaticBrightnessController;
-        private final WakelockController mWakelockController;
-        private final BrightnessMappingStrategy mBrightnessMappingStrategy;
-        private final HysteresisLevels mHysteresisLevels;
-        private final ScreenOffBrightnessSensorController mScreenOffBrightnessSensorController;
-        private final HighBrightnessModeController mHighBrightnessModeController;
-
-        private final HdrClamper mHdrClamper;
-
-        private final BrightnessClamperController mClamperController;
-
-        private final DisplayManagerFlags mFlags;
-
-        TestInjector(DisplayPowerState dps, DualRampAnimator<DisplayPowerState> animator,
-                AutomaticBrightnessController automaticBrightnessController,
-                WakelockController wakelockController,
-                BrightnessMappingStrategy brightnessMappingStrategy,
-                HysteresisLevels hysteresisLevels,
-                ScreenOffBrightnessSensorController screenOffBrightnessSensorController,
-                HighBrightnessModeController highBrightnessModeController,
-                HdrClamper hdrClamper,
-                BrightnessClamperController clamperController,
-                DisplayManagerFlags flags) {
-            mDisplayPowerState = dps;
-            mAnimator = animator;
-            mAutomaticBrightnessController = automaticBrightnessController;
-            mWakelockController = wakelockController;
-            mBrightnessMappingStrategy = brightnessMappingStrategy;
-            mHysteresisLevels = hysteresisLevels;
-            mScreenOffBrightnessSensorController = screenOffBrightnessSensorController;
-            mHighBrightnessModeController = highBrightnessModeController;
-            mHdrClamper = hdrClamper;
-            mClamperController = clamperController;
-            mFlags = flags;
-        }
-
-        @Override
-        DisplayPowerController2.Clock getClock() {
-            return mClock::now;
-        }
-
-        @Override
-        DisplayPowerState getDisplayPowerState(DisplayBlanker blanker, ColorFade colorFade,
-                int displayId, int displayState) {
-            return mDisplayPowerState;
-        }
-
-        @Override
-        DualRampAnimator<DisplayPowerState> getDualRampAnimator(DisplayPowerState dps,
-                FloatProperty<DisplayPowerState> firstProperty,
-                FloatProperty<DisplayPowerState> secondProperty) {
-            return mAnimator;
-        }
-
-        @Override
-        WakelockController getWakelockController(int displayId,
-                DisplayPowerCallbacks displayPowerCallbacks) {
-            return mWakelockController;
-        }
-
-        @Override
-        DisplayPowerProximityStateController getDisplayPowerProximityStateController(
-                WakelockController wakelockController, DisplayDeviceConfig displayDeviceConfig,
-                Looper looper, Runnable nudgeUpdatePowerState, int displayId,
-                SensorManager sensorManager) {
-            return new DisplayPowerProximityStateController(wakelockController,
-                    displayDeviceConfig, looper, nudgeUpdatePowerState, displayId,
-                    sensorManager,
-                    new DisplayPowerProximityStateController.Injector() {
-                        @Override
-                        DisplayPowerProximityStateController.Clock createClock() {
-                            return mClock::now;
-                        }
-                    });
-        }
-
-        @Override
-        AutomaticBrightnessController getAutomaticBrightnessController(
-                AutomaticBrightnessController.Callbacks callbacks, Looper looper,
-                SensorManager sensorManager, Sensor lightSensor,
-                SparseArray<BrightnessMappingStrategy> brightnessMappingStrategyMap,
-                int lightSensorWarmUpTime, float brightnessMin, float brightnessMax,
-                float dozeScaleFactor, int lightSensorRate, int initialLightSensorRate,
-                long brighteningLightDebounceConfig, long darkeningLightDebounceConfig,
-                long brighteningLightDebounceConfigIdle, long darkeningLightDebounceConfigIdle,
-                boolean resetAmbientLuxAfterWarmUpConfig,
-                HysteresisLevels ambientBrightnessThresholds,
-                HysteresisLevels screenBrightnessThresholds,
-                HysteresisLevels ambientBrightnessThresholdsIdle,
-                HysteresisLevels screenBrightnessThresholdsIdle, Context context,
-                BrightnessRangeController brightnessRangeController,
-                BrightnessThrottler brightnessThrottler, int ambientLightHorizonShort,
-                int ambientLightHorizonLong, float userLux, float userNits) {
-            return mAutomaticBrightnessController;
-        }
-
-        @Override
-        BrightnessMappingStrategy getDefaultModeBrightnessMapper(Context context,
-                DisplayDeviceConfig displayDeviceConfig,
-                DisplayWhiteBalanceController displayWhiteBalanceController) {
-            return mBrightnessMappingStrategy;
-        }
-
-        @Override
-        HysteresisLevels getHysteresisLevels(float[] brighteningThresholdsPercentages,
-                float[] darkeningThresholdsPercentages, float[] brighteningThresholdLevels,
-                float[] darkeningThresholdLevels, float minDarkeningThreshold,
-                float minBrighteningThreshold) {
-            return mHysteresisLevels;
-        }
-
-        @Override
-        HysteresisLevels getHysteresisLevels(float[] brighteningThresholdsPercentages,
-                float[] darkeningThresholdsPercentages, float[] brighteningThresholdLevels,
-                float[] darkeningThresholdLevels, float minDarkeningThreshold,
-                float minBrighteningThreshold, boolean potentialOldBrightnessRange) {
-            return mHysteresisLevels;
-        }
-
-        @Override
-        ScreenOffBrightnessSensorController getScreenOffBrightnessSensorController(
-                SensorManager sensorManager, Sensor lightSensor, Handler handler,
-                ScreenOffBrightnessSensorController.Clock clock, int[] sensorValueToLux,
-                BrightnessMappingStrategy brightnessMapper) {
-            return mScreenOffBrightnessSensorController;
-        }
-
-        @Override
-        HighBrightnessModeController getHighBrightnessModeController(Handler handler, int width,
-                int height, IBinder displayToken, String displayUniqueId, float brightnessMin,
-                float brightnessMax, DisplayDeviceConfig.HighBrightnessModeData hbmData,
-                HighBrightnessModeController.HdrBrightnessDeviceConfig hdrBrightnessCfg,
-                Runnable hbmChangeCallback, HighBrightnessModeMetadata hbmMetadata,
-                Context context) {
-            return mHighBrightnessModeController;
-        }
-
-        @Override
-        BrightnessRangeController getBrightnessRangeController(
-                HighBrightnessModeController hbmController, Runnable modeChangeCallback,
-                DisplayDeviceConfig displayDeviceConfig, Handler handler,
-                DisplayManagerFlags flags, IBinder displayToken, DisplayDeviceInfo info) {
-            return new BrightnessRangeController(hbmController, modeChangeCallback,
-                    displayDeviceConfig, mHdrClamper, mFlags, displayToken, info);
-        }
-
-        @Override
-        BrightnessClamperController getBrightnessClamperController(Handler handler,
-                BrightnessClamperController.ClamperChangeListener clamperChangeListener,
-                BrightnessClamperController.DisplayDeviceData data, Context context,
-                DisplayManagerFlags flags) {
-            return mClamperController;
-        }
-
-        @Override
-        DisplayWhiteBalanceController getDisplayWhiteBalanceController(Handler handler,
-                SensorManager sensorManager, Resources resources) {
-            return mDisplayWhiteBalanceControllerMock;
-        }
-    }
-}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
index 943862f..88a9758 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
@@ -18,6 +18,8 @@
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DEFAULT;
+import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DOZE;
 import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_IDLE;
 
 import static org.junit.Assert.assertNotNull;
@@ -67,15 +69,16 @@
 import android.view.DisplayInfo;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.FlakyTest;
 import androidx.test.filters.SmallTest;
 import androidx.test.platform.app.InstrumentationRegistry;
 
-import com.android.internal.util.test.LocalServiceKeeperRule;
 import com.android.modules.utils.testing.ExtendedMockitoRule;
+import com.android.server.LocalServices;
 import com.android.server.am.BatteryStatsService;
 import com.android.server.display.RampAnimator.DualRampAnimator;
 import com.android.server.display.brightness.BrightnessEvent;
+import com.android.server.display.brightness.clamper.BrightnessClamperController;
+import com.android.server.display.brightness.clamper.HdrClamper;
 import com.android.server.display.color.ColorDisplayService;
 import com.android.server.display.config.SensorData;
 import com.android.server.display.feature.DisplayManagerFlags;
@@ -85,6 +88,7 @@
 import com.android.server.policy.WindowManagerPolicy;
 import com.android.server.testutils.OffsettableClock;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -147,7 +151,6 @@
     private DisplayManagerFlags mDisplayManagerFlagsMock;
     @Mock
     private DisplayManagerInternal.DisplayOffloadSession mDisplayOffloadSession;
-
     @Captor
     private ArgumentCaptor<SensorEventListener> mSensorEventListenerCaptor;
 
@@ -165,9 +168,6 @@
                     .build();
 
     @Rule
-    public LocalServiceKeeperRule mLocalServiceKeeperRule = new LocalServiceKeeperRule();
-
-    @Rule
     public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
 
     @Before
@@ -183,10 +183,9 @@
         Settings.System.putFloatForUser(mContext.getContentResolver(),
                 Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, 0, UserHandle.USER_CURRENT);
 
-        mLocalServiceKeeperRule.overrideLocalService(
-                WindowManagerPolicy.class, mWindowManagerPolicyMock);
-        mLocalServiceKeeperRule.overrideLocalService(
-                ColorDisplayService.ColorDisplayServiceInternal.class, mCdsiMock);
+        addLocalServiceMock(WindowManagerPolicy.class, mWindowManagerPolicyMock);
+        addLocalServiceMock(ColorDisplayService.ColorDisplayServiceInternal.class,
+                mCdsiMock);
 
         mContext.addMockSystemService(PowerManager.class, mPowerManagerMock);
 
@@ -203,6 +202,12 @@
         mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID);
     }
 
+    @After
+    public void tearDown() {
+        LocalServices.removeServiceForTest(WindowManagerPolicy.class);
+        LocalServices.removeServiceForTest(ColorDisplayService.ColorDisplayServiceInternal.class);
+    }
+
     @Test
     public void testReleaseProxSuspendBlockersOnExit() throws Exception {
         when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON);
@@ -222,19 +227,18 @@
         advanceTime(1);
 
         // two times, one for unfinished business and one for proximity
-        verify(mDisplayPowerCallbacksMock).acquireSuspendBlocker(
-                mHolder.dpc.getSuspendBlockerUnfinishedBusinessId(DISPLAY_ID));
-        verify(mDisplayPowerCallbacksMock).acquireSuspendBlocker(
-                mHolder.dpc.getSuspendBlockerProxDebounceId(DISPLAY_ID));
+        verify(mHolder.wakelockController, times(2)).acquireWakelock(
+                WakelockController.WAKE_LOCK_UNFINISHED_BUSINESS);
+        verify(mHolder.wakelockController).acquireWakelock(
+                WakelockController.WAKE_LOCK_PROXIMITY_DEBOUNCE);
 
         mHolder.dpc.stop();
         advanceTime(1);
-
         // two times, one for unfinished business and one for proximity
-        verify(mDisplayPowerCallbacksMock).releaseSuspendBlocker(
-                mHolder.dpc.getSuspendBlockerUnfinishedBusinessId(DISPLAY_ID));
-        verify(mDisplayPowerCallbacksMock).releaseSuspendBlocker(
-                mHolder.dpc.getSuspendBlockerProxDebounceId(DISPLAY_ID));
+        verify(mHolder.wakelockController, times(2)).acquireWakelock(
+                WakelockController.WAKE_LOCK_UNFINISHED_BUSINESS);
+        verify(mHolder.wakelockController).acquireWakelock(
+                WakelockController.WAKE_LOCK_PROXIMITY_DEBOUNCE);
     }
 
     @Test
@@ -316,14 +320,13 @@
 
     @Test
     public void testProximitySensorListenerNotRegisteredForNonDefaultDisplay() {
-        DisplayPowerControllerHolder followerDpc =
-                createDisplayPowerController(FOLLOWER_DISPLAY_ID, FOLLOWER_UNIQUE_ID);
-
         when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON);
         // send a display power request
         DisplayPowerRequest dpr = new DisplayPowerRequest();
         dpr.policy = DisplayPowerRequest.POLICY_BRIGHT;
         dpr.useProximitySensor = true;
+        final DisplayPowerControllerHolder followerDpc =
+                createDisplayPowerController(FOLLOWER_DISPLAY_ID, FOLLOWER_UNIQUE_ID);
         followerDpc.dpc.requestPowerState(dpr, false /* waitForNegativeProximity */);
 
         // Run updatePowerState
@@ -334,7 +337,6 @@
     }
 
     @Test
-    @FlakyTest(bugId = 294107062)
     public void testDisplayBrightnessFollowers_BothDpcsSupportNits() {
         DisplayPowerControllerHolder followerDpc =
                 createDisplayPowerController(FOLLOWER_DISPLAY_ID, FOLLOWER_UNIQUE_ID);
@@ -363,13 +365,10 @@
         when(mHolder.brightnessSetting.getBrightness()).thenReturn(leadBrightness);
         listener.onBrightnessChanged(leadBrightness);
         advanceTime(1); // Send messages, run updatePowerState
-        verify(mHolder.animator).animateTo(eq(leadBrightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
+        verify(mHolder.animator).animateTo(eq(leadBrightness), anyFloat(), anyFloat(), eq(false));
         verify(followerDpc.animator).animateTo(eq(followerBrightness), anyFloat(),
-                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
+                anyFloat(), eq(false));
 
-        when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(leadBrightness);
-        when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(followerBrightness);
         clearInvocations(mHolder.animator, followerDpc.animator);
 
         // Test the same float scale value
@@ -388,7 +387,6 @@
     }
 
     @Test
-    @FlakyTest(bugId = 294107062)
     public void testDisplayBrightnessFollowers_FollowerDoesNotSupportNits() {
         DisplayPowerControllerHolder followerDpc =
                 createDisplayPowerController(FOLLOWER_DISPLAY_ID, FOLLOWER_UNIQUE_ID);
@@ -421,7 +419,6 @@
     }
 
     @Test
-    @FlakyTest(bugId = 294107062)
     public void testDisplayBrightnessFollowers_LeadDpcDoesNotSupportNits() {
         DisplayPowerControllerHolder followerDpc =
                 createDisplayPowerController(FOLLOWER_DISPLAY_ID, FOLLOWER_UNIQUE_ID);
@@ -452,7 +449,6 @@
     }
 
     @Test
-    @FlakyTest(bugId = 294107062)
     public void testDisplayBrightnessFollowers_NeitherDpcSupportsNits() {
         DisplayPowerControllerHolder followerDpc =
                 createDisplayPowerController(FOLLOWER_DISPLAY_ID, FOLLOWER_UNIQUE_ID);
@@ -485,7 +481,6 @@
     }
 
     @Test
-    @FlakyTest(bugId = 294107062)
     public void testDisplayBrightnessFollowers_AutomaticBrightness() {
         Settings.System.putInt(mContext.getContentResolver(),
                 Settings.System.SCREEN_BRIGHTNESS_MODE,
@@ -557,7 +552,6 @@
     }
 
     @Test
-    @FlakyTest(bugId = 294107062)
     public void testDisplayBrightnessFollowersRemoval_RemoveSingleFollower() {
         DisplayPowerControllerHolder followerDpc = createDisplayPowerController(FOLLOWER_DISPLAY_ID,
                 FOLLOWER_UNIQUE_ID);
@@ -650,7 +644,6 @@
     }
 
     @Test
-    @FlakyTest(bugId = 294107062)
     public void testDisplayBrightnessFollowersRemoval_RemoveAllFollowers() {
         DisplayPowerControllerHolder followerHolder =
                 createDisplayPowerController(FOLLOWER_DISPLAY_ID, FOLLOWER_UNIQUE_ID);
@@ -737,6 +730,82 @@
     }
 
     @Test
+    @RequiresFlagsEnabled(Flags.FLAG_FAST_HDR_TRANSITIONS)
+    public void testDisplayBrightnessHdr_SkipAnimationOnHdrAppearance() {
+        Settings.System.putInt(mContext.getContentResolver(),
+                Settings.System.SCREEN_BRIGHTNESS_MODE,
+                Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
+        final float sdrBrightness = 0.1f;
+        final float hdrBrightness = 0.3f;
+        when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON);
+        when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
+        when(mHolder.automaticBrightnessController.getAutomaticScreenBrightness(
+                any(BrightnessEvent.class))).thenReturn(sdrBrightness);
+        when(mHolder.hdrClamper.getMaxBrightness()).thenReturn(1.0f);
+
+        DisplayPowerRequest dpr = new DisplayPowerRequest();
+        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+        advanceTime(1); // Run updatePowerState
+
+        verify(mHolder.animator).animateTo(eq(sdrBrightness), eq(sdrBrightness),
+                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
+
+        when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(sdrBrightness);
+        when(mHolder.displayPowerState.getSdrScreenBrightness()).thenReturn(sdrBrightness);
+
+        when(mHolder.hbmController.getHighBrightnessMode()).thenReturn(
+                BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR);
+        when(mHolder.hbmController.getHdrBrightnessValue()).thenReturn(hdrBrightness);
+        clearInvocations(mHolder.animator);
+
+        mHolder.dpc.updateBrightness();
+        advanceTime(1); // Run updatePowerState
+
+        verify(mHolder.animator).animateTo(eq(hdrBrightness), eq(sdrBrightness),
+                eq(BRIGHTNESS_RAMP_RATE_MINIMUM), eq(false));
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_FAST_HDR_TRANSITIONS)
+    public void testDisplayBrightnessHdr_SkipAnimationOnHdrRemoval() {
+        Settings.System.putInt(mContext.getContentResolver(),
+                Settings.System.SCREEN_BRIGHTNESS_MODE,
+                Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
+        final float sdrBrightness = 0.1f;
+        final float hdrBrightness = 0.3f;
+        when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON);
+        when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
+        when(mHolder.automaticBrightnessController.isInIdleMode()).thenReturn(true);
+        when(mHolder.automaticBrightnessController.getAutomaticScreenBrightness(
+                any(BrightnessEvent.class))).thenReturn(sdrBrightness);
+        when(mHolder.hdrClamper.getMaxBrightness()).thenReturn(1.0f);
+
+        when(mHolder.hbmController.getHighBrightnessMode()).thenReturn(
+                BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR);
+        when(mHolder.hbmController.getHdrBrightnessValue()).thenReturn(hdrBrightness);
+
+        DisplayPowerRequest dpr = new DisplayPowerRequest();
+        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+        advanceTime(1); // Run updatePowerState
+
+        verify(mHolder.animator).animateTo(eq(hdrBrightness), eq(sdrBrightness),
+                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
+
+        when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(hdrBrightness);
+        when(mHolder.displayPowerState.getSdrScreenBrightness()).thenReturn(sdrBrightness);
+        when(mHolder.hbmController.getHighBrightnessMode()).thenReturn(
+                BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF);
+
+        clearInvocations(mHolder.animator);
+
+        mHolder.dpc.updateBrightness();
+        advanceTime(1); // Run updatePowerState
+
+        verify(mHolder.animator).animateTo(eq(sdrBrightness), eq(sdrBrightness),
+                eq(BRIGHTNESS_RAMP_RATE_MINIMUM), eq(false));
+    }
+
+    @Test
     public void testDoesNotSetScreenStateForNonDefaultDisplayUntilBootCompleted() {
         // We should still set screen state for the default display
         DisplayPowerRequest dpr = new DisplayPowerRequest();
@@ -761,6 +830,8 @@
                 Settings.System.SCREEN_BRIGHTNESS_MODE,
                 Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
 
+        when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_OFF);
+
         DisplayPowerRequest dpr = new DisplayPowerRequest();
         dpr.policy = DisplayPowerRequest.POLICY_OFF;
         mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
@@ -798,6 +869,7 @@
                 Settings.System.SCREEN_BRIGHTNESS_MODE,
                 Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
 
+        when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_DOZE);
         DisplayPowerRequest dpr = new DisplayPowerRequest();
         dpr.policy = DisplayPowerRequest.POLICY_DOZE;
         mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
@@ -842,7 +914,6 @@
         Settings.System.putInt(mContext.getContentResolver(),
                 Settings.System.SCREEN_BRIGHTNESS_MODE,
                 Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
-
         mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID, /* isEnabled= */ false);
 
         DisplayPowerRequest dpr = new DisplayPowerRequest();
@@ -1048,7 +1119,6 @@
         mContext.getOrCreateTestableResources().addOverride(
                 com.android.internal.R.bool.config_persistBrightnessNitsForDefaultDisplay,
                 true);
-
         mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID);
         when(mHolder.automaticBrightnessController.convertToNits(brightness)).thenReturn(nits);
 
@@ -1199,76 +1269,98 @@
     }
 
     @Test
-    @RequiresFlagsEnabled(Flags.FLAG_FAST_HDR_TRANSITIONS)
-    public void testDisplayBrightnessHdr_SkipAnimationOnHdrAppearance() {
-        Settings.System.putInt(mContext.getContentResolver(),
-                Settings.System.SCREEN_BRIGHTNESS_MODE,
-                Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
-        final float sdrBrightness = 0.1f;
-        final float hdrBrightness = 0.3f;
-        when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON);
-        when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
-        when(mHolder.automaticBrightnessController.getAutomaticScreenBrightness(
-                any(BrightnessEvent.class))).thenReturn(sdrBrightness);
+    public void testRampRateForHdrContent_HdrClamperOff() {
+        float hdrBrightness = 0.8f;
+        float clampedBrightness = 0.6f;
+        float transitionRate = 1.5f;
 
         DisplayPowerRequest dpr = new DisplayPowerRequest();
-        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
-        advanceTime(1); // Run updatePowerState
-
-        verify(mHolder.animator).animateTo(eq(sdrBrightness), eq(sdrBrightness),
-                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
-
-        when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(sdrBrightness);
-        when(mHolder.displayPowerState.getSdrScreenBrightness()).thenReturn(sdrBrightness);
-
+        when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
+        when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(.2f);
+        when(mHolder.displayPowerState.getSdrScreenBrightness()).thenReturn(.1f);
         when(mHolder.hbmController.getHighBrightnessMode()).thenReturn(
                 BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR);
         when(mHolder.hbmController.getHdrBrightnessValue()).thenReturn(hdrBrightness);
-        clearInvocations(mHolder.animator);
+        when(mHolder.hdrClamper.getMaxBrightness()).thenReturn(clampedBrightness);
+        when(mHolder.hdrClamper.getTransitionRate()).thenReturn(transitionRate);
 
-        mHolder.dpc.updateBrightness();
+        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
         advanceTime(1); // Run updatePowerState
 
-        verify(mHolder.animator).animateTo(eq(hdrBrightness), eq(sdrBrightness),
-                eq(BRIGHTNESS_RAMP_RATE_MINIMUM), eq(false));
+        verify(mHolder.animator, atLeastOnce()).animateTo(eq(hdrBrightness), anyFloat(),
+                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
     }
 
     @Test
-    @RequiresFlagsEnabled(Flags.FLAG_FAST_HDR_TRANSITIONS)
-    public void testDisplayBrightnessHdr_SkipAnimationOnHdrRemoval() {
-        Settings.System.putInt(mContext.getContentResolver(),
-                Settings.System.SCREEN_BRIGHTNESS_MODE,
-                Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
-        final float sdrBrightness = 0.1f;
-        final float hdrBrightness = 0.3f;
-        when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON);
-        when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
-        when(mHolder.automaticBrightnessController.getAutomaticScreenBrightness(
-                any(BrightnessEvent.class))).thenReturn(sdrBrightness);
-
-        when(mHolder.hbmController.getHighBrightnessMode()).thenReturn(
-                BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR);
-        when(mHolder.hbmController.getHdrBrightnessValue()).thenReturn(hdrBrightness);
+    public void testRampRateForHdrContent_HdrClamperOn() {
+        float clampedBrightness = 0.6f;
+        float transitionRate = 1.5f;
+        when(mDisplayManagerFlagsMock.isHdrClamperEnabled()).thenReturn(true);
+        mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID, /* isEnabled= */ true);
 
         DisplayPowerRequest dpr = new DisplayPowerRequest();
+        when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
+        when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(.2f);
+        when(mHolder.displayPowerState.getSdrScreenBrightness()).thenReturn(.1f);
+        when(mHolder.hbmController.getHighBrightnessMode()).thenReturn(
+                BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR);
+        when(mHolder.hbmController.getHdrBrightnessValue()).thenReturn(PowerManager.BRIGHTNESS_MAX);
+        when(mHolder.hdrClamper.getMaxBrightness()).thenReturn(clampedBrightness);
+        when(mHolder.hdrClamper.getTransitionRate()).thenReturn(transitionRate);
+
         mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
         advanceTime(1); // Run updatePowerState
 
-        verify(mHolder.animator).animateTo(eq(hdrBrightness), eq(sdrBrightness),
-                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
+        verify(mHolder.animator, atLeastOnce()).animateTo(eq(clampedBrightness), anyFloat(),
+                eq(transitionRate), eq(true));
+    }
 
-        when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(hdrBrightness);
-        when(mHolder.displayPowerState.getSdrScreenBrightness()).thenReturn(sdrBrightness);
-        when(mHolder.hbmController.getHighBrightnessMode()).thenReturn(
-                BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF);
+    @Test
+    public void testRampRateForClampersControllerApplied() {
+        float transitionRate = 1.5f;
+        mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID);
+        DisplayPowerRequest dpr = new DisplayPowerRequest();
+        when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
+        when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(.2f);
+        when(mHolder.displayPowerState.getSdrScreenBrightness()).thenReturn(.1f);
+        when(mHolder.clamperController.clamp(any(), anyFloat(), anyBoolean())).thenAnswer(
+                invocation -> DisplayBrightnessState.builder()
+                        .setIsSlowChange(invocation.getArgument(2))
+                        .setBrightness(invocation.getArgument(1))
+                        .setMaxBrightness(PowerManager.BRIGHTNESS_MAX)
+                        .setCustomAnimationRate(transitionRate).build());
 
-        clearInvocations(mHolder.animator);
-
-        mHolder.dpc.updateBrightness();
+        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
         advanceTime(1); // Run updatePowerState
 
-        verify(mHolder.animator).animateTo(eq(sdrBrightness), eq(sdrBrightness),
-                eq(BRIGHTNESS_RAMP_RATE_MINIMUM), eq(false));
+        verify(mHolder.animator, atLeastOnce()).animateTo(anyFloat(), anyFloat(),
+                eq(transitionRate), anyBoolean());
+    }
+
+    @Test
+    public void testRampRateForClampersControllerNotApplied_ifDoze() {
+        float transitionRate = 1.5f;
+        mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID);
+        DisplayPowerRequest dpr = new DisplayPowerRequest();
+        dpr.policy = DisplayPowerRequest.POLICY_DOZE;
+        dpr.dozeScreenState = Display.STATE_UNKNOWN;
+        when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
+        when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(.2f);
+        when(mHolder.displayPowerState.getSdrScreenBrightness()).thenReturn(.1f);
+        when(mHolder.clamperController.clamp(any(), anyFloat(), anyBoolean())).thenAnswer(
+                invocation -> DisplayBrightnessState.builder()
+                        .setIsSlowChange(invocation.getArgument(2))
+                        .setBrightness(invocation.getArgument(1))
+                        .setMaxBrightness(PowerManager.BRIGHTNESS_MAX)
+                        .setCustomAnimationRate(transitionRate).build());
+
+        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+        advanceTime(1); // Run updatePowerState
+
+        verify(mHolder.animator, atLeastOnce()).animateTo(anyFloat(), anyFloat(),
+                eq(BRIGHTNESS_RAMP_RATE_FAST_DECREASE), anyBoolean());
+        verify(mHolder.animator, never()).animateTo(anyFloat(), anyFloat(),
+                eq(transitionRate), anyBoolean());
     }
 
     @Test
@@ -1285,14 +1377,14 @@
 
         setUpDisplay(DISPLAY_ID, "new_unique_id", mHolder.display, mock(DisplayDevice.class),
                 mHolder.config, /* isEnabled= */ true);
-
         verify(mHolder.animator).setAnimationTimeLimits(BRIGHTNESS_RAMP_INCREASE_MAX,
                 BRIGHTNESS_RAMP_DECREASE_MAX);
 
-        // switch to idle
+        // switch to idle mode
         mHolder.dpc.setAutomaticScreenBrightnessMode(AUTO_BRIGHTNESS_MODE_IDLE);
         advanceTime(1);
 
+        // A second time, when switching to idle mode.
         verify(mHolder.animator, times(2)).setAnimationTimeLimits(BRIGHTNESS_RAMP_INCREASE_MAX,
                 BRIGHTNESS_RAMP_DECREASE_MAX);
     }
@@ -1301,6 +1393,8 @@
     @RequiresFlagsEnabled(Flags.FLAG_ENABLE_ADAPTIVE_TONE_IMPROVEMENTS_1)
     public void testRampMaxTimeInteractiveThenIdle_DifferentValues() {
         when(mDisplayManagerFlagsMock.isAdaptiveTone1Enabled()).thenReturn(true);
+        mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID, /* isEnabled= */ true);
+
         // Send a display power request
         DisplayPowerRequest dpr = new DisplayPowerRequest();
         dpr.policy = DisplayPowerRequest.POLICY_BRIGHT;
@@ -1312,14 +1406,14 @@
 
         setUpDisplay(DISPLAY_ID, "new_unique_id", mHolder.display, mock(DisplayDevice.class),
                 mHolder.config, /* isEnabled= */ true);
-
         verify(mHolder.animator).setAnimationTimeLimits(BRIGHTNESS_RAMP_INCREASE_MAX,
                 BRIGHTNESS_RAMP_DECREASE_MAX);
 
-        // switch to idle
+        // switch to idle mode
         mHolder.dpc.setAutomaticScreenBrightnessMode(AUTO_BRIGHTNESS_MODE_IDLE);
         advanceTime(1);
 
+        // A second time, when switching to idle mode.
         verify(mHolder.animator).setAnimationTimeLimits(BRIGHTNESS_RAMP_INCREASE_MAX_IDLE,
                 BRIGHTNESS_RAMP_DECREASE_MAX_IDLE);
     }
@@ -1332,11 +1426,9 @@
         dpr.policy = DisplayPowerRequest.POLICY_BRIGHT;
         dpr.useProximitySensor = true;
         mHolder.dpc.requestPowerState(dpr, false /* waitForNegativeProximity */);
-
         // Run updatePowerState
         advanceTime(1);
-
-        // once on setup
+        // Once on setup
         verify(mHolder.animator).setAnimationTimeLimits(BRIGHTNESS_RAMP_INCREASE_MAX,
                 BRIGHTNESS_RAMP_DECREASE_MAX);
 
@@ -1346,7 +1438,7 @@
         // switch to idle mode
         mHolder.dpc.setAutomaticScreenBrightnessMode(AUTO_BRIGHTNESS_MODE_IDLE);
 
-        // second time when switching to idle screen brightness mode
+        // A second time when switching to idle mode.
         verify(mHolder.animator, times(2)).setAnimationTimeLimits(BRIGHTNESS_RAMP_INCREASE_MAX,
                 BRIGHTNESS_RAMP_DECREASE_MAX);
     }
@@ -1355,6 +1447,7 @@
     @RequiresFlagsEnabled(Flags.FLAG_ENABLE_ADAPTIVE_TONE_IMPROVEMENTS_1)
     public void testRampMaxTimeIdle_DifferentValues() {
         when(mDisplayManagerFlagsMock.isAdaptiveTone1Enabled()).thenReturn(true);
+        mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID, /* isEnabled= */ true);
 
         // Send a display power request
         DisplayPowerRequest dpr = new DisplayPowerRequest();
@@ -1374,6 +1467,7 @@
         verify(mHolder.animator).setAnimationTimeLimits(BRIGHTNESS_RAMP_INCREASE_MAX_IDLE,
                 BRIGHTNESS_RAMP_DECREASE_MAX_IDLE);
     }
+
     @Test
     public void testDozeScreenStateOverride_toSupportedOffloadStateFromDoze_DisplayStateChanges() {
         // set up.
@@ -1451,6 +1545,89 @@
         verify(mHolder.displayPowerState, never()).setScreenState(anyInt());
     }
 
+    @Test
+    public void testBrightnessFromOffload() {
+        when(mDisplayManagerFlagsMock.isDisplayOffloadEnabled()).thenReturn(true);
+        mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID);
+        Settings.System.putInt(mContext.getContentResolver(),
+                Settings.System.SCREEN_BRIGHTNESS_MODE,
+                Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
+        float brightness = 0.34f;
+        when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
+        when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON);
+        when(mHolder.automaticBrightnessController.getAutomaticScreenBrightness(
+                any(BrightnessEvent.class))).thenReturn(PowerManager.BRIGHTNESS_INVALID_FLOAT);
+        mHolder.dpc.setDisplayOffloadSession(mDisplayOffloadSession);
+
+        mHolder.dpc.setBrightnessFromOffload(brightness);
+        DisplayPowerRequest dpr = new DisplayPowerRequest();
+        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+        advanceTime(1); // Run updatePowerState
+
+        // One triggered by handleBrightnessModeChange, another triggered by
+        // setBrightnessFromOffload
+        verify(mHolder.animator, times(2)).animateTo(eq(brightness), anyFloat(),
+                eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
+    }
+
+    @Test
+    public void testSwitchToDozeAutoBrightnessMode() {
+        when(mDisplayManagerFlagsMock.areAutoBrightnessModesEnabled()).thenReturn(true);
+        when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_DOZE);
+
+        DisplayPowerRequest dpr = new DisplayPowerRequest();
+        dpr.policy = DisplayPowerRequest.POLICY_DOZE;
+        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+        advanceTime(1); // Run updatePowerState
+
+        // One triggered by handleBrightnessModeChange, another triggered by requestPowerState
+        verify(mHolder.automaticBrightnessController, times(2))
+                .switchMode(AUTO_BRIGHTNESS_MODE_DOZE);
+
+        // Back to default mode
+        when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON);
+        dpr.policy = DisplayPowerRequest.POLICY_BRIGHT;
+        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+        advanceTime(1); // Run updatePowerState
+
+        verify(mHolder.automaticBrightnessController).switchMode(AUTO_BRIGHTNESS_MODE_DEFAULT);
+    }
+
+    @Test
+    public void testDoesNotSwitchFromIdleToDozeAutoBrightnessMode() {
+        when(mDisplayManagerFlagsMock.areAutoBrightnessModesEnabled()).thenReturn(true);
+        when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_DOZE);
+        when(mHolder.automaticBrightnessController.isInIdleMode()).thenReturn(true);
+
+        DisplayPowerRequest dpr = new DisplayPowerRequest();
+        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+        advanceTime(1); // Run updatePowerState
+
+        verify(mHolder.automaticBrightnessController, never())
+                .switchMode(AUTO_BRIGHTNESS_MODE_DOZE);
+    }
+
+    @Test
+    public void testDoesNotSwitchDozeAutoBrightnessModeIfFeatureFlagOff() {
+        when(mDisplayManagerFlagsMock.areAutoBrightnessModesEnabled()).thenReturn(false);
+        when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_DOZE);
+
+        DisplayPowerRequest dpr = new DisplayPowerRequest();
+        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+        advanceTime(1); // Run updatePowerState
+
+        verify(mHolder.automaticBrightnessController, never())
+                .switchMode(AUTO_BRIGHTNESS_MODE_DOZE);
+    }
+
+    /**
+     * Creates a mock and registers it to {@link LocalServices}.
+     */
+    private static <T> void addLocalServiceMock(Class<T> clazz, T mock) {
+        LocalServices.removeServiceForTest(clazz);
+        LocalServices.addService(clazz, mock);
+    }
+
     private void advanceTime(long timeMs) {
         mClock.fastForward(timeMs);
         mTestLooper.dispatchAll();
@@ -1505,10 +1682,10 @@
                 .thenReturn(BRIGHTNESS_RAMP_RATE_SLOW_DECREASE);
         when(displayDeviceConfigMock.getBrightnessRampSlowIncrease())
                 .thenReturn(BRIGHTNESS_RAMP_RATE_SLOW_INCREASE);
-        when(displayDeviceConfigMock.getBrightnessRampSlowDecreaseIdle())
-                .thenReturn(BRIGHTNESS_RAMP_RATE_SLOW_DECREASE_IDLE);
         when(displayDeviceConfigMock.getBrightnessRampSlowIncreaseIdle())
                 .thenReturn(BRIGHTNESS_RAMP_RATE_SLOW_INCREASE_IDLE);
+        when(displayDeviceConfigMock.getBrightnessRampSlowDecreaseIdle())
+                .thenReturn(BRIGHTNESS_RAMP_RATE_SLOW_DECREASE_IDLE);
 
         when(displayDeviceConfigMock.getBrightnessRampIncreaseMaxMillis())
                 .thenReturn(BRIGHTNESS_RAMP_INCREASE_MAX);
@@ -1531,18 +1708,28 @@
         final DualRampAnimator<DisplayPowerState> animator = mock(DualRampAnimator.class);
         final AutomaticBrightnessController automaticBrightnessController =
                 mock(AutomaticBrightnessController.class);
+        final WakelockController wakelockController = mock(WakelockController.class);
         final BrightnessMappingStrategy brightnessMappingStrategy =
                 mock(BrightnessMappingStrategy.class);
         final HysteresisLevels hysteresisLevels = mock(HysteresisLevels.class);
         final ScreenOffBrightnessSensorController screenOffBrightnessSensorController =
                 mock(ScreenOffBrightnessSensorController.class);
         final HighBrightnessModeController hbmController = mock(HighBrightnessModeController.class);
+        final HdrClamper hdrClamper = mock(HdrClamper.class);
+        BrightnessClamperController clamperController = mock(BrightnessClamperController.class);
 
         when(hbmController.getCurrentBrightnessMax()).thenReturn(PowerManager.BRIGHTNESS_MAX);
+        when(clamperController.clamp(any(), anyFloat(), anyBoolean())).thenAnswer(
+                invocation -> DisplayBrightnessState.builder()
+                        .setIsSlowChange(invocation.getArgument(2))
+                        .setBrightness(invocation.getArgument(1))
+                        .setMaxBrightness(PowerManager.BRIGHTNESS_MAX)
+                        .setCustomAnimationRate(-1).build());
 
-        DisplayPowerController.Injector injector = spy(new TestInjector(displayPowerState, animator,
-                automaticBrightnessController, brightnessMappingStrategy, hysteresisLevels,
-                screenOffBrightnessSensorController, hbmController));
+        TestInjector injector = spy(new TestInjector(displayPowerState, animator,
+                automaticBrightnessController, wakelockController, brightnessMappingStrategy,
+                hysteresisLevels, screenOffBrightnessSensorController, hbmController, hdrClamper,
+                clamperController, mDisplayManagerFlagsMock));
 
         final LogicalDisplay display = mock(LogicalDisplay.class);
         final DisplayDevice device = mock(DisplayDevice.class);
@@ -1555,12 +1742,14 @@
         final DisplayPowerController dpc = new DisplayPowerController(
                 mContext, injector, mDisplayPowerCallbacksMock, mHandler,
                 mSensorManagerMock, mDisplayBlankerMock, display,
-                mBrightnessTrackerMock, brightnessSetting, () -> {},
+                mBrightnessTrackerMock, brightnessSetting, () -> {
+        },
                 hbmMetadata, /* bootCompleted= */ false, mDisplayManagerFlagsMock);
 
         return new DisplayPowerControllerHolder(dpc, display, displayPowerState, brightnessSetting,
-                animator, automaticBrightnessController, screenOffBrightnessSensorController,
-                hbmController, hbmMetadata, brightnessMappingStrategy, injector, config);
+                animator, automaticBrightnessController, wakelockController,
+                screenOffBrightnessSensorController, hbmController, hdrClamper, clamperController,
+                hbmMetadata, brightnessMappingStrategy, injector, config);
     }
 
     /**
@@ -1574,8 +1763,12 @@
         public final BrightnessSetting brightnessSetting;
         public final DualRampAnimator<DisplayPowerState> animator;
         public final AutomaticBrightnessController automaticBrightnessController;
+        public final WakelockController wakelockController;
         public final ScreenOffBrightnessSensorController screenOffBrightnessSensorController;
         public final HighBrightnessModeController hbmController;
+
+        public final HdrClamper hdrClamper;
+        public final BrightnessClamperController clamperController;
         public final HighBrightnessModeMetadata hbmMetadata;
         public final BrightnessMappingStrategy brightnessMappingStrategy;
         public final DisplayPowerController.Injector injector;
@@ -1585,8 +1778,11 @@
                 DisplayPowerState displayPowerState, BrightnessSetting brightnessSetting,
                 DualRampAnimator<DisplayPowerState> animator,
                 AutomaticBrightnessController automaticBrightnessController,
+                WakelockController wakelockController,
                 ScreenOffBrightnessSensorController screenOffBrightnessSensorController,
                 HighBrightnessModeController hbmController,
+                HdrClamper hdrClamper,
+                BrightnessClamperController clamperController,
                 HighBrightnessModeMetadata hbmMetadata,
                 BrightnessMappingStrategy brightnessMappingStrategy,
                 DisplayPowerController.Injector injector,
@@ -1597,8 +1793,11 @@
             this.brightnessSetting = brightnessSetting;
             this.animator = animator;
             this.automaticBrightnessController = automaticBrightnessController;
+            this.wakelockController = wakelockController;
             this.screenOffBrightnessSensorController = screenOffBrightnessSensorController;
             this.hbmController = hbmController;
+            this.hdrClamper = hdrClamper;
+            this.clamperController = clamperController;
             this.hbmMetadata = hbmMetadata;
             this.brightnessMappingStrategy = brightnessMappingStrategy;
             this.injector = injector;
@@ -1610,24 +1809,39 @@
         private final DisplayPowerState mDisplayPowerState;
         private final DualRampAnimator<DisplayPowerState> mAnimator;
         private final AutomaticBrightnessController mAutomaticBrightnessController;
+        private final WakelockController mWakelockController;
         private final BrightnessMappingStrategy mBrightnessMappingStrategy;
         private final HysteresisLevels mHysteresisLevels;
         private final ScreenOffBrightnessSensorController mScreenOffBrightnessSensorController;
         private final HighBrightnessModeController mHighBrightnessModeController;
 
+        private final HdrClamper mHdrClamper;
+
+        private final BrightnessClamperController mClamperController;
+
+        private final DisplayManagerFlags mFlags;
+
         TestInjector(DisplayPowerState dps, DualRampAnimator<DisplayPowerState> animator,
                 AutomaticBrightnessController automaticBrightnessController,
+                WakelockController wakelockController,
                 BrightnessMappingStrategy brightnessMappingStrategy,
                 HysteresisLevels hysteresisLevels,
                 ScreenOffBrightnessSensorController screenOffBrightnessSensorController,
-                HighBrightnessModeController highBrightnessModeController) {
+                HighBrightnessModeController highBrightnessModeController,
+                HdrClamper hdrClamper,
+                BrightnessClamperController clamperController,
+                DisplayManagerFlags flags) {
             mDisplayPowerState = dps;
             mAnimator = animator;
             mAutomaticBrightnessController = automaticBrightnessController;
+            mWakelockController = wakelockController;
             mBrightnessMappingStrategy = brightnessMappingStrategy;
             mHysteresisLevels = hysteresisLevels;
             mScreenOffBrightnessSensorController = screenOffBrightnessSensorController;
             mHighBrightnessModeController = highBrightnessModeController;
+            mHdrClamper = hdrClamper;
+            mClamperController = clamperController;
+            mFlags = flags;
         }
 
         @Override
@@ -1649,6 +1863,28 @@
         }
 
         @Override
+        WakelockController getWakelockController(int displayId,
+                DisplayPowerCallbacks displayPowerCallbacks) {
+            return mWakelockController;
+        }
+
+        @Override
+        DisplayPowerProximityStateController getDisplayPowerProximityStateController(
+                WakelockController wakelockController, DisplayDeviceConfig displayDeviceConfig,
+                Looper looper, Runnable nudgeUpdatePowerState, int displayId,
+                SensorManager sensorManager) {
+            return new DisplayPowerProximityStateController(wakelockController,
+                    displayDeviceConfig, looper, nudgeUpdatePowerState, displayId,
+                    sensorManager,
+                    new DisplayPowerProximityStateController.Injector() {
+                        @Override
+                        DisplayPowerProximityStateController.Clock createClock() {
+                            return mClock::now;
+                        }
+                    });
+        }
+
+        @Override
         AutomaticBrightnessController getAutomaticBrightnessController(
                 AutomaticBrightnessController.Callbacks callbacks, Looper looper,
                 SensorManager sensorManager, Sensor lightSensor,
@@ -1710,6 +1946,23 @@
         }
 
         @Override
+        BrightnessRangeController getBrightnessRangeController(
+                HighBrightnessModeController hbmController, Runnable modeChangeCallback,
+                DisplayDeviceConfig displayDeviceConfig, Handler handler,
+                DisplayManagerFlags flags, IBinder displayToken, DisplayDeviceInfo info) {
+            return new BrightnessRangeController(hbmController, modeChangeCallback,
+                    displayDeviceConfig, mHdrClamper, mFlags, displayToken, info);
+        }
+
+        @Override
+        BrightnessClamperController getBrightnessClamperController(Handler handler,
+                BrightnessClamperController.ClamperChangeListener clamperChangeListener,
+                BrightnessClamperController.DisplayDeviceData data, Context context,
+                DisplayManagerFlags flags) {
+            return mClamperController;
+        }
+
+        @Override
         DisplayWhiteBalanceController getDisplayWhiteBalanceController(Handler handler,
                 SensorManager sensorManager, Resources resources) {
             return mDisplayWhiteBalanceControllerMock;
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerStateTest.kt b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerStateTest.kt
index 49fa254..33d3020 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerStateTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerStateTest.kt
@@ -16,15 +16,25 @@
 
 package com.android.server.display
 
+import android.content.Context
+import android.os.Looper
 import android.view.Display
 import androidx.test.filters.SmallTest
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
+import org.mockito.ArgumentMatchers.anyFloat
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.eq
 import org.mockito.junit.MockitoJUnit
 
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.clearInvocations
 import org.mockito.kotlin.mock
+import org.mockito.kotlin.never
 import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+import java.util.concurrent.Executor
 
 @SmallTest
 class DisplayPowerStateTest {
@@ -36,16 +46,54 @@
 
     private val mockBlanker = mock<DisplayBlanker>()
     private val mockColorFade = mock<ColorFade>()
+    private val mockExecutor = mock<Executor>()
+    private val mockContext = mock<Context>()
 
     @Before
     fun setUp() {
-        displayPowerState = DisplayPowerState(mockBlanker, mockColorFade, 123, Display.STATE_ON)
+        if (Looper.myLooper() == null) {
+            Looper.prepare()
+        }
+        displayPowerState = DisplayPowerState(mockBlanker, mockColorFade, 123, Display.STATE_ON,
+                mockExecutor)
+        whenever(mockColorFade.prepare(eq(mockContext), anyInt())).thenReturn(true)
     }
 
     @Test
     fun `destroys ColorFade on stop`() {
         displayPowerState.stop()
+        val runnableCaptor = argumentCaptor<Runnable>()
+
+        verify(mockExecutor).execute(runnableCaptor.capture())
+        runnableCaptor.firstValue.run()
 
         verify(mockColorFade).destroy()
     }
+
+    @Test
+    fun `GIVEN not prepared WHEN draw runnable is called THEN colorFade not drawn`() {
+        displayPowerState.mColorFadeDrawRunnable.run()
+
+        verify(mockColorFade, never()).draw(anyFloat())
+    }
+    @Test
+    fun `GIVEN prepared WHEN draw runnable is called THEN colorFade is drawn`() {
+        displayPowerState.prepareColorFade(mockContext, ColorFade.MODE_FADE)
+        clearInvocations(mockColorFade)
+
+        displayPowerState.mColorFadeDrawRunnable.run()
+
+        verify(mockColorFade).draw(anyFloat())
+    }
+
+    @Test
+    fun `GIVEN prepared AND stopped WHEN draw runnable is called THEN colorFade is not drawn`() {
+        displayPowerState.prepareColorFade(mockContext, ColorFade.MODE_FADE)
+        clearInvocations(mockColorFade)
+        displayPowerState.stop()
+
+        displayPowerState.mColorFadeDrawRunnable.run()
+
+        verify(mockColorFade, never()).draw(anyFloat())
+    }
 }
\ No newline at end of file
diff --git a/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java b/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java
index 00f9892..c92ce25 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java
@@ -176,6 +176,10 @@
         when(mockArray.length()).thenReturn(0);
         when(mMockedResources.obtainTypedArray(R.array.config_maskBuiltInDisplayCutoutArray))
                 .thenReturn(mockArray);
+        when(mMockedResources.obtainTypedArray(R.array.config_displayCutoutSideOverrideArray))
+                .thenReturn(mockArray);
+        when(mMockedResources.getStringArray(R.array.config_mainBuiltInDisplayCutoutSideOverride))
+                .thenReturn(new String[]{});
         when(mMockedResources.obtainTypedArray(R.array.config_waterfallCutoutArray))
                 .thenReturn(mockArray);
         when(mMockedResources.obtainTypedArray(R.array.config_roundedCornerRadiusArray))
diff --git a/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java b/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java
index 28ec896..bed6f92 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java
@@ -49,8 +49,9 @@
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.clearInvocations;
-import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -83,7 +84,6 @@
 import org.mockito.Captor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
-import org.mockito.Spy;
 
 import java.io.File;
 import java.io.InputStream;
@@ -111,14 +111,14 @@
     private final DisplayIdProducer mIdProducer = (isDefault) ->
             isDefault ? DEFAULT_DISPLAY : sNextNonDefaultDisplayId++;
 
+    private DeviceStateToLayoutMap mDeviceStateToLayoutMapSpy;
+
     @Mock LogicalDisplayMapper.Listener mListenerMock;
     @Mock Context mContextMock;
     @Mock FoldSettingProvider mFoldSettingProviderMock;
     @Mock Resources mResourcesMock;
     @Mock IPowerManager mIPowerManagerMock;
     @Mock IThermalService mIThermalServiceMock;
-    @Spy DeviceStateToLayoutMap mDeviceStateToLayoutMapSpy =
-            new DeviceStateToLayoutMap(mIdProducer, NON_EXISTING_FILE);
     @Mock DisplayManagerFlags mFlagsMock;
     @Mock DisplayAdapter mDisplayAdapterMock;
 
@@ -131,6 +131,8 @@
         System.setProperty("dexmaker.share_classloader", "true");
         MockitoAnnotations.initMocks(this);
 
+        mDeviceStateToLayoutMapSpy =
+                spy(new DeviceStateToLayoutMap(mIdProducer, mFlagsMock, NON_EXISTING_FILE));
         mDisplayDeviceRepo = new DisplayDeviceRepository(
                 new DisplayManagerService.SyncRoot(),
                 new PersistentDataStore(new PersistentDataStore.Injector() {
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/BrightnessReasonTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/BrightnessReasonTest.java
index e58b3e8..990c383 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/BrightnessReasonTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/BrightnessReasonTest.java
@@ -58,9 +58,14 @@
 
     @Test
     public void setModifierDoesntSetIfModifierIsBeyondExtremes() {
-        int extremeModifier = 0x16;
+        int extremeModifier = 0x40; // equal to BrightnessReason.MODIFIER_MASK * 2
+
+        // reset modifier
+        mBrightnessReason.setModifier(0);
+
+        // test extreme
         mBrightnessReason.setModifier(extremeModifier);
-        assertEquals(mBrightnessReason.getModifier(), BrightnessReason.MODIFIER_LOW_POWER);
+        assertEquals(0, mBrightnessReason.getModifier());
     }
 
     @Test
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessClamperControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessClamperControllerTest.java
index 6ba7368..5294943 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessClamperControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessClamperControllerTest.java
@@ -29,8 +29,10 @@
 import android.os.Handler;
 import android.os.PowerManager;
 import android.provider.DeviceConfig;
+import android.testing.TestableContext;
 
 import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
 
 import com.android.server.display.DisplayBrightnessState;
 import com.android.server.display.brightness.BrightnessReason;
@@ -39,6 +41,7 @@
 import com.android.server.testutils.TestHandler;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
@@ -52,14 +55,15 @@
 
     private final TestHandler mTestHandler = new TestHandler(null);
 
+    @Rule
+    public final TestableContext mMockContext = new TestableContext(
+            InstrumentationRegistry.getInstrumentation().getContext());
     @Mock
     private BrightnessClamperController.ClamperChangeListener mMockExternalListener;
 
     @Mock
     private BrightnessClamperController.DisplayDeviceData mMockDisplayDeviceData;
     @Mock
-    private Context mMockContext;
-    @Mock
     private DeviceConfigParameterProvider mMockDeviceConfigParameterProvider;
     @Mock
     private BrightnessClamper<BrightnessClamperController.DisplayDeviceData> mMockClamper;
@@ -231,6 +235,13 @@
         assertEquals(initialSlowChange, state.isSlowChange());
     }
 
+    @Test
+    public void testStop() {
+        mClamperController.stop();
+        verify(mMockModifier).stop();
+        verify(mMockClamper).stop();
+    }
+
     private BrightnessClamperController createBrightnessClamperController() {
         return new BrightnessClamperController(mTestInjector, mTestHandler, mMockExternalListener,
                 mMockDisplayDeviceData, mMockContext, mFlags);
@@ -240,14 +251,14 @@
 
         private final List<BrightnessClamper<? super BrightnessClamperController.DisplayDeviceData>>
                 mClampers;
-        private final List<BrightnessModifier> mModifiers;
+        private final List<BrightnessStateModifier> mModifiers;
 
         private BrightnessClamperController.ClamperChangeListener mCapturedChangeListener;
 
         private TestInjector(
                 List<BrightnessClamper<? super BrightnessClamperController.DisplayDeviceData>>
                         clampers,
-                List<BrightnessModifier> modifiers) {
+                List<BrightnessStateModifier> modifiers) {
             mClampers = clampers;
             mModifiers = modifiers;
         }
@@ -268,7 +279,8 @@
         }
 
         @Override
-        List<BrightnessModifier> getModifiers(Context context) {
+        List<BrightnessStateModifier> getModifiers(DisplayManagerFlags flags, Context context,
+                Handler handler, BrightnessClamperController.ClamperChangeListener listener) {
             return mModifiers;
         }
     }
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessLowLuxModifierTest.kt b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessLowLuxModifierTest.kt
new file mode 100644
index 0000000..ac7d1f5
--- /dev/null
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessLowLuxModifierTest.kt
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.display.brightness.clamper
+
+import android.os.PowerManager
+import android.os.UserHandle
+import android.provider.Settings
+import android.testing.TestableContext
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.display.brightness.BrightnessReason
+import com.android.server.testutils.TestHandler
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.mockito.kotlin.mock
+
+private const val userId = UserHandle.USER_CURRENT
+
+class BrightnessLowLuxModifierTest {
+
+    private var mockClamperChangeListener =
+            mock<BrightnessClamperController.ClamperChangeListener>()
+
+    val context = TestableContext(
+            InstrumentationRegistry.getInstrumentation().getContext())
+
+    private val testHandler = TestHandler(null)
+    private lateinit var modifier: BrightnessLowLuxModifier
+
+    @Before
+    fun setUp() {
+        modifier = BrightnessLowLuxModifier(testHandler, mockClamperChangeListener, context)
+        testHandler.flush()
+    }
+
+    @Test
+    fun testThrottlingBounds() {
+        Settings.Secure.putIntForUser(context.contentResolver,
+                Settings.Secure.EVEN_DIMMER_ACTIVATED, 1, userId) // true
+        Settings.Secure.putFloatForUser(context.contentResolver,
+                Settings.Secure.EVEN_DIMMER_MIN_NITS, 0.7f, userId)
+        modifier.recalculateLowerBound()
+        testHandler.flush()
+        assertThat(modifier.isActive).isTrue()
+
+        // TODO: code currently returns MIN/MAX; update with lux values
+        assertThat(modifier.brightnessLowerBound).isEqualTo(PowerManager.BRIGHTNESS_MIN)
+    }
+
+    @Test
+    fun testGetReason_UserSet() {
+        Settings.Secure.putIntForUser(context.contentResolver,
+                Settings.Secure.EVEN_DIMMER_ACTIVATED, 1, userId)
+        Settings.Secure.putFloatForUser(context.contentResolver,
+                Settings.Secure.EVEN_DIMMER_MIN_NITS, 0.7f, userId)
+        modifier.recalculateLowerBound()
+        testHandler.flush()
+        assertThat(modifier.isActive).isTrue()
+
+        // Test restriction from user setting
+        assertThat(modifier.brightnessReason)
+                .isEqualTo(BrightnessReason.MODIFIER_MIN_USER_SET_LOWER_BOUND)
+    }
+
+    @Test
+    fun testGetReason_Lux() {
+        Settings.Secure.putIntForUser(context.contentResolver,
+                Settings.Secure.EVEN_DIMMER_ACTIVATED, 1, userId)
+        Settings.Secure.putFloatForUser(context.contentResolver,
+                Settings.Secure.EVEN_DIMMER_MIN_NITS, 0.0f, userId)
+        modifier.recalculateLowerBound()
+        testHandler.flush()
+        assertThat(modifier.isActive).isTrue()
+
+        // Test restriction from lux setting
+        assertThat(modifier.brightnessReason).isEqualTo(BrightnessReason.MODIFIER_MIN_LUX)
+    }
+}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/HdrClamperTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/HdrClamperTest.java
index 8d8274c..87fc7a4 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/HdrClamperTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/HdrClamperTest.java
@@ -122,6 +122,16 @@
     }
 
     @Test
+    public void testRegisterHdrListener_ZeroMinHdrPercent() {
+        IBinder otherBinder = mock(IBinder.class);
+        mHdrClamper.resetHdrConfig(TEST_HDR_DATA, WIDTH, HEIGHT,
+            /* minimumHdrPercentOfScreen= */ 0, otherBinder);
+
+        verify(mMockHdrInfoListener).unregister(mMockBinder);
+        verify(mMockHdrInfoListener).register(otherBinder);
+    }
+
+    @Test
     public void testRegisterNotCalledIfHbmConfigIsMissing() {
         IBinder otherBinder = mock(IBinder.class);
         mHdrClamper.resetHdrConfig(TEST_HDR_DATA, WIDTH, HEIGHT, -1, otherBinder);
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
index 60a0c03..a0e5fd8 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
@@ -3434,6 +3434,11 @@
             return mSensorManagerInternal;
         }
 
+        @Override
+        public VotesStatsReporter getVotesStatsReporter(boolean refreshRateVotingTelemetryEnabled) {
+            return null;
+        }
+
         protected Display createDisplay(int id) {
             return new Display(DisplayManagerGlobal.getInstance(), id, mDisplayInfo,
                     ApplicationProvider.getApplicationContext().getResources());
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/SkinThermalStatusObserverTest.java b/services/tests/displayservicetests/src/com/android/server/display/mode/SkinThermalStatusObserverTest.java
index f677401..2815fcb 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/SkinThermalStatusObserverTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/SkinThermalStatusObserverTest.java
@@ -58,7 +58,7 @@
     private RegisteringFakesInjector mInjector = new RegisteringFakesInjector();
 
     private final TestHandler mHandler = new TestHandler(null);
-    private final VotesStorage mStorage = new VotesStorage(() -> {});
+    private final VotesStorage mStorage = new VotesStorage(() -> {}, null);
 
     @Before
     public void setUp() {
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/VotesStorageTest.java b/services/tests/displayservicetests/src/com/android/server/display/mode/VotesStorageTest.java
index 50e2392..1f6f1a4 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/VotesStorageTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/VotesStorageTest.java
@@ -51,7 +51,7 @@
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
-        mVotesStorage = new VotesStorage(mVotesListener);
+        mVotesStorage = new VotesStorage(mVotesListener, null);
     }
 
     @Test
diff --git a/services/tests/media/mediarouterservicetest/src/com/android/server/media/AudioPoliciesDeviceRouteControllerTest.java b/services/tests/media/mediarouterservicetest/src/com/android/server/media/AudioManagerRouteControllerTest.java
similarity index 98%
rename from services/tests/media/mediarouterservicetest/src/com/android/server/media/AudioPoliciesDeviceRouteControllerTest.java
rename to services/tests/media/mediarouterservicetest/src/com/android/server/media/AudioManagerRouteControllerTest.java
index 6f9b6fa..8f5d125 100644
--- a/services/tests/media/mediarouterservicetest/src/com/android/server/media/AudioPoliciesDeviceRouteControllerTest.java
+++ b/services/tests/media/mediarouterservicetest/src/com/android/server/media/AudioManagerRouteControllerTest.java
@@ -62,7 +62,7 @@
 import java.util.Set;
 
 @RunWith(JUnit4.class)
-public class AudioPoliciesDeviceRouteControllerTest {
+public class AudioManagerRouteControllerTest {
 
     private static final String FAKE_ROUTE_NAME = "fake name";
     private static final AudioDeviceInfo FAKE_AUDIO_DEVICE_INFO_BUILTIN_SPEAKER =
@@ -89,7 +89,7 @@
     private Set<AudioDeviceInfo> mAvailableAudioDeviceInfos;
     @Mock private AudioManager mMockAudioManager;
     @Mock private DeviceRouteController.OnDeviceRouteChangedListener mOnDeviceRouteChangedListener;
-    private AudioPoliciesDeviceRouteController mControllerUnderTest;
+    private AudioManagerRouteController mControllerUnderTest;
     private AudioDeviceCallback mAudioDeviceCallback;
     private AudioProductStrategy mMediaAudioProductStrategy;
 
@@ -116,7 +116,7 @@
         BluetoothAdapter btAdapter =
                 realContext.getSystemService(BluetoothManager.class).getAdapter();
         mControllerUnderTest =
-                new AudioPoliciesDeviceRouteController(
+                new AudioManagerRouteController(
                         mockContext,
                         mMockAudioManager,
                         Looper.getMainLooper(),
diff --git a/services/tests/mockingservicestests/Android.bp b/services/tests/mockingservicestests/Android.bp
index 113511e..321d945 100644
--- a/services/tests/mockingservicestests/Android.bp
+++ b/services/tests/mockingservicestests/Android.bp
@@ -73,6 +73,7 @@
         // TODO: remove once Android migrates to JUnit 4.12, which provides assertThrows
         "testng",
         "compatibility-device-util-axt",
+        "flag-junit",
     ],
 
     libs: [
diff --git a/services/tests/mockingservicestests/src/com/android/server/SensitiveContentProtectionManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/SensitiveContentProtectionManagerServiceTest.java
new file mode 100644
index 0000000..b363fd4
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/SensitiveContentProtectionManagerServiceTest.java
@@ -0,0 +1,381 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.media.projection.MediaProjectionInfo;
+import android.media.projection.MediaProjectionManager;
+import android.service.notification.NotificationListenerService.Ranking;
+import android.service.notification.NotificationListenerService.RankingMap;
+import android.service.notification.StatusBarNotification;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableContext;
+import android.testing.TestableLooper.RunWithLooper;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.server.wm.SensitiveContentPackages.PackageInfo;
+import com.android.server.wm.WindowManagerInternal;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Collections;
+import java.util.Set;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
+public class SensitiveContentProtectionManagerServiceTest {
+    private static final String NOTIFICATION_KEY_1 = "com.android.server.notification.TEST_KEY_1";
+    private static final String NOTIFICATION_KEY_2 = "com.android.server.notification.TEST_KEY_2";
+
+    private static final String NOTIFICATION_PKG_1 = "com.android.server.notification.one";
+    private static final String NOTIFICATION_PKG_2 = "com.android.server.notification.two";
+
+    private static final int NOTIFICATION_UID_1 = 5;
+    private static final int NOTIFICATION_UID_2 = 6;
+
+    @Rule
+    public final TestableContext mContext =
+            new TestableContext(getInstrumentation().getTargetContext(), null);
+
+    private SensitiveContentProtectionManagerService mSensitiveContentProtectionManagerService;
+
+    @Captor
+    ArgumentCaptor<MediaProjectionManager.Callback> mMediaProjectionCallbackCaptor;
+
+    @Mock
+    private MediaProjectionManager mProjectionManager;
+
+    @Mock
+    private WindowManagerInternal mWindowManager;
+
+    @Mock
+    private StatusBarNotification mNotification1;
+
+    @Mock
+    private StatusBarNotification mNotification2;
+
+    @Mock
+    private RankingMap mRankingMap;
+
+    @Mock
+    private Ranking mSensitiveRanking;
+
+    @Mock
+    private Ranking mNonSensitiveRanking;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mSensitiveContentProtectionManagerService =
+                new SensitiveContentProtectionManagerService(mContext);
+
+        mSensitiveContentProtectionManagerService.mNotificationListener =
+                spy(mSensitiveContentProtectionManagerService.mNotificationListener);
+
+        // Setup RankingMap and two possilbe rankings
+        when(mSensitiveRanking.hasSensitiveContent()).thenReturn(true);
+        when(mNonSensitiveRanking.hasSensitiveContent()).thenReturn(false);
+        doReturn(mRankingMap)
+                .when(mSensitiveContentProtectionManagerService.mNotificationListener)
+                .getCurrentRanking();
+
+        setupSensitiveNotification();
+
+        mSensitiveContentProtectionManagerService.init(mProjectionManager, mWindowManager);
+
+        // Obtain useful mMediaProjectionCallback
+        verify(mProjectionManager).addCallback(mMediaProjectionCallbackCaptor.capture(), any());
+    }
+
+    @After
+    public void tearDown() {
+        mSensitiveContentProtectionManagerService.onDestroy();
+    }
+
+    private Set<PackageInfo> setupSensitiveNotification() {
+        // Setup Notification Values
+        when(mNotification1.getKey()).thenReturn(NOTIFICATION_KEY_1);
+        when(mNotification1.getPackageName()).thenReturn(NOTIFICATION_PKG_1);
+        when(mNotification1.getUid()).thenReturn(NOTIFICATION_UID_1);
+
+        when(mNotification2.getKey()).thenReturn(NOTIFICATION_KEY_2);
+        when(mNotification2.getPackageName()).thenReturn(NOTIFICATION_PKG_2);
+        when(mNotification2.getUid()).thenReturn(NOTIFICATION_UID_1);
+
+        StatusBarNotification[] mNotifications =
+                new StatusBarNotification[] {mNotification1, mNotification2};
+        doReturn(mNotifications)
+                .when(mSensitiveContentProtectionManagerService.mNotificationListener)
+                .getActiveNotifications();
+
+        when(mRankingMap.getRawRankingObject(eq(NOTIFICATION_KEY_1)))
+                .thenReturn(mSensitiveRanking);
+        when(mRankingMap.getRawRankingObject(eq(NOTIFICATION_KEY_2)))
+                .thenReturn(mNonSensitiveRanking);
+
+        return Set.of(new PackageInfo(NOTIFICATION_PKG_1, NOTIFICATION_UID_1));
+    }
+
+    private Set<PackageInfo> setupMultipleSensitiveNotificationsFromSamePackageAndUid() {
+        // Setup Notification Values
+        when(mNotification1.getKey()).thenReturn(NOTIFICATION_KEY_1);
+        when(mNotification1.getPackageName()).thenReturn(NOTIFICATION_PKG_1);
+        when(mNotification1.getUid()).thenReturn(NOTIFICATION_UID_1);
+
+        when(mNotification2.getKey()).thenReturn(NOTIFICATION_KEY_2);
+        when(mNotification2.getPackageName()).thenReturn(NOTIFICATION_PKG_1);
+        when(mNotification2.getUid()).thenReturn(NOTIFICATION_UID_1);
+
+        StatusBarNotification[] mNotifications =
+                new StatusBarNotification[] {mNotification1, mNotification2};
+        doReturn(mNotifications)
+                .when(mSensitiveContentProtectionManagerService.mNotificationListener)
+                .getActiveNotifications();
+
+        when(mRankingMap.getRawRankingObject(eq(NOTIFICATION_KEY_1)))
+                .thenReturn(mSensitiveRanking);
+        when(mRankingMap.getRawRankingObject(eq(NOTIFICATION_KEY_2)))
+                .thenReturn(mSensitiveRanking);
+
+        return Set.of(new PackageInfo(NOTIFICATION_PKG_1, NOTIFICATION_UID_1));
+    }
+
+    private Set<PackageInfo> setupMultipleSensitiveNotificationsFromDifferentPackage() {
+        // Setup Notification Values
+        when(mNotification1.getKey()).thenReturn(NOTIFICATION_KEY_1);
+        when(mNotification1.getPackageName()).thenReturn(NOTIFICATION_PKG_1);
+        when(mNotification1.getUid()).thenReturn(NOTIFICATION_UID_1);
+
+        when(mNotification2.getKey()).thenReturn(NOTIFICATION_KEY_2);
+        when(mNotification2.getPackageName()).thenReturn(NOTIFICATION_PKG_2);
+        when(mNotification2.getUid()).thenReturn(NOTIFICATION_UID_1);
+
+        StatusBarNotification[] mNotifications =
+                new StatusBarNotification[] {mNotification1, mNotification2};
+        doReturn(mNotifications)
+                .when(mSensitiveContentProtectionManagerService.mNotificationListener)
+                .getActiveNotifications();
+
+        when(mRankingMap.getRawRankingObject(eq(NOTIFICATION_KEY_1)))
+                .thenReturn(mSensitiveRanking);
+        when(mRankingMap.getRawRankingObject(eq(NOTIFICATION_KEY_2)))
+                .thenReturn(mSensitiveRanking);
+
+        return Set.of(new PackageInfo(NOTIFICATION_PKG_1, NOTIFICATION_UID_1),
+                new PackageInfo(NOTIFICATION_PKG_2, NOTIFICATION_UID_1));
+    }
+
+    private Set<PackageInfo> setupMultipleSensitiveNotificationsFromDifferentUid() {
+        // Setup Notification Values
+        when(mNotification1.getKey()).thenReturn(NOTIFICATION_KEY_1);
+        when(mNotification1.getPackageName()).thenReturn(NOTIFICATION_PKG_1);
+        when(mNotification1.getUid()).thenReturn(NOTIFICATION_UID_1);
+
+        when(mNotification2.getKey()).thenReturn(NOTIFICATION_KEY_2);
+        when(mNotification2.getPackageName()).thenReturn(NOTIFICATION_PKG_1);
+        when(mNotification2.getUid()).thenReturn(NOTIFICATION_UID_2);
+
+        StatusBarNotification[] mNotifications =
+                new StatusBarNotification[] {mNotification1, mNotification2};
+        doReturn(mNotifications)
+                .when(mSensitiveContentProtectionManagerService.mNotificationListener)
+                .getActiveNotifications();
+
+        when(mRankingMap.getRawRankingObject(eq(NOTIFICATION_KEY_1)))
+                .thenReturn(mSensitiveRanking);
+        when(mRankingMap.getRawRankingObject(eq(NOTIFICATION_KEY_2)))
+                .thenReturn(mSensitiveRanking);
+
+        return Set.of(new PackageInfo(NOTIFICATION_PKG_1, NOTIFICATION_UID_1),
+                new PackageInfo(NOTIFICATION_PKG_1, NOTIFICATION_UID_2));
+    }
+
+    private void setupNoSensitiveNotifications() {
+        // Setup Notification Values
+        when(mNotification1.getKey()).thenReturn(NOTIFICATION_KEY_1);
+        when(mNotification1.getPackageName()).thenReturn(NOTIFICATION_PKG_1);
+        when(mNotification1.getUid()).thenReturn(NOTIFICATION_UID_1);
+
+        StatusBarNotification[] mNotifications = new StatusBarNotification[] {mNotification1};
+        doReturn(mNotifications)
+                .when(mSensitiveContentProtectionManagerService.mNotificationListener)
+                .getActiveNotifications();
+
+        when(mRankingMap.getRawRankingObject(eq(NOTIFICATION_KEY_1)))
+                .thenReturn(mNonSensitiveRanking);
+    }
+
+    private void setupNoNotifications() {
+        // Setup Notification Values
+        StatusBarNotification[] mNotifications = new StatusBarNotification[] {};
+        doReturn(mNotifications)
+                .when(mSensitiveContentProtectionManagerService.mNotificationListener)
+                .getActiveNotifications();
+    }
+
+    @Test
+    public void mediaProjectionOnStart_onProjectionStart_setWmBlockedPackages() {
+        Set<PackageInfo> expectedBlockedPackages = setupSensitiveNotification();
+
+        mMediaProjectionCallbackCaptor.getValue().onStart(mock(MediaProjectionInfo.class));
+
+        verify(mWindowManager).setShouldBlockScreenCaptureForApp(expectedBlockedPackages);
+    }
+
+    @Test
+    public void mediaProjectionOnStart_noSensitiveNotifications_noBlockedPackages() {
+        setupNoSensitiveNotifications();
+
+        mMediaProjectionCallbackCaptor.getValue().onStart(mock(MediaProjectionInfo.class));
+
+        verify(mWindowManager).setShouldBlockScreenCaptureForApp(Collections.emptySet());
+    }
+
+    @Test
+    public void mediaProjectionOnStart_noNotifications_noBlockedPackages() {
+        setupNoNotifications();
+
+        mMediaProjectionCallbackCaptor.getValue().onStart(mock(MediaProjectionInfo.class));
+
+        verify(mWindowManager).setShouldBlockScreenCaptureForApp(Collections.emptySet());
+    }
+
+    @Test
+    public void mediaProjectionOnStart_multipleNotifications_setWmBlockedPackages() {
+        Set<PackageInfo> expectedBlockedPackages =
+                setupMultipleSensitiveNotificationsFromSamePackageAndUid();
+
+        mMediaProjectionCallbackCaptor.getValue().onStart(mock(MediaProjectionInfo.class));
+
+        verify(mWindowManager).setShouldBlockScreenCaptureForApp(expectedBlockedPackages);
+    }
+
+    @Test
+    public void mediaProjectionOnStart_multiplePackages_setWmBlockedPackages() {
+        Set<PackageInfo> expectedBlockedPackages =
+                setupMultipleSensitiveNotificationsFromDifferentPackage();
+
+        mMediaProjectionCallbackCaptor.getValue().onStart(mock(MediaProjectionInfo.class));
+
+        verify(mWindowManager).setShouldBlockScreenCaptureForApp(expectedBlockedPackages);
+    }
+
+    @Test
+    public void mediaProjectionOnStart_multipleUid_setWmBlockedPackages() {
+        Set<PackageInfo> expectedBlockedPackages =
+                setupMultipleSensitiveNotificationsFromDifferentUid();
+
+        mMediaProjectionCallbackCaptor.getValue().onStart(mock(MediaProjectionInfo.class));
+
+        verify(mWindowManager).setShouldBlockScreenCaptureForApp(expectedBlockedPackages);
+    }
+
+    @Test
+    public void mediaProjectionOnStop_onProjectionEnd_clearWmBlockedPackages() {
+        setupSensitiveNotification();
+
+        MediaProjectionInfo mediaProjectionInfo = mock(MediaProjectionInfo.class);
+        mMediaProjectionCallbackCaptor.getValue().onStart(mediaProjectionInfo);
+        Mockito.reset(mWindowManager);
+
+        mMediaProjectionCallbackCaptor.getValue().onStop(mediaProjectionInfo);
+
+        verify(mWindowManager).setShouldBlockScreenCaptureForApp(Collections.emptySet());
+    }
+
+    @Test
+    public void mediaProjectionOnStart_afterOnStop_onProjectionStart_setWmBlockedPackages() {
+        Set<PackageInfo> expectedBlockedPackages = setupSensitiveNotification();
+
+        MediaProjectionInfo mediaProjectionInfo = mock(MediaProjectionInfo.class);
+        mMediaProjectionCallbackCaptor.getValue().onStart(mediaProjectionInfo);
+        mMediaProjectionCallbackCaptor.getValue().onStop(mediaProjectionInfo);
+        Mockito.reset(mWindowManager);
+
+        mMediaProjectionCallbackCaptor.getValue().onStart(mediaProjectionInfo);
+
+        verify(mWindowManager).setShouldBlockScreenCaptureForApp(expectedBlockedPackages);
+    }
+
+    @Test
+    public void mediaProjectionOnStart_getActiveNotificationsThrows_noBlockedPackages() {
+        doThrow(SecurityException.class)
+                .when(mSensitiveContentProtectionManagerService.mNotificationListener)
+                .getActiveNotifications();
+
+        mMediaProjectionCallbackCaptor.getValue().onStart(mock(MediaProjectionInfo.class));
+
+        verify(mWindowManager).setShouldBlockScreenCaptureForApp(Collections.emptySet());
+    }
+
+    @Test
+    public void mediaProjectionOnStart_getCurrentRankingThrows_noBlockedPackages() {
+        doThrow(SecurityException.class)
+                .when(mSensitiveContentProtectionManagerService.mNotificationListener)
+                .getCurrentRanking();
+
+        mMediaProjectionCallbackCaptor.getValue().onStart(mock(MediaProjectionInfo.class));
+
+        verify(mWindowManager).setShouldBlockScreenCaptureForApp(Collections.emptySet());
+    }
+
+    @Test
+    public void mediaProjectionOnStart_getCurrentRanking_nullRankingMap_noBlockedPackages() {
+        doReturn(null)
+                .when(mSensitiveContentProtectionManagerService.mNotificationListener)
+                .getCurrentRanking();
+
+        mMediaProjectionCallbackCaptor.getValue().onStart(mock(MediaProjectionInfo.class));
+
+        verify(mWindowManager).setShouldBlockScreenCaptureForApp(Collections.emptySet());
+    }
+
+    @Test
+    public void mediaProjectionOnStart_getCurrentRanking_missingRanking_noBlockedPackages() {
+        when(mRankingMap.getRawRankingObject(eq(NOTIFICATION_KEY_1))).thenReturn(null);
+
+        doReturn(mRankingMap)
+                .when(mSensitiveContentProtectionManagerService.mNotificationListener)
+                .getCurrentRanking();
+
+        mMediaProjectionCallbackCaptor.getValue().onStart(mock(MediaProjectionInfo.class));
+
+        verify(mWindowManager).setShouldBlockScreenCaptureForApp(Collections.emptySet());
+    }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java
index 2f909f8..fcb3caa 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java
@@ -824,24 +824,24 @@
         final BroadcastOptions options = BroadcastOptions.makeWithDeferUntilActive(true);
 
         broadcastIntent(intent1, null, true);
-        assertStickyBroadcasts(mAms.getStickyBroadcasts(TEST_ACTION1, TEST_USER),
+        assertStickyBroadcasts(mAms.getStickyBroadcastsForTest(TEST_ACTION1, TEST_USER),
                 StickyBroadcast.create(intent1, false, Process.myUid(), PROCESS_STATE_UNKNOWN));
-        assertNull(mAms.getStickyBroadcasts(TEST_ACTION2, TEST_USER));
-        assertNull(mAms.getStickyBroadcasts(TEST_ACTION3, TEST_USER));
+        assertNull(mAms.getStickyBroadcastsForTest(TEST_ACTION2, TEST_USER));
+        assertNull(mAms.getStickyBroadcastsForTest(TEST_ACTION3, TEST_USER));
 
         broadcastIntent(intent2, options.toBundle(), true);
-        assertStickyBroadcasts(mAms.getStickyBroadcasts(TEST_ACTION1, TEST_USER),
+        assertStickyBroadcasts(mAms.getStickyBroadcastsForTest(TEST_ACTION1, TEST_USER),
                 StickyBroadcast.create(intent1, false, Process.myUid(), PROCESS_STATE_UNKNOWN));
-        assertStickyBroadcasts(mAms.getStickyBroadcasts(TEST_ACTION2, TEST_USER),
+        assertStickyBroadcasts(mAms.getStickyBroadcastsForTest(TEST_ACTION2, TEST_USER),
                 StickyBroadcast.create(intent2, true, Process.myUid(), PROCESS_STATE_UNKNOWN));
-        assertNull(mAms.getStickyBroadcasts(TEST_ACTION3, TEST_USER));
+        assertNull(mAms.getStickyBroadcastsForTest(TEST_ACTION3, TEST_USER));
 
         broadcastIntent(intent3, null, true);
-        assertStickyBroadcasts(mAms.getStickyBroadcasts(TEST_ACTION1, TEST_USER),
+        assertStickyBroadcasts(mAms.getStickyBroadcastsForTest(TEST_ACTION1, TEST_USER),
                 StickyBroadcast.create(intent1, false, Process.myUid(), PROCESS_STATE_UNKNOWN));
-        assertStickyBroadcasts(mAms.getStickyBroadcasts(TEST_ACTION2, TEST_USER),
+        assertStickyBroadcasts(mAms.getStickyBroadcastsForTest(TEST_ACTION2, TEST_USER),
                 StickyBroadcast.create(intent2, true, Process.myUid(), PROCESS_STATE_UNKNOWN));
-        assertStickyBroadcasts(mAms.getStickyBroadcasts(TEST_ACTION3, TEST_USER),
+        assertStickyBroadcasts(mAms.getStickyBroadcastsForTest(TEST_ACTION3, TEST_USER),
                 StickyBroadcast.create(intent3, false, Process.myUid(), PROCESS_STATE_UNKNOWN));
     }
 
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
index 115a5b0..e7aaed4 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
@@ -67,6 +67,7 @@
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.DeadObjectException;
@@ -1878,6 +1879,32 @@
     }
 
     @Test
+    public void testReplacePending_withSingletonReceiver() throws Exception {
+        final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_PHONE);
+        final ProcessRecord systemApp = makeActiveProcessRecord(PACKAGE_ANDROID, PROCESS_SYSTEM);
+
+        final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED)
+                .addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
+
+        final ResolveInfo systemReceiverA = makeManifestReceiver(PACKAGE_ANDROID, PROCESS_SYSTEM,
+                CLASS_BLUE, USER_SYSTEM);
+        final ResolveInfo systemReceiverB = makeManifestReceiver(PACKAGE_ANDROID, PROCESS_SYSTEM,
+                CLASS_BLUE, USER_GUEST);
+
+        enqueueBroadcast(makeBroadcastRecord(airplane, callerApp, List.of(
+                systemReceiverA, systemReceiverB)));
+
+        assertEquals("Unexpected userId for receiverA", USER_SYSTEM,
+                UserHandle.getUserId(systemReceiverA.activityInfo.applicationInfo.uid));
+        assertEquals("Unexpected userId for receiverB", USER_SYSTEM,
+                UserHandle.getUserId(systemReceiverB.activityInfo.applicationInfo.uid));
+
+        waitForIdle();
+
+        verifyScheduleReceiver(times(2), systemApp, airplane);
+    }
+
+    @Test
     public void testIdleAndBarrier() throws Exception {
         final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED);
         final ProcessRecord receiverApp = makeActiveProcessRecord(PACKAGE_GREEN);
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
index f386c3b..d876dae 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
@@ -80,10 +80,13 @@
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
 
 import android.app.ActivityManager;
 import android.app.AppOpsManager;
+import android.app.ApplicationExitInfo;
 import android.app.IApplicationThread;
 import android.app.IServiceConnection;
 import android.content.ComponentName;
@@ -94,9 +97,11 @@
 import android.os.Build;
 import android.os.IBinder;
 import android.os.PowerManagerInternal;
+import android.os.Process;
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.SetFlagsRule;
 import android.util.ArrayMap;
 import android.util.SparseArray;
 
@@ -107,7 +112,9 @@
 
 import org.junit.After;
 import org.junit.AfterClass;
+import org.junit.Before;
 import org.junit.BeforeClass;
+import org.junit.Rule;
 import org.junit.Test;
 
 import java.io.File;
@@ -148,12 +155,18 @@
     private static final String MOCKAPP5_PROCESSNAME = "test #5";
     private static final String MOCKAPP5_PACKAGENAME = "com.android.test.test5";
     private static final int MOCKAPP2_UID_OTHER = MOCKAPP2_UID + UserHandle.PER_USER_RANGE;
+    private static final int MOCKAPP_ISOLATED_UID = Process.FIRST_ISOLATED_UID + 321;
+    private static final String MOCKAPP_ISOLATED_PROCESSNAME = "isolated test #1";
+
     private static int sFirstCachedAdj = ProcessList.CACHED_APP_MIN_ADJ
                                           + ProcessList.CACHED_APP_IMPORTANCE_LEVELS;
     private static Context sContext;
     private static PackageManagerInternal sPackageManagerInternal;
     private static ActivityManagerService sService;
 
+    @Rule
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
     @SuppressWarnings("GuardedBy")
     @BeforeClass
     public static void setUpOnce() {
@@ -220,6 +233,11 @@
         }
     }
 
+    @Before
+    public void setUp() {
+        mSetFlagsRule.enableFlags(Flags.FLAG_NEW_FGS_RESTRICTION_LOGIC);
+    }
+
     @AfterClass
     public static void tearDownOnce() {
         LocalServices.removeServiceForTest(PackageManagerInternal.class);
@@ -286,6 +304,20 @@
     }
 
     /**
+     * Run updateOomAdjPendingTargetsLocked().
+     * - enqueues all provided processes to the pending list and lru before running
+     */
+    @SuppressWarnings("GuardedBy")
+    private void updateOomAdjPending(ProcessRecord... apps) {
+        setProcessesToLru(apps);
+        for (ProcessRecord app : apps) {
+            sService.mOomAdjuster.enqueueOomAdjTargetLocked(app);
+        }
+        sService.mOomAdjuster.updateOomAdjPendingTargetsLocked(OOM_ADJ_REASON_NONE);
+        sService.mProcessList.getLruProcessesLOSP().clear();
+    }
+
+    /**
      * Fix up the pointers in the {@link ProcessRecordNode#mApp}:
      * because we used the mokito spy objects all over the tests here, but the internal
      * pointers in the {@link ProcessRecordNode#mApp} actually point to the real object.
@@ -519,6 +551,7 @@
         sService.mConstants.mShortFgsProcStateExtraWaitDuration = 200_000;
 
         ServiceRecord s = ServiceRecord.newEmptyInstanceForTest(sService);
+        s.appInfo = new ApplicationInfo();
         s.startRequested = true;
         s.isForeground = true;
         s.foregroundServiceType = FOREGROUND_SERVICE_TYPE_SHORT_SERVICE;
@@ -561,6 +594,7 @@
 
         // SHORT_SERVICE, timed out already.
         s = ServiceRecord.newEmptyInstanceForTest(sService);
+        s.appInfo = new ApplicationInfo();
         s.startRequested = true;
         s.isForeground = true;
         s.foregroundServiceType = FOREGROUND_SERVICE_TYPE_SHORT_SERVICE;
@@ -1079,6 +1113,7 @@
 
         // In order to trick OomAdjuster to think it has a short-service, we need this logic.
         ServiceRecord s = ServiceRecord.newEmptyInstanceForTest(sService);
+        s.appInfo = new ApplicationInfo();
         s.startRequested = true;
         s.isForeground = true;
         s.foregroundServiceType = FOREGROUND_SERVICE_TYPE_SHORT_SERVICE;
@@ -1109,6 +1144,7 @@
 
         // In order to trick OomAdjuster to think it has a short-service, we need this logic.
         ServiceRecord s = ServiceRecord.newEmptyInstanceForTest(sService);
+        s.appInfo = new ApplicationInfo();
         s.startRequested = true;
         s.isForeground = true;
         s.foregroundServiceType = FOREGROUND_SERVICE_TYPE_SHORT_SERVICE;
@@ -1400,6 +1436,7 @@
 
         // In order to trick OomAdjuster to think it has a short-service, we need this logic.
         ServiceRecord s = ServiceRecord.newEmptyInstanceForTest(sService);
+        s.appInfo = new ApplicationInfo();
         s.startRequested = true;
         s.isForeground = true;
         s.foregroundServiceType = FOREGROUND_SERVICE_TYPE_SHORT_SERVICE;
@@ -2651,6 +2688,90 @@
         assertNotEquals(FOREGROUND_APP_ADJ, app.mState.getSetAdj());
     }
 
+    @SuppressWarnings("GuardedBy")
+    @Test
+    public void testUpdateOomAdj_DoAll_Isolated_stopService() {
+        ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_ISOLATED_UID,
+                MOCKAPP_ISOLATED_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
+
+        setProcessesToLru(app);
+        ServiceRecord s = makeServiceRecord(app);
+        s.startRequested = true;
+        s.lastActivity = SystemClock.uptimeMillis();
+        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        updateOomAdj();
+        assertProcStates(app, PROCESS_STATE_SERVICE, SERVICE_ADJ, SCHED_GROUP_BACKGROUND);
+
+        app.mServices.stopService(s);
+        updateOomAdj();
+        // isolated process should be killed immediately after service stop.
+        verify(app).killLocked("isolated not needed", ApplicationExitInfo.REASON_OTHER,
+                ApplicationExitInfo.SUBREASON_ISOLATED_NOT_NEEDED, true);
+    }
+
+    @SuppressWarnings("GuardedBy")
+    @Test
+    public void testUpdateOomAdj_DoPending_Isolated_stopService() {
+        ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_ISOLATED_UID,
+                MOCKAPP_ISOLATED_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
+
+        ServiceRecord s = makeServiceRecord(app);
+        s.startRequested = true;
+        s.lastActivity = SystemClock.uptimeMillis();
+        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        updateOomAdjPending(app);
+        assertProcStates(app, PROCESS_STATE_SERVICE, SERVICE_ADJ, SCHED_GROUP_BACKGROUND);
+
+        app.mServices.stopService(s);
+        updateOomAdjPending(app);
+        // isolated process should be killed immediately after service stop.
+        verify(app).killLocked("isolated not needed", ApplicationExitInfo.REASON_OTHER,
+                ApplicationExitInfo.SUBREASON_ISOLATED_NOT_NEEDED, true);
+    }
+
+    @SuppressWarnings("GuardedBy")
+    @Test
+    public void testUpdateOomAdj_DoAll_Isolated_stopServiceWithEntryPoint() {
+        ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_ISOLATED_UID,
+                MOCKAPP_ISOLATED_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
+        app.setIsolatedEntryPoint("test");
+
+        setProcessesToLru(app);
+        ServiceRecord s = makeServiceRecord(app);
+        s.startRequested = true;
+        s.lastActivity = SystemClock.uptimeMillis();
+        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        updateOomAdj();
+        assertProcStates(app, PROCESS_STATE_SERVICE, SERVICE_ADJ, SCHED_GROUP_BACKGROUND);
+
+        app.mServices.stopService(s);
+        updateOomAdj();
+        // isolated process with entry point should not be killed
+        verify(app, never()).killLocked("isolated not needed", ApplicationExitInfo.REASON_OTHER,
+                ApplicationExitInfo.SUBREASON_ISOLATED_NOT_NEEDED, true);
+    }
+
+    @SuppressWarnings("GuardedBy")
+    @Test
+    public void testUpdateOomAdj_DoPending_Isolated_stopServiceWithEntryPoint() {
+        ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_ISOLATED_UID,
+                MOCKAPP_ISOLATED_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
+        app.setIsolatedEntryPoint("test");
+
+        ServiceRecord s = makeServiceRecord(app);
+        s.startRequested = true;
+        s.lastActivity = SystemClock.uptimeMillis();
+        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        updateOomAdjPending(app);
+        assertProcStates(app, PROCESS_STATE_SERVICE, SERVICE_ADJ, SCHED_GROUP_BACKGROUND);
+
+        app.mServices.stopService(s);
+        updateOomAdjPending(app);
+        // isolated process with entry point should not be killed
+        verify(app, never()).killLocked("isolated not needed", ApplicationExitInfo.REASON_OTHER,
+                ApplicationExitInfo.SUBREASON_ISOLATED_NOT_NEEDED, true);
+    }
+
     private ProcessRecord makeDefaultProcessRecord(int pid, int uid, String processName,
             String packageName, boolean hasShownUi) {
         long now = SystemClock.uptimeMillis();
diff --git a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
index fc2e5b0..0703db2 100644
--- a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
@@ -118,7 +118,8 @@
 @Presubmit
 public class GameManagerServiceTests {
     @Mock MockContext mMockContext;
-    @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+    @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(
+                SetFlagsRule.DefaultInitValueType.NULL_DEFAULT);
     private static final String TAG = "GameManagerServiceTests";
     private static final String PACKAGE_NAME_INVALID = "com.android.app";
     private static final int USER_ID_1 = 1001;
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
index 293003d..32878b3 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
@@ -67,6 +67,7 @@
 import android.location.LocationManagerInternal.ProviderEnabledListener;
 import android.location.LocationRequest;
 import android.location.LocationResult;
+import android.location.flags.Flags;
 import android.location.provider.IProviderRequestListener;
 import android.location.provider.ProviderProperties;
 import android.location.provider.ProviderRequest;
@@ -78,8 +79,10 @@
 import android.os.PowerManager;
 import android.os.Process;
 import android.os.RemoteException;
+import android.os.SystemClock;
 import android.os.WorkSource;
 import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.SetFlagsRule;
 import android.provider.DeviceConfig;
 import android.provider.Settings;
 import android.util.Log;
@@ -97,6 +100,7 @@
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
@@ -140,6 +144,9 @@
     private static final WorkSource WORK_SOURCE = new WorkSource(IDENTITY.getUid());
     private static final String MISSING_PERMISSION = "missing_permission";
 
+    @Rule
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
     private Random mRandom;
 
     @Mock
@@ -1347,6 +1354,24 @@
         assertThat(mManager.isVisibleToCaller()).isFalse();
     }
 
+    @Test
+    public void testValidateLocation_futureLocation() {
+        mSetFlagsRule.enableFlags(Flags.FLAG_LOCATION_VALIDATION);
+        Location location = createLocation(NAME, mRandom);
+        mProvider.setProviderLocation(location);
+
+        assertThat(mPassive.getLastLocation(new LastLocationRequest.Builder().build(), IDENTITY,
+                PERMISSION_FINE)).isEqualTo(location);
+
+        Location futureLocation = createLocation(NAME, mRandom);
+        futureLocation.setElapsedRealtimeNanos(
+                SystemClock.elapsedRealtimeNanos() + TimeUnit.SECONDS.toNanos(2));
+        mProvider.setProviderLocation(futureLocation);
+
+        assertThat(mPassive.getLastLocation(new LastLocationRequest.Builder().build(), IDENTITY,
+                PERMISSION_FINE)).isEqualTo(location);
+    }
+
     @MediumTest
     @Test
     public void testEnableMsl_expectedBehavior() throws Exception {
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java
index 57326b2..d08cdc7 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java
@@ -343,10 +343,12 @@
         List<ApexManager.ScanResult> scanResults = scanApexInfos(apexInfo);
         mApexManager.notifyScanResult(scanResults);
 
-        assertThat(mApexManager.getApkInApexInstallError(activeApex.apexModuleName)).isNull();
+        final String apexPackageName = mApexManager.getActivePackageNameForApexModuleName(
+                activeApex.apexModuleName);
+        assertThat(mApexManager.getApkInApexInstallError(apexPackageName)).isNull();
         mApexManager.reportErrorWithApkInApex(activeApex.apexDirectory.getAbsolutePath(),
                 "Some random error");
-        assertThat(mApexManager.getApkInApexInstallError(activeApex.apexModuleName))
+        assertThat(mApexManager.getApkInApexInstallError(apexPackageName))
                 .isEqualTo("Some random error");
     }
 
@@ -370,9 +372,11 @@
         List<ApexManager.ScanResult> scanResults = scanApexInfos(apexInfo);
         mApexManager.notifyScanResult(scanResults);
 
-        assertThat(mApexManager.getApksInApex(activeApex.apexModuleName)).isEmpty();
+        final String apexPackageName = mApexManager.getActivePackageNameForApexModuleName(
+                activeApex.apexModuleName);
+        assertThat(mApexManager.getApksInApex(apexPackageName)).isEmpty();
         mApexManager.registerApkInApex(fakeApkInApex);
-        assertThat(mApexManager.getApksInApex(activeApex.apexModuleName)).isEmpty();
+        assertThat(mApexManager.getApksInApex(apexPackageName)).isEmpty();
     }
 
     @Test
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java
index e5ecdc4..0403c64 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java
@@ -210,6 +210,9 @@
                 anyInt())).thenReturn(1);
         when(mInstallerService.getExistingDraftSessionId(anyInt(), any(), anyInt())).thenReturn(
                 PackageInstaller.SessionInfo.INVALID_ID);
+        PackageInstallerSession session = mock(PackageInstallerSession.class);
+        when(mInstallerService.getSession(anyInt())).thenReturn(session);
+        when(session.getUnarchivalStatus()).thenReturn(PackageInstaller.UNARCHIVAL_STATUS_UNSET);
         doReturn(new ParceledListSlice<>(List.of(mock(ResolveInfo.class))))
                 .when(mPackageManagerService).queryIntentReceivers(any(), any(), any(), anyLong(),
                         eq(mUserId));
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/PackageMonitorCallbackHelperTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/PackageMonitorCallbackHelperTest.java
index 6c44fd0..24e7242 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/PackageMonitorCallbackHelperTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/PackageMonitorCallbackHelperTest.java
@@ -44,6 +44,7 @@
 import org.mockito.ArgumentCaptor;
 
 import java.util.ArrayList;
+import java.util.function.BiFunction;
 
 /**
  * A unit test for PackageMonitorCallbackHelper implementation.
@@ -78,7 +79,8 @@
 
         mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED,
                 FAKE_PACKAGE_NAME, createFakeBundle(), new int[]{0} /* userIds */,
-                null /* instantUserIds */, null /* broadcastAllowList */, mHandler);
+                null /* instantUserIds */, null /* broadcastAllowList */, mHandler,
+                null /* filterExtras */);
 
         verify(callback, after(WAIT_CALLBACK_CALLED_IN_MS).never()).sendResult(any());
     }
@@ -91,7 +93,7 @@
                 Binder.getCallingUid());
         mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED,
                 FAKE_PACKAGE_NAME, createFakeBundle(), new int[]{0}, null /* instantUserIds */,
-                null /* broadcastAllowList */, mHandler);
+                null /* broadcastAllowList */, mHandler, null /* filterExtras */);
 
         verify(callback, after(WAIT_CALLBACK_CALLED_IN_MS).times(1)).sendResult(any());
 
@@ -99,12 +101,55 @@
         mPackageMonitorCallbackHelper.unregisterPackageMonitorCallback(callback);
         mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED,
                 FAKE_PACKAGE_NAME, createFakeBundle(), new int[]{0} /* userIds */,
-                null /* instantUserIds */, null /* broadcastAllowList */, mHandler);
+                null /* instantUserIds */, null /* broadcastAllowList */, mHandler,
+                null /* filterExtras */);
 
         verify(callback, after(WAIT_CALLBACK_CALLED_IN_MS).never()).sendResult(any());
     }
 
     @Test
+    public void testPackageMonitorCallback_SuspendNoAccessCallbackNotCalled() throws Exception {
+        BiFunction<Integer, Bundle, Bundle> filterExtras = (callingUid, intentExtras) -> null;
+
+        IRemoteCallback callback = createMockPackageMonitorCallback();
+        mPackageMonitorCallbackHelper.registerPackageMonitorCallback(callback, 0 /* userId */,
+                Binder.getCallingUid());
+        mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGES_SUSPENDED,
+                FAKE_PACKAGE_NAME, createFakeBundle(), new int[]{0}, null /* instantUserIds */,
+                null /* broadcastAllowList */, mHandler, filterExtras);
+
+        verify(callback, after(WAIT_CALLBACK_CALLED_IN_MS).never()).sendResult(any());
+    }
+
+    @Test
+    public void testPackageMonitorCallback_SuspendCallbackCalled() throws Exception {
+        Bundle result = new Bundle();
+        result.putInt(Intent.EXTRA_UID, FAKE_PACKAGE_UID);
+        result.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, new String[]{FAKE_PACKAGE_NAME});
+        BiFunction<Integer, Bundle, Bundle> filterExtras = (callingUid, intentExtras) -> result;
+
+        IRemoteCallback callback = createMockPackageMonitorCallback();
+        mPackageMonitorCallbackHelper.registerPackageMonitorCallback(callback, 0 /* userId */,
+                Binder.getCallingUid());
+        mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGES_SUSPENDED,
+                FAKE_PACKAGE_NAME, createFakeBundle(), new int[]{0}, null /* instantUserIds */,
+                null /* broadcastAllowList */, mHandler, filterExtras);
+
+        ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
+        verify(callback, after(WAIT_CALLBACK_CALLED_IN_MS).times(1)).sendResult(
+                bundleCaptor.capture());
+        Bundle bundle = bundleCaptor.getValue();
+        Intent intent = bundle.getParcelable(
+                PackageManager.EXTRA_PACKAGE_MONITOR_CALLBACK_RESULT, Intent.class);
+        assertThat(intent).isNotNull();
+        assertThat(intent.getAction()).isEqualTo(Intent.ACTION_PACKAGES_SUSPENDED);
+        String[] pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
+        assertThat(pkgList).isNotNull();
+        assertThat(pkgList.length).isEqualTo(1);
+        assertThat(pkgList[0]).isEqualTo(FAKE_PACKAGE_NAME);
+    }
+
+    @Test
     public void testRegisterPackageMonitorCallback_callbackCalled() throws Exception {
         IRemoteCallback callback = createMockPackageMonitorCallback();
 
@@ -112,7 +157,8 @@
                 Binder.getCallingUid());
         mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED,
                 FAKE_PACKAGE_NAME, createFakeBundle(), new int[]{0} /* userIds */,
-                null /* instantUserIds */, null /* broadcastAllowList */, mHandler);
+                null /* instantUserIds */, null /* broadcastAllowList */, mHandler,
+                null /* filterExtras */);
 
         ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
         verify(callback, after(WAIT_CALLBACK_CALLED_IN_MS).times(1)).sendResult(
@@ -136,7 +182,8 @@
         // Notify for user 10
         mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED,
                 FAKE_PACKAGE_NAME, createFakeBundle(), new int[]{10} /* userIds */,
-                null /* instantUserIds */, null /* broadcastAllowList */, mHandler);
+                null /* instantUserIds */, null /* broadcastAllowList */, mHandler,
+                null /* filterExtras */);
 
         verify(callback, after(WAIT_CALLBACK_CALLED_IN_MS).never()).sendResult(any());
     }
@@ -239,7 +286,8 @@
         mPackageMonitorCallbackHelper.onUserRemoved(10);
         mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED,
                 FAKE_PACKAGE_NAME, createFakeBundle(), new int[]{10} /* userIds */,
-                null /* instantUserIds */, null /* broadcastAllowList */, mHandler);
+                null /* instantUserIds */, null /* broadcastAllowList */, mHandler,
+                null /* filterExtras */);
 
         verify(callback, after(WAIT_CALLBACK_CALLED_IN_MS).never()).sendResult(any());
     }
@@ -255,7 +303,7 @@
                 Binder.getCallingUid());
         mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED,
                 FAKE_PACKAGE_NAME, createFakeBundle(), new int[]{0} /* userIds */,
-                null /* instantUserIds */, broadcastAllowList, mHandler);
+                null /* instantUserIds */, broadcastAllowList, mHandler, null /* filterExtras */);
 
         verify(callback, after(WAIT_CALLBACK_CALLED_IN_MS).times(1)).sendResult(any());
     }
@@ -271,7 +319,7 @@
                 Binder.getCallingUid());
         mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED,
                 FAKE_PACKAGE_NAME, createFakeBundle(), new int[]{0} /* userIds */,
-                null /* instantUserIds */, broadcastAllowList, mHandler);
+                null /* instantUserIds */, broadcastAllowList, mHandler, null /* filterExtras */);
 
         verify(callback, after(WAIT_CALLBACK_CALLED_IN_MS).never()).sendResult(any());
     }
@@ -287,7 +335,7 @@
                 Process.SYSTEM_UID);
         mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED,
                 FAKE_PACKAGE_NAME, createFakeBundle(), new int[]{0} /* userIds */,
-                null /* instantUserIds */, broadcastAllowList, mHandler);
+                null /* instantUserIds */, broadcastAllowList, mHandler, null /* filterExtras */);
 
         verify(callback, after(WAIT_CALLBACK_CALLED_IN_MS).times(1)).sendResult(any());
     }
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceDemoModeTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceDemoModeTest.kt
new file mode 100644
index 0000000..dfdb0c7
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceDemoModeTest.kt
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm
+
+import android.content.res.Configuration
+import android.os.Looper
+import android.os.SystemProperties
+import android.os.UserHandle
+import android.util.ArrayMap
+import com.android.server.LockGuard
+import com.android.server.extendedtestutils.wheneverStatic
+import com.android.server.testutils.whenever
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.MockitoAnnotations
+
+@RunWith(JUnit4::class)
+class UserManagerServiceDemoModeTest {
+    private lateinit var ums: UserManagerService
+
+    @Rule
+    @JvmField
+    val rule = MockSystemRule()
+
+    @Before
+    @Throws(Exception::class)
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+        rule.system().stageNominalSystemState()
+
+        if (Looper.myLooper() == null) {
+            Looper.prepare()
+        }
+
+        wheneverStatic { LockGuard.installNewLock(LockGuard.INDEX_USER) }.thenReturn(Object())
+        whenever(rule.mocks().systemConfig.getAndClearPackageToUserTypeWhitelist()).thenReturn(ArrayMap<String, Set<String>>())
+        whenever(rule.mocks().systemConfig.getAndClearPackageToUserTypeBlacklist()).thenReturn(ArrayMap<String, Set<String>>())
+        whenever(rule.mocks().resources.getStringArray(com.android.internal.R.array.config_defaultFirstUserRestrictions)).thenReturn(arrayOf<String>())
+        whenever(rule.mocks().resources.configuration).thenReturn(Configuration())
+
+        ums = UserManagerService(rule.mocks().context)
+    }
+
+    @Test
+    fun isDemoUser_returnsTrue_whenSystemPropertyIsSet() {
+        wheneverStatic { SystemProperties.getBoolean("ro.boot.arc_demo_mode", false) }.thenReturn(true)
+
+        assertThat(ums.isDemoUser(0)).isTrue()
+    }
+
+    @Test
+    fun isDemoUser_returnsFalse_whenSystemPropertyIsSet() {
+        wheneverStatic { SystemProperties.getBoolean("ro.boot.arc_demo_mode", false) }.thenReturn(false)
+
+        assertThat(ums.isDemoUser(0)).isFalse()
+    }
+
+    @Test
+    fun isDemoUser_returnsFalse_whenSystemPropertyIsNotSet() {
+        assertThat(ums.isDemoUser(0)).isFalse()
+    }
+}
\ No newline at end of file
diff --git a/services/tests/mockingservicestests/src/com/android/server/trust/TrustManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/trust/TrustManagerServiceTest.java
index 9851bc1..37ca09d 100644
--- a/services/tests/mockingservicestests/src/com/android/server/trust/TrustManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/trust/TrustManagerServiceTest.java
@@ -16,24 +16,24 @@
 
 package com.android.server.trust;
 
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.argThat;
+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.eq;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static org.mockito.ArgumentMatchers.anyBoolean;
-
 import android.Manifest;
 import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.app.IActivityManager;
+import android.app.admin.DevicePolicyManager;
 import android.app.trust.ITrustListener;
 import android.app.trust.ITrustManager;
 import android.content.BroadcastReceiver;
@@ -45,14 +45,22 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
-import android.net.Uri;
+import android.content.pm.UserInfo;
+import android.hardware.biometrics.BiometricManager;
+import android.os.Bundle;
 import android.os.Handler;
+import android.os.HandlerThread;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.UserHandle;
-import android.os.test.TestLooper;
+import android.os.UserManager;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 import android.provider.Settings;
+import android.security.Authorization;
+import android.security.authorization.IKeystoreAuthorization;
 import android.service.trust.TrustAgentService;
 import android.testing.TestableContext;
 import android.view.IWindowManager;
@@ -61,12 +69,11 @@
 import androidx.test.core.app.ApplicationProvider;
 
 import com.android.internal.widget.LockPatternUtils;
+import com.android.modules.utils.testing.ExtendedMockitoRule;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
 import com.android.server.SystemServiceManager;
 
-import com.google.android.collect.Lists;
-
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
@@ -74,37 +81,74 @@
 import org.mockito.ArgumentCaptor;
 import org.mockito.ArgumentMatcher;
 import org.mockito.Mock;
-import org.mockito.MockitoSession;
-import org.mockito.junit.MockitoJUnit;
-import org.mockito.junit.MockitoRule;
 
 import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Random;
+import java.util.Collection;
+import java.util.List;
 
 public class TrustManagerServiceTest {
 
     @Rule
-    public MockitoRule mMockitoRule = MockitoJUnit.rule();
+    public final ExtendedMockitoRule mExtendedMockitoRule = new ExtendedMockitoRule.Builder(this)
+            .spyStatic(ActivityManager.class)
+            .spyStatic(Authorization.class)
+            .mockStatic(ServiceManager.class)
+            .mockStatic(WindowManagerGlobal.class)
+            .build();
+
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
     @Rule
     public final MockContext mMockContext = new MockContext(
             ApplicationProvider.getApplicationContext());
 
     private static final String URI_SCHEME_PACKAGE = "package";
-    private static final int TEST_USER_ID = UserHandle.USER_SYSTEM;
+    private static final int TEST_USER_ID = 50;
+    private static final int PARENT_USER_ID = 60;
+    private static final int PROFILE_USER_ID = 70;
+    private static final long[] PARENT_BIOMETRIC_SIDS = new long[] { 600L, 601L };
+    private static final long[] PROFILE_BIOMETRIC_SIDS = new long[] { 700L, 701L };
 
-    private final TestLooper mLooper = new TestLooper();
     private final ArrayList<ResolveInfo> mTrustAgentResolveInfoList = new ArrayList<>();
-    private final LockPatternUtils mLockPatternUtils = new LockPatternUtils(mMockContext);
-    private final TrustManagerService mService = new TrustManagerService(mMockContext);
+    private final ArrayList<ComponentName> mKnownTrustAgents = new ArrayList<>();
+    private final ArrayList<ComponentName> mEnabledTrustAgents = new ArrayList<>();
 
-    @Mock
-    private PackageManager mPackageManagerMock;
+    private @Mock ActivityManager mActivityManager;
+    private @Mock BiometricManager mBiometricManager;
+    private @Mock DevicePolicyManager mDevicePolicyManager;
+    private @Mock IKeystoreAuthorization mKeystoreAuthorization;
+    private @Mock LockPatternUtils mLockPatternUtils;
+    private @Mock PackageManager mPackageManager;
+    private @Mock UserManager mUserManager;
+    private @Mock IWindowManager mWindowManager;
+
+    private HandlerThread mHandlerThread;
+    private TrustManagerService.Injector mInjector;
+    private TrustManagerService mService;
+    private ITrustManager mTrustManager;
 
     @Before
-    public void setUp() {
-        resetTrustAgentLockSettings();
-        LocalServices.addService(SystemServiceManager.class, mock(SystemServiceManager.class));
+    public void setUp() throws Exception {
+        when(mActivityManager.isUserRunning(TEST_USER_ID)).thenReturn(true);
+        doReturn(mock(IActivityManager.class)).when(() -> ActivityManager.getService());
+
+        doReturn(mKeystoreAuthorization).when(() -> Authorization.getService());
+
+        when(mLockPatternUtils.getDevicePolicyManager()).thenReturn(mDevicePolicyManager);
+        when(mLockPatternUtils.isSecure(TEST_USER_ID)).thenReturn(true);
+        when(mLockPatternUtils.getKnownTrustAgents(TEST_USER_ID)).thenReturn(mKnownTrustAgents);
+        when(mLockPatternUtils.getEnabledTrustAgents(TEST_USER_ID)).thenReturn(mEnabledTrustAgents);
+        doAnswer(invocation -> {
+            mKnownTrustAgents.clear();
+            mKnownTrustAgents.addAll((Collection<ComponentName>) invocation.getArgument(0));
+            return null;
+        }).when(mLockPatternUtils).setKnownTrustAgents(any(), eq(TEST_USER_ID));
+        doAnswer(invocation -> {
+            mEnabledTrustAgents.clear();
+            mEnabledTrustAgents.addAll((Collection<ComponentName>) invocation.getArgument(0));
+            return null;
+        }).when(mLockPatternUtils).setEnabledTrustAgents(any(), eq(TEST_USER_ID));
 
         ArgumentMatcher<Intent> trustAgentIntentMatcher = new ArgumentMatcher<Intent>() {
             @Override
@@ -112,17 +156,43 @@
                 return TrustAgentService.SERVICE_INTERFACE.equals(argument.getAction());
             }
         };
-        when(mPackageManagerMock.queryIntentServicesAsUser(argThat(trustAgentIntentMatcher),
+        when(mPackageManager.queryIntentServicesAsUser(argThat(trustAgentIntentMatcher),
                 anyInt(), anyInt())).thenReturn(mTrustAgentResolveInfoList);
-        when(mPackageManagerMock.checkPermission(any(), any())).thenReturn(
+        when(mPackageManager.checkPermission(any(), any())).thenReturn(
                 PackageManager.PERMISSION_GRANTED);
-        mMockContext.setMockPackageManager(mPackageManagerMock);
+
+        when(mUserManager.getAliveUsers()).thenReturn(
+                List.of(new UserInfo(TEST_USER_ID, "user", UserInfo.FLAG_FULL)));
+
+        when(mWindowManager.isKeyguardLocked()).thenReturn(true);
+
+        mMockContext.addMockSystemService(ActivityManager.class, mActivityManager);
+        mMockContext.addMockSystemService(BiometricManager.class, mBiometricManager);
+        mMockContext.setMockPackageManager(mPackageManager);
+        mMockContext.addMockSystemService(UserManager.class, mUserManager);
+        doReturn(mWindowManager).when(() -> WindowManagerGlobal.getWindowManagerService());
+        LocalServices.addService(SystemServiceManager.class, mock(SystemServiceManager.class));
+
+        grantPermission(Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE);
+        grantPermission(Manifest.permission.TRUST_LISTENER);
+
+        mHandlerThread = new HandlerThread("handler");
+        mHandlerThread.start();
+        mInjector = new TrustManagerService.Injector(mLockPatternUtils, mHandlerThread.getLooper());
+        mService = new TrustManagerService(mMockContext, mInjector);
+
+        // Get the ITrustManager from the new TrustManagerService.
+        mService.onStart();
+        ArgumentCaptor<IBinder> binderArgumentCaptor = ArgumentCaptor.forClass(IBinder.class);
+        verify(() -> ServiceManager.addService(eq(Context.TRUST_SERVICE),
+                    binderArgumentCaptor.capture(), anyBoolean(), anyInt()));
+        mTrustManager = ITrustManager.Stub.asInterface(binderArgumentCaptor.getValue());
     }
 
     @After
     public void tearDown() {
-        resetTrustAgentLockSettings();
         LocalServices.removeServiceForTest(SystemServiceManager.class);
+        mHandlerThread.quit();
     }
 
     @Test
@@ -142,10 +212,9 @@
 
         bootService();
 
-        assertThat(mLockPatternUtils.getEnabledTrustAgents(TEST_USER_ID)).containsExactly(
-                systemTrustAgent1, systemTrustAgent2);
-        assertThat(mLockPatternUtils.getKnownTrustAgents(TEST_USER_ID)).containsExactly(
-                systemTrustAgent1, systemTrustAgent2, userTrustAgent1, userTrustAgent2);
+        assertThat(mEnabledTrustAgents).containsExactly(systemTrustAgent1, systemTrustAgent2);
+        assertThat(mKnownTrustAgents).containsExactly(systemTrustAgent1, systemTrustAgent2,
+                    userTrustAgent1, userTrustAgent2);
     }
 
     @Test
@@ -162,10 +231,8 @@
 
         bootService();
 
-        assertThat(mLockPatternUtils.getEnabledTrustAgents(TEST_USER_ID)).containsExactly(
-                defaultTrustAgent);
-        assertThat(mLockPatternUtils.getKnownTrustAgents(TEST_USER_ID)).containsExactly(
-                systemTrustAgent, defaultTrustAgent);
+        assertThat(mEnabledTrustAgents).containsExactly(defaultTrustAgent);
+        assertThat(mKnownTrustAgents).containsExactly(systemTrustAgent, defaultTrustAgent);
     }
 
     @Test
@@ -174,16 +241,16 @@
                 "com.android/.SystemTrustAgent");
         ComponentName trustAgent2 = ComponentName.unflattenFromString(
                 "com.android/.AnotherSystemTrustAgent");
-        initializeEnabledAgents(trustAgent1);
+        mEnabledTrustAgents.add(trustAgent1);
+        Settings.Secure.putIntForUser(mMockContext.getContentResolver(),
+                Settings.Secure.TRUST_AGENTS_INITIALIZED, 1, TEST_USER_ID);
         addTrustAgent(trustAgent1, /* isSystemApp= */ true);
         addTrustAgent(trustAgent2, /* isSystemApp= */ true);
 
         bootService();
 
-        assertThat(mLockPatternUtils.getEnabledTrustAgents(TEST_USER_ID)).containsExactly(
-                trustAgent1);
-        assertThat(mLockPatternUtils.getKnownTrustAgents(TEST_USER_ID)).containsExactly(
-                trustAgent1, trustAgent2);
+        assertThat(mEnabledTrustAgents).containsExactly(trustAgent1);
+        assertThat(mKnownTrustAgents).containsExactly(trustAgent1, trustAgent2);
     }
 
     @Test
@@ -192,17 +259,17 @@
                 "com.android/.SystemTrustAgent");
         ComponentName trustAgent2 = ComponentName.unflattenFromString(
                 "com.android/.AnotherSystemTrustAgent");
-        initializeEnabledAgents(trustAgent1);
-        initializeKnownAgents(trustAgent1);
+        Settings.Secure.putIntForUser(mMockContext.getContentResolver(),
+                Settings.Secure.TRUST_AGENTS_INITIALIZED, 1, TEST_USER_ID);
+        Settings.Secure.putIntForUser(mMockContext.getContentResolver(),
+                Settings.Secure.KNOWN_TRUST_AGENTS_INITIALIZED, 1, TEST_USER_ID);
         addTrustAgent(trustAgent1, /* isSystemApp= */ true);
         addTrustAgent(trustAgent2, /* isSystemApp= */ true);
 
         bootService();
 
-        assertThat(mLockPatternUtils.getEnabledTrustAgents(TEST_USER_ID)).containsExactly(
-                trustAgent1, trustAgent2);
-        assertThat(mLockPatternUtils.getKnownTrustAgents(TEST_USER_ID)).containsExactly(
-                trustAgent1, trustAgent2);
+        assertThat(mEnabledTrustAgents).containsExactly(trustAgent1, trustAgent2);
+        assertThat(mKnownTrustAgents).containsExactly(trustAgent1, trustAgent2);
     }
 
     @Test
@@ -212,12 +279,10 @@
                 "com.android/.SystemTrustAgent");
         addTrustAgent(newAgentComponentName, /* isSystemApp= */ true);
 
-        mMockContext.sendPackageChangedBroadcast(newAgentComponentName);
+        notifyPackageChanged(newAgentComponentName);
 
-        assertThat(mLockPatternUtils.getEnabledTrustAgents(TEST_USER_ID)).containsExactly(
-                newAgentComponentName);
-        assertThat(mLockPatternUtils.getKnownTrustAgents(TEST_USER_ID)).containsExactly(
-                newAgentComponentName);
+        assertThat(mEnabledTrustAgents).containsExactly(newAgentComponentName);
+        assertThat(mKnownTrustAgents).containsExactly(newAgentComponentName);
     }
 
     @Test
@@ -233,12 +298,10 @@
                 "com.android/.SystemTrustAgent");
         addTrustAgent(newAgentComponentName, /* isSystemApp= */ true);
 
-        mMockContext.sendPackageChangedBroadcast(newAgentComponentName);
+        notifyPackageChanged(newAgentComponentName);
 
-        assertThat(mLockPatternUtils.getEnabledTrustAgents(TEST_USER_ID)).containsExactly(
-                defaultTrustAgent);
-        assertThat(mLockPatternUtils.getKnownTrustAgents(TEST_USER_ID)).containsExactly(
-                defaultTrustAgent, newAgentComponentName);
+        assertThat(mEnabledTrustAgents).containsExactly(defaultTrustAgent);
+        assertThat(mKnownTrustAgents).containsExactly(defaultTrustAgent, newAgentComponentName);
     }
 
     @Test
@@ -248,11 +311,10 @@
                 "com.user/.UserTrustAgent");
         addTrustAgent(newAgentComponentName, /* isSystemApp= */ false);
 
-        mMockContext.sendPackageChangedBroadcast(newAgentComponentName);
+        notifyPackageChanged(newAgentComponentName);
 
-        assertThat(mLockPatternUtils.getEnabledTrustAgents(TEST_USER_ID)).isEmpty();
-        assertThat(mLockPatternUtils.getKnownTrustAgents(TEST_USER_ID)).containsExactly(
-                newAgentComponentName);
+        assertThat(mEnabledTrustAgents).isEmpty();
+        assertThat(mKnownTrustAgents).containsExactly(newAgentComponentName);
     }
 
     @Test
@@ -265,50 +327,88 @@
         addTrustAgent(systemTrustAgent2, /* isSystemApp= */ true);
         bootService();
         // Simulate user turning off systemTrustAgent2
-        mLockPatternUtils.setEnabledTrustAgents(Collections.singletonList(systemTrustAgent1),
-                TEST_USER_ID);
+        mLockPatternUtils.setEnabledTrustAgents(List.of(systemTrustAgent1), TEST_USER_ID);
 
-        mMockContext.sendPackageChangedBroadcast(systemTrustAgent2);
+        notifyPackageChanged(systemTrustAgent2);
 
-        assertThat(mLockPatternUtils.getEnabledTrustAgents(TEST_USER_ID)).containsExactly(
-                systemTrustAgent1);
+        assertThat(mEnabledTrustAgents).containsExactly(systemTrustAgent1);
     }
 
     @Test
     public void reportEnabledTrustAgentsChangedInformsListener() throws RemoteException {
-        final LockPatternUtils utils = mock(LockPatternUtils.class);
-        final TrustManagerService service = new TrustManagerService(mMockContext,
-                new TrustManagerService.Injector(utils, mLooper.getLooper()));
         final ITrustListener trustListener = mock(ITrustListener.class);
-        final IWindowManager windowManager = mock(IWindowManager.class);
-        final int userId = new Random().nextInt();
+        mTrustManager.registerTrustListener(trustListener);
+        mService.waitForIdle();
+        mTrustManager.reportEnabledTrustAgentsChanged(TEST_USER_ID);
+        mService.waitForIdle();
+        verify(trustListener).onEnabledTrustAgentsChanged(TEST_USER_ID);
+    }
 
-        mMockContext.getTestablePermissions().setPermission(Manifest.permission.TRUST_LISTENER,
-                PERMISSION_GRANTED);
+    // Tests that when the device is locked for a managed profile with a *unified* challenge, the
+    // device locked notification that is sent to Keystore contains the biometric SIDs of the parent
+    // user, not the profile.  This matches the authentication that is needed to unlock the device
+    // for the profile again.
+    @Test
+    @RequiresFlagsEnabled(android.security.Flags.FLAG_FIX_UNLOCKED_DEVICE_REQUIRED_KEYS_V2)
+    public void testLockDeviceForManagedProfileWithUnifiedChallenge_usesParentBiometricSids()
+            throws Exception {
+        setupMocksForProfile(/* unifiedChallenge= */ true);
 
-        when(utils.getKnownTrustAgents(anyInt())).thenReturn(new ArrayList<>());
+        when(mWindowManager.isKeyguardLocked()).thenReturn(false);
+        mTrustManager.reportKeyguardShowingChanged();
+        verify(mKeystoreAuthorization).onDeviceUnlocked(PARENT_USER_ID, null);
+        verify(mKeystoreAuthorization).onDeviceUnlocked(PROFILE_USER_ID, null);
 
-        MockitoSession mockSession = mockitoSession()
-                .initMocks(this)
-                .mockStatic(ServiceManager.class)
-                .mockStatic(WindowManagerGlobal.class)
-                .startMocking();
+        when(mWindowManager.isKeyguardLocked()).thenReturn(true);
+        mTrustManager.reportKeyguardShowingChanged();
+        verify(mKeystoreAuthorization)
+                .onDeviceLocked(eq(PARENT_USER_ID), eq(PARENT_BIOMETRIC_SIDS));
+        verify(mKeystoreAuthorization)
+                .onDeviceLocked(eq(PROFILE_USER_ID), eq(PARENT_BIOMETRIC_SIDS));
+    }
 
-        doReturn(windowManager).when(() -> {
-            WindowManagerGlobal.getWindowManagerService();
-        });
+    // Tests that when the device is locked for a managed profile with a *separate* challenge, the
+    // device locked notification that is sent to Keystore contains the biometric SIDs of the
+    // profile itself.  This matches the authentication that is needed to unlock the device for the
+    // profile again.
+    @Test
+    public void testLockDeviceForManagedProfileWithSeparateChallenge_usesProfileBiometricSids()
+            throws Exception {
+        setupMocksForProfile(/* unifiedChallenge= */ false);
 
-        service.onStart();
-        ArgumentCaptor<IBinder> binderArgumentCaptor = ArgumentCaptor.forClass(IBinder.class);
-        verify(() -> ServiceManager.addService(eq(Context.TRUST_SERVICE),
-                binderArgumentCaptor.capture(), anyBoolean(), anyInt()));
-        ITrustManager manager = ITrustManager.Stub.asInterface(binderArgumentCaptor.getValue());
-        manager.registerTrustListener(trustListener);
-        mLooper.dispatchAll();
-        manager.reportEnabledTrustAgentsChanged(userId);
-        mLooper.dispatchAll();
-        verify(trustListener).onEnabledTrustAgentsChanged(eq(userId));
-        mockSession.finishMocking();
+        mTrustManager.setDeviceLockedForUser(PROFILE_USER_ID, false);
+        verify(mKeystoreAuthorization).onDeviceUnlocked(PROFILE_USER_ID, null);
+
+        mTrustManager.setDeviceLockedForUser(PROFILE_USER_ID, true);
+        verify(mKeystoreAuthorization)
+                .onDeviceLocked(eq(PROFILE_USER_ID), eq(PROFILE_BIOMETRIC_SIDS));
+    }
+
+    private void setupMocksForProfile(boolean unifiedChallenge) {
+        UserInfo parent = new UserInfo(PARENT_USER_ID, "parent", UserInfo.FLAG_FULL);
+        UserInfo profile = new UserInfo(PROFILE_USER_ID, "profile", UserInfo.FLAG_MANAGED_PROFILE);
+        when(mUserManager.getAliveUsers()).thenReturn(List.of(parent, profile));
+        when(mUserManager.getUserInfo(PARENT_USER_ID)).thenReturn(parent);
+        when(mUserManager.getUserInfo(PROFILE_USER_ID)).thenReturn(profile);
+        when(mUserManager.getProfileParent(PROFILE_USER_ID)).thenReturn(parent);
+        when(mUserManager.getEnabledProfileIds(PARENT_USER_ID))
+                .thenReturn(new int[] { PROFILE_USER_ID });
+
+        when(mLockPatternUtils.isSecure(anyInt())).thenReturn(true);
+        when(mLockPatternUtils.isProfileWithUnifiedChallenge(PROFILE_USER_ID))
+                .thenReturn(unifiedChallenge);
+        when(mLockPatternUtils.isManagedProfileWithUnifiedChallenge(PROFILE_USER_ID))
+                .thenReturn(unifiedChallenge);
+        when(mLockPatternUtils.isSeparateProfileChallengeEnabled(PROFILE_USER_ID))
+                .thenReturn(!unifiedChallenge);
+
+        when(mBiometricManager.getAuthenticatorIds(PARENT_USER_ID))
+                .thenReturn(PARENT_BIOMETRIC_SIDS);
+        when(mBiometricManager.getAuthenticatorIds(PROFILE_USER_ID))
+                .thenReturn(PROFILE_BIOMETRIC_SIDS);
+
+        bootService();
+        mService.onUserSwitching(null, new SystemService.TargetUser(parent));
     }
 
     private void addTrustAgent(ComponentName agentComponentName, boolean isSystemApp) {
@@ -327,33 +427,29 @@
         mTrustAgentResolveInfoList.add(resolveInfo);
     }
 
-    private void initializeEnabledAgents(ComponentName... enabledAgents) {
-        mLockPatternUtils.setEnabledTrustAgents(Lists.newArrayList(enabledAgents), TEST_USER_ID);
-        Settings.Secure.putIntForUser(mMockContext.getContentResolver(),
-                Settings.Secure.TRUST_AGENTS_INITIALIZED, 1, TEST_USER_ID);
-    }
-
-    private void initializeKnownAgents(ComponentName... knownAgents) {
-        mLockPatternUtils.setKnownTrustAgents(Lists.newArrayList(knownAgents), TEST_USER_ID);
-        Settings.Secure.putIntForUser(mMockContext.getContentResolver(),
-                Settings.Secure.KNOWN_TRUST_AGENTS_INITIALIZED, 1, TEST_USER_ID);
-    }
-
     private void bootService() {
         mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
         mService.onBootPhase(SystemService.PHASE_THIRD_PARTY_APPS_CAN_START);
         mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
+        mMockContext.sendUserStartedBroadcast();
     }
 
-    private void resetTrustAgentLockSettings() {
-        mLockPatternUtils.setEnabledTrustAgents(Collections.emptyList(), TEST_USER_ID);
-        mLockPatternUtils.setKnownTrustAgents(Collections.emptyList(), TEST_USER_ID);
+    private void grantPermission(String permission) {
+        mMockContext.getTestablePermissions().setPermission(
+                permission, PackageManager.PERMISSION_GRANTED);
+    }
+
+    private void notifyPackageChanged(ComponentName changedComponent) {
+        mService.mPackageMonitor.onPackageChanged(
+                changedComponent.getPackageName(),
+                UserHandle.of(TEST_USER_ID).getUid(1234),
+                new String[] { changedComponent.getClassName() });
     }
 
     /** A mock Context that allows the test process to send protected broadcasts. */
     private static final class MockContext extends TestableContext {
 
-        private final ArrayList<BroadcastReceiver> mPackageChangedBroadcastReceivers =
+        private final ArrayList<BroadcastReceiver> mUserStartedBroadcastReceivers =
                 new ArrayList<>();
 
         MockContext(Context base) {
@@ -366,23 +462,22 @@
                 UserHandle user, IntentFilter filter, @Nullable String broadcastPermission,
                 @Nullable Handler scheduler) {
 
-            if (filter.hasAction(Intent.ACTION_PACKAGE_CHANGED)) {
-                mPackageChangedBroadcastReceivers.add(receiver);
+            if (filter.hasAction(Intent.ACTION_USER_STARTED)) {
+                mUserStartedBroadcastReceivers.add(receiver);
             }
             return super.registerReceiverAsUser(receiver, user, filter, broadcastPermission,
                     scheduler);
         }
 
-        void sendPackageChangedBroadcast(ComponentName changedComponent) {
-            Intent intent = new Intent(
-                    Intent.ACTION_PACKAGE_CHANGED,
-                    Uri.fromParts(URI_SCHEME_PACKAGE,
-                            changedComponent.getPackageName(), /* fragment= */ null))
-                    .putExtra(Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST,
-                            new String[]{changedComponent.getClassName()})
-                    .putExtra(Intent.EXTRA_USER_HANDLE, TEST_USER_ID)
-                    .putExtra(Intent.EXTRA_UID, UserHandle.of(TEST_USER_ID).getUid(1234));
-            for (BroadcastReceiver receiver : mPackageChangedBroadcastReceivers) {
+        @Override
+        public void sendBroadcastAsUser(Intent intent, UserHandle user,
+                @Nullable String receiverPermission, @Nullable Bundle options) {
+        }
+
+        void sendUserStartedBroadcast() {
+            Intent intent = new Intent(Intent.ACTION_USER_STARTED)
+                    .putExtra(Intent.EXTRA_USER_HANDLE, TEST_USER_ID);
+            for (BroadcastReceiver receiver : mUserStartedBroadcastReceivers) {
                 receiver.onReceive(this, intent);
             }
         }
diff --git a/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java b/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java
index 91d8ceb..a9ff3a1 100644
--- a/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java
+++ b/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java
@@ -26,6 +26,7 @@
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
@@ -113,7 +114,8 @@
         mTestExecutor.simulateAsyncExecutionOfLastCommand();
 
         // THEN the device vibrates once
-        verify(mVibrator, times(1)).vibrate(any(), any(VibrationAttributes.class));
+        verify(mVibrator, times(1)).vibrate(anyInt(), any(), any(), any(),
+                any(VibrationAttributes.class));
     }
 
     @Test
@@ -129,7 +131,7 @@
         mTestExecutor.simulateAsyncExecutionOfLastCommand();
 
         // THEN the device doesn't vibrate
-        verify(mVibrator, never()).vibrate(any(), any(VibrationAttributes.class));
+        verifyZeroInteractions(mVibrator);
     }
 
     @Test
@@ -145,14 +147,15 @@
         mTestExecutor.simulateAsyncExecutionOfLastCommand();
 
         // THEN the device vibrates once
-        verify(mVibrator, times(1)).vibrate(any(), any(VibrationAttributes.class));
+        verify(mVibrator, times(1)).vibrate(anyInt(), any(), any(), any(),
+                any(VibrationAttributes.class));
     }
 
     @Test
     public void testVibrateDisabled_wirelessCharging() {
         createNotifier();
 
-        // GIVEN the charging vibration is disabeld
+        // GIVEN the charging vibration is disabled
         enableChargingVibration(false);
 
         // WHEN wireless charging starts
@@ -161,7 +164,7 @@
         mTestExecutor.simulateAsyncExecutionOfLastCommand();
 
         // THEN the device doesn't vibrate
-        verify(mVibrator, never()).vibrate(any(), any(VibrationAttributes.class));
+        verifyZeroInteractions(mVibrator);
     }
 
     @Test
diff --git a/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java
index d752ae4..aec896f 100644
--- a/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -97,6 +97,7 @@
 import androidx.test.core.app.ApplicationProvider;
 
 import com.android.internal.app.IBatteryStats;
+import com.android.internal.foldables.FoldGracePeriodProvider;
 import com.android.internal.util.test.FakeSettingsProvider;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
@@ -164,6 +165,7 @@
     @Mock private AttentionManagerInternal mAttentionManagerInternalMock;
     @Mock private DreamManagerInternal mDreamManagerInternalMock;
     @Mock private PowerManagerService.NativeWrapper mNativeWrapperMock;
+    @Mock private FoldGracePeriodProvider mFoldGracePeriodProvider;
     @Mock private Notifier mNotifierMock;
     @Mock private WirelessChargerDetector mWirelessChargerDetectorMock;
     @Mock private AmbientDisplayConfiguration mAmbientDisplayConfigurationMock;
@@ -359,6 +361,11 @@
             DeviceConfigParameterProvider createDeviceConfigParameterProvider() {
                 return mDeviceParameterProvider;
             }
+
+            @Override
+            FoldGracePeriodProvider createFoldGracePeriodProvider() {
+                return mFoldGracePeriodProvider;
+            }
         });
         return mService;
     }
@@ -520,6 +527,8 @@
 
     @Test
     public void testWakefulnessSleep_SoftSleepFlag_NoWakelocks() {
+        when(mFoldGracePeriodProvider.isEnabled()).thenReturn(false);
+
         createService();
         // Start with AWAKE state
         startSystem();
@@ -533,6 +542,23 @@
     }
 
     @Test
+    public void testWakefulnessAwakeShowKeyguard_SoftSleepFlag_NoWakelocks() {
+        when(mFoldGracePeriodProvider.isEnabled()).thenReturn(true);
+
+        createService();
+        // Start with AWAKE state
+        startSystem();
+        assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
+
+        // Take a nap and verify we stay awake and the keyguard is requested
+        mService.getBinderServiceInstance().goToSleep(mClock.now(),
+                PowerManager.GO_TO_SLEEP_REASON_APPLICATION,
+                PowerManager.GO_TO_SLEEP_FLAG_SOFT_SLEEP);
+        assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
+        verify(mNotifierMock).showDismissibleKeyguard();
+    }
+
+    @Test
     public void testWakefulnessSleep_SoftSleepFlag_WithPartialWakelock() {
         createService();
         // Start with AWAKE state
@@ -548,7 +574,7 @@
                 null /* workSource */, null /* historyTag */, Display.INVALID_DISPLAY,
                 null /* callback */);
 
-        // Take a nap and verify we stay awake.
+        // Take a nap and verify we go to sleep.
         mService.getBinderServiceInstance().goToSleep(mClock.now(),
                 PowerManager.GO_TO_SLEEP_REASON_APPLICATION,
                 PowerManager.GO_TO_SLEEP_FLAG_SOFT_SLEEP);
diff --git a/services/tests/powerstatstests/Android.bp b/services/tests/powerstatstests/Android.bp
index 05e0e8f..654d7a8d 100644
--- a/services/tests/powerstatstests/Android.bp
+++ b/services/tests/powerstatstests/Android.bp
@@ -11,6 +11,7 @@
         "src/com/android/server/power/stats/MultiStateStatsTest.java",
         "src/com/android/server/power/stats/PowerStatsAggregatorTest.java",
         "src/com/android/server/power/stats/PowerStatsCollectorTest.java",
+        "src/com/android/server/power/stats/PowerStatsExporterTest.java",
         "src/com/android/server/power/stats/PowerStatsSchedulerTest.java",
         "src/com/android/server/power/stats/PowerStatsStoreTest.java",
         "src/com/android/server/power/stats/PowerStatsUidResolverTest.java",
@@ -83,6 +84,9 @@
     ],
     srcs: [
         ":power_stats_ravenwood_tests",
+
+        "src/com/android/server/power/stats/BatteryUsageStatsRule.java",
+        "src/com/android/server/power/stats/MockBatteryStatsImpl.java",
         "src/com/android/server/power/stats/MockClock.java",
     ],
     auto_gen_config: true,
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryChargeCalculatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryChargeCalculatorTest.java
index 4ea0805..3f058a2 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryChargeCalculatorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryChargeCalculatorTest.java
@@ -37,12 +37,12 @@
     private static final double PRECISION = 0.00001;
 
     @Rule
-    public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule();
+    public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
+                    .setAveragePower(PowerProfile.POWER_BATTERY_CAPACITY, 4000.0);
 
     @Test
     public void testDischargeTotals() {
         // Nominal battery capacity should be ignored
-        mStatsRule.setAveragePower(PowerProfile.POWER_BATTERY_CAPACITY, 1234.0);
 
         final BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats();
 
@@ -84,8 +84,6 @@
 
     @Test
     public void testDischargeTotals_chargeUahUnavailable() {
-        mStatsRule.setAveragePower(PowerProfile.POWER_BATTERY_CAPACITY, 4000.0);
-
         final BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats();
 
         batteryStats.setBatteryStateLocked(BatteryManager.BATTERY_STATUS_DISCHARGING, 100,
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsRule.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsRule.java
index e61dd0b..ca162e0 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsRule.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsRule.java
@@ -18,11 +18,11 @@
 
 import static org.mockito.ArgumentMatchers.anyDouble;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.when;
 
 import android.annotation.XmlRes;
-import android.content.Context;
 import android.net.NetworkStats;
 import android.os.BatteryConsumer;
 import android.os.BatteryStats;
@@ -54,12 +54,11 @@
                     .powerProfileModeledOnly()
                     .includePowerModels()
                     .build();
-    private final Context mContext;
 
     private final PowerProfile mPowerProfile;
     private final MockClock mMockClock = new MockClock();
     private final MockBatteryStatsImpl mBatteryStats;
-    private final Handler mHandler;
+    private Handler mHandler;
 
     private BatteryUsageStats mBatteryUsageStats;
     private boolean mScreenOn;
@@ -76,11 +75,8 @@
     }
 
     public BatteryUsageStatsRule(long currentTime, File historyDir) {
-        HandlerThread bgThread = new HandlerThread("bg thread");
-        bgThread.start();
-        mHandler = new Handler(bgThread.getLooper());
-        mContext = InstrumentationRegistry.getContext();
-        mPowerProfile = spy(new PowerProfile(mContext, true /* forTest */));
+        mHandler = mock(Handler.class);
+        mPowerProfile = spy(new PowerProfile());
         mMockClock.currentTime = currentTime;
         mBatteryStats = new MockBatteryStatsImpl(mMockClock, historyDir, mHandler);
         mBatteryStats.setPowerProfile(mPowerProfile);
@@ -103,7 +99,7 @@
     }
 
     public BatteryUsageStatsRule setTestPowerProfile(@XmlRes int xmlId) {
-        mPowerProfile.forceInitForTesting(mContext, xmlId);
+        mPowerProfile.forceInitForTesting(InstrumentationRegistry.getContext(), xmlId);
         return this;
     }
 
@@ -222,13 +218,17 @@
         return new Statement() {
             @Override
             public void evaluate() throws Throwable {
-                noteOnBattery();
+                before();
                 base.evaluate();
             }
         };
     }
 
-    private void noteOnBattery() {
+    private void before() {
+        HandlerThread bgThread = new HandlerThread("bg thread");
+        bgThread.start();
+        mHandler = new Handler(bgThread.getLooper());
+        mBatteryStats.setHandler(mHandler);
         mBatteryStats.setOnBatteryInternal(true);
         mBatteryStats.getOnBatteryTimeBase().setRunning(true, 0, 0);
         mBatteryStats.getOnBatteryScreenOffTimeBase().setRunning(!mScreenOn, 0, 0);
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/MockBatteryStatsImpl.java b/services/tests/powerstatstests/src/com/android/server/power/stats/MockBatteryStatsImpl.java
index fb71ac8..78c4bac 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/MockBatteryStatsImpl.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/MockBatteryStatsImpl.java
@@ -271,6 +271,10 @@
     public void writeSyncLocked() {
     }
 
+    public void setHandler(Handler handler) {
+        mHandler = handler;
+    }
+
     public static class DummyExternalStatsSync implements ExternalStatsSync {
         public int flags = 0;
 
@@ -315,4 +319,3 @@
         }
     }
 }
-
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsExporterTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsExporterTest.java
index 3c48262..3560a26 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsExporterTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsExporterTest.java
@@ -16,8 +16,6 @@
 
 package com.android.server.power.stats;
 
-import static androidx.test.InstrumentationRegistry.getContext;
-
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.Truth.assertWithMessage;
 
@@ -34,6 +32,7 @@
 import android.os.Parcel;
 import android.os.PersistableBundle;
 import android.os.UidBatteryConsumer;
+import android.platform.test.ravenwood.RavenwoodRule;
 
 import androidx.test.runner.AndroidJUnit4;
 
@@ -48,6 +47,8 @@
 import org.junit.runner.RunWith;
 
 import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
 import java.util.List;
 
 @RunWith(AndroidJUnit4.class)
@@ -57,7 +58,12 @@
     private static final int APP_UID2 = 84;
     private static final double TOLERANCE = 0.01;
 
-    @Rule
+    @Rule(order = 0)
+    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
+            .setProvideMainThread(true)
+            .build();
+
+    @Rule(order = 1)
     public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
             .setAveragePower(PowerProfile.POWER_CPU_ACTIVE, 720)
             .setCpuScalingPolicy(0, new int[]{0}, new int[]{100})
@@ -75,8 +81,8 @@
     private PowerStats.Descriptor mPowerStatsDescriptor;
 
     @Before
-    public void setup() {
-        File storeDirectory = new File(getContext().getCacheDir(), getClass().getSimpleName());
+    public void setup() throws IOException {
+        File storeDirectory = Files.createTempDirectory("PowerStatsExporterTest").toFile();
         clearDirectory(storeDirectory);
 
         AggregatedPowerStatsConfig config = new AggregatedPowerStatsConfig();
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index 9b84190..0045026 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -215,6 +215,16 @@
 }
 
 java_library {
+    name: "mockito-test-utils",
+    srcs: [
+        "utils-mockito/**/*.kt",
+    ],
+    static_libs: [
+        "mockito-target-minus-junit4",
+    ],
+}
+
+java_library {
     name: "servicestests-utils-mockito-extended",
     srcs: [
         "utils/**/*.java",
diff --git a/services/tests/servicestests/jni/Android.bp b/services/tests/servicestests/jni/Android.bp
index 174beb8..c30e4eb 100644
--- a/services/tests/servicestests/jni/Android.bp
+++ b/services/tests/servicestests/jni/Android.bp
@@ -23,6 +23,7 @@
         ":lib_cachedAppOptimizer_native",
         ":lib_gameManagerService_native",
         ":lib_oomConnection_native",
+        ":lib_anrTimer_native",
         "onload.cpp",
     ],
 
@@ -55,4 +56,4 @@
         "android.hardware.graphics.mapper@4.0",
         "android.hidl.token@1.0-utils",
     ],
-}
\ No newline at end of file
+}
diff --git a/services/tests/servicestests/jni/onload.cpp b/services/tests/servicestests/jni/onload.cpp
index f160b3d..25487c5 100644
--- a/services/tests/servicestests/jni/onload.cpp
+++ b/services/tests/servicestests/jni/onload.cpp
@@ -27,6 +27,7 @@
 int register_android_server_am_CachedAppOptimizer(JNIEnv* env);
 int register_android_server_app_GameManagerService(JNIEnv* env);
 int register_android_server_am_OomConnection(JNIEnv* env);
+int register_android_server_utils_AnrTimer(JNIEnv *env);
 };
 
 using namespace android;
@@ -44,5 +45,6 @@
     register_android_server_am_CachedAppOptimizer(env);
     register_android_server_app_GameManagerService(env);
     register_android_server_am_OomConnection(env);
+    register_android_server_utils_AnrTimer(env);
     return JNI_VERSION_1_4;
 }
diff --git a/services/tests/servicestests/src/com/android/server/BootReceiverTest.java b/services/tests/servicestests/src/com/android/server/BootReceiverTest.java
new file mode 100644
index 0000000..523c5c0
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/BootReceiverTest.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.test.AndroidTestCase;
+
+import com.android.server.os.TombstoneProtos;
+import com.android.server.os.TombstoneProtos.Tombstone;
+
+public class BootReceiverTest extends AndroidTestCase {
+    private static final String TAG = "BootReceiverTest";
+
+    public void testRemoveMemoryFromTombstone() {
+        Tombstone tombstoneBase = Tombstone.newBuilder()
+                .setBuildFingerprint("build_fingerprint")
+                .setRevision("revision")
+                .setPid(123)
+                .setTid(23)
+                .setUid(34)
+                .setSelinuxLabel("selinux_label")
+                .addCommandLine("cmd1")
+                .addCommandLine("cmd2")
+                .addCommandLine("cmd3")
+                .setProcessUptime(300)
+                .setAbortMessage("abort")
+                .addCauses(TombstoneProtos.Cause.newBuilder()
+                        .setHumanReadable("cause1")
+                        .setMemoryError(TombstoneProtos.MemoryError.newBuilder()
+                                .setTool(TombstoneProtos.MemoryError.Tool.SCUDO)
+                                .setType(TombstoneProtos.MemoryError.Type.DOUBLE_FREE)))
+                .addLogBuffers(TombstoneProtos.LogBuffer.newBuilder().setName("name").addLogs(
+                        TombstoneProtos.LogMessage.newBuilder()
+                                .setTimestamp("123")
+                                .setMessage("message")))
+                .addOpenFds(TombstoneProtos.FD.newBuilder().setFd(1).setPath("path"))
+                .build();
+
+        Tombstone tombstoneWithoutMemory = tombstoneBase.toBuilder()
+                .putThreads(1, TombstoneProtos.Thread.newBuilder()
+                        .setId(1)
+                        .setName("thread1")
+                        .addRegisters(TombstoneProtos.Register.newBuilder().setName("r1").setU64(1))
+                        .addRegisters(TombstoneProtos.Register.newBuilder().setName("r2").setU64(2))
+                        .addBacktraceNote("backtracenote1")
+                        .addUnreadableElfFiles("files1")
+                        .setTaggedAddrCtrl(1)
+                        .setPacEnabledKeys(10)
+                        .build())
+                .build();
+
+        Tombstone tombstoneWithMemory = tombstoneBase.toBuilder()
+                .addMemoryMappings(TombstoneProtos.MemoryMapping.newBuilder()
+                        .setBeginAddress(1)
+                        .setEndAddress(100)
+                        .setOffset(10)
+                        .setRead(true)
+                        .setWrite(true)
+                        .setExecute(false)
+                        .setMappingName("mapping")
+                        .setBuildId("build")
+                        .setLoadBias(70))
+                .putThreads(1, TombstoneProtos.Thread.newBuilder()
+                        .setId(1)
+                        .setName("thread1")
+                        .addRegisters(TombstoneProtos.Register.newBuilder().setName("r1").setU64(1))
+                        .addRegisters(TombstoneProtos.Register.newBuilder().setName("r2").setU64(2))
+                        .addBacktraceNote("backtracenote1")
+                        .addUnreadableElfFiles("files1")
+                        .addMemoryDump(TombstoneProtos.MemoryDump.newBuilder()
+                                .setRegisterName("register1")
+                                .setMappingName("mapping")
+                                .setBeginAddress(10))
+                        .setTaggedAddrCtrl(1)
+                        .setPacEnabledKeys(10)
+                        .build())
+                .build();
+
+        assertThat(BootReceiver.removeMemoryFromTombstone(tombstoneWithMemory))
+                .isEqualTo(tombstoneWithoutMemory);
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java
index efcdbd4..1cd61e9 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java
@@ -44,10 +44,6 @@
 import android.graphics.PointF;
 import android.os.Looper;
 import android.os.SystemClock;
-import android.platform.test.annotations.RequiresFlagsDisabled;
-import android.platform.test.annotations.RequiresFlagsEnabled;
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 import android.testing.DexmakerShareClassLoaderRule;
 import android.view.InputDevice;
 import android.view.MotionEvent;
@@ -60,7 +56,6 @@
 import com.android.server.accessibility.AccessibilityManagerService;
 import com.android.server.accessibility.AccessibilityTraceManager;
 import com.android.server.accessibility.EventStreamTransformation;
-import com.android.server.accessibility.Flags;
 import com.android.server.accessibility.utils.GestureLogParser;
 import com.android.server.testutils.OffsettableClock;
 
@@ -81,7 +76,6 @@
 import java.util.ArrayList;
 import java.util.List;
 
-
 @RunWith(AndroidJUnit4.class)
 public class TouchExplorerTest {
 
@@ -125,9 +119,6 @@
     public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule =
             new DexmakerShareClassLoaderRule();
 
-    @Rule
-    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
-
     /**
      * {@link TouchExplorer#sendDownForAllNotInjectedPointers} injecting events with the same object
      * is resulting {@link ArgumentCaptor} to capture events with last state. Before implementation
@@ -170,16 +161,11 @@
         goFromStateClearTo(STATE_TOUCH_EXPLORING_1FINGER);
         // Wait for transiting to touch exploring state.
         mHandler.fastForward(2 * USER_INTENT_TIMEOUT);
-        assertState(STATE_TOUCH_EXPLORING);
-        // Manually construct the next move event. Using moveEachPointers() will batch the move
-        // event which produces zero movement for some reason.
-        float[] x = new float[1];
-        float[] y = new float[1];
-        x[0] = mLastEvent.getX(0) + mTouchSlop;
-        y[0] = mLastEvent.getY(0) + mTouchSlop;
-        send(manyPointerEvent(ACTION_MOVE, x, y));
+        moveEachPointers(mLastEvent, p(10, 10));
+        send(mLastEvent);
         goToStateClearFrom(STATE_TOUCH_EXPLORING_1FINGER);
         assertCapturedEvents(ACTION_HOVER_ENTER, ACTION_HOVER_MOVE, ACTION_HOVER_EXIT);
+        assertState(STATE_TOUCH_EXPLORING);
     }
 
     /**
@@ -187,8 +173,7 @@
      * change the coordinates.
      */
     @Test
-    @RequiresFlagsEnabled(Flags.FLAG_REDUCE_TOUCH_EXPLORATION_SENSITIVITY)
-    public void testOneFingerMoveWithExtraMoveEvents_generatesOneMoveEvent() {
+    public void testOneFingerMoveWithExtraMoveEvents() {
         goFromStateClearTo(STATE_TOUCH_EXPLORING_1FINGER);
         // Inject a set of move events that have the same coordinates as the down event.
         moveEachPointers(mLastEvent, p(0, 0));
@@ -196,33 +181,7 @@
         // Wait for transition to touch exploring state.
         mHandler.fastForward(2 * USER_INTENT_TIMEOUT);
         // Now move for real.
-        moveAtLeastTouchSlop(mLastEvent);
-        send(mLastEvent);
-        // One more move event with no change.
-        moveEachPointers(mLastEvent, p(0, 0));
-        send(mLastEvent);
-        goToStateClearFrom(STATE_TOUCH_EXPLORING_1FINGER);
-        assertCapturedEvents(
-                ACTION_HOVER_ENTER,
-                ACTION_HOVER_MOVE,
-                ACTION_HOVER_EXIT);
-    }
-
-    /**
-     * Test the case where ACTION_DOWN is followed by a number of ACTION_MOVE events that do not
-     * change the coordinates.
-     */
-    @Test
-    @RequiresFlagsDisabled(Flags.FLAG_REDUCE_TOUCH_EXPLORATION_SENSITIVITY)
-    public void testOneFingerMoveWithExtraMoveEvents_generatesThreeMoveEvent() {
-        goFromStateClearTo(STATE_TOUCH_EXPLORING_1FINGER);
-        // Inject a set of move events that have the same coordinates as the down event.
-        moveEachPointers(mLastEvent, p(0, 0));
-        send(mLastEvent);
-        // Wait for transition to touch exploring state.
-        mHandler.fastForward(2 * USER_INTENT_TIMEOUT);
-        // Now move for real.
-        moveAtLeastTouchSlop(mLastEvent);
+        moveEachPointers(mLastEvent, p(10, 10));
         send(mLastEvent);
         // One more move event with no change.
         moveEachPointers(mLastEvent, p(0, 0));
@@ -283,7 +242,7 @@
         moveEachPointers(mLastEvent, p(0, 0), p(0, 0));
         send(mLastEvent);
         // Now move for real.
-        moveEachPointers(mLastEvent, p(mTouchSlop, mTouchSlop), p(mTouchSlop, mTouchSlop));
+        moveEachPointers(mLastEvent, p(10, 10), p(10, 10));
         send(mLastEvent);
         goToStateClearFrom(STATE_DRAGGING_2FINGERS);
         assertCapturedEvents(ACTION_DOWN, ACTION_MOVE, ACTION_MOVE, ACTION_MOVE, ACTION_UP);
@@ -292,7 +251,7 @@
     @Test
     public void testUpEvent_OneFingerMove_clearStateAndInjectHoverEvents() {
         goFromStateClearTo(STATE_TOUCH_EXPLORING_1FINGER);
-        moveAtLeastTouchSlop(mLastEvent);
+        moveEachPointers(mLastEvent, p(10, 10));
         send(mLastEvent);
         // Wait 10 ms to make sure that hover enter and exit are not scheduled for the same moment.
         mHandler.fastForward(10);
@@ -318,7 +277,7 @@
 
         // Wait for the finger moving to the second view.
         mHandler.fastForward(oneThirdUserIntentTimeout);
-        moveAtLeastTouchSlop(mLastEvent);
+        moveEachPointers(mLastEvent, p(10, 10));
         send(mLastEvent);
 
         // Wait for the finger lifting from the second view.
@@ -443,6 +402,7 @@
         // Manually construct the next move event. Using moveEachPointers() will batch the move
         // event onto the pointer up event which will mean that the move event still has a pointer
         // count of 3.
+        // Todo: refactor to avoid using batching as there is no special reason to do it that way.
         float[] x = new float[2];
         float[] y = new float[2];
         x[0] = mLastEvent.getX(0) + 100;
@@ -774,9 +734,6 @@
         }
     }
 
-    private void moveAtLeastTouchSlop(MotionEvent event) {
-        moveEachPointers(event, p(2 * mTouchSlop, 0));
-    }
     /**
      * A {@link android.os.Handler} that doesn't process messages until {@link #fastForward(int)} is
      * invoked.
diff --git a/services/tests/servicestests/src/com/android/server/am/AnrHelperTest.java b/services/tests/servicestests/src/com/android/server/am/AnrHelperTest.java
index c0051c6..eee3752 100644
--- a/services/tests/servicestests/src/com/android/server/am/AnrHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/AnrHelperTest.java
@@ -133,7 +133,8 @@
         verify(mAnrApp.mErrorState, timeout(TIMEOUT_MS)).appNotResponding(
                 eq(activityShortComponentName), eq(appInfo), eq(parentShortComponentName),
                 eq(parentProcess), eq(aboveSystem), eq(timeoutRecord), eq(mAuxExecutorService),
-                eq(false) /* onlyDumpSelf */, eq(false) /*isContinuousAnr*/, eq(mEarlyDumpFuture));
+                anyBoolean() /* onlyDumpSelf */, eq(false) /*isContinuousAnr*/,
+                eq(mEarlyDumpFuture));
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
index 77b1455..26934d8 100644
--- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
@@ -763,8 +763,7 @@
 
         mUserController.startUser(TEST_USER_ID, USER_START_MODE_BACKGROUND);
 
-        verify(mInjector.mStorageManagerMock, never())
-                .unlockCeStorage(eq(TEST_USER_ID), anyInt(), any());
+        verify(mInjector.mStorageManagerMock, never()).unlockCeStorage(eq(TEST_USER_ID), any());
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthenticationStatsCollectorTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthenticationStatsCollectorTest.java
index d2e83e9..9eeb4f3 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/AuthenticationStatsCollectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthenticationStatsCollectorTest.java
@@ -271,6 +271,7 @@
                 .thenReturn(true);
         when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE)).thenReturn(false);
         when(mFingerprintManager.hasEnrolledTemplates(anyInt())).thenReturn(true);
+        when(mContext.getSystemService(Context.FACE_SERVICE)).thenReturn(null);
 
         mAuthenticationStatsCollector.authenticate(USER_ID_1, false /* authenticated */);
 
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 8929900..f7480de 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
@@ -44,12 +44,18 @@
 import android.hardware.biometrics.BiometricConstants;
 import android.hardware.biometrics.BiometricsProtoEnums;
 import android.hardware.biometrics.IBiometricService;
+import android.hardware.biometrics.fingerprint.IFingerprint;
+import android.hardware.biometrics.fingerprint.ISession;
 import android.hardware.fingerprint.Fingerprint;
 import android.os.Binder;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.platform.test.annotations.Presubmit;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableContext;
 import android.testing.TestableLooper;
@@ -60,6 +66,7 @@
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
 
+import com.android.server.biometrics.Flags;
 import com.android.server.biometrics.log.BiometricContext;
 import com.android.server.biometrics.log.BiometricLogger;
 import com.android.server.biometrics.nano.BiometricSchedulerProto;
@@ -95,14 +102,39 @@
     @Rule
     public final TestableContext mContext = new TestableContext(
             InstrumentationRegistry.getContext(), null);
-    private BiometricScheduler mScheduler;
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule =
+            DeviceFlagsValueProvider.createCheckFlagsRule();
+    private BiometricScheduler<IFingerprint, ISession> mScheduler;
     private IBinder mToken;
+    private int mCurrentUserId = UserHandle.USER_SYSTEM;
+    private boolean mShouldFailStopUser = false;
+    private final List<Integer> mStartedUsers = new ArrayList<>();
+    private final StartUserClient.UserStartedCallback<ISession> mUserStartedCallback =
+            (newUserId, newUser, halInterfaceVersion) -> {
+                mStartedUsers.add(newUserId);
+                mCurrentUserId = newUserId;
+            };
+    private int mUsersStoppedCount = 0;
+    private final StopUserClient.UserStoppedCallback mUserStoppedCallback =
+            () -> {
+                mUsersStoppedCount++;
+                mCurrentUserId = UserHandle.USER_NULL;
+            };
+    private boolean mStartOperationsFinish = true;
+    private int mStartUserClientCount = 0;
     @Mock
     private IBiometricService mBiometricService;
     @Mock
     private BiometricContext mBiometricContext;
     @Mock
     private AuthSessionCoordinator mAuthSessionCoordinator;
+    @Mock
+    private BiometricLogger mBiometricLogger;
+    @Mock
+    private ISession mSession;
+    @Mock
+    private IFingerprint mFingerprint;
 
     @Before
     public void setUp() {
@@ -111,9 +143,39 @@
         when(mAuthSessionCoordinator.getLockoutStateFor(anyInt(), anyInt())).thenReturn(
                 BIOMETRIC_SUCCESS);
         when(mBiometricContext.getAuthSessionCoordinator()).thenReturn(mAuthSessionCoordinator);
-        mScheduler = new BiometricScheduler(TAG, new Handler(TestableLooper.get(this).getLooper()),
-                BiometricScheduler.SENSOR_TYPE_UNKNOWN, null /* gestureAvailabilityTracker */,
-                mBiometricService, LOG_NUM_RECENT_OPERATIONS);
+        if (Flags.deHidl()) {
+            mScheduler = new BiometricScheduler<>(
+                    new Handler(TestableLooper.get(this).getLooper()),
+                    BiometricScheduler.SENSOR_TYPE_UNKNOWN,
+                    null /* gestureAvailabilityDispatcher */,
+                    mBiometricService,
+                    LOG_NUM_RECENT_OPERATIONS,
+                    () -> mCurrentUserId,
+                    new UserSwitchProvider<IFingerprint, ISession>() {
+                        @NonNull
+                        @Override
+                        public StopUserClient<ISession> getStopUserClient(int userId) {
+                            return new TestStopUserClient(mContext, () -> mSession, mToken, userId,
+                                    TEST_SENSOR_ID, mBiometricLogger, mBiometricContext,
+                                    mUserStoppedCallback, () -> mShouldFailStopUser);
+                        }
+
+                        @NonNull
+                        @Override
+                        public StartUserClient<IFingerprint, ISession> getStartUserClient(
+                                int newUserId) {
+                            mStartUserClientCount++;
+                            return new TestStartUserClient(mContext, () -> mFingerprint, mToken,
+                                    newUserId, TEST_SENSOR_ID, mBiometricLogger, mBiometricContext,
+                                    mUserStartedCallback, mStartOperationsFinish);
+                        }
+                    });
+        } else {
+            mScheduler = new BiometricScheduler<>(
+                    new Handler(TestableLooper.get(this).getLooper()),
+                    BiometricScheduler.SENSOR_TYPE_UNKNOWN, null /* gestureAvailabilityTracker */,
+                    mBiometricService, LOG_NUM_RECENT_OPERATIONS);
+        }
     }
 
     @Test
@@ -479,6 +541,7 @@
         final boolean isEnroll = client instanceof TestEnrollClient;
 
         mScheduler.scheduleClientMonitor(client);
+        waitForIdle();
         if (started) {
             mScheduler.startPreparedClient(client.getCookie());
         }
@@ -789,6 +852,172 @@
         assertEquals(1,  client1.getFingerprints().size());
     }
 
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_DE_HIDL)
+    public void testScheduleOperation_whenNoUser() {
+        mCurrentUserId = UserHandle.USER_NULL;
+
+        final BaseClientMonitor nextClient = mock(BaseClientMonitor.class);
+        when(nextClient.getTargetUserId()).thenReturn(0);
+
+        mScheduler.scheduleClientMonitor(nextClient);
+        waitForIdle();
+
+        assertThat(mUsersStoppedCount).isEqualTo(0);
+        assertThat(mStartedUsers).containsExactly(0);
+        verify(nextClient).start(any());
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_DE_HIDL)
+    public void testScheduleOperation_whenNoUser_notStarted() {
+        mCurrentUserId = UserHandle.USER_NULL;
+        mStartOperationsFinish = false;
+
+        final BaseClientMonitor[] nextClients = new BaseClientMonitor[]{
+                mock(BaseClientMonitor.class),
+                mock(BaseClientMonitor.class),
+                mock(BaseClientMonitor.class)
+        };
+        for (BaseClientMonitor client : nextClients) {
+            when(client.getTargetUserId()).thenReturn(5);
+            mScheduler.scheduleClientMonitor(client);
+            waitForIdle();
+        }
+
+        assertThat(mUsersStoppedCount).isEqualTo(0);
+        assertThat(mStartedUsers).isEmpty();
+        assertThat(mStartUserClientCount).isEqualTo(1);
+        for (BaseClientMonitor client : nextClients) {
+            verify(client, never()).start(any());
+        }
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_DE_HIDL)
+    public void testScheduleOperation_whenNoUser_notStarted_andReset() {
+        mCurrentUserId = UserHandle.USER_NULL;
+        mStartOperationsFinish = false;
+
+        final BaseClientMonitor client = mock(BaseClientMonitor.class);
+
+        when(client.getTargetUserId()).thenReturn(5);
+
+        mScheduler.scheduleClientMonitor(client);
+        waitForIdle();
+
+        final TestStartUserClient startUserClient =
+                (TestStartUserClient) mScheduler.mCurrentOperation.getClientMonitor();
+        mScheduler.reset();
+
+        assertThat(mScheduler.mCurrentOperation).isNull();
+
+        final BiometricSchedulerOperation fakeOperation = new BiometricSchedulerOperation(
+                mock(BaseClientMonitor.class), new ClientMonitorCallback() {});
+        mScheduler.mCurrentOperation = fakeOperation;
+        startUserClient.mCallback.onClientFinished(startUserClient, true);
+
+        assertThat(fakeOperation).isSameInstanceAs(mScheduler.mCurrentOperation);
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_DE_HIDL)
+    public void testScheduleOperation_whenSameUser() {
+        mCurrentUserId = 10;
+
+        BaseClientMonitor nextClient = mock(BaseClientMonitor.class);
+        when(nextClient.getTargetUserId()).thenReturn(mCurrentUserId);
+
+        mScheduler.scheduleClientMonitor(nextClient);
+
+        waitForIdle();
+
+        verify(nextClient).start(any());
+        assertThat(mUsersStoppedCount).isEqualTo(0);
+        assertThat(mStartedUsers).isEmpty();
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_DE_HIDL)
+    public void testScheduleOperation_whenDifferentUser() {
+        mCurrentUserId = 10;
+
+        final int nextUserId = 11;
+        BaseClientMonitor nextClient = mock(BaseClientMonitor.class);
+        when(nextClient.getTargetUserId()).thenReturn(nextUserId);
+
+        mScheduler.scheduleClientMonitor(nextClient);
+
+        waitForIdle();
+        assertThat(mUsersStoppedCount).isEqualTo(1);
+
+        waitForIdle();
+        assertThat(mStartedUsers).containsExactly(nextUserId);
+
+        waitForIdle();
+        verify(nextClient).start(any());
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_DE_HIDL)
+    public void testStartUser_alwaysStartsNextOperation() {
+        mCurrentUserId = UserHandle.USER_NULL;
+
+        BaseClientMonitor nextClient = mock(BaseClientMonitor.class);
+        when(nextClient.getTargetUserId()).thenReturn(10);
+
+        mScheduler.scheduleClientMonitor(nextClient);
+
+        waitForIdle();
+        verify(nextClient).start(any());
+
+        // finish first operation
+        mScheduler.getInternalCallback().onClientFinished(nextClient, true /* success */);
+        waitForIdle();
+
+        // schedule second operation but swap out the current operation
+        // before it runs so that it's not current when it's completion callback runs
+        nextClient = mock(BaseClientMonitor.class);
+        when(nextClient.getTargetUserId()).thenReturn(11);
+        mScheduler.scheduleClientMonitor(nextClient);
+
+        waitForIdle();
+        verify(nextClient).start(any());
+        assertThat(mStartedUsers).containsExactly(10, 11).inOrder();
+        assertThat(mUsersStoppedCount).isEqualTo(1);
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_DE_HIDL)
+    public void testStartUser_failsClearsStopUserClient() {
+        mCurrentUserId = UserHandle.USER_NULL;
+
+        // When a stop user client fails, check that mStopUserClient
+        // is set to null to prevent the scheduler from getting stuck.
+        BaseClientMonitor nextClient = mock(BaseClientMonitor.class);
+        when(nextClient.getTargetUserId()).thenReturn(10);
+
+        mScheduler.scheduleClientMonitor(nextClient);
+
+        waitForIdle();
+        verify(nextClient).start(any());
+
+        // finish first operation
+        mScheduler.getInternalCallback().onClientFinished(nextClient, true /* success */);
+        waitForIdle();
+
+        // schedule second operation but swap out the current operation
+        // before it runs so that it's not current when it's completion callback runs
+        nextClient = mock(BaseClientMonitor.class);
+        when(nextClient.getTargetUserId()).thenReturn(11);
+        mShouldFailStopUser = true;
+        mScheduler.scheduleClientMonitor(nextClient);
+
+        waitForIdle();
+        assertThat(mStartedUsers).containsExactly(10, 11).inOrder();
+        assertThat(mUsersStoppedCount).isEqualTo(0);
+        assertThat(mScheduler.getStopUserClient()).isEqualTo(null);
+    }
 
     private BiometricSchedulerProto getDump(boolean clearSchedulerBuffer) throws Exception {
         return BiometricSchedulerProto.parseFrom(mScheduler.dumpProtoState(clearSchedulerBuffer));
@@ -1069,4 +1298,82 @@
             return mFingerprints;
         }
     }
+
+    private interface StopUserClientShouldFail {
+        boolean shouldFail();
+    }
+
+    private class TestStopUserClient extends StopUserClient<ISession> {
+        private StopUserClientShouldFail mShouldFailClient;
+        TestStopUserClient(@NonNull Context context,
+                @NonNull Supplier<ISession> lazyDaemon, @Nullable IBinder token, int userId,
+                int sensorId, @NonNull BiometricLogger logger,
+                @NonNull BiometricContext biometricContext,
+                @NonNull UserStoppedCallback callback, StopUserClientShouldFail shouldFail) {
+            super(context, lazyDaemon, token, userId, sensorId, logger, biometricContext, callback);
+            mShouldFailClient = shouldFail;
+        }
+
+        @Override
+        protected void startHalOperation() {
+
+        }
+
+        @Override
+        public void start(@NonNull ClientMonitorCallback callback) {
+            super.start(callback);
+            if (mShouldFailClient.shouldFail()) {
+                getCallback().onClientFinished(this, false /* success */);
+                // When the above fails, it means that the HAL has died, in this case we
+                // need to ensure the UserSwitchCallback correctly returns the NULL user handle.
+                mCurrentUserId = UserHandle.USER_NULL;
+            } else {
+                onUserStopped();
+            }
+        }
+
+        @Override
+        public void unableToStart() {
+
+        }
+    }
+
+    private static class TestStartUserClient extends StartUserClient<IFingerprint, ISession> {
+
+        @Mock
+        private ISession mSession;
+        private final boolean mShouldFinish;
+        ClientMonitorCallback mCallback;
+
+        TestStartUserClient(@NonNull Context context,
+                @NonNull Supplier<IFingerprint> lazyDaemon, @Nullable IBinder token, int userId,
+                int sensorId, @NonNull BiometricLogger logger,
+                @NonNull BiometricContext biometricContext,
+                @NonNull UserStartedCallback<ISession> callback, boolean shouldFinish) {
+            super(context, lazyDaemon, token, userId, sensorId, logger, biometricContext, callback);
+            mShouldFinish = shouldFinish;
+        }
+
+        @Override
+        protected void startHalOperation() {
+
+        }
+
+        @Override
+        public void start(@NonNull ClientMonitorCallback callback) {
+            super.start(callback);
+
+            mCallback = callback;
+            if (mShouldFinish) {
+                mUserStartedCallback.onUserStarted(
+                        getTargetUserId(), mSession, 1 /* halInterfaceVersion */);
+                callback.onClientFinished(this, true /* success */);
+            }
+        }
+
+        @Override
+        public void unableToStart() {
+
+        }
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/FaceServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/FaceServiceTest.java
index c9e1c4a..3aaac2e 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/FaceServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/FaceServiceTest.java
@@ -16,21 +16,30 @@
 
 package com.android.server.biometrics.sensors.face;
 
+import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
 import static android.hardware.biometrics.SensorProperties.STRENGTH_STRONG;
 import static android.hardware.face.FaceSensorProperties.TYPE_UNKNOWN;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.content.ComponentName;
+import android.content.pm.PackageManager;
 import android.hardware.biometrics.BiometricAuthenticator;
 import android.hardware.biometrics.IBiometricService;
 import android.hardware.biometrics.face.IFace;
 import android.hardware.biometrics.face.SensorProps;
+import android.hardware.face.FaceAuthenticateOptions;
 import android.hardware.face.FaceSensorConfigurations;
 import android.hardware.face.FaceSensorPropertiesInternal;
 import android.hardware.face.IFaceAuthenticatorsRegisteredCallback;
+import android.hardware.face.IFaceServiceReceiver;
+import android.os.IBinder;
 import android.os.RemoteException;
 import android.platform.test.annotations.Presubmit;
 import android.platform.test.annotations.RequiresFlagsEnabled;
@@ -42,6 +51,7 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.platform.app.InstrumentationRegistry;
 
+import com.android.internal.R;
 import com.android.internal.util.test.FakeSettingsProvider;
 import com.android.internal.util.test.FakeSettingsProviderRule;
 import com.android.server.biometrics.Flags;
@@ -66,6 +76,7 @@
     private static final int ID_VIRTUAL = 6;
     private static final String NAME_DEFAULT = "default";
     private static final String NAME_VIRTUAL = "virtual";
+    private static final String OP_PACKAGE_NAME = "FaceServiceTest/SystemUi";
 
     @Rule
     public final MockitoRule mMockito = MockitoJUnit.rule();
@@ -78,15 +89,19 @@
     @Rule
     public final FakeSettingsProviderRule mSettingsRule = FakeSettingsProvider.rule();
     @Mock
-    FaceProvider mFaceProviderDefault;
+    private FaceProvider mFaceProviderDefault;
     @Mock
-    FaceProvider mFaceProviderVirtual;
+    private FaceProvider mFaceProviderVirtual;
     @Mock
-    IFace mDefaultFaceDaemon;
+    private IFace mDefaultFaceDaemon;
     @Mock
-    IFace mVirtualFaceDaemon;
+    private IFace mVirtualFaceDaemon;
     @Mock
-    IBiometricService mIBiometricService;
+    private IBiometricService mIBiometricService;
+    @Mock
+    private IBinder mToken;
+    @Mock
+    private IFaceServiceReceiver mFaceServiceReceiver;
 
     private final SensorProps mDefaultSensorProps = new SensorProps();
     private final SensorProps mVirtualSensorProps = new SensorProps();
@@ -117,7 +132,13 @@
                 new SensorProps[]{mVirtualSensorProps});
         when(mFaceProviderDefault.getSensorProperties()).thenReturn(List.of(mSensorPropsDefault));
         when(mFaceProviderVirtual.getSensorProperties()).thenReturn(List.of(mSensorPropsVirtual));
+        when(mFaceProviderDefault.containsSensor(anyInt()))
+                .thenAnswer(i -> i.getArguments()[0].equals(ID_DEFAULT));
+        when(mFaceProviderVirtual.containsSensor(anyInt()))
+                .thenAnswer(i -> i.getArguments()[0].equals(ID_VIRTUAL));
 
+        mContext.getTestablePermissions().setPermission(
+                USE_BIOMETRIC_INTERNAL, PackageManager.PERMISSION_GRANTED);
         mFaceSensorConfigurations = new FaceSensorConfigurations(false);
         mFaceSensorConfigurations.addAidlConfigs(new String[]{NAME_DEFAULT, NAME_VIRTUAL},
                 (name) -> {
@@ -136,7 +157,13 @@
                     if (NAME_DEFAULT.equals(filteredSensorProps.first)) return mFaceProviderDefault;
                     if (NAME_VIRTUAL.equals(filteredSensorProps.first)) return mFaceProviderVirtual;
                     return null;
-                }, () -> mIBiometricService);
+                }, () -> mIBiometricService,
+                (name) -> {
+                    if (NAME_DEFAULT.equals(name)) return mFaceProviderDefault;
+                    if (NAME_VIRTUAL.equals(name)) return mFaceProviderVirtual;
+                    return null;
+                },
+                () -> new String[]{NAME_DEFAULT, NAME_VIRTUAL});
     }
 
     @Test
@@ -191,6 +218,39 @@
                 eq(Utils.propertyStrengthToAuthenticatorStrength(STRENGTH_STRONG)), any());
     }
 
+    @Test
+    public void testOptionsForAuthentication() throws Exception {
+        FaceAuthenticateOptions faceAuthenticateOptions = new FaceAuthenticateOptions.Builder()
+                .build();
+        initService();
+        mFaceService.mServiceWrapper.registerAuthenticators(List.of());
+        waitForRegistration();
+
+        final long operationId = 5;
+        mFaceService.mServiceWrapper.authenticate(mToken, operationId, mFaceServiceReceiver,
+                faceAuthenticateOptions);
+
+        assertThat(faceAuthenticateOptions.getSensorId()).isEqualTo(ID_DEFAULT);
+    }
+
+    @Test
+    public void testOptionsForDetect() throws Exception {
+        FaceAuthenticateOptions faceAuthenticateOptions = new FaceAuthenticateOptions.Builder()
+                .setOpPackageName(ComponentName.unflattenFromString(OP_PACKAGE_NAME)
+                        .getPackageName())
+                .build();
+        mContext.getOrCreateTestableResources().addOverride(
+                R.string.config_keyguardComponent,
+                OP_PACKAGE_NAME);
+        initService();
+        mFaceService.mServiceWrapper.registerAuthenticators(List.of());
+        waitForRegistration();
+        mFaceService.mServiceWrapper.detectFace(mToken, mFaceServiceReceiver,
+                faceAuthenticateOptions);
+
+        assertThat(faceAuthenticateOptions.getSensorId()).isEqualTo(ID_DEFAULT);
+    }
+
     private void waitForRegistration() throws Exception {
         final CountDownLatch latch = new CountDownLatch(1);
         mFaceService.mServiceWrapper.addAuthenticatorsRegisteredCallback(
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceProviderTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceProviderTest.java
index 8b1a291..772ec8b 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceProviderTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceProviderTest.java
@@ -36,8 +36,10 @@
 import android.hardware.biometrics.face.ISession;
 import android.hardware.biometrics.face.SensorProps;
 import android.hardware.face.HidlFaceSensorConfig;
+import android.os.Handler;
 import android.os.RemoteException;
 import android.os.UserManager;
+import android.os.test.TestLooper;
 import android.platform.test.annotations.Presubmit;
 import android.platform.test.annotations.RequiresFlagsEnabled;
 import android.platform.test.flag.junit.CheckFlagsRule;
@@ -88,14 +90,11 @@
     @Mock
     private BiometricStateCallback mBiometricStateCallback;
 
+    private final TestLooper mLooper = new TestLooper();
     private SensorProps[] mSensorProps;
     private LockoutResetDispatcher mLockoutResetDispatcher;
     private FaceProvider mFaceProvider;
 
-    private static void waitForIdle() {
-        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
-    }
-
     @Before
     public void setUp() throws RemoteException {
         MockitoAnnotations.initMocks(this);
@@ -121,7 +120,8 @@
 
         mFaceProvider = new FaceProvider(mContext, mBiometricStateCallback,
                 mSensorProps, TAG, mLockoutResetDispatcher, mBiometricContext,
-                mDaemon, false /* resetLockoutRequiresChallenge */, false /* testHalEnabled */);
+                mDaemon, new Handler(mLooper.getLooper()),
+                false /* resetLockoutRequiresChallenge */, false /* testHalEnabled */);
     }
 
     @Test
@@ -156,6 +156,7 @@
         mFaceProvider = new FaceProvider(mContext,
                 mBiometricStateCallback, hidlFaceSensorConfig, TAG,
                 mLockoutResetDispatcher, mBiometricContext, mDaemon,
+                new Handler(mLooper.getLooper()),
                 true /* resetLockoutRequiresChallenge */,
                 true /* testHalEnabled */);
 
@@ -210,4 +211,12 @@
             assertEquals(0, scheduler.getCurrentPendingCount());
         }
     }
+
+    private void waitForIdle() {
+        if (Flags.deHidl()) {
+            mLooper.dispatchAll();
+        } else {
+            InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+        }
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java
index e7f7195..fe9cd43 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java
@@ -31,6 +31,7 @@
 import android.content.Context;
 import android.hardware.biometrics.IBiometricService;
 import android.hardware.biometrics.common.CommonProps;
+import android.hardware.biometrics.face.IFace;
 import android.hardware.biometrics.face.ISession;
 import android.hardware.biometrics.face.SensorProps;
 import android.hardware.face.FaceSensorPropertiesInternal;
@@ -40,6 +41,7 @@
 
 import androidx.test.filters.SmallTest;
 
+import com.android.server.biometrics.Flags;
 import com.android.server.biometrics.log.BiometricContext;
 import com.android.server.biometrics.log.BiometricLogger;
 import com.android.server.biometrics.sensors.AuthSessionCoordinator;
@@ -49,6 +51,7 @@
 import com.android.server.biometrics.sensors.LockoutResetDispatcher;
 import com.android.server.biometrics.sensors.LockoutTracker;
 import com.android.server.biometrics.sensors.UserAwareBiometricScheduler;
+import com.android.server.biometrics.sensors.UserSwitchProvider;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -74,6 +77,8 @@
     @Mock
     private UserAwareBiometricScheduler.UserSwitchCallback mUserSwitchCallback;
     @Mock
+    private UserSwitchProvider<IFace, ISession> mUserSwitchProvider;
+    @Mock
     private AidlResponseHandler.HardwareUnavailableCallback mHardwareUnavailableCallback;
     @Mock
     private LockoutResetDispatcher mLockoutResetDispatcher;
@@ -84,16 +89,16 @@
     @Mock
     private AuthSessionCoordinator mAuthSessionCoordinator;
     @Mock
-    FaceProvider mFaceProvider;
+    private FaceProvider mFaceProvider;
     @Mock
-    BaseClientMonitor mClientMonitor;
+    private BaseClientMonitor mClientMonitor;
     @Mock
-    AidlSession mCurrentSession;
+    private AidlSession mCurrentSession;
 
     private final TestLooper mLooper = new TestLooper();
     private final LockoutCache mLockoutCache = new LockoutCache();
 
-    private UserAwareBiometricScheduler mScheduler;
+    private BiometricScheduler<IFace, ISession> mScheduler;
     private AidlResponseHandler mHalCallback;
 
     @Before
@@ -101,16 +106,26 @@
         MockitoAnnotations.initMocks(this);
 
         when(mContext.getSystemService(Context.BIOMETRIC_SERVICE)).thenReturn(mBiometricService);
-
         when(mBiometricContext.getAuthSessionCoordinator()).thenReturn(mAuthSessionCoordinator);
 
-        mScheduler = new UserAwareBiometricScheduler(TAG,
-                new Handler(mLooper.getLooper()),
-                BiometricScheduler.SENSOR_TYPE_FACE,
-                null /* gestureAvailabilityDispatcher */,
-                mBiometricService,
-                () -> USER_ID,
-                mUserSwitchCallback);
+        if (Flags.deHidl()) {
+            mScheduler = new BiometricScheduler<>(
+                    new Handler(mLooper.getLooper()),
+                    BiometricScheduler.SENSOR_TYPE_FACE,
+                    null /* gestureAvailabilityDispatcher */,
+                    mBiometricService,
+                    2 /* recentOperationsLimit */,
+                    () -> USER_ID,
+                    mUserSwitchProvider);
+        } else {
+            mScheduler = new UserAwareBiometricScheduler<>(TAG,
+                    new Handler(mLooper.getLooper()),
+                    BiometricScheduler.SENSOR_TYPE_FACE,
+                    null /* gestureAvailabilityDispatcher */,
+                    mBiometricService,
+                    () -> USER_ID,
+                    mUserSwitchCallback);
+        }
         mHalCallback = new AidlResponseHandler(mContext, mScheduler, SENSOR_ID, USER_ID,
                 mLockoutCache, mLockoutResetDispatcher, mAuthSessionCoordinator,
                 mHardwareUnavailableCallback);
@@ -146,18 +161,8 @@
     @Test
     public void onBinderDied_noErrorOnNullClient() {
         mLooper.dispatchAll();
-
-        final SensorProps sensorProps = new SensorProps();
-        sensorProps.commonProps = new CommonProps();
-        sensorProps.commonProps.sensorId = 1;
-        final FaceSensorPropertiesInternal internalProp = new FaceSensorPropertiesInternal(
-                sensorProps.commonProps.sensorId, sensorProps.commonProps.sensorStrength,
-                sensorProps.commonProps.maxEnrollmentsPerUser, null /* componentInfo */,
-                sensorProps.sensorType, sensorProps.supportsDetectInteraction,
-                sensorProps.halControlsPreview, false /* resetLockoutRequiresChallenge */);
-        final Sensor sensor = new Sensor("SensorTest", mFaceProvider, mContext,
-                null /* handler */, internalProp, mLockoutResetDispatcher, mBiometricContext);
-        sensor.init(mLockoutResetDispatcher, mFaceProvider);
+        final Sensor sensor = getSensor();
+        mScheduler = sensor.getScheduler();
         mScheduler.reset();
 
         assertNull(mScheduler.getCurrentClient());
@@ -175,18 +180,8 @@
         when(mClientMonitor.getTargetUserId()).thenReturn(USER_ID);
         when(mClientMonitor.isInterruptable()).thenReturn(false);
 
-        final SensorProps sensorProps = new SensorProps();
-        sensorProps.commonProps = new CommonProps();
-        sensorProps.commonProps.sensorId = 1;
-        final FaceSensorPropertiesInternal internalProp = new FaceSensorPropertiesInternal(
-                sensorProps.commonProps.sensorId, sensorProps.commonProps.sensorStrength,
-                sensorProps.commonProps.maxEnrollmentsPerUser, null /* componentInfo */,
-                sensorProps.sensorType, sensorProps.supportsDetectInteraction,
-                sensorProps.halControlsPreview, false /* resetLockoutRequiresChallenge */);
-        final Sensor sensor = new Sensor("SensorTest", mFaceProvider, mContext, null,
-                internalProp, mLockoutResetDispatcher, mBiometricContext, mCurrentSession);
-        sensor.init(mLockoutResetDispatcher, mFaceProvider);
-        mScheduler = (UserAwareBiometricScheduler) sensor.getScheduler();
+        final Sensor sensor = getSensor();
+        mScheduler = sensor.getScheduler();
         sensor.mCurrentSession = new AidlSession(0, mock(ISession.class),
                 USER_ID, mHalCallback);
 
@@ -206,4 +201,20 @@
         verify(mLockoutResetDispatcher).notifyLockoutResetCallbacks(eq(SENSOR_ID));
         verify(mAuthSessionCoordinator).resetLockoutFor(eq(USER_ID), anyInt(), anyLong());
     }
+
+    private Sensor getSensor() {
+        final SensorProps sensorProps = new SensorProps();
+        sensorProps.commonProps = new CommonProps();
+        sensorProps.commonProps.sensorId = 1;
+        final FaceSensorPropertiesInternal internalProp = new FaceSensorPropertiesInternal(
+                sensorProps.commonProps.sensorId, sensorProps.commonProps.sensorStrength,
+                sensorProps.commonProps.maxEnrollmentsPerUser, null /* componentInfo */,
+                sensorProps.sensorType, sensorProps.supportsDetectInteraction,
+                sensorProps.halControlsPreview, false /* resetLockoutRequiresChallenge */);
+        final Sensor sensor = new Sensor(mFaceProvider, mContext,
+                null /* handler */, internalProp, mBiometricContext);
+        sensor.init(mLockoutResetDispatcher, mFaceProvider);
+
+        return sensor;
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSensorAdapterTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSensorAdapterTest.java
index 4e43332..940fe69 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSensorAdapterTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSensorAdapterTest.java
@@ -45,7 +45,6 @@
 import com.android.server.biometrics.log.BiometricContext;
 import com.android.server.biometrics.log.BiometricLogger;
 import com.android.server.biometrics.sensors.AuthSessionCoordinator;
-import com.android.server.biometrics.sensors.BiometricScheduler;
 import com.android.server.biometrics.sensors.BiometricUtils;
 import com.android.server.biometrics.sensors.LockoutResetDispatcher;
 import com.android.server.biometrics.sensors.LockoutTracker;
@@ -88,9 +87,9 @@
     @Mock
     private IBiometricsFace mDaemon;
     @Mock
-    AidlResponseHandler.AidlResponseHandlerCallback mAidlResponseHandlerCallback;
+    private AidlResponseHandler.AidlResponseHandlerCallback mAidlResponseHandlerCallback;
     @Mock
-    BiometricUtils<Face> mBiometricUtils;
+    private BiometricUtils<Face> mBiometricUtils;
 
     private final TestLooper mLooper = new TestLooper();
     private HidlToAidlSensorAdapter mHidlToAidlSensorAdapter;
@@ -118,20 +117,14 @@
         mContext.getOrCreateTestableResources();
 
         final String config = String.format("%d:8:15", SENSOR_ID);
-        final BiometricScheduler scheduler = new BiometricScheduler(TAG,
-                new Handler(mLooper.getLooper()),
-                BiometricScheduler.SENSOR_TYPE_FACE,
-                null /* gestureAvailabilityTracker */,
-                mBiometricService, 10 /* recentOperationsLimit */);
         final HidlFaceSensorConfig faceSensorConfig = new HidlFaceSensorConfig();
         faceSensorConfig.parse(config, mContext);
-        mHidlToAidlSensorAdapter = new HidlToAidlSensorAdapter(TAG, mFaceProvider,
+        mHidlToAidlSensorAdapter = new HidlToAidlSensorAdapter(mFaceProvider,
                 mContext, new Handler(mLooper.getLooper()), faceSensorConfig,
                 mLockoutResetDispatcherForSensor, mBiometricContext,
                 false /* resetLockoutRequiresChallenge */, mInternalCleanupAndGetFeatureRunnable,
                 mAuthSessionCoordinator, mDaemon, mAidlResponseHandlerCallback);
         mHidlToAidlSensorAdapter.init(mLockoutResetDispatcherForSensor, mFaceProvider);
-        mHidlToAidlSensorAdapter.setScheduler(scheduler);
         mHidlToAidlSensorAdapter.handleUserChanged(USER_ID);
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceTest.java
index f570ba2..88956b6 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceTest.java
@@ -40,6 +40,7 @@
 import static org.mockito.Mockito.when;
 
 import android.app.AppOpsManager;
+import android.content.ComponentName;
 import android.content.pm.PackageManager;
 import android.hardware.biometrics.IBiometricService;
 import android.hardware.biometrics.fingerprint.IFingerprint;
@@ -61,6 +62,7 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.platform.app.InstrumentationRegistry;
 
+import com.android.internal.R;
 import com.android.internal.util.test.FakeSettingsProvider;
 import com.android.internal.util.test.FakeSettingsProviderRule;
 import com.android.server.LocalServices;
@@ -92,6 +94,7 @@
     private static final String NAME_VIRTUAL = "virtual";
     private static final List<FingerprintSensorPropertiesInternal> HIDL_AUTHENTICATORS =
             List.of();
+    private static final String OP_PACKAGE_NAME = "FingerprintServiceTest/SystemUi";
 
     @Rule
     public final MockitoRule mMockito = MockitoJUnit.rule();
@@ -343,6 +346,24 @@
         assertEquals((int) (uidCaptor.getValue()), Binder.getCallingUid());
     }
 
+    @Test
+    public void testOptionsForDetect() throws Exception {
+        FingerprintAuthenticateOptions fingerprintAuthenticateOptions =
+                new FingerprintAuthenticateOptions.Builder()
+                        .setOpPackageName(ComponentName.unflattenFromString(
+                                OP_PACKAGE_NAME).getPackageName())
+                        .build();
+
+        mContext.getOrCreateTestableResources().addOverride(
+                R.string.config_keyguardComponent,
+                OP_PACKAGE_NAME);
+        initServiceWithAndWait(NAME_DEFAULT);
+        mService.mServiceWrapper.detectFingerprint(mToken, mServiceReceiver,
+                fingerprintAuthenticateOptions);
+
+        assertThat(fingerprintAuthenticateOptions.getSensorId()).isEqualTo(ID_DEFAULT);
+    }
+
 
     private FingerprintAuthenticateOptions verifyAuthenticateWithNewRequestId(
             FingerprintProvider provider, long operationId) {
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java
index bf5986c..258be57 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java
@@ -38,8 +38,10 @@
 import android.hardware.biometrics.fingerprint.SensorLocation;
 import android.hardware.biometrics.fingerprint.SensorProps;
 import android.hardware.fingerprint.HidlFingerprintSensorConfig;
+import android.os.Handler;
 import android.os.RemoteException;
 import android.os.UserManager;
+import android.os.test.TestLooper;
 import android.platform.test.annotations.Presubmit;
 import android.platform.test.annotations.RequiresFlagsEnabled;
 import android.platform.test.flag.junit.CheckFlagsRule;
@@ -92,14 +94,12 @@
     @Mock
     private BiometricContext mBiometricContext;
 
+    private final TestLooper mLooper = new TestLooper();
+
     private SensorProps[] mSensorProps;
     private LockoutResetDispatcher mLockoutResetDispatcher;
     private FingerprintProvider mFingerprintProvider;
 
-    private static void waitForIdle() {
-        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
-    }
-
     @Before
     public void setUp() throws RemoteException {
         MockitoAnnotations.initMocks(this);
@@ -126,7 +126,8 @@
         mFingerprintProvider = new FingerprintProvider(mContext,
                 mBiometricStateCallback, mAuthenticationStateListeners, mSensorProps, TAG,
                 mLockoutResetDispatcher, mGestureAvailabilityDispatcher, mBiometricContext,
-                mDaemon, false /* resetLockoutRequiresHardwareAuthToken */,
+                mDaemon, new Handler(mLooper.getLooper()),
+                false /* resetLockoutRequiresHardwareAuthToken */,
                 true /* testHalEnabled */);
     }
 
@@ -159,6 +160,7 @@
                 mBiometricStateCallback, mAuthenticationStateListeners,
                 hidlFingerprintSensorConfigs, TAG, mLockoutResetDispatcher,
                 mGestureAvailabilityDispatcher, mBiometricContext, mDaemon,
+                new Handler(mLooper.getLooper()),
                 false /* resetLockoutRequiresHardwareAuthToken */,
                 true /* testHalEnabled */);
 
@@ -215,4 +217,12 @@
             assertEquals(0, scheduler.getCurrentPendingCount());
         }
     }
+
+    private void waitForIdle() {
+        if (Flags.deHidl()) {
+            mLooper.dispatchAll();
+        } else {
+            InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+        }
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java
index 126a05e..b4c2ee8 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java
@@ -32,14 +32,17 @@
 import android.hardware.biometrics.IBiometricService;
 import android.hardware.biometrics.common.CommonProps;
 import android.hardware.biometrics.face.SensorProps;
+import android.hardware.biometrics.fingerprint.IFingerprint;
 import android.hardware.biometrics.fingerprint.ISession;
 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
 import android.os.Handler;
+import android.os.HandlerThread;
 import android.os.test.TestLooper;
 import android.platform.test.annotations.Presubmit;
 
 import androidx.test.filters.SmallTest;
 
+import com.android.server.biometrics.Flags;
 import com.android.server.biometrics.log.BiometricContext;
 import com.android.server.biometrics.log.BiometricLogger;
 import com.android.server.biometrics.sensors.AuthSessionCoordinator;
@@ -49,6 +52,7 @@
 import com.android.server.biometrics.sensors.LockoutResetDispatcher;
 import com.android.server.biometrics.sensors.LockoutTracker;
 import com.android.server.biometrics.sensors.UserAwareBiometricScheduler;
+import com.android.server.biometrics.sensors.UserSwitchProvider;
 import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDispatcher;
 
 import org.junit.Before;
@@ -75,6 +79,8 @@
     @Mock
     private UserAwareBiometricScheduler.UserSwitchCallback mUserSwitchCallback;
     @Mock
+    private UserSwitchProvider<IFingerprint, ISession> mUserSwitchProvider;
+    @Mock
     private AidlResponseHandler.HardwareUnavailableCallback mHardwareUnavailableCallback;
     @Mock
     private LockoutResetDispatcher mLockoutResetDispatcher;
@@ -92,11 +98,13 @@
     private AidlSession mCurrentSession;
     @Mock
     private BaseClientMonitor mClientMonitor;
+    @Mock
+    private HandlerThread mThread;
 
     private final TestLooper mLooper = new TestLooper();
     private final LockoutCache mLockoutCache = new LockoutCache();
 
-    private BiometricScheduler mScheduler;
+    private BiometricScheduler<IFingerprint, ISession> mScheduler;
     private AidlResponseHandler mHalCallback;
 
     @Before
@@ -105,14 +113,26 @@
 
         when(mContext.getSystemService(Context.BIOMETRIC_SERVICE)).thenReturn(mBiometricService);
         when(mBiometricContext.getAuthSessionCoordinator()).thenReturn(mAuthSessionCoordinator);
+        when(mThread.getLooper()).thenReturn(mLooper.getLooper());
 
-        mScheduler = new UserAwareBiometricScheduler(TAG,
-                new Handler(mLooper.getLooper()),
-                BiometricScheduler.SENSOR_TYPE_FP_OTHER,
-                null /* gestureAvailabilityDispatcher */,
-                mBiometricService,
-                () -> USER_ID,
-                mUserSwitchCallback);
+        if (Flags.deHidl()) {
+            mScheduler = new BiometricScheduler<>(
+                    new Handler(mLooper.getLooper()),
+                    BiometricScheduler.SENSOR_TYPE_FP_OTHER,
+                    null /* gestureAvailabilityDispatcher */,
+                    mBiometricService,
+                    2 /* recentOperationsLimit */,
+                    () -> USER_ID,
+                    mUserSwitchProvider);
+        } else {
+            mScheduler = new UserAwareBiometricScheduler<>(TAG,
+                    new Handler(mLooper.getLooper()),
+                    BiometricScheduler.SENSOR_TYPE_FP_OTHER,
+                    null /* gestureAvailabilityDispatcher */,
+                    mBiometricService,
+                    () -> USER_ID,
+                    mUserSwitchCallback);
+        }
         mHalCallback = new AidlResponseHandler(mContext, mScheduler, SENSOR_ID, USER_ID,
                 mLockoutCache, mLockoutResetDispatcher, mAuthSessionCoordinator,
                 mHardwareUnavailableCallback);
@@ -153,18 +173,7 @@
         when(mClientMonitor.getTargetUserId()).thenReturn(USER_ID);
         when(mClientMonitor.isInterruptable()).thenReturn(false);
 
-        final SensorProps sensorProps = new SensorProps();
-        sensorProps.commonProps = new CommonProps();
-        sensorProps.commonProps.sensorId = 1;
-        final FingerprintSensorPropertiesInternal internalProp = new
-                FingerprintSensorPropertiesInternal(
-                        sensorProps.commonProps.sensorId, sensorProps.commonProps.sensorStrength,
-                        sensorProps.commonProps.maxEnrollmentsPerUser, null,
-                        sensorProps.sensorType, false /* resetLockoutRequiresHardwareAuthToken */);
-        final Sensor sensor = new Sensor("SensorTest", mFingerprintProvider, mContext,
-                null /* handler */, internalProp, mLockoutResetDispatcher,
-                mGestureAvailabilityDispatcher, mBiometricContext, mCurrentSession);
-        sensor.init(mGestureAvailabilityDispatcher, mLockoutResetDispatcher);
+        final Sensor sensor = getSensor();
         mScheduler = sensor.getScheduler();
         sensor.mCurrentSession = new AidlSession(0, mock(ISession.class),
                 USER_ID, mHalCallback);
@@ -185,4 +194,21 @@
         verify(mLockoutResetDispatcher).notifyLockoutResetCallbacks(eq(SENSOR_ID));
         verify(mAuthSessionCoordinator).resetLockoutFor(eq(USER_ID), anyInt(), anyLong());
     }
+
+    private Sensor getSensor() {
+        final SensorProps sensorProps = new SensorProps();
+        sensorProps.commonProps = new CommonProps();
+        sensorProps.commonProps.sensorId = 1;
+        final FingerprintSensorPropertiesInternal internalProp = new
+                FingerprintSensorPropertiesInternal(
+                sensorProps.commonProps.sensorId, sensorProps.commonProps.sensorStrength,
+                sensorProps.commonProps.maxEnrollmentsPerUser, null,
+                sensorProps.sensorType, false /* resetLockoutRequiresHardwareAuthToken */);
+        final Sensor sensor = new Sensor(mFingerprintProvider, mContext,
+                null /* handler */, internalProp,
+                mBiometricContext, mCurrentSession);
+        sensor.init(mGestureAvailabilityDispatcher, mLockoutResetDispatcher);
+
+        return sensor;
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSensorAdapterTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSensorAdapterTest.java
index 89a4961..cbbc545 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSensorAdapterTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSensorAdapterTest.java
@@ -36,12 +36,12 @@
 import android.hardware.fingerprint.Fingerprint;
 import android.hardware.fingerprint.HidlFingerprintSensorConfig;
 import android.os.Handler;
+import android.os.HandlerThread;
 import android.os.RemoteException;
 import android.os.test.TestLooper;
 import android.platform.test.annotations.Presubmit;
 import android.testing.TestableContext;
 
-import androidx.annotation.NonNull;
 import androidx.test.core.app.ApplicationProvider;
 import androidx.test.filters.SmallTest;
 
@@ -49,17 +49,11 @@
 import com.android.server.biometrics.log.BiometricLogger;
 import com.android.server.biometrics.sensors.AuthSessionCoordinator;
 import com.android.server.biometrics.sensors.AuthenticationStateListeners;
-import com.android.server.biometrics.sensors.BiometricScheduler;
 import com.android.server.biometrics.sensors.BiometricUtils;
-import com.android.server.biometrics.sensors.ClientMonitorCallback;
 import com.android.server.biometrics.sensors.LockoutResetDispatcher;
 import com.android.server.biometrics.sensors.LockoutTracker;
-import com.android.server.biometrics.sensors.StartUserClient;
-import com.android.server.biometrics.sensors.StopUserClient;
-import com.android.server.biometrics.sensors.UserAwareBiometricScheduler;
 import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDispatcher;
 import com.android.server.biometrics.sensors.fingerprint.aidl.AidlResponseHandler;
-import com.android.server.biometrics.sensors.fingerprint.aidl.AidlSession;
 import com.android.server.biometrics.sensors.fingerprint.aidl.FingerprintEnrollClient;
 import com.android.server.biometrics.sensors.fingerprint.aidl.FingerprintProvider;
 import com.android.server.biometrics.sensors.fingerprint.aidl.FingerprintResetLockoutClient;
@@ -111,60 +105,18 @@
     private BiometricUtils<Fingerprint> mBiometricUtils;
     @Mock
     private AuthenticationStateListeners mAuthenticationStateListeners;
+    @Mock
+    private HandlerThread mThread;
 
     private final TestLooper mLooper = new TestLooper();
     private HidlToAidlSensorAdapter mHidlToAidlSensorAdapter;
     private final TestableContext mContext = new TestableContext(
             ApplicationProvider.getApplicationContext());
 
-    private final UserAwareBiometricScheduler.UserSwitchCallback mUserSwitchCallback =
-            new UserAwareBiometricScheduler.UserSwitchCallback() {
-                @NonNull
-                @Override
-                public StopUserClient<?> getStopUserClient(int userId) {
-                    return new StopUserClient<IBiometricsFingerprint>(mContext,
-                            mHidlToAidlSensorAdapter::getIBiometricsFingerprint, null, USER_ID,
-                            SENSOR_ID, mLogger, mBiometricContext, () -> {}) {
-                        @Override
-                        protected void startHalOperation() {
-                            getCallback().onClientFinished(this, true /* success */);
-                        }
-
-                        @Override
-                        public void unableToStart() {}
-                    };
-                }
-
-                @NonNull
-                @Override
-                public StartUserClient<?, ?> getStartUserClient(int newUserId) {
-                    return new StartUserClient<IBiometricsFingerprint, AidlSession>(mContext,
-                            mHidlToAidlSensorAdapter::getIBiometricsFingerprint, null,
-                            USER_ID, SENSOR_ID,
-                            mLogger, mBiometricContext,
-                            (newUserId1, newUser, halInterfaceVersion) ->
-                                    mHidlToAidlSensorAdapter.handleUserChanged(newUserId1)) {
-                        @Override
-                        public void start(@NonNull ClientMonitorCallback callback) {
-                            super.start(callback);
-                            startHalOperation();
-                        }
-
-                        @Override
-                        protected void startHalOperation() {
-                            mUserStartedCallback.onUserStarted(USER_ID, null, 0);
-                            getCallback().onClientFinished(this, true /* success */);
-                        }
-
-                        @Override
-                        public void unableToStart() {}
-                    };
-                }
-            };;
-
     @Before
     public void setUp() throws RemoteException {
         when(mBiometricContext.getAuthSessionCoordinator()).thenReturn(mAuthSessionCoordinator);
+        when(mThread.getLooper()).thenReturn(mLooper.getLooper());
         doAnswer((answer) -> {
             mHidlToAidlSensorAdapter.getLazySession().get().getHalSessionCallback()
                     .onEnrollmentProgress(1 /* enrollmentId */, 0 /* remaining */);
@@ -175,26 +127,18 @@
         mContext.getOrCreateTestableResources();
 
         final String config = String.format("%d:2:15", SENSOR_ID);
-        final UserAwareBiometricScheduler scheduler = new UserAwareBiometricScheduler(TAG,
-                new Handler(mLooper.getLooper()),
-                BiometricScheduler.SENSOR_TYPE_FP_OTHER,
-                null /* gestureAvailabilityDispatcher */,
-                mBiometricService,
-                () -> USER_ID,
-                mUserSwitchCallback);
         final HidlFingerprintSensorConfig fingerprintSensorConfig =
                 new HidlFingerprintSensorConfig();
         fingerprintSensorConfig.parse(config, mContext);
-        mHidlToAidlSensorAdapter = new HidlToAidlSensorAdapter(TAG,
+        mHidlToAidlSensorAdapter = new HidlToAidlSensorAdapter(
                 mFingerprintProvider, mContext, new Handler(mLooper.getLooper()),
                 fingerprintSensorConfig, mLockoutResetDispatcherForSensor,
-                mGestureAvailabilityDispatcher, mBiometricContext,
-                false /* resetLockoutRequiresHardwareAuthToken */,
+                mBiometricContext, false /* resetLockoutRequiresHardwareAuthToken */,
                 mInternalCleanupRunnable, mAuthSessionCoordinator, mDaemon,
                 mAidlResponseHandlerCallback);
         mHidlToAidlSensorAdapter.init(mGestureAvailabilityDispatcher,
                 mLockoutResetDispatcherForSensor);
-        mHidlToAidlSensorAdapter.setScheduler(scheduler);
+
         mHidlToAidlSensorAdapter.handleUserChanged(USER_ID);
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/GenericWindowPolicyControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/GenericWindowPolicyControllerTest.java
index 18e6f0a..132b621 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/GenericWindowPolicyControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/GenericWindowPolicyControllerTest.java
@@ -164,6 +164,19 @@
     }
 
     @Test
+    public void userNotAllowlisted_systemUserCanLaunchBlockedAppStreamingActivity() {
+        GenericWindowPolicyController gwpc = createGwpcWithNoAllowedUsers();
+        gwpc.setDisplayId(DISPLAY_ID, /* isMirrorDisplay= */ false);
+
+        ActivityInfo activityInfo = getActivityInfo(
+                BLOCKED_APP_STREAMING_COMPONENT.getPackageName(),
+                BLOCKED_APP_STREAMING_COMPONENT.getClassName(),
+                /* displayOnRemoteDevices */ true,
+                /* targetDisplayCategory */ null);
+        assertActivityCanBeLaunched(gwpc, activityInfo);
+    }
+
+    @Test
     public void openNonBlockedAppOnVirtualDisplay_isNotBlocked() {
         GenericWindowPolicyController gwpc = createGwpc();
         gwpc.setDisplayId(DISPLAY_ID, /* isMirrorDisplay= */ false);
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 995d1f4..e1f490a 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
@@ -341,7 +341,8 @@
         mSetFlagsRule.initAllFlagsToReleaseConfigDefault();
 
         doReturn(true).when(mInputManagerInternalMock).setVirtualMousePointerDisplayId(anyInt());
-        doNothing().when(mInputManagerInternalMock).setPointerAcceleration(anyFloat(), anyInt());
+        doNothing().when(mInputManagerInternalMock)
+                .setMousePointerAccelerationEnabled(anyBoolean(), anyInt());
         doNothing().when(mInputManagerInternalMock).setPointerIconVisible(anyBoolean(), anyInt());
         LocalServices.removeServiceForTest(InputManagerInternal.class);
         LocalServices.addService(InputManagerInternal.class, mInputManagerInternalMock);
@@ -1982,8 +1983,8 @@
         return new AssociationInfo(associationId, /* userId= */ 0, /* packageName=*/ null,
                 /* tag= */ null, MacAddress.BROADCAST_ADDRESS, /* displayName= */ "", deviceProfile,
                 /* associatedDevice= */ null, /* selfManaged= */ true,
-                /* notifyOnDeviceNearby= */ false, /* revoked= */false,  /* timeApprovedMs= */0,
-                /* lastTimeConnectedMs= */0, /* systemDataSyncFlags= */ -1);
+                /* notifyOnDeviceNearby= */ false, /* revoked= */ false, /* pending= */ false,
+                /* timeApprovedMs= */0, /* lastTimeConnectedMs= */0, /* systemDataSyncFlags= */ -1);
     }
 
     /** Helper class to drop permissions temporarily and restore them at the end of a test. */
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/camera/VirtualCameraControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/camera/VirtualCameraControllerTest.java
index 071d571..9b28b81 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/camera/VirtualCameraControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/camera/VirtualCameraControllerTest.java
@@ -53,11 +53,11 @@
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 public class VirtualCameraControllerTest {
 
-    private static final int CAMERA_DISPLAY_NAME_RES_ID_1 = 10;
+    private static final String CAMERA_NAME_1 = "Virtual camera 1";
     private static final int CAMERA_WIDTH_1 = 100;
     private static final int CAMERA_HEIGHT_1 = 200;
 
-    private static final int CAMERA_DISPLAY_NAME_RES_ID_2 = 11;
+    private static final String CAMERA_NAME_2 = "Virtual camera 2";
     private static final int CAMERA_WIDTH_2 = 400;
     private static final int CAMERA_HEIGHT_2 = 600;
     private static final int CAMERA_FORMAT = ImageFormat.YUV_420_888;
@@ -84,7 +84,7 @@
     @Test
     public void registerCamera_registersCamera() throws Exception {
         mVirtualCameraController.registerCamera(createVirtualCameraConfig(
-                CAMERA_WIDTH_1, CAMERA_HEIGHT_1, CAMERA_FORMAT, CAMERA_DISPLAY_NAME_RES_ID_1));
+                CAMERA_WIDTH_1, CAMERA_HEIGHT_1, CAMERA_FORMAT, CAMERA_NAME_1));
 
         ArgumentCaptor<VirtualCameraConfiguration> configurationCaptor =
                 ArgumentCaptor.forClass(VirtualCameraConfiguration.class);
@@ -98,7 +98,7 @@
     @Test
     public void unregisterCamera_unregistersCamera() throws Exception {
         VirtualCameraConfig config = createVirtualCameraConfig(
-                CAMERA_WIDTH_1, CAMERA_HEIGHT_1, CAMERA_FORMAT, CAMERA_DISPLAY_NAME_RES_ID_1);
+                CAMERA_WIDTH_1, CAMERA_HEIGHT_1, CAMERA_FORMAT, CAMERA_NAME_1);
         mVirtualCameraController.registerCamera(config);
 
         mVirtualCameraController.unregisterCamera(config);
@@ -109,9 +109,9 @@
     @Test
     public void close_unregistersAllCameras() throws Exception {
         mVirtualCameraController.registerCamera(createVirtualCameraConfig(
-                CAMERA_WIDTH_1, CAMERA_HEIGHT_1, CAMERA_FORMAT, CAMERA_DISPLAY_NAME_RES_ID_1));
+                CAMERA_WIDTH_1, CAMERA_HEIGHT_1, CAMERA_FORMAT, CAMERA_NAME_1));
         mVirtualCameraController.registerCamera(createVirtualCameraConfig(
-                CAMERA_WIDTH_2, CAMERA_HEIGHT_2, CAMERA_FORMAT, CAMERA_DISPLAY_NAME_RES_ID_2));
+                CAMERA_WIDTH_2, CAMERA_HEIGHT_2, CAMERA_FORMAT, CAMERA_NAME_2));
 
         mVirtualCameraController.close();
 
@@ -129,10 +129,10 @@
     }
 
     private VirtualCameraConfig createVirtualCameraConfig(
-            int width, int height, int format, int displayNameResId) {
+            int width, int height, int format, String displayName) {
         return new VirtualCameraConfig.Builder()
                 .addStreamConfig(width, height, format)
-                .setDisplayNameStringRes(displayNameResId)
+                .setName(displayName)
                 .setVirtualCameraCallback(mCallbackHandler, createNoOpCallback())
                 .build();
     }
diff --git a/services/tests/servicestests/src/com/android/server/credentials/CredentialManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/credentials/CredentialManagerServiceTest.java
index fd1abff..d850c73 100644
--- a/services/tests/servicestests/src/com/android/server/credentials/CredentialManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/credentials/CredentialManagerServiceTest.java
@@ -53,6 +53,12 @@
     }
 
     @Test
+    public void getStoredProviders_nullValue_success() {
+        Set<String> providers = CredentialManagerService.getStoredProviders(null, null);
+        assertThat(providers.size()).isEqualTo(0);
+    }
+
+    @Test
     public void getStoredProviders_success() {
         Set<String> providers =
                 CredentialManagerService.getStoredProviders(
diff --git a/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodUtilsTest.java b/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodUtilsTest.java
index 0d25faf..6b85a32 100644
--- a/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodUtilsTest.java
@@ -25,10 +25,8 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.content.ContentResolver;
@@ -1223,42 +1221,6 @@
                 StartInputFlags.VIEW_HAS_FOCUS | StartInputFlags.IS_TEXT_EDITOR));
     }
 
-    @Test
-    public void testInputMethodSettings_SwitchCurrentUser() {
-        TestContext ownerUserContext = createMockContext(0 /* userId */);
-        final InputMethodInfo systemIme = createFakeInputMethodInfo(
-                "SystemIme", "fake.latin", true /* isSystem */);
-        final InputMethodInfo nonSystemIme = createFakeInputMethodInfo("NonSystemIme",
-                "fake.voice0", false /* isSystem */);
-        final ArrayMap<String, InputMethodInfo> methodMap = new ArrayMap<>();
-        methodMap.put(systemIme.getId(), systemIme);
-
-        // Init InputMethodSettings for the owner user (userId=0), verify calls can get the
-        // corresponding user's context, contentResolver and the resources configuration.
-        InputMethodUtils.InputMethodSettings settings = new InputMethodUtils.InputMethodSettings(
-                methodMap, 0 /* userId */, true);
-        assertEquals(0, settings.getCurrentUserId());
-
-        settings.isShowImeWithHardKeyboardEnabled();
-        verify(ownerUserContext.getContentResolver(), atLeastOnce()).getAttributionSource();
-
-        settings.getEnabledInputMethodSubtypeListLocked(nonSystemIme, true);
-        verify(ownerUserContext.getResources(), atLeastOnce()).getConfiguration();
-
-        // Calling switchCurrentUser to the secondary user (userId=10), verify calls can get the
-        // corresponding user's context, contentResolver and the resources configuration.
-        settings.switchCurrentUser(10 /* userId */, true);
-        assertEquals(10, settings.getCurrentUserId());
-
-        settings.isShowImeWithHardKeyboardEnabled();
-        verify(TestContext.getSecondaryUserContext().getContentResolver(),
-                atLeastOnce()).getAttributionSource();
-
-        settings.getEnabledInputMethodSubtypeListLocked(nonSystemIme, true);
-        verify(TestContext.getSecondaryUserContext().getResources(),
-                atLeastOnce()).getConfiguration();
-    }
-
     private static IntArray createSubtypeHashCodeArrayFromStr(String subtypeHashCodesStr) {
         final IntArray subtypes = new IntArray();
         final TextUtils.SimpleStringSplitter imeSubtypeSplitter =
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
index f5d50d1..6986cab 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
@@ -305,9 +305,9 @@
         doAnswer(invocation -> {
             Object[] args = invocation.getArguments();
             mStorageManager.unlockCeStorage(/* userId= */ (int) args[0],
-                    /* secret= */ (byte[]) args[2]);
+                    /* secret= */ (byte[]) args[1]);
             return null;
-        }).when(sm).unlockCeStorage(anyInt(), anyInt(), any());
+        }).when(sm).unlockCeStorage(anyInt(), any());
 
         doAnswer(invocation -> {
             Object[] args = invocation.getArguments();
diff --git a/services/tests/servicestests/src/com/android/server/media/BluetoothRouteControllerTest.java b/services/tests/servicestests/src/com/android/server/media/BluetoothRouteControllerTest.java
deleted file mode 100644
index 06f117b..0000000
--- a/services/tests/servicestests/src/com/android/server/media/BluetoothRouteControllerTest.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.media;
-
-import static com.android.media.flags.Flags.FLAG_ENABLE_AUDIO_POLICIES_DEVICE_AND_BLUETOOTH_CONTROLLER;
-
-import android.content.Context;
-import android.platform.test.annotations.RequiresFlagsDisabled;
-import android.platform.test.annotations.RequiresFlagsEnabled;
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.DeviceFlagsValueProvider;
-
-import androidx.test.platform.app.InstrumentationRegistry;
-
-import com.google.common.truth.Truth;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-@RunWith(JUnit4.class)
-public class BluetoothRouteControllerTest {
-
-    private final BluetoothRouteController.BluetoothRoutesUpdatedListener
-            mBluetoothRoutesUpdatedListener =
-                    () -> {
-                        // Empty on purpose.
-                    };
-
-    @Rule
-    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
-
-    private Context mContext;
-
-    @Before
-    public void setUp() {
-        mContext = InstrumentationRegistry.getInstrumentation().getContext();
-    }
-
-    @Test
-    @RequiresFlagsDisabled(FLAG_ENABLE_AUDIO_POLICIES_DEVICE_AND_BLUETOOTH_CONTROLLER)
-    public void createInstance_audioPoliciesFlagIsDisabled_createsLegacyController() {
-        BluetoothRouteController deviceRouteController =
-                BluetoothRouteController.createInstance(mContext, mBluetoothRoutesUpdatedListener);
-
-        Truth.assertThat(deviceRouteController).isInstanceOf(LegacyBluetoothRouteController.class);
-    }
-
-    @Test
-    @RequiresFlagsEnabled(FLAG_ENABLE_AUDIO_POLICIES_DEVICE_AND_BLUETOOTH_CONTROLLER)
-    public void createInstance_audioPoliciesFlagIsEnabled_createsAudioPoliciesController() {
-        BluetoothRouteController deviceRouteController =
-                BluetoothRouteController.createInstance(mContext, mBluetoothRoutesUpdatedListener);
-
-        Truth.assertThat(deviceRouteController)
-                .isInstanceOf(AudioPoliciesBluetoothRouteController.class);
-    }
-}
diff --git a/services/tests/servicestests/src/com/android/server/media/DeviceRouteControllerTest.java b/services/tests/servicestests/src/com/android/server/media/DeviceRouteControllerTest.java
index 0961b7d..eb78961 100644
--- a/services/tests/servicestests/src/com/android/server/media/DeviceRouteControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/media/DeviceRouteControllerTest.java
@@ -70,7 +70,6 @@
                 DeviceRouteController.createInstance(
                         mContext, Looper.getMainLooper(), mOnDeviceRouteChangedListener);
 
-        Truth.assertThat(deviceRouteController)
-                .isInstanceOf(AudioPoliciesDeviceRouteController.class);
+        Truth.assertThat(deviceRouteController).isInstanceOf(AudioManagerRouteController.class);
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingValidationTest.kt b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingValidationTest.kt
index 6a088d9..757abde 100644
--- a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingValidationTest.kt
+++ b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingValidationTest.kt
@@ -157,7 +157,7 @@
         validateTagCount("uses-library", 1000, tag)
         validateTagCount("activity-alias", 4000, tag)
         validateTagCount("provider", 8000, tag)
-        validateTagCount("activity", 40000, tag)
+        validateTagCount("activity", 30000, tag)
     }
 
     @Test
@@ -465,7 +465,8 @@
             R.styleable.AndroidManifestData_pathAdvancedPattern,
             4000
         )
-        validateTagAttr(tag, "mimeType", R.styleable.AndroidManifestData_mimeType, 512)
+        validateTagAttr(tag, "mimeType", R.styleable.AndroidManifestData_mimeType, 255)
+        validateTagAttr(tag, "mimeGroup", R.styleable.AndroidManifestData_mimeGroup, 1024)
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverPolicyTest.java b/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverPolicyTest.java
index 835ccf0..6fffd75 100644
--- a/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverPolicyTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverPolicyTest.java
@@ -112,7 +112,7 @@
         testServiceDefaultValue_On(ServiceType.NULL);
     }
 
-    @Suppress
+    @Suppress // TODO: b/317823111 - Remove once test fixed.
     @SmallTest
     public void testGetBatterySaverPolicy_PolicyVibration_DefaultValueCorrect() {
         testDefaultValue(
@@ -219,7 +219,7 @@
                 ServiceType.QUICK_DOZE);
     }
 
-    @Suppress
+    @Suppress // TODO: b/317823111 - Remove once test fixed.
     @SmallTest
     public void testUpdateConstants_getCorrectData() {
         mBatterySaverPolicy.updateConstantsLocked(BATTERY_SAVER_CONSTANTS, "");
@@ -327,6 +327,7 @@
         }
     }
 
+    @Suppress // TODO: b/317823111 - Remove once test fixed.
     public void testSetPolicyLevel_Adaptive() {
         mBatterySaverPolicy.setPolicyLevel(POLICY_LEVEL_ADAPTIVE);
 
diff --git a/services/tests/servicestests/src/com/android/server/utils/AnrTimerTest.java b/services/tests/servicestests/src/com/android/server/utils/AnrTimerTest.java
index 861d14a..6c085e0 100644
--- a/services/tests/servicestests/src/com/android/server/utils/AnrTimerTest.java
+++ b/services/tests/servicestests/src/com/android/server/utils/AnrTimerTest.java
@@ -23,17 +23,21 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
+import android.util.Log;
 
 import android.platform.test.annotations.Presubmit;
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.annotations.GuardedBy;
 
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
 import org.junit.runners.Parameterized.Parameters;
 
+import java.io.PrintWriter;
+import java.io.StringWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -45,6 +49,9 @@
 @RunWith(Parameterized.class)
 public class AnrTimerTest {
 
+    // A log tag.
+    private static final String TAG = "AnrTimerTest";
+
     // The commonly used message timeout key.
     private static final int MSG_TIMEOUT = 1;
 
@@ -63,9 +70,7 @@
         }
     }
 
-    /**
-     * The test handler is a self-contained object for a single test.
-     */
+    /** The test helper is a self-contained object for a single test. */
     private static class Helper {
         final Object mLock = new Object();
 
@@ -114,7 +119,7 @@
     /**
      * Force AnrTimer to use the test parameter for the feature flag.
      */
-    class TestInjector extends AnrTimer.Injector {
+    private class TestInjector extends AnrTimer.Injector {
         @Override
         boolean anrTimerServiceEnabled() {
             return mEnabled;
@@ -124,9 +129,9 @@
     /**
      * An instrumented AnrTimer.
      */
-    private static class TestAnrTimer extends AnrTimer<TestArg> {
+    private class TestAnrTimer extends AnrTimer<TestArg> {
         private TestAnrTimer(Handler h, int key, String tag) {
-            super(h, key, tag);
+            super(h, key, tag, false, new TestInjector());
         }
 
         TestAnrTimer(Helper helper) {
@@ -173,35 +178,103 @@
     @Test
     public void testSimpleTimeout() throws Exception {
         Helper helper = new Helper(1);
-        TestAnrTimer timer = new TestAnrTimer(helper);
-        TestArg t = new TestArg(1, 1);
-        timer.start(t, 10);
-        // Delivery is immediate but occurs on a different thread.
-        assertTrue(helper.await(5000));
-        TestArg[] result = helper.messages(1);
-        validate(t, result[0]);
+        try (TestAnrTimer timer = new TestAnrTimer(helper)) {
+            // One-time check that the injector is working as expected.
+            assertEquals(mEnabled, timer.serviceEnabled());
+            TestArg t = new TestArg(1, 1);
+            timer.start(t, 10);
+            // Delivery is immediate but occurs on a different thread.
+            assertTrue(helper.await(5000));
+            TestArg[] result = helper.messages(1);
+            validate(t, result[0]);
+        }
     }
 
     /**
-     * Verify that if three timers are scheduled, they are delivered in time order.
+     * Verify that a restarted timer is delivered exactly once.  The initial timer value is very
+     * large, to ensure it does not expire before the timer can be restarted.
+     */
+    @Test
+    public void testTimerRestart() throws Exception {
+        Helper helper = new Helper(1);
+        try (TestAnrTimer timer = new TestAnrTimer(helper)) {
+            TestArg t = new TestArg(1, 1);
+            timer.start(t, 10000);
+            // Briefly pause.
+            assertFalse(helper.await(10));
+            timer.start(t, 10);
+            // Delivery is immediate but occurs on a different thread.
+            assertTrue(helper.await(5000));
+            TestArg[] result = helper.messages(1);
+            validate(t, result[0]);
+        }
+    }
+
+    /**
+     * Verify that a restarted timer is delivered exactly once.  The initial timer value is very
+     * large, to ensure it does not expire before the timer can be restarted.
+     */
+    @Test
+    public void testTimerZero() throws Exception {
+        Helper helper = new Helper(1);
+        try (TestAnrTimer timer = new TestAnrTimer(helper)) {
+            TestArg t = new TestArg(1, 1);
+            timer.start(t, 0);
+            // Delivery is immediate but occurs on a different thread.
+            assertTrue(helper.await(5000));
+            TestArg[] result = helper.messages(1);
+            validate(t, result[0]);
+        }
+    }
+
+    /**
+     * Verify that if three timers are scheduled on a single AnrTimer, they are delivered in time
+     * order.
      */
     @Test
     public void testMultipleTimers() throws Exception {
         // Expect three messages.
         Helper helper = new Helper(3);
-        TestAnrTimer timer = new TestAnrTimer(helper);
         TestArg t1 = new TestArg(1, 1);
         TestArg t2 = new TestArg(1, 2);
         TestArg t3 = new TestArg(1, 3);
-        timer.start(t1, 50);
-        timer.start(t2, 60);
-        timer.start(t3, 40);
-        // Delivery is immediate but occurs on a different thread.
-        assertTrue(helper.await(5000));
-        TestArg[] result = helper.messages(3);
-        validate(t3, result[0]);
-        validate(t1, result[1]);
-        validate(t2, result[2]);
+        try (TestAnrTimer timer = new TestAnrTimer(helper)) {
+            timer.start(t1, 50);
+            timer.start(t2, 60);
+            timer.start(t3, 40);
+            // Delivery is immediate but occurs on a different thread.
+            assertTrue(helper.await(5000));
+            TestArg[] result = helper.messages(3);
+            validate(t3, result[0]);
+            validate(t1, result[1]);
+            validate(t2, result[2]);
+        }
+    }
+
+    /**
+     * Verify that if three timers are scheduled on three separate AnrTimers, they are delivered
+     * in time order.
+     */
+    @Test
+    public void testMultipleServices() throws Exception {
+        // Expect three messages.
+        Helper helper = new Helper(3);
+        TestArg t1 = new TestArg(1, 1);
+        TestArg t2 = new TestArg(1, 2);
+        TestArg t3 = new TestArg(1, 3);
+        try (TestAnrTimer x1 = new TestAnrTimer(helper);
+             TestAnrTimer x2 = new TestAnrTimer(helper);
+             TestAnrTimer x3 = new TestAnrTimer(helper)) {
+            x1.start(t1, 50);
+            x2.start(t2, 60);
+            x3.start(t3, 40);
+            // Delivery is immediate but occurs on a different thread.
+            assertTrue(helper.await(5000));
+            TestArg[] result = helper.messages(3);
+            validate(t3, result[0]);
+            validate(t1, result[1]);
+            validate(t2, result[2]);
+        }
     }
 
     /**
@@ -211,20 +284,109 @@
     public void testCancelTimer() throws Exception {
         // Expect two messages.
         Helper helper = new Helper(2);
-        TestAnrTimer timer = new TestAnrTimer(helper);
         TestArg t1 = new TestArg(1, 1);
         TestArg t2 = new TestArg(1, 2);
         TestArg t3 = new TestArg(1, 3);
-        timer.start(t1, 50);
-        timer.start(t2, 60);
-        timer.start(t3, 40);
-        // Briefly pause.
-        assertFalse(helper.await(10));
-        timer.cancel(t1);
-        // Delivery is immediate but occurs on a different thread.
-        assertTrue(helper.await(5000));
-        TestArg[] result = helper.messages(2);
-        validate(t3, result[0]);
-        validate(t2, result[1]);
+        try (TestAnrTimer timer = new TestAnrTimer(helper)) {
+            timer.start(t1, 50);
+            timer.start(t2, 60);
+            timer.start(t3, 40);
+            // Briefly pause.
+            assertFalse(helper.await(10));
+            timer.cancel(t1);
+            // Delivery is immediate but occurs on a different thread.
+            assertTrue(helper.await(5000));
+            TestArg[] result = helper.messages(2);
+            validate(t3, result[0]);
+            validate(t2, result[1]);
+        }
+    }
+
+    /**
+     * Return the dump string.
+     */
+    private String getDumpOutput() {
+        StringWriter sw = new StringWriter();
+        PrintWriter pw = new PrintWriter(sw);
+        AnrTimer.dump(pw, true, new TestInjector());
+        pw.close();
+        return sw.getBuffer().toString();
+    }
+
+    /**
+     * Verify the dump output.
+     */
+    @Test
+    public void testDumpOutput() throws Exception {
+        String r1 = getDumpOutput();
+        assertEquals(false, r1.contains("timer:"));
+
+        Helper helper = new Helper(2);
+        TestArg t1 = new TestArg(1, 1);
+        TestArg t2 = new TestArg(1, 2);
+        TestArg t3 = new TestArg(1, 3);
+        try (TestAnrTimer timer = new TestAnrTimer(helper)) {
+            timer.start(t1, 5000);
+            timer.start(t2, 5000);
+            timer.start(t3, 5000);
+
+            String r2 = getDumpOutput();
+            // There are timers in the list if and only if the feature is enabled.
+            final boolean expected = mEnabled;
+            assertEquals(expected, r2.contains("timer:"));
+        }
+
+        String r3 = getDumpOutput();
+        assertEquals(false, r3.contains("timer:"));
+    }
+
+    /**
+     * Verify that GC works as expected.  This test will almost certainly be flaky, since it
+     * relies on the finalizers running, which is a best-effort on the part of the JVM.
+     * Therefore, the test is marked @Ignore.  Remove that annotation to run the test locally.
+     */
+    @Ignore
+    @Test
+    public void testGarbageCollection() throws Exception {
+        if (!mEnabled) return;
+
+        String r1 = getDumpOutput();
+        assertEquals(false, r1.contains("timer:"));
+
+        Helper helper = new Helper(2);
+        TestArg t1 = new TestArg(1, 1);
+        TestArg t2 = new TestArg(1, 2);
+        TestArg t3 = new TestArg(1, 3);
+        // The timer is explicitly not closed.  It is, however, scoped to the next block.
+        {
+            TestAnrTimer timer = new TestAnrTimer(helper);
+            timer.start(t1, 5000);
+            timer.start(t2, 5000);
+            timer.start(t3, 5000);
+
+            String r2 = getDumpOutput();
+            // There are timers in the list if and only if the feature is enabled.
+            final boolean expected = mEnabled;
+            assertEquals(expected, r2.contains("timer:"));
+        }
+
+        // Try to make finalizers run.  The timer object above should be a candidate.  Finalizers
+        // are run on their own thread, so pause this thread to give that thread some time.
+        String r3 = getDumpOutput();
+        for (int i = 0; i < 10 && r3.contains("timer:"); i++) {
+            Log.i(TAG, "requesting finalization " + i);
+            System.gc();
+            System.runFinalization();
+            Thread.sleep(4 * 1000);
+            r3 = getDumpOutput();
+        }
+
+        // The timer was not explicitly closed but it should have been implicitly closed by GC.
+        assertEquals(false, r3.contains("timer:"));
+    }
+
+    // TODO: [b/302724778] Remove manual JNI load
+    static {
+        System.loadLibrary("servicestestjni");
     }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
index 42ad73a..8622488 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
@@ -158,6 +158,9 @@
         when(mAudioManager.isAudioFocusExclusive()).thenReturn(false);
         when(mAudioManager.getRingtonePlayer()).thenReturn(mRingtonePlayer);
         when(mAudioManager.getStreamVolume(anyInt())).thenReturn(10);
+        // consistent with focus not exclusive and volume not muted
+        when(mAudioManager.shouldNotificationSoundPlay(any(AudioAttributes.class)))
+                .thenReturn(true);
         when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
         when(mAudioManager.getFocusRampTimeMs(anyInt(), any(AudioAttributes.class))).thenReturn(50);
         when(mUsageStats.isAlertRateLimited(any())).thenReturn(false);
@@ -869,6 +872,7 @@
         // the phone is quiet
         when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_VIBRATE);
         when(mAudioManager.getStreamVolume(anyInt())).thenReturn(0);
+        when(mAudioManager.shouldNotificationSoundPlay(any())).thenReturn(false);
 
         mService.buzzBeepBlinkLocked(r);
 
@@ -886,6 +890,8 @@
         // the phone is quiet
         when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_VIBRATE);
         when(mAudioManager.getStreamVolume(anyInt())).thenReturn(1);
+        // all streams at 1 means no muting from audio framework
+        when(mAudioManager.shouldNotificationSoundPlay(any())).thenReturn(true);
 
         mService.buzzBeepBlinkLocked(r);
 
@@ -904,6 +910,7 @@
         // the phone is quiet
         when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_VIBRATE);
         when(mAudioManager.getStreamVolume(anyInt())).thenReturn(0);
+        when(mAudioManager.shouldNotificationSoundPlay(any())).thenReturn(false);
 
         mService.buzzBeepBlinkLocked(r);
 
@@ -924,6 +931,7 @@
         // the phone is quiet
         when(mAudioManager.getStreamVolume(anyInt())).thenReturn(0);
         when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_VIBRATE);
+        when(mAudioManager.shouldNotificationSoundPlay(any())).thenReturn(false);
 
         mService.buzzBeepBlinkLocked(r);
 
@@ -1195,6 +1203,7 @@
         // the phone is quiet
         when(mAudioManager.getStreamVolume(anyInt())).thenReturn(0);
         when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_VIBRATE);
+        when(mAudioManager.shouldNotificationSoundPlay(any())).thenReturn(false);
 
         mService.buzzBeepBlinkLocked(r);
         verifyDelayedVibrate(mService.getVibratorHelper().createFallbackVibration(false));
@@ -1923,6 +1932,7 @@
         NotificationRecord r = getBuzzyBeepyNotification();
         when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_SILENT);
         when(mAudioManager.getStreamVolume(anyInt())).thenReturn(0);
+        when(mAudioManager.shouldNotificationSoundPlay(any())).thenReturn(false);
 
         mService.buzzBeepBlinkLocked(r);
 
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java
index cf8548c..bfd2df2d 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java
@@ -19,17 +19,21 @@
 import static android.app.Notification.GROUP_ALERT_ALL;
 import static android.app.Notification.GROUP_ALERT_CHILDREN;
 import static android.app.Notification.GROUP_ALERT_SUMMARY;
+import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
 import static android.app.NotificationManager.IMPORTANCE_HIGH;
 import static android.app.NotificationManager.IMPORTANCE_LOW;
 import static android.app.NotificationManager.IMPORTANCE_MIN;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS;
 import static android.media.AudioAttributes.USAGE_NOTIFICATION;
 import static android.media.AudioAttributes.USAGE_NOTIFICATION_RINGTONE;
+
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertNull;
 import static junit.framework.Assert.assertTrue;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
+
 import static org.mockito.ArgumentMatchers.anyFloat;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -178,6 +182,8 @@
         when(mAudioManager.getStreamVolume(anyInt())).thenReturn(10);
         when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
         when(mAudioManager.getFocusRampTimeMs(anyInt(), any(AudioAttributes.class))).thenReturn(50);
+        when(mAudioManager.shouldNotificationSoundPlay(any(AudioAttributes.class)))
+                .thenReturn(true);
         when(mUsageStats.isAlertRateLimited(any())).thenReturn(false);
         when(mVibrator.hasFrequencyControl()).thenReturn(false);
         when(mKeyguardManager.isDeviceLocked(anyInt())).thenReturn(false);
@@ -195,7 +201,8 @@
         // TODO (b/291907312): remove feature flag
         mSetFlagsRule.enableFlags(Flags.FLAG_REFACTOR_ATTENTION_HELPER);
         // Disable feature flags by default. Tests should enable as needed.
-        mSetFlagsRule.disableFlags(Flags.FLAG_POLITE_NOTIFICATIONS, Flags.FLAG_EXPIRE_BITMAPS);
+        mSetFlagsRule.disableFlags(Flags.FLAG_POLITE_NOTIFICATIONS,
+                Flags.FLAG_CROSS_APP_POLITE_NOTIFICATIONS, Flags.FLAG_VIBRATE_WHILE_UNLOCKED);
 
         mService = spy(new NotificationManagerService(getContext(), mNotificationRecordLogger,
             mNotificationInstanceIdSequence));
@@ -364,10 +371,20 @@
     }
 
     private NotificationRecord getNotificationRecord(int id,
-        boolean insistent, boolean once,
-        boolean noisy, boolean buzzy, boolean lights, boolean defaultVibration,
-        boolean defaultSound, boolean defaultLights, String groupKey, int groupAlertBehavior,
-        boolean isLeanback, UserHandle userHandle) {
+            boolean insistent, boolean once,
+            boolean noisy, boolean buzzy, boolean lights, boolean defaultVibration,
+            boolean defaultSound, boolean defaultLights, String groupKey, int groupAlertBehavior,
+            boolean isLeanback, UserHandle userHandle) {
+        return getNotificationRecord(id, insistent, once, noisy, buzzy, lights, defaultVibration,
+                defaultSound, defaultLights, groupKey, groupAlertBehavior, isLeanback, userHandle,
+                mPkg);
+    }
+
+    private NotificationRecord getNotificationRecord(int id,
+            boolean insistent, boolean once,
+            boolean noisy, boolean buzzy, boolean lights, boolean defaultVibration,
+            boolean defaultSound, boolean defaultLights, String groupKey, int groupAlertBehavior,
+            boolean isLeanback, UserHandle userHandle, String packageName) {
 
         final Builder builder = new Builder(getContext())
             .setContentTitle("foo")
@@ -427,8 +444,8 @@
         when(packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK))
             .thenReturn(isLeanback);
 
-        StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, id, mTag, mUid,
-            mPid, n, userHandle, null, System.currentTimeMillis());
+        StatusBarNotification sbn = new StatusBarNotification(packageName, packageName, id, mTag,
+                mUid, mPid, n, userHandle, null, System.currentTimeMillis());
         NotificationRecord r = new NotificationRecord(context, sbn, mChannel);
         mService.addNotification(r);
         return r;
@@ -915,6 +932,8 @@
         // the phone is quiet
         when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_VIBRATE);
         when(mAudioManager.getStreamVolume(anyInt())).thenReturn(0);
+        when(mAudioManager.shouldNotificationSoundPlay(any(AudioAttributes.class)))
+                .thenReturn(false);
 
         mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
 
@@ -932,6 +951,8 @@
         // the phone is quiet
         when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_VIBRATE);
         when(mAudioManager.getStreamVolume(anyInt())).thenReturn(1);
+        // all streams at 1 means no muting from audio framework
+        when(mAudioManager.shouldNotificationSoundPlay(any())).thenReturn(true);
 
         mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
 
@@ -950,6 +971,7 @@
         // the phone is quiet
         when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_VIBRATE);
         when(mAudioManager.getStreamVolume(anyInt())).thenReturn(0);
+        when(mAudioManager.shouldNotificationSoundPlay(any())).thenReturn(false);
 
         mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
 
@@ -971,6 +993,7 @@
         // the phone is quiet
         when(mAudioManager.getStreamVolume(anyInt())).thenReturn(0);
         when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_VIBRATE);
+        when(mAudioManager.shouldNotificationSoundPlay(any())).thenReturn(false);
 
         mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
 
@@ -1243,6 +1266,7 @@
         // the phone is quiet
         when(mAudioManager.getStreamVolume(anyInt())).thenReturn(0);
         when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_VIBRATE);
+        when(mAudioManager.shouldNotificationSoundPlay(any())).thenReturn(false);
 
         mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
         verifyDelayedVibrate(mAttentionHelper.getVibratorHelper().createFallbackVibration(false));
@@ -1973,6 +1997,7 @@
         NotificationRecord r = getBuzzyBeepyNotification();
         when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_SILENT);
         when(mAudioManager.getStreamVolume(anyInt())).thenReturn(0);
+        when(mAudioManager.shouldNotificationSoundPlay(any())).thenReturn(false);
 
         mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
 
@@ -1990,7 +2015,6 @@
     public void testBeepVolume_politeNotif() throws Exception {
         mSetFlagsRule.enableFlags(Flags.FLAG_POLITE_NOTIFICATIONS);
         TestableFlagResolver flagResolver = new TestableFlagResolver();
-        flagResolver.setFlagOverride(NotificationFlags.NOTIF_COOLDOWN_RULE, "rule1");
         flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME1, 50);
         flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME2, 0);
         initAttentionHelper(flagResolver);
@@ -2015,13 +2039,11 @@
         assertNotEquals(-1, r.getLastAudiblyAlertedMs());
     }
 
-    // TODO b/270456865: Only one of the two strategies will be released.
-    //  The other one need to be removed
     @Test
-    public void testBeepVolume_politeNotif_Strategy2() throws Exception {
+    public void testBeepVolume_politeNotif_GlobalStrategy() throws Exception {
         mSetFlagsRule.enableFlags(Flags.FLAG_POLITE_NOTIFICATIONS);
+        mSetFlagsRule.enableFlags(Flags.FLAG_CROSS_APP_POLITE_NOTIFICATIONS);
         TestableFlagResolver flagResolver = new TestableFlagResolver();
-        flagResolver.setFlagOverride(NotificationFlags.NOTIF_COOLDOWN_RULE, "rule2");
         flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME1, 50);
         flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME2, 0);
         initAttentionHelper(flagResolver);
@@ -2032,14 +2054,58 @@
         mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
         Mockito.reset(mRingtonePlayer);
 
-        // update should beep at 0% volume
-        r.isUpdate = true;
-        mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+        // Use different package for next notifications
+        NotificationRecord r2 = getNotificationRecord(mId, false /* insistent */, false /* once */,
+                true /* noisy */, false /* buzzy*/, false /* lights */, true, true,
+                false, null, Notification.GROUP_ALERT_ALL, false, mUser, "anotherPkg");
+
+        // update should beep at 50% volume
+        mAttentionHelper.buzzBeepBlinkLocked(r2, DEFAULT_SIGNALS);
+        verifyBeepVolume(0.5f);
+
+        // Use different package for next notifications
+        NotificationRecord r3 = getNotificationRecord(mId, false /* insistent */, false /* once */,
+                true /* noisy */, false /* buzzy*/, false /* lights */, true, true,
+                false, null, Notification.GROUP_ALERT_ALL, false, mUser, "yetAnotherPkg");
+
+        // 2nd update should beep at 0% volume
+        Mockito.reset(mRingtonePlayer);
+        mAttentionHelper.buzzBeepBlinkLocked(r3, DEFAULT_SIGNALS);
         verifyBeepVolume(0.0f);
 
+        verify(mAccessibilityService, times(3)).sendAccessibilityEvent(any(), anyInt());
+        assertNotEquals(-1, r.getLastAudiblyAlertedMs());
+    }
+
+    @Test
+    public void testBeepVolume_politeNotif_GlobalStrategy_ChannelHasUserSound() throws Exception {
+        mSetFlagsRule.enableFlags(Flags.FLAG_POLITE_NOTIFICATIONS);
+        mSetFlagsRule.enableFlags(Flags.FLAG_CROSS_APP_POLITE_NOTIFICATIONS);
+        TestableFlagResolver flagResolver = new TestableFlagResolver();
+        flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME1, 50);
+        flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME2, 0);
+        initAttentionHelper(flagResolver);
+
+        NotificationRecord r = getBeepyNotification();
+
+        // set up internal state
+        mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+        Mockito.reset(mRingtonePlayer);
+
+        // Use package with user-set sounds for next notifications
+        mChannel = new NotificationChannel("test2", "test2", IMPORTANCE_DEFAULT);
+        mChannel.lockFields(NotificationChannel.USER_LOCKED_SOUND);
+        NotificationRecord r2 = getNotificationRecord(mId, false /* insistent */, false /* once */,
+                true /* noisy */, false /* buzzy*/, false /* lights */, true, true,
+                false, null, Notification.GROUP_ALERT_ALL, false, mUser, "anotherPkg");
+
+        // update should beep at 100% volume
+        mAttentionHelper.buzzBeepBlinkLocked(r2, DEFAULT_SIGNALS);
+        verifyBeepVolume(1.0f);
+
         // 2nd update should beep at 50% volume
         Mockito.reset(mRingtonePlayer);
-        mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+        mAttentionHelper.buzzBeepBlinkLocked(r2, DEFAULT_SIGNALS);
         verifyBeepVolume(0.5f);
 
         verify(mAccessibilityService, times(3)).sendAccessibilityEvent(any(), anyInt());
@@ -2047,10 +2113,101 @@
     }
 
     @Test
+    public void testBeepVolume_politeNotif_applyPerApp() throws Exception {
+        mSetFlagsRule.enableFlags(Flags.FLAG_POLITE_NOTIFICATIONS);
+        mSetFlagsRule.disableFlags(Flags.FLAG_CROSS_APP_POLITE_NOTIFICATIONS);
+        TestableFlagResolver flagResolver = new TestableFlagResolver();
+        flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME1, 50);
+        flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME2, 0);
+        // NOTIFICATION_COOLDOWN_ALL setting is enabled
+        Settings.System.putInt(getContext().getContentResolver(),
+                Settings.System.NOTIFICATION_COOLDOWN_ALL, 1);
+        initAttentionHelper(flagResolver);
+
+        NotificationRecord r = getBeepyNotification();
+
+        // set up internal state
+        mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+        Mockito.reset(mRingtonePlayer);
+
+        // Use different channel for next notifications
+        mChannel = new NotificationChannel("test2", "test2", IMPORTANCE_DEFAULT);
+
+        // update should beep at 50% volume
+        NotificationRecord r2 = getBeepyNotification();
+        mAttentionHelper.buzzBeepBlinkLocked(r2, DEFAULT_SIGNALS);
+        verifyBeepVolume(0.5f);
+
+        // 2nd update should beep at 0% volume
+        Mockito.reset(mRingtonePlayer);
+        mAttentionHelper.buzzBeepBlinkLocked(r2, DEFAULT_SIGNALS);
+        verifyBeepVolume(0.0f);
+
+        // Use different package for next notifications
+        NotificationRecord r3 = getNotificationRecord(mId, false /* insistent */, false /* once */,
+                true /* noisy */, false /* buzzy*/, false /* lights */, true, true,
+                false, null, Notification.GROUP_ALERT_ALL, false, mUser, "anotherPkg");
+
+        // Update from new package should beep at 100% volume
+        Mockito.reset(mRingtonePlayer);
+        mAttentionHelper.buzzBeepBlinkLocked(r3, DEFAULT_SIGNALS);
+        verifyBeepVolume(1.0f);
+
+        verify(mAccessibilityService, times(4)).sendAccessibilityEvent(any(), anyInt());
+        assertNotEquals(-1, r.getLastAudiblyAlertedMs());
+    }
+
+    @Test
+    public void testBeepVolume_politeNotif_applyPerApp_ChannelHasUserSound() throws Exception {
+        mSetFlagsRule.enableFlags(Flags.FLAG_POLITE_NOTIFICATIONS);
+        mSetFlagsRule.disableFlags(Flags.FLAG_CROSS_APP_POLITE_NOTIFICATIONS);
+        TestableFlagResolver flagResolver = new TestableFlagResolver();
+        flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME1, 50);
+        flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME2, 0);
+        // NOTIFICATION_COOLDOWN_ALL setting is enabled
+        Settings.System.putInt(getContext().getContentResolver(),
+                Settings.System.NOTIFICATION_COOLDOWN_ALL, 1);
+        initAttentionHelper(flagResolver);
+
+        NotificationRecord r = getBeepyNotification();
+
+        // set up internal state
+        mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+        Mockito.reset(mRingtonePlayer);
+
+        // Use different channel for next notifications
+        mChannel = new NotificationChannel("test2", "test2", IMPORTANCE_DEFAULT);
+        mChannel.lockFields(NotificationChannel.USER_LOCKED_SOUND);
+
+        // update should beep at 100% volume
+        NotificationRecord r2 = getBeepyNotification();
+        mAttentionHelper.buzzBeepBlinkLocked(r2, DEFAULT_SIGNALS);
+        verifyBeepVolume(1.0f);
+
+        // 2nd update should beep at 50% volume
+        Mockito.reset(mRingtonePlayer);
+        mAttentionHelper.buzzBeepBlinkLocked(r2, DEFAULT_SIGNALS);
+        verifyBeepVolume(0.5f);
+
+        // Use different package for next notifications
+        mChannel = new NotificationChannel("test3", "test3", IMPORTANCE_DEFAULT);
+        NotificationRecord r3 = getNotificationRecord(mId, false /* insistent */, false /* once */,
+                true /* noisy */, false /* buzzy*/, false /* lights */, true, true,
+                false, null, Notification.GROUP_ALERT_ALL, false, mUser, "anotherPkg");
+
+        // Update from new package should beep at 100% volume
+        Mockito.reset(mRingtonePlayer);
+        mAttentionHelper.buzzBeepBlinkLocked(r3, DEFAULT_SIGNALS);
+        verifyBeepVolume(1.0f);
+
+        verify(mAccessibilityService, times(4)).sendAccessibilityEvent(any(), anyInt());
+        assertNotEquals(-1, r.getLastAudiblyAlertedMs());
+    }
+
+    @Test
     public void testVibrationIntensity_politeNotif() throws Exception {
         mSetFlagsRule.enableFlags(Flags.FLAG_POLITE_NOTIFICATIONS);
         TestableFlagResolver flagResolver = new TestableFlagResolver();
-        flagResolver.setFlagOverride(NotificationFlags.NOTIF_COOLDOWN_RULE, "rule1");
         flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME1, 50);
         flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME2, 0);
         initAttentionHelper(flagResolver);
@@ -2076,37 +2233,9 @@
     }
 
     @Test
-    public void testVibrationIntensity_politeNotif_Strategy2() throws Exception {
-        mSetFlagsRule.enableFlags(Flags.FLAG_POLITE_NOTIFICATIONS);
-        TestableFlagResolver flagResolver = new TestableFlagResolver();
-        flagResolver.setFlagOverride(NotificationFlags.NOTIF_COOLDOWN_RULE, "rule2");
-        flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME1, 50);
-        flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME2, 0);
-        initAttentionHelper(flagResolver);
-
-        NotificationRecord r = getBuzzyBeepyNotification();
-
-        // set up internal state
-        mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
-
-        VibratorHelper vibratorHelper = mAttentionHelper.getVibratorHelper();
-        Mockito.reset(vibratorHelper);
-
-        // update should buzz at 0% intensity
-        r.isUpdate = true;
-        mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
-
-        verify(vibratorHelper, times(1)).scale(any(), eq(0.0f));
-        Mockito.reset(vibratorHelper);
-
-        // 2nd update should buzz at 50% intensity
-        mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
-        verify(vibratorHelper, times(1)).scale(any(), eq(0.5f));
-    }
-
-    @Test
     public void testBuzzOnlyOnScreenUnlock_politeNotif() throws Exception {
         mSetFlagsRule.enableFlags(Flags.FLAG_POLITE_NOTIFICATIONS);
+        mSetFlagsRule.enableFlags(Flags.FLAG_VIBRATE_WHILE_UNLOCKED);
         TestableFlagResolver flagResolver = new TestableFlagResolver();
 
         // When NOTIFICATION_COOLDOWN_VIBRATE_UNLOCKED setting is enabled
@@ -2161,7 +2290,6 @@
     public void testBeepVolume_politeNotif_workProfile() throws Exception {
         mSetFlagsRule.enableFlags(Flags.FLAG_POLITE_NOTIFICATIONS);
         TestableFlagResolver flagResolver = new TestableFlagResolver();
-        flagResolver.setFlagOverride(NotificationFlags.NOTIF_COOLDOWN_RULE, "rule1");
         flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME1, 50);
         flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME2, 0);
 
@@ -2202,7 +2330,6 @@
     public void testBeepVolume_politeNotif_workProfile_disabled() throws Exception {
         mSetFlagsRule.enableFlags(Flags.FLAG_POLITE_NOTIFICATIONS);
         TestableFlagResolver flagResolver = new TestableFlagResolver();
-        flagResolver.setFlagOverride(NotificationFlags.NOTIF_COOLDOWN_RULE, "rule1");
         flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME1, 50);
         flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME2, 0);
 
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryManagerTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryManagerTest.java
index 5892793..9b25f58 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryManagerTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryManagerTest.java
@@ -170,6 +170,10 @@
         Settings.Secure.putIntForUser(getContext().getContentResolver(),
                 Settings.Secure.NOTIFICATION_HISTORY_ENABLED, 0, USER_SYSTEM);
         mHistoryManager.mSettingsObserver.update(null, USER_SYSTEM);
+        // fake cloned settings to profile
+        Settings.Secure.putIntForUser(getContext().getContentResolver(),
+                Settings.Secure.NOTIFICATION_HISTORY_ENABLED, 0, mProfileId);
+        mHistoryManager.mSettingsObserver.update(null, mProfileId);
 
         // unlock user, verify that history is disabled for self and profile
         mHistoryManager.onUserUnlocked(USER_SYSTEM);
@@ -179,6 +183,36 @@
         assertThat(mHistoryManager.doesHistoryExistForUser(mProfileId)).isFalse();
         verify(mDb, times(2)).disableHistory();
     }
+    @Test
+    public void testAddProfile_historyEnabledInPrimary() {
+        // create a history
+        mHistoryManager.onUserUnlocked(MIN_SECONDARY_USER_ID);
+        assertThat(mHistoryManager.doesHistoryExistForUser(MIN_SECONDARY_USER_ID)).isTrue();
+
+        // fake Settings#CLONE_TO_MANAGED_PROFILE
+        int newProfileId = 99;
+        Settings.Secure.putIntForUser(getContext().getContentResolver(),
+                Settings.Secure.NOTIFICATION_HISTORY_ENABLED, 1, newProfileId);
+        mUsers = new ArrayList<>();
+        UserInfo userFullSecondary = new UserInfo();
+        userFullSecondary.id = MIN_SECONDARY_USER_ID;
+        mUsers.add(userFullSecondary);
+        UserInfo userProfile = new UserInfo();
+        userProfile.id = newProfileId;
+        mUsers.add(userProfile);
+        when(mUserManager.getUsers()).thenReturn(mUsers);
+
+        mProfiles = new int[] {MIN_SECONDARY_USER_ID, userProfile.id};
+        when(mUserManager.getProfileIds(MIN_SECONDARY_USER_ID, true)).thenReturn(mProfiles);
+        when(mUserManager.getProfileIds(userProfile.id, true))
+                .thenReturn(new int[] {userProfile.id});
+        when(mUserManager.getProfileParent(userProfile.id)).thenReturn(userFullSecondary);
+
+        // add profile
+        mHistoryManager.onUserAdded(newProfileId);
+        mHistoryManager.onUserUnlocked(newProfileId);
+        assertThat(mHistoryManager.doesHistoryExistForUser(newProfileId)).isTrue();
+    }
 
     @Test
     public void testOnUserUnlocked_historyDisabledThenEnabled() {
@@ -575,4 +609,14 @@
 
         assertThat(mHistoryManager.isHistoryEnabled(USER_SYSTEM)).isFalse();
     }
+    @Test
+    public void testDelayedPackageRemoval_userLocked() {
+        String pkg = "pkg";
+        mHistoryManager.onPackageRemoved(USER_SYSTEM, pkg);
+        mHistoryManager.onUserUnlocked(USER_SYSTEM);
+        mHistoryManager.onUserStopped(USER_SYSTEM);
+        mHistoryManager.onPackageRemoved(USER_SYSTEM, pkg);
+
+        // no exception, yay
+    }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
index 53ca704..bf850cf 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
@@ -44,8 +44,10 @@
 import android.content.pm.ShortcutInfo;
 import android.graphics.Bitmap;
 import android.graphics.drawable.Icon;
+import android.os.BadParcelableException;
 import android.os.Binder;
 import android.os.Build;
+import android.os.DeadObjectException;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.UserHandle;
@@ -99,6 +101,20 @@
     }
 
     @Test
+    public void testGetActiveNotifications_handlesBinderErrors() throws RemoteException {
+        TestListenerService service = new TestListenerService();
+        INotificationManager noMan = service.getNoMan();
+        when(noMan.getActiveNotificationsFromListener(any(), any(), anyInt()))
+                .thenThrow(new BadParcelableException("oops", new DeadObjectException("")));
+
+        assertNotNull(service.getActiveNotifications());
+        assertNotNull(service.getActiveNotifications(NotificationListenerService.TRIM_FULL));
+        assertNotNull(service.getActiveNotifications(new String[0]));
+        assertNull(service.getActiveNotifications(
+                new String[0], NotificationListenerService.TRIM_LIGHT));
+    }
+
+    @Test
     public void testGetActiveNotifications_preP_mapsExtraPeople() throws RemoteException {
         TestListenerService service = new TestListenerService();
         service.attachBaseContext(mContext);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 9408a8b..c1f35cc 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -16,11 +16,14 @@
 
 package com.android.server.notification;
 
+import static android.Manifest.permission.CONTROL_KEYGUARD_SECURE_NOTIFICATIONS;
+import static android.Manifest.permission.STATUS_BAR_SERVICE;
 import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
 import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE;
 import static android.app.ActivityManagerInternal.ServiceNotificationPolicy.NOT_FOREGROUND_SERVICE;
 import static android.app.ActivityManagerInternal.ServiceNotificationPolicy.SHOW_IMMEDIATELY;
 import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+import static android.app.Flags.FLAG_KEYGUARD_PRIVATE_NOTIFICATIONS;
 import static android.app.Notification.EXTRA_ALLOW_DURING_SETUP;
 import static android.app.Notification.EXTRA_PICTURE;
 import static android.app.Notification.EXTRA_PICTURE_ICON;
@@ -60,6 +63,8 @@
 import static android.app.PendingIntent.FLAG_IMMUTABLE;
 import static android.app.PendingIntent.FLAG_MUTABLE;
 import static android.app.PendingIntent.FLAG_ONE_SHOT;
+import static android.app.StatusBarManager.ACTION_KEYGUARD_PRIVATE_NOTIFICATIONS_CHANGED;
+import static android.app.StatusBarManager.EXTRA_KM_PRIVATE_NOTIFS_ALLOWED;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
 import static android.content.pm.PackageManager.FEATURE_TELECOM;
 import static android.content.pm.PackageManager.FEATURE_WATCH;
@@ -106,6 +111,7 @@
 import static com.android.server.notification.NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_POSTED;
 import static com.android.server.notification.NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_UPDATED;
 
+import static com.google.common.collect.Iterables.getOnlyElement;
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.Truth.assertWithMessage;
 
@@ -118,6 +124,7 @@
 import static junit.framework.Assert.assertTrue;
 import static junit.framework.Assert.fail;
 
+import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertThrows;
 import static org.mockito.ArgumentMatchers.isNull;
 import static org.mockito.Matchers.anyBoolean;
@@ -296,7 +303,6 @@
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.ClassRule;
-import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.TestRule;
@@ -547,6 +553,7 @@
         mContext.addMockSystemService(Context.ALARM_SERVICE, mAlarmManager);
         mContext.addMockSystemService(NotificationManager.class, mMockNm);
 
+        doNothing().when(mContext).sendBroadcast(any(), anyString());
         doNothing().when(mContext).sendBroadcastAsUser(any(), any());
         doNothing().when(mContext).sendBroadcastAsUser(any(), any(), any());
 
@@ -570,6 +577,9 @@
         when(mPackageManagerClient.getPackageUidAsUser(any(), anyInt())).thenReturn(mUid);
         when(mPackageManagerInternal.isSameApp(anyString(), anyInt(), anyInt())).thenAnswer(
                 (Answer<Boolean>) invocation -> {
+                    // TODO: b/317957802 - This is overly broad and basically makes ANY 
+                    //  isSameApp() check pass,  requiring Mockito.reset() for meaningful
+                    //  tests! Make it more precise.
                     Object[] args = invocation.getArguments();
                     return (int) args[1] == mUid;
                 });
@@ -909,7 +919,9 @@
     }
     private ApplicationInfo getApplicationInfo(String pkg, int uid) {
         final ApplicationInfo applicationInfo = new ApplicationInfo();
+        applicationInfo.packageName = pkg;
         applicationInfo.uid = uid;
+        applicationInfo.sourceDir = mContext.getApplicationInfo().sourceDir;
         switch (pkg) {
             case PKG_N_MR1:
                 applicationInfo.targetSdkVersion = Build.VERSION_CODES.N_MR1;
@@ -946,22 +958,24 @@
         mTestNotificationChannel.setAllowBubbles(channelEnabled);
     }
 
-    private void setUpPrefsForHistory(int uid, boolean globalEnabled) throws Exception {
+    private void setUpPrefsForHistory(@UserIdInt int userId, boolean globalEnabled)
+            throws Exception {
         initNMS(SystemService.PHASE_ACTIVITY_MANAGER_READY);
 
         // Sets NOTIFICATION_HISTORY_ENABLED setting for calling process uid
         Settings.Secure.putIntForUser(mContext.getContentResolver(),
-                Settings.Secure.NOTIFICATION_HISTORY_ENABLED, globalEnabled ? 1 : 0, uid);
+                Settings.Secure.NOTIFICATION_HISTORY_ENABLED, globalEnabled ? 1 : 0, userId);
         // Sets NOTIFICATION_HISTORY_ENABLED setting for uid 0
         Settings.Secure.putInt(mContext.getContentResolver(),
                 Settings.Secure.NOTIFICATION_HISTORY_ENABLED, globalEnabled ? 1 : 0);
+        setUsers(new int[] {0, userId});
 
         // Forces an update by calling observe on mSettingsObserver, which picks up the settings
         // changes above.
         mService.onBootPhase(SystemService.PHASE_THIRD_PARTY_APPS_CAN_START, mMainLooper);
 
         assertEquals(globalEnabled, Settings.Secure.getIntForUser(mContext.getContentResolver(),
-                Settings.Secure.NOTIFICATION_HISTORY_ENABLED, 0 /* =def */, uid) != 0);
+                Settings.Secure.NOTIFICATION_HISTORY_ENABLED, 0 /* =def */, userId) != 0);
     }
 
     private StatusBarNotification generateSbn(String pkg, int uid, long postTime, int userId) {
@@ -5535,15 +5549,6 @@
 
     @Test
     public void testBumpFGImportance_channelChangePreOApp() throws Exception {
-        String preOPkg = PKG_N_MR1;
-        final ApplicationInfo legacy = new ApplicationInfo();
-        legacy.targetSdkVersion = Build.VERSION_CODES.N_MR1;
-        when(mPackageManagerClient.getApplicationInfoAsUser(eq(preOPkg), anyInt(), anyInt()))
-                .thenReturn(legacy);
-        when(mPackageManagerClient.getPackageUidAsUser(eq(preOPkg), anyInt()))
-                .thenReturn(Binder.getCallingUid());
-        getContext().setMockPackageManager(mPackageManagerClient);
-
         Notification.Builder nb = new Notification.Builder(mContext,
                 NotificationChannel.DEFAULT_CHANNEL_ID)
                 .setContentTitle("foo")
@@ -5551,7 +5556,7 @@
                 .setFlag(FLAG_FOREGROUND_SERVICE, true)
                 .setPriority(Notification.PRIORITY_MIN);
 
-        StatusBarNotification sbn = new StatusBarNotification(preOPkg, preOPkg, 9,
+        StatusBarNotification sbn = new StatusBarNotification(PKG_N_MR1, PKG_N_MR1, 9,
                 "testBumpFGImportance_channelChangePreOApp",
                 Binder.getCallingUid(), 0, nb.build(),
                 UserHandle.getUserHandleForUid(Binder.getCallingUid()), null, 0);
@@ -5571,11 +5576,11 @@
                 .setFlag(FLAG_FOREGROUND_SERVICE, true)
                 .setPriority(Notification.PRIORITY_MIN);
 
-        sbn = new StatusBarNotification(preOPkg, preOPkg, 9,
+        sbn = new StatusBarNotification(PKG_N_MR1, PKG_N_MR1, 9,
                 "testBumpFGImportance_channelChangePreOApp", Binder.getCallingUid(),
                 0, nb.build(), UserHandle.getUserHandleForUid(Binder.getCallingUid()), null, 0);
 
-        mBinderService.enqueueNotificationWithTag(preOPkg, preOPkg,
+        mBinderService.enqueueNotificationWithTag(PKG_N_MR1, PKG_N_MR1,
                 "testBumpFGImportance_channelChangePreOApp",
                 sbn.getId(), sbn.getNotification(), sbn.getUserId());
         waitForIdle();
@@ -5583,7 +5588,7 @@
                 mService.getNotificationRecord(sbn.getKey()).getImportance());
 
         NotificationChannel defaultChannel = mBinderService.getNotificationChannel(
-                preOPkg, mContext.getUserId(), preOPkg, NotificationChannel.DEFAULT_CHANNEL_ID);
+                PKG_N_MR1, mContext.getUserId(), PKG_N_MR1, NotificationChannel.DEFAULT_CHANNEL_ID);
         assertEquals(IMPORTANCE_LOW, defaultChannel.getImportance());
     }
 
@@ -9139,31 +9144,98 @@
 
         AutomaticZenRule rule = new AutomaticZenRule.Builder("rule", Uri.parse("uri"))
                 .setType(AutomaticZenRule.TYPE_MANAGED)
-                .setOwner(new ComponentName("pkg", "cls"))
+                .setOwner(new ComponentName(PKG, "cls"))
                 .build();
         when(mDevicePolicyManager.isActiveDeviceOwner(anyInt())).thenReturn(true);
 
-        mBinderService.addAutomaticZenRule(rule, "pkg", /* fromUser= */ false);
+        mBinderService.addAutomaticZenRule(rule, PKG, /* fromUser= */ false);
 
-        verify(zenModeHelper).addAutomaticZenRule(eq("pkg"), eq(rule), anyInt(), any(), anyInt());
+        verify(zenModeHelper).addAutomaticZenRule(eq(PKG), eq(rule), anyInt(), any(), anyInt());
+    }
+
+    @Test
+    @EnableFlags(android.app.Flags.FLAG_MODES_API)
+    public void testAddAutomaticZenRule_typeManagedCanBeUsedBySystem() throws Exception {
+        addAutomaticZenRule_restrictedRuleTypeCanBeUsedBySystem(AutomaticZenRule.TYPE_MANAGED);
     }
 
     @Test
     @EnableFlags(android.app.Flags.FLAG_MODES_API)
     public void testAddAutomaticZenRule_typeManagedCannotBeUsedByRegularApps() throws Exception {
+        addAutomaticZenRule_restrictedRuleTypeCannotBeUsedByRegularApps(
+                AutomaticZenRule.TYPE_MANAGED);
+    }
+
+    @Test
+    @EnableFlags(android.app.Flags.FLAG_MODES_API)
+    public void testAddAutomaticZenRule_typeBedtimeCanBeUsedByWellbeing() throws Exception {
+        ZenModeHelper zenModeHelper = setUpMockZenTest();
+        mService.setCallerIsNormalPackage();
+        reset(mPackageManagerInternal);
+        when(mPackageManagerInternal.isSameApp(eq(PKG), eq(mUid), anyInt())).thenReturn(true);
+        when(mResources
+                .getString(com.android.internal.R.string.config_systemWellbeing))
+                .thenReturn(PKG);
+        when(mContext.getResources()).thenReturn(mResources);
+
+        AutomaticZenRule rule = new AutomaticZenRule.Builder("rule", Uri.parse("uri"))
+                .setType(AutomaticZenRule.TYPE_BEDTIME)
+                .setOwner(new ComponentName(PKG, "cls"))
+                .build();
+
+        mBinderService.addAutomaticZenRule(rule, PKG, /* fromUser= */ false);
+
+        verify(zenModeHelper).addAutomaticZenRule(eq(PKG), eq(rule), anyInt(), any(), anyInt());
+    }
+
+    @Test
+    @EnableFlags(android.app.Flags.FLAG_MODES_API)
+    public void testAddAutomaticZenRule_typeBedtimeCanBeUsedBySystem() throws Exception {
+        reset(mPackageManagerInternal);
+        when(mPackageManagerInternal.isSameApp(eq(PKG), eq(mUid), anyInt())).thenReturn(true);
+        addAutomaticZenRule_restrictedRuleTypeCanBeUsedBySystem(AutomaticZenRule.TYPE_BEDTIME);
+    }
+
+    @Test
+    @EnableFlags(android.app.Flags.FLAG_MODES_API)
+    public void testAddAutomaticZenRule_typeBedtimeCannotBeUsedByRegularApps() throws Exception {
+        reset(mPackageManagerInternal);
+        when(mPackageManagerInternal.isSameApp(eq(PKG), eq(mUid), anyInt())).thenReturn(true);
+        addAutomaticZenRule_restrictedRuleTypeCannotBeUsedByRegularApps(
+                AutomaticZenRule.TYPE_BEDTIME);
+    }
+
+    private void addAutomaticZenRule_restrictedRuleTypeCanBeUsedBySystem(
+            @AutomaticZenRule.Type int ruleType) throws Exception {
+        ZenModeHelper zenModeHelper = setUpMockZenTest();
+        mService.isSystemUid = true;
+
+        AutomaticZenRule rule = new AutomaticZenRule.Builder("rule", Uri.parse("uri"))
+                .setType(ruleType)
+                .setOwner(new ComponentName(PKG, "cls"))
+                .build();
+        when(mDevicePolicyManager.isActiveDeviceOwner(anyInt())).thenReturn(true);
+
+        mBinderService.addAutomaticZenRule(rule, PKG, /* fromUser= */ false);
+
+        verify(zenModeHelper).addAutomaticZenRule(eq(PKG), eq(rule), anyInt(), any(), anyInt());
+    }
+
+    private void addAutomaticZenRule_restrictedRuleTypeCannotBeUsedByRegularApps(
+            @AutomaticZenRule.Type int ruleType) {
         mService.setCallerIsNormalPackage();
         mService.setZenHelper(mock(ZenModeHelper.class));
         when(mConditionProviders.isPackageOrComponentAllowed(anyString(), anyInt()))
                 .thenReturn(true);
 
         AutomaticZenRule rule = new AutomaticZenRule.Builder("rule", Uri.parse("uri"))
-                .setType(AutomaticZenRule.TYPE_MANAGED)
-                .setOwner(new ComponentName("pkg", "cls"))
+                .setType(ruleType)
+                .setOwner(new ComponentName(PKG, "cls"))
                 .build();
         when(mDevicePolicyManager.isActiveDeviceOwner(anyInt())).thenReturn(false);
 
         assertThrows(IllegalArgumentException.class,
-                () -> mBinderService.addAutomaticZenRule(rule, "pkg", /* fromUser= */ false));
+                () -> mBinderService.addAutomaticZenRule(rule, PKG, /* fromUser= */ false));
     }
 
     @Test
@@ -9258,43 +9330,49 @@
 
     @Test
     @EnableFlags(android.app.Flags.FLAG_MODES_API)
-    public void setAutomaticZenRuleState_fromUserMatchesConditionSource_okay() throws Exception {
+    public void setAutomaticZenRuleState_conditionFromUser_mappedToOriginUser() throws Exception {
         ZenModeHelper zenModeHelper = setUpMockZenTest();
         mService.setCallerIsNormalPackage();
 
-        Condition withSourceContext = new Condition(Uri.parse("uri"), "summary", STATE_TRUE,
-                SOURCE_CONTEXT);
-        mBinderService.setAutomaticZenRuleState("id", withSourceContext, /* fromUser= */ false);
-        verify(zenModeHelper).setAutomaticZenRuleState(eq("id"), eq(withSourceContext),
-                eq(ZenModeConfig.UPDATE_ORIGIN_APP), anyInt());
-
         Condition withSourceUser = new Condition(Uri.parse("uri"), "summary", STATE_TRUE,
                 SOURCE_USER_ACTION);
-        mBinderService.setAutomaticZenRuleState("id", withSourceUser, /* fromUser= */ true);
+        mBinderService.setAutomaticZenRuleState("id", withSourceUser);
+
         verify(zenModeHelper).setAutomaticZenRuleState(eq("id"), eq(withSourceUser),
                 eq(ZenModeConfig.UPDATE_ORIGIN_USER), anyInt());
     }
 
     @Test
     @EnableFlags(android.app.Flags.FLAG_MODES_API)
-    public void setAutomaticZenRuleState_fromUserDoesNotMatchConditionSource_blocked()
+    public void setAutomaticZenRuleState_fromAppWithConditionNotFromUser_mappedToOriginApp()
             throws Exception {
         ZenModeHelper zenModeHelper = setUpMockZenTest();
         mService.setCallerIsNormalPackage();
 
         Condition withSourceContext = new Condition(Uri.parse("uri"), "summary", STATE_TRUE,
                 SOURCE_CONTEXT);
-        assertThrows(IllegalArgumentException.class,
-                () -> mBinderService.setAutomaticZenRuleState("id", withSourceContext,
-                        /* fromUser= */ true));
+        mBinderService.setAutomaticZenRuleState("id", withSourceContext);
 
-        Condition withSourceUser = new Condition(Uri.parse("uri"), "summary", STATE_TRUE,
-                SOURCE_USER_ACTION);
-        assertThrows(IllegalArgumentException.class,
-                () -> mBinderService.setAutomaticZenRuleState("id", withSourceUser,
-                        /* fromUser= */ false));
+        verify(zenModeHelper).setAutomaticZenRuleState(eq("id"), eq(withSourceContext),
+                eq(ZenModeConfig.UPDATE_ORIGIN_APP), anyInt());
     }
 
+    @Test
+    @EnableFlags(android.app.Flags.FLAG_MODES_API)
+    public void setAutomaticZenRuleState_fromSystemWithConditionNotFromUser_mappedToOriginSystem()
+            throws Exception {
+        ZenModeHelper zenModeHelper = setUpMockZenTest();
+        mService.isSystemUid = true;
+
+        Condition withSourceContext = new Condition(Uri.parse("uri"), "summary", STATE_TRUE,
+                SOURCE_CONTEXT);
+        mBinderService.setAutomaticZenRuleState("id", withSourceContext);
+
+        verify(zenModeHelper).setAutomaticZenRuleState(eq("id"), eq(withSourceContext),
+                eq(ZenModeConfig.UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI), anyInt());
+    }
+
+    /** Prepares for a zen-related test that uses a mocked {@link ZenModeHelper}. */
     private ZenModeHelper setUpMockZenTest() {
         ZenModeHelper zenModeHelper = mock(ZenModeHelper.class);
         mService.setZenHelper(zenModeHelper);
@@ -10367,7 +10445,7 @@
     @Test
     public void testHandleOnPackageRemoved_ClearsHistory() throws Exception {
         // Enables Notification History setting
-        setUpPrefsForHistory(mUid, true /* =enabled */);
+        setUpPrefsForHistory(mUserId, true /* =enabled */);
 
         // Posts a notification to the mTestNotificationChannel.
         final NotificationRecord notif = generateNotificationRecord(
@@ -11261,6 +11339,40 @@
     }
 
     @Test
+    public void testMigrateNotificationFilter_invalidPackage() throws Exception {
+        int[] userIds = new int[] {mUserId, 1000};
+        when(mUm.getProfileIds(anyInt(), anyBoolean())).thenReturn(userIds);
+        List<String> disallowedApps = ImmutableList.of("apples", "bananas", "cherries");
+        for (int userId : userIds) {
+            when(mPackageManager.getPackageUid("apples", 0, userId)).thenThrow(
+                    new RemoteException(""));
+            when(mPackageManager.getPackageUid("bananas", 0, userId)).thenReturn(9000);
+            when(mPackageManager.getPackageUid("cherries", 0, userId)).thenReturn(9001);
+        }
+
+        when(mListeners.getNotificationListenerFilter(any())).thenReturn(
+                new NotificationListenerFilter());
+
+        mBinderService.migrateNotificationFilter(null,
+                FLAG_FILTER_TYPE_CONVERSATIONS | FLAG_FILTER_TYPE_ONGOING,
+                disallowedApps);
+
+        ArgumentCaptor<NotificationListenerFilter> captor =
+                ArgumentCaptor.forClass(NotificationListenerFilter.class);
+        verify(mListeners).setNotificationListenerFilter(any(), captor.capture());
+
+        assertEquals(FLAG_FILTER_TYPE_CONVERSATIONS | FLAG_FILTER_TYPE_ONGOING,
+                captor.getValue().getTypes());
+        // valid values stay
+        assertFalse(captor.getValue().isPackageAllowed(new VersionedPackage("bananas", 9000)));
+        assertFalse(captor.getValue().isPackageAllowed(new VersionedPackage("cherries", 9001)));
+        // don't store invalid values
+        for (VersionedPackage vp : captor.getValue().getDisallowedPackages()) {
+            assertNotEquals("apples", vp.getPackageName());
+        }
+    }
+
+    @Test
     public void testMigrateNotificationFilter_noPreexistingFilter() throws Exception {
         int[] userIds = new int[] {mUserId};
         when(mUm.getProfileIds(anyInt(), anyBoolean())).thenReturn(userIds);
@@ -13866,6 +13978,76 @@
     }
 
     @Test
+    @EnableFlags(android.app.Flags.FLAG_MODES_API)
+    @EnableCompatChanges(NotificationManagerService.MANAGE_GLOBAL_ZEN_VIA_IMPLICIT_RULES)
+    public void updateAutomaticZenRule_implicitRuleWithoutCPS_disallowedFromApp() throws Exception {
+        setUpRealZenTest();
+        mService.setCallerIsNormalPackage();
+        assertThat(mBinderService.getAutomaticZenRules()).isEmpty();
+
+        // Create an implicit zen rule by calling setNotificationPolicy from an app.
+        mBinderService.setNotificationPolicy(PKG, new NotificationManager.Policy(0, 0, 0), false);
+        assertThat(mBinderService.getAutomaticZenRules()).hasSize(1);
+        Map.Entry<String, AutomaticZenRule> rule = getOnlyElement(
+                mBinderService.getAutomaticZenRules().entrySet());
+        assertThat(rule.getValue().getOwner()).isNull();
+        assertThat(rule.getValue().getConfigurationActivity()).isNull();
+
+        // Now try to update said rule (e.g. disable it). Should fail.
+        // We also validate the exception message because NPE could be thrown by all sorts of test
+        // issues (e.g. misconfigured mocks).
+        rule.getValue().setEnabled(false);
+        NullPointerException e = assertThrows(NullPointerException.class,
+                () -> mBinderService.updateAutomaticZenRule(rule.getKey(), rule.getValue(), false));
+        assertThat(e.getMessage()).isEqualTo(
+                "Rule must have a ConditionProviderService and/or configuration activity");
+    }
+
+    @Test
+    @EnableFlags(android.app.Flags.FLAG_MODES_API)
+    @EnableCompatChanges(NotificationManagerService.MANAGE_GLOBAL_ZEN_VIA_IMPLICIT_RULES)
+    public void updateAutomaticZenRule_implicitRuleWithoutCPS_allowedFromSystem() throws Exception {
+        setUpRealZenTest();
+        mService.setCallerIsNormalPackage();
+        assertThat(mBinderService.getAutomaticZenRules()).isEmpty();
+
+        // Create an implicit zen rule by calling setNotificationPolicy from an app.
+        mBinderService.setNotificationPolicy(PKG, new NotificationManager.Policy(0, 0, 0), false);
+        assertThat(mBinderService.getAutomaticZenRules()).hasSize(1);
+        Map.Entry<String, AutomaticZenRule> rule = getOnlyElement(
+                mBinderService.getAutomaticZenRules().entrySet());
+        assertThat(rule.getValue().getOwner()).isNull();
+        assertThat(rule.getValue().getConfigurationActivity()).isNull();
+
+        // Now update said rule from Settings (e.g. disable it). Should work!
+        mService.isSystemUid = true;
+        rule.getValue().setEnabled(false);
+        mBinderService.updateAutomaticZenRule(rule.getKey(), rule.getValue(), false);
+
+        Map.Entry<String, AutomaticZenRule> updatedRule = getOnlyElement(
+                mBinderService.getAutomaticZenRules().entrySet());
+        assertThat(updatedRule.getValue().isEnabled()).isFalse();
+    }
+
+    /** Prepares for a zen-related test that uses the real {@link ZenModeHelper}. */
+    private void setUpRealZenTest() throws Exception {
+        when(mConditionProviders.isPackageOrComponentAllowed(anyString(), anyInt()))
+                .thenReturn(true);
+
+        int iconResId = 79;
+        String iconResName = "icon_79";
+        String pkg = mContext.getPackageName();
+        ApplicationInfo appInfoSpy = spy(new ApplicationInfo());
+        appInfoSpy.icon = iconResId;
+        when(appInfoSpy.loadLabel(any())).thenReturn("Test App");
+        when(mPackageManagerClient.getApplicationInfo(eq(pkg), anyInt())).thenReturn(appInfoSpy);
+
+        when(mResources.getResourceName(eq(iconResId))).thenReturn(iconResName);
+        when(mResources.getIdentifier(eq(iconResName), any(), any())).thenReturn(iconResId);
+        when(mPackageManagerClient.getResourcesForApplication(eq(pkg))).thenReturn(mResources);
+    }
+
+    @Test
     public void testFixNotification_clearsLifetimeExtendedFlag() throws Exception {
         mSetFlagsRule.enableFlags(android.app.Flags.FLAG_LIFETIME_EXTENSION_REFACTOR);
         Notification n = new Notification.Builder(mContext, "test")
@@ -13880,7 +14062,6 @@
     }
 
     @Test
-    @Ignore("b/316989461")
     public void cancelNotificationsFromListener_rapidClear_oldNew_cancelOne()
             throws RemoteException {
         mSetFlagsRule.enableFlags(android.view.contentprotection.flags.Flags
@@ -13892,7 +14073,8 @@
         mService.addNotification(nr1);
 
         // Create old notification.
-        final NotificationRecord nr2 = generateNotificationRecord(mTestNotificationChannel, 0);
+        final NotificationRecord nr2 = generateNotificationRecord(mTestNotificationChannel,
+                System.currentTimeMillis() - 60000);
         mService.addNotification(nr2);
 
         // Cancel specific notifications via listener.
@@ -13910,16 +14092,17 @@
     }
 
     @Test
-    @Ignore("b/316989461")
     public void cancelNotificationsFromListener_rapidClear_old_cancelOne() throws RemoteException {
         mSetFlagsRule.enableFlags(android.view.contentprotection.flags.Flags
                 .FLAG_RAPID_CLEAR_NOTIFICATIONS_BY_LISTENER_APP_OP_ENABLED);
 
         // Create old notifications.
-        final NotificationRecord nr1 = generateNotificationRecord(mTestNotificationChannel, 0);
+        final NotificationRecord nr1 = generateNotificationRecord(mTestNotificationChannel,
+                System.currentTimeMillis() - 60000);
         mService.addNotification(nr1);
 
-        final NotificationRecord nr2 = generateNotificationRecord(mTestNotificationChannel, 0);
+        final NotificationRecord nr2 = generateNotificationRecord(mTestNotificationChannel,
+                System.currentTimeMillis() - 60000);
         mService.addNotification(nr2);
 
         // Cancel specific notifications via listener.
@@ -13938,7 +14121,6 @@
     }
 
     @Test
-    @Ignore("b/316989461")
     public void cancelNotificationsFromListener_rapidClear_oldNew_cancelOne_flagDisabled()
             throws RemoteException {
         mSetFlagsRule.disableFlags(android.view.contentprotection.flags.Flags
@@ -13950,7 +14132,8 @@
         mService.addNotification(nr1);
 
         // Create old notification.
-        final NotificationRecord nr2 = generateNotificationRecord(mTestNotificationChannel, 0);
+        final NotificationRecord nr2 = generateNotificationRecord(mTestNotificationChannel,
+                System.currentTimeMillis() - 60000);
         mService.addNotification(nr2);
 
         // Cancel specific notifications via listener.
@@ -13969,7 +14152,6 @@
     }
 
     @Test
-    @Ignore("b/316989461")
     public void cancelNotificationsFromListener_rapidClear_oldNew_cancelAll()
             throws RemoteException {
         mSetFlagsRule.enableFlags(android.view.contentprotection.flags.Flags
@@ -13981,7 +14163,8 @@
         mService.addNotification(nr1);
 
         // Create old notification.
-        final NotificationRecord nr2 = generateNotificationRecord(mTestNotificationChannel, 0);
+        final NotificationRecord nr2 = generateNotificationRecord(mTestNotificationChannel,
+                System.currentTimeMillis() - 60000);
         mService.addNotification(nr2);
 
         // Cancel all notifications via listener.
@@ -13998,16 +14181,17 @@
     }
 
     @Test
-    @Ignore("b/316989461")
     public void cancelNotificationsFromListener_rapidClear_old_cancelAll() throws RemoteException {
         mSetFlagsRule.enableFlags(android.view.contentprotection.flags.Flags
                 .FLAG_RAPID_CLEAR_NOTIFICATIONS_BY_LISTENER_APP_OP_ENABLED);
 
         // Create old notifications.
-        final NotificationRecord nr1 = generateNotificationRecord(mTestNotificationChannel, 0);
+        final NotificationRecord nr1 = generateNotificationRecord(mTestNotificationChannel,
+                System.currentTimeMillis() - 60000);
         mService.addNotification(nr1);
 
-        final NotificationRecord nr2 = generateNotificationRecord(mTestNotificationChannel, 0);
+        final NotificationRecord nr2 = generateNotificationRecord(mTestNotificationChannel,
+                System.currentTimeMillis() - 60000);
         mService.addNotification(nr2);
 
         // Cancel all notifications via listener.
@@ -14025,7 +14209,6 @@
     }
 
     @Test
-    @Ignore("b/316989461")
     public void cancelNotificationsFromListener_rapidClear_oldNew_cancelAll_flagDisabled()
             throws RemoteException {
         mSetFlagsRule.disableFlags(android.view.contentprotection.flags.Flags
@@ -14037,7 +14220,8 @@
         mService.addNotification(nr1);
 
         // Create old notification.
-        final NotificationRecord nr2 = generateNotificationRecord(mTestNotificationChannel, 0);
+        final NotificationRecord nr2 = generateNotificationRecord(mTestNotificationChannel,
+                System.currentTimeMillis() - 60000);
         mService.addNotification(nr2);
 
         // Cancel all notifications via listener.
@@ -14054,6 +14238,22 @@
                 any(), any());
     }
 
+    @Test
+    @EnableFlags(FLAG_KEYGUARD_PRIVATE_NOTIFICATIONS)
+    public void testSetPrivateNotificationsAllowed() throws Exception {
+        when(mContext.checkCallingPermission(CONTROL_KEYGUARD_SECURE_NOTIFICATIONS))
+                .thenReturn(PERMISSION_GRANTED);
+        mBinderService.setPrivateNotificationsAllowed(false);
+        Intent expected = new Intent(ACTION_KEYGUARD_PRIVATE_NOTIFICATIONS_CHANGED)
+                .putExtra(EXTRA_KM_PRIVATE_NOTIFS_ALLOWED, false);
+        ArgumentCaptor<Intent> actual = ArgumentCaptor.forClass(Intent.class);
+        verify(mContext).sendBroadcast(actual.capture(), eq(STATUS_BAR_SERVICE));
+
+        assertEquals(ACTION_KEYGUARD_PRIVATE_NOTIFICATIONS_CHANGED, actual.getValue().getAction());
+        assertFalse(actual.getValue().getBooleanExtra(EXTRA_KM_PRIVATE_NOTIFS_ALLOWED, true));
+        assertFalse(mBinderService.getPrivateNotificationsAllowed());
+    }
+
     private NotificationRecord createAndPostNotification(Notification.Builder nb, String testName)
             throws RemoteException {
         StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1, testName, mUid, 0,
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenAdaptersTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenAdaptersTest.java
index 6cc1c43..08af09c 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenAdaptersTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenAdaptersTest.java
@@ -20,7 +20,11 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import android.app.Flags;
 import android.app.NotificationManager.Policy;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
 import android.service.notification.ZenPolicy;
 
 import androidx.test.filters.SmallTest;
@@ -28,6 +32,7 @@
 
 import com.android.server.UiServiceTestCase;
 
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -35,6 +40,9 @@
 @RunWith(AndroidJUnit4.class)
 public class ZenAdaptersTest extends UiServiceTestCase {
 
+    @Rule
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
     @Test
     public void notificationPolicyToZenPolicy_allCallers() {
         Policy policy = new Policy(Policy.PRIORITY_CATEGORY_CALLS, Policy.PRIORITY_SENDERS_ANY, 0);
@@ -127,4 +135,35 @@
         assertThat(zenPolicy.getVisualEffectPeek()).isEqualTo(ZenPolicy.STATE_UNSET);
         assertThat(zenPolicy.getVisualEffectStatusBar()).isEqualTo(ZenPolicy.STATE_UNSET);
     }
+
+    @Test
+    @EnableFlags(Flags.FLAG_MODES_API)
+    public void notificationPolicyToZenPolicy_modesApi_priorityChannels() {
+        Policy policy = new Policy(0, 0, 0, 0,
+                Policy.policyState(false, true), 0);
+
+        ZenPolicy zenPolicy = notificationPolicyToZenPolicy(policy);
+        assertThat(zenPolicy.getAllowedChannels()).isEqualTo(ZenPolicy.CHANNEL_TYPE_PRIORITY);
+
+        Policy notAllowed = new Policy(0, 0, 0, 0,
+                Policy.policyState(false, false), 0);
+        ZenPolicy zenPolicyNotAllowed = notificationPolicyToZenPolicy(notAllowed);
+        assertThat(zenPolicyNotAllowed.getAllowedChannels()).isEqualTo(ZenPolicy.CHANNEL_TYPE_NONE);
+    }
+
+    @Test
+    @DisableFlags(Flags.FLAG_MODES_API)
+    public void notificationPolicyToZenPolicy_noModesApi_priorityChannelsUnset() {
+        Policy policy = new Policy(0, 0, 0, 0,
+                Policy.policyState(false, true), 0);
+
+        ZenPolicy zenPolicy = notificationPolicyToZenPolicy(policy);
+        assertThat(zenPolicy.getAllowedChannels()).isEqualTo(ZenPolicy.CHANNEL_TYPE_UNSET);
+
+        Policy notAllowed = new Policy(0, 0, 0, 0,
+                Policy.policyState(false, false), 0);
+        ZenPolicy zenPolicyNotAllowed = notificationPolicyToZenPolicy(notAllowed);
+        assertThat(zenPolicyNotAllowed.getAllowedChannels())
+                .isEqualTo(ZenPolicy.CHANNEL_TYPE_UNSET);
+    }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenDeviceEffectsTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenDeviceEffectsTest.java
index 999e33c..3d8ec2e 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenDeviceEffectsTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenDeviceEffectsTest.java
@@ -18,19 +18,31 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import android.app.Flags;
 import android.os.Parcel;
+import android.platform.test.flag.junit.SetFlagsRule;
 import android.service.notification.ZenDeviceEffects;
 
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.server.UiServiceTestCase;
 
+import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 @RunWith(AndroidJUnit4.class)
 public class ZenDeviceEffectsTest extends UiServiceTestCase {
 
+    @Rule
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
+    @Before
+    public final void setUp() {
+        mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
+    }
+
     @Test
     public void builder() {
         ZenDeviceEffects deviceEffects = new ZenDeviceEffects.Builder()
@@ -40,6 +52,7 @@
                 .setShouldMaximizeDoze(true)
                 .setShouldUseNightMode(false)
                 .setShouldSuppressAmbientDisplay(false).setShouldSuppressAmbientDisplay(true)
+                .setUserModifiedFields(8)
                 .build();
 
         assertThat(deviceEffects.shouldDimWallpaper()).isTrue();
@@ -52,6 +65,7 @@
         assertThat(deviceEffects.shouldMinimizeRadioUsage()).isFalse();
         assertThat(deviceEffects.shouldUseNightMode()).isFalse();
         assertThat(deviceEffects.shouldSuppressAmbientDisplay()).isTrue();
+        assertThat(deviceEffects.getUserModifiedFields()).isEqualTo(8);
     }
 
     @Test
@@ -83,6 +97,7 @@
                 .setShouldMinimizeRadioUsage(true)
                 .setShouldUseNightMode(true)
                 .setShouldSuppressAmbientDisplay(true)
+                .setUserModifiedFields(6)
                 .build();
 
         Parcel parcel = Parcel.obtain();
@@ -101,6 +116,7 @@
         assertThat(copy.shouldUseNightMode()).isTrue();
         assertThat(copy.shouldSuppressAmbientDisplay()).isTrue();
         assertThat(copy.shouldDisplayGrayscale()).isFalse();
+        assertThat(copy.getUserModifiedFields()).isEqualTo(6);
     }
 
     @Test
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
index 3185c50..dd252f3 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
@@ -19,12 +19,15 @@
 import static android.app.AutomaticZenRule.TYPE_BEDTIME;
 import static android.service.notification.ZenPolicy.CONVERSATION_SENDERS_IMPORTANT;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static junit.framework.TestCase.assertEquals;
 import static junit.framework.TestCase.assertFalse;
 import static junit.framework.TestCase.assertNotNull;
 import static junit.framework.TestCase.assertNull;
 import static junit.framework.TestCase.assertTrue;
 
+import android.app.AutomaticZenRule;
 import android.app.Flags;
 import android.app.NotificationManager.Policy;
 import android.content.ComponentName;
@@ -46,6 +49,7 @@
 import com.android.modules.utils.TypedXmlSerializer;
 import com.android.server.UiServiceTestCase;
 
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -56,6 +60,7 @@
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
+import java.time.Instant;
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
@@ -83,6 +88,11 @@
     @Rule
     public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
 
+    @Before
+    public final void setUp() {
+        mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
+    }
+
     @Test
     public void testPriorityOnlyMutingAllNotifications() {
         ZenModeConfig config = getMutedRingerConfig();
@@ -275,6 +285,7 @@
         assertEquals(expected.getPriorityCallSenders(), actual.getPriorityCallSenders());
         assertEquals(expected.getPriorityMessageSenders(), actual.getPriorityMessageSenders());
         assertEquals(expected.getAllowedChannels(), actual.getAllowedChannels());
+        assertEquals(expected.getUserModifiedFields(), actual.getUserModifiedFields());
     }
 
     @Test
@@ -327,6 +338,53 @@
     }
 
     @Test
+    public void testCanBeUpdatedByApp_nullPolicyAndDeviceEffects() throws Exception {
+        ZenModeConfig.ZenRule rule = new ZenModeConfig.ZenRule();
+        rule.zenPolicy = null;
+        rule.zenDeviceEffects = null;
+
+        assertThat(rule.canBeUpdatedByApp()).isTrue();
+
+        rule.userModifiedFields = 1;
+        assertThat(rule.canBeUpdatedByApp()).isFalse();
+    }
+
+    @Test
+    public void testCanBeUpdatedByApp_policyModified() throws Exception {
+        ZenPolicy.Builder policyBuilder = new ZenPolicy.Builder().setUserModifiedFields(0);
+        ZenPolicy policy = policyBuilder.build();
+
+        ZenModeConfig.ZenRule rule = new ZenModeConfig.ZenRule();
+        rule.zenPolicy = policy;
+
+        assertThat(rule.userModifiedFields).isEqualTo(0);
+        assertThat(rule.canBeUpdatedByApp()).isTrue();
+
+        policy = policyBuilder.setUserModifiedFields(1).build();
+        assertThat(policy.getUserModifiedFields()).isEqualTo(1);
+        rule.zenPolicy = policy;
+        assertThat(rule.canBeUpdatedByApp()).isFalse();
+    }
+
+    @Test
+    public void testCanBeUpdatedByApp_deviceEffectsModified() throws Exception {
+        ZenDeviceEffects.Builder deviceEffectsBuilder =
+                new ZenDeviceEffects.Builder().setUserModifiedFields(0);
+        ZenDeviceEffects deviceEffects = deviceEffectsBuilder.build();
+
+        ZenModeConfig.ZenRule rule = new ZenModeConfig.ZenRule();
+        rule.zenDeviceEffects = deviceEffects;
+
+        assertThat(rule.userModifiedFields).isEqualTo(0);
+        assertThat(rule.canBeUpdatedByApp()).isTrue();
+
+        deviceEffects = deviceEffectsBuilder.setUserModifiedFields(1).build();
+        assertThat(deviceEffects.getUserModifiedFields()).isEqualTo(1);
+        rule.zenDeviceEffects = deviceEffects;
+        assertThat(rule.canBeUpdatedByApp()).isFalse();
+    }
+
+    @Test
     public void testWriteToParcel() {
         mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
 
@@ -347,8 +405,10 @@
 
         rule.allowManualInvocation = ALLOW_MANUAL;
         rule.type = TYPE;
+        rule.userModifiedFields = 16;
         rule.iconResName = ICON_RES_NAME;
         rule.triggerDescription = TRIGGER_DESC;
+        rule.deletionInstant = Instant.ofEpochMilli(1701790147000L);
 
         Parcel parcel = Parcel.obtain();
         rule.writeToParcel(parcel, 0);
@@ -371,11 +431,13 @@
         assertEquals(rule.allowManualInvocation, parceled.allowManualInvocation);
         assertEquals(rule.iconResName, parceled.iconResName);
         assertEquals(rule.type, parceled.type);
+        assertEquals(rule.userModifiedFields, parceled.userModifiedFields);
         assertEquals(rule.triggerDescription, parceled.triggerDescription);
         assertEquals(rule.zenPolicy, parceled.zenPolicy);
+        assertEquals(rule.deletionInstant, parceled.deletionInstant);
+
         assertEquals(rule, parceled);
         assertEquals(rule.hashCode(), parceled.hashCode());
-
     }
 
     @Test
@@ -448,8 +510,10 @@
 
         rule.allowManualInvocation = ALLOW_MANUAL;
         rule.type = TYPE;
+        rule.userModifiedFields = 4;
         rule.iconResName = ICON_RES_NAME;
         rule.triggerDescription = TRIGGER_DESC;
+        rule.deletionInstant = Instant.ofEpochMilli(1701790147000L);
 
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
         writeRuleXml(rule, baos);
@@ -476,8 +540,10 @@
 
         assertEquals(rule.allowManualInvocation, fromXml.allowManualInvocation);
         assertEquals(rule.type, fromXml.type);
+        assertEquals(rule.userModifiedFields, fromXml.userModifiedFields);
         assertEquals(rule.triggerDescription, fromXml.triggerDescription);
         assertEquals(rule.iconResName, fromXml.iconResName);
+        assertEquals(rule.deletionInstant, fromXml.deletionInstant);
     }
 
     @Test
@@ -550,6 +616,22 @@
     }
 
     @Test
+    public void testRuleXml_userModifiedField() throws Exception {
+        ZenModeConfig.ZenRule rule = new ZenModeConfig.ZenRule();
+        rule.userModifiedFields |= AutomaticZenRule.FIELD_NAME;
+        assertThat(rule.userModifiedFields).isEqualTo(1);
+        assertThat(rule.canBeUpdatedByApp()).isFalse();
+
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        writeRuleXml(rule, baos);
+        ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
+        ZenModeConfig.ZenRule fromXml = readRuleXml(bais);
+
+        assertThat(fromXml.userModifiedFields).isEqualTo(rule.userModifiedFields);
+        assertThat(fromXml.canBeUpdatedByApp()).isFalse();
+    }
+
+    @Test
     public void testZenPolicyXml_classic() throws Exception {
         ZenPolicy policy = new ZenPolicy.Builder()
                 .allowCalls(ZenPolicy.PEOPLE_TYPE_CONTACTS)
@@ -615,6 +697,7 @@
                 .allowChannels(ZenPolicy.CHANNEL_TYPE_NONE)
                 .hideAllVisualEffects()
                 .showVisualEffect(ZenPolicy.VISUAL_EFFECT_AMBIENT, true)
+                .setUserModifiedFields(4)
                 .build();
 
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
@@ -649,6 +732,7 @@
         assertEquals(policy.getVisualEffectAmbient(), fromXml.getVisualEffectAmbient());
         assertEquals(policy.getVisualEffectNotificationList(),
                 fromXml.getVisualEffectNotificationList());
+        assertEquals(policy.getUserModifiedFields(), fromXml.getUserModifiedFields());
     }
 
     private ZenModeConfig getMutedRingerConfig() {
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java
index 93cd44e..9d7cf53 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java
@@ -65,18 +65,22 @@
 @TestableLooper.RunWithLooper
 public class ZenModeDiffTest extends UiServiceTestCase {
     // Base set of exempt fields independent of fields that are enabled/disabled via flags.
-    // version is not included in the diff; manual & automatic rules have special handling
+    // version is not included in the diff; manual & automatic rules have special handling;
+    // deleted rules are not included in the diff.
     public static final Set<String> ZEN_MODE_CONFIG_EXEMPT_FIELDS =
-            Set.of("version", "manualRule", "automaticRules");
+            android.app.Flags.modesApi()
+                    ? Set.of("version", "manualRule", "automaticRules", "deletedRules")
+                    : Set.of("version", "manualRule", "automaticRules");
 
     // Differences for flagged fields are only generated if the flag is enabled.
-    // TODO: b/310620812 - Remove this exempt list when flag is inlined.
+    // "Metadata" fields (userModifiedFields, deletionInstant) are not compared.
     private static final Set<String> ZEN_RULE_EXEMPT_FIELDS =
             android.app.Flags.modesApi()
-                    ? Set.of()
+                    ? Set.of("userModifiedFields", "deletionInstant")
                     : Set.of(RuleDiff.FIELD_TYPE, RuleDiff.FIELD_TRIGGER_DESCRIPTION,
                             RuleDiff.FIELD_ICON_RES, RuleDiff.FIELD_ALLOW_MANUAL,
-                            RuleDiff.FIELD_ZEN_DEVICE_EFFECTS);
+                            RuleDiff.FIELD_ZEN_DEVICE_EFFECTS, "userModifiedFields",
+                            "deletionInstant");
 
     // allowPriorityChannels is flagged by android.app.modes_api
     public static final Set<String> ZEN_MODE_CONFIG_FLAGGED_FIELDS =
@@ -304,6 +308,7 @@
             rule.zenDeviceEffects = new ZenDeviceEffects.Builder()
                     .setShouldDimWallpaper(true)
                     .build();
+            rule.userModifiedFields = AutomaticZenRule.FIELD_NAME;
         }
         return rule;
     }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeEventLoggerFake.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeEventLoggerFake.java
index 1fcee06..5b35e34 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeEventLoggerFake.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeEventLoggerFake.java
@@ -118,10 +118,13 @@
     public DNDPolicyProto getPolicyProto(int i) throws IllegalArgumentException {
         checkInRange(i);
         byte[] policyBytes = mChanges.get(i).getDNDPolicyProto();
+        if (policyBytes == null) {
+            return null;
+        }
         try {
             return DNDPolicyProto.parseFrom(policyBytes);
         } catch (InvalidProtocolBufferException e) {
-            return null; // couldn't turn it into proto
+            throw new RuntimeException("Couldn't parse DNDPolicyProto!", e);
         }
     }
 
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index 44f0894..9e3e336 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -21,6 +21,9 @@
 import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_DEACTIVATED;
 import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_DISABLED;
 import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_ENABLED;
+import static android.app.NotificationManager.INTERRUPTION_FILTER_ALARMS;
+import static android.app.NotificationManager.INTERRUPTION_FILTER_ALL;
+import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
 import static android.app.NotificationManager.Policy.CONVERSATION_SENDERS_ANYONE;
 import static android.app.NotificationManager.Policy.CONVERSATION_SENDERS_IMPORTANT;
 import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_ALARMS;
@@ -40,6 +43,7 @@
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.provider.Settings.Global.ZEN_MODE_ALARMS;
 import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
 import static android.provider.Settings.Global.ZEN_MODE_OFF;
@@ -89,6 +93,7 @@
 import static org.mockito.Mockito.when;
 import static org.mockito.Mockito.withSettings;
 
+import android.Manifest;
 import android.annotation.Nullable;
 import android.annotation.SuppressLint;
 import android.app.AppGlobals;
@@ -101,6 +106,7 @@
 import android.content.ContentResolver;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.res.Resources;
@@ -113,6 +119,7 @@
 import android.net.Uri;
 import android.os.Parcel;
 import android.os.Process;
+import android.os.SimpleClock;
 import android.os.UserHandle;
 import android.platform.test.annotations.EnableFlags;
 import android.platform.test.flag.junit.SetFlagsRule;
@@ -169,12 +176,16 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.Reader;
+import java.time.Instant;
+import java.time.ZoneOffset;
+import java.time.temporal.ChronoUnit;
 import java.util.ArrayList;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Objects;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
 
 @SmallTest
 @SuppressLint("GuardedBy") // It's ok for this test to access guarded methods from the service.
@@ -182,8 +193,9 @@
 @TestableLooper.RunWithLooper
 public class ZenModeHelperTest extends UiServiceTestCase {
 
-    private static final String EVENTS_DEFAULT_RULE_ID = "EVENTS_DEFAULT_RULE";
-    private static final String SCHEDULE_DEFAULT_RULE_ID = "EVERY_NIGHT_DEFAULT_RULE";
+    private static final String EVENTS_DEFAULT_RULE_ID = ZenModeConfig.EVENTS_DEFAULT_RULE_ID;
+    private static final String SCHEDULE_DEFAULT_RULE_ID =
+            ZenModeConfig.EVERY_NIGHT_DEFAULT_RULE_ID;
     private static final String CUSTOM_PKG_NAME = "not.android";
     private static final String CUSTOM_APP_LABEL = "This is not Android";
     private static final int CUSTOM_PKG_UID = 1;
@@ -228,6 +240,7 @@
     @Mock PackageManager mPackageManager;
     private Resources mResources;
     private TestableLooper mTestableLooper;
+    private final TestClock mTestClock = new TestClock();
     private ZenModeHelper mZenModeHelper;
     private ContentResolver mContentResolver;
     @Mock DeviceEffectsApplier mDeviceEffectsApplier;
@@ -265,7 +278,7 @@
         mConditionProviders.addSystemProvider(new CountdownConditionProvider());
         mConditionProviders.addSystemProvider(new ScheduleConditionProvider());
         mZenModeEventLogger = new ZenModeEventLoggerFake(mPackageManager);
-        mZenModeHelper = new ZenModeHelper(mContext, mTestableLooper.getLooper(),
+        mZenModeHelper = new ZenModeHelper(mContext, mTestableLooper.getLooper(), mTestClock,
                 mConditionProviders, mTestFlagResolver, mZenModeEventLogger);
 
         ResolveInfo ri = new ResolveInfo();
@@ -1194,7 +1207,7 @@
     @Test
     public void ruleUidAutomaticZenRuleRemovedUpdatesCache() throws Exception {
         when(mContext.checkCallingPermission(anyString()))
-                .thenReturn(PackageManager.PERMISSION_GRANTED);
+                .thenReturn(PERMISSION_GRANTED);
 
         setupZenConfig();
         // one enabled automatic rule
@@ -1776,7 +1789,7 @@
     public void testDoNotUpdateModifiedDefaultAutoRule() {
         // mDefaultConfig is set to default config in setup by getDefaultConfigParser
         when(mContext.checkCallingPermission(anyString()))
-                .thenReturn(PackageManager.PERMISSION_GRANTED);
+                .thenReturn(PERMISSION_GRANTED);
 
         // shouldn't update rule that's been modified
         ZenModeConfig.ZenRule updatedDefaultRule = new ZenModeConfig.ZenRule();
@@ -1802,7 +1815,7 @@
     public void testDoNotUpdateEnabledDefaultAutoRule() {
         // mDefaultConfig is set to default config in setup by getDefaultConfigParser
         when(mContext.checkCallingPermission(anyString()))
-                .thenReturn(PackageManager.PERMISSION_GRANTED);
+                .thenReturn(PERMISSION_GRANTED);
 
         // shouldn't update the rule that's enabled
         ZenModeConfig.ZenRule updatedDefaultRule = new ZenModeConfig.ZenRule();
@@ -1829,7 +1842,7 @@
         // mDefaultConfig is set to default config in setup by getDefaultConfigParser
         final String defaultRuleName = "rule name test";
         when(mContext.checkCallingPermission(anyString()))
-                .thenReturn(PackageManager.PERMISSION_GRANTED);
+                .thenReturn(PERMISSION_GRANTED);
 
         // will update rule that is not enabled and modified
         ZenModeConfig.ZenRule customDefaultRule = new ZenModeConfig.ZenRule();
@@ -2197,7 +2210,7 @@
     }
 
     @Test
-    public void addAutomaticZenRule_fromUser_respectsHiddenEffects() {
+    public void addAutomaticZenRule_fromUser_respectsHiddenEffects() throws Exception {
         mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
 
         ZenDeviceEffects zde = new ZenDeviceEffects.Builder()
@@ -2222,7 +2235,13 @@
                 "reasons", 0);
 
         AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(ruleId);
-        assertThat(savedRule.getDeviceEffects()).isEqualTo(zde);
+
+        // savedRule.getDeviceEffects() is equal to zde, except for the userModifiedFields.
+        // So we clear before comparing.
+        ZenDeviceEffects savedEffects = new ZenDeviceEffects.Builder(savedRule.getDeviceEffects())
+                .setUserModifiedFields(0).build();
+
+        assertThat(savedEffects).isEqualTo(zde);
     }
 
     @Test
@@ -2298,8 +2317,11 @@
                 UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "reasons", 0);
 
         ZenDeviceEffects updateFromUser = new ZenDeviceEffects.Builder()
-                .setShouldUseNightMode(true) // Good
-                .setShouldMaximizeDoze(true) // Also good
+                .setShouldUseNightMode(true)
+                .setShouldMaximizeDoze(true)
+                // Just to emphasize that unset values default to false;
+                // even with this line removed, tap to wake would be set to false.
+                .setShouldDisableTapToWake(false)
                 .build();
         mZenModeHelper.updateAutomaticZenRule(ruleId,
                 new AutomaticZenRule.Builder("Rule", CONDITION_ID)
@@ -2308,7 +2330,75 @@
                 UPDATE_ORIGIN_USER, "reasons", 0);
 
         AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(ruleId);
-        assertThat(savedRule.getDeviceEffects()).isEqualTo(updateFromUser);
+
+        // savedRule.getDeviceEffects() is equal to updateFromUser, except for the
+        // userModifiedFields, so we clear before comparing.
+        ZenDeviceEffects savedEffects = new ZenDeviceEffects.Builder(savedRule.getDeviceEffects())
+                .setUserModifiedFields(0).build();
+
+        assertThat(savedEffects).isEqualTo(updateFromUser);
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_MODES_API)
+    public void addAutomaticZenRule_withTypeBedtime_replacesDisabledSleeping() {
+        ZenRule sleepingRule = createCustomAutomaticRule(ZEN_MODE_IMPORTANT_INTERRUPTIONS,
+                ZenModeConfig.EVERY_NIGHT_DEFAULT_RULE_ID);
+        sleepingRule.enabled = false;
+        sleepingRule.userModifiedFields = 0;
+        sleepingRule.name = "ZZZZZZZ...";
+        mZenModeHelper.mConfig.automaticRules.clear();
+        mZenModeHelper.mConfig.automaticRules.put(sleepingRule.id, sleepingRule);
+
+        AutomaticZenRule bedtime = new AutomaticZenRule.Builder("Bedtime Mode (TM)", CONDITION_ID)
+                .setType(TYPE_BEDTIME)
+                .build();
+        String bedtimeRuleId = mZenModeHelper.addAutomaticZenRule("pkg", bedtime, UPDATE_ORIGIN_APP,
+                "reason", CUSTOM_PKG_UID);
+
+        assertThat(mZenModeHelper.mConfig.automaticRules.keySet()).containsExactly(bedtimeRuleId);
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_MODES_API)
+    public void addAutomaticZenRule_withTypeBedtime_keepsEnabledSleeping() {
+        ZenRule sleepingRule = createCustomAutomaticRule(ZEN_MODE_IMPORTANT_INTERRUPTIONS,
+                ZenModeConfig.EVERY_NIGHT_DEFAULT_RULE_ID);
+        sleepingRule.enabled = true;
+        sleepingRule.userModifiedFields = 0;
+        sleepingRule.name = "ZZZZZZZ...";
+        mZenModeHelper.mConfig.automaticRules.clear();
+        mZenModeHelper.mConfig.automaticRules.put(sleepingRule.id, sleepingRule);
+
+        AutomaticZenRule bedtime = new AutomaticZenRule.Builder("Bedtime Mode (TM)", CONDITION_ID)
+                .setType(TYPE_BEDTIME)
+                .build();
+        String bedtimeRuleId = mZenModeHelper.addAutomaticZenRule("pkg", bedtime, UPDATE_ORIGIN_APP,
+                "reason", CUSTOM_PKG_UID);
+
+        assertThat(mZenModeHelper.mConfig.automaticRules.keySet()).containsExactly(
+                ZenModeConfig.EVERY_NIGHT_DEFAULT_RULE_ID, bedtimeRuleId);
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_MODES_API)
+    public void addAutomaticZenRule_withTypeBedtime_keepsCustomizedSleeping() {
+        ZenRule sleepingRule = createCustomAutomaticRule(ZEN_MODE_IMPORTANT_INTERRUPTIONS,
+                ZenModeConfig.EVERY_NIGHT_DEFAULT_RULE_ID);
+        sleepingRule.enabled = false;
+        sleepingRule.userModifiedFields = AutomaticZenRule.FIELD_INTERRUPTION_FILTER;
+        sleepingRule.name = "ZZZZZZZ...";
+        mZenModeHelper.mConfig.automaticRules.clear();
+        mZenModeHelper.mConfig.automaticRules.put(sleepingRule.id, sleepingRule);
+
+        AutomaticZenRule bedtime = new AutomaticZenRule.Builder("Bedtime Mode (TM)", CONDITION_ID)
+                .setType(TYPE_BEDTIME)
+                .build();
+        String bedtimeRuleId = mZenModeHelper.addAutomaticZenRule("pkg", bedtime, UPDATE_ORIGIN_APP,
+                "reason", CUSTOM_PKG_UID);
+
+        assertThat(mZenModeHelper.mConfig.automaticRules.keySet()).containsExactly(
+                ZenModeConfig.EVERY_NIGHT_DEFAULT_RULE_ID, bedtimeRuleId);
     }
 
     @Test
@@ -2429,7 +2519,11 @@
         assertEquals(0, mZenModeEventLogger.getNumRulesActive(1));
         assertFalse(mZenModeEventLogger.getIsUserAction(1));
         assertEquals(CUSTOM_PKG_UID, mZenModeEventLogger.getPackageUid(1));
-        checkDndProtoMatchesSetupZenConfig(mZenModeEventLogger.getPolicyProto(1));
+        if (Flags.modesApi()) {
+            assertThat(mZenModeEventLogger.getPolicyProto(1)).isNull();
+        } else {
+            checkDndProtoMatchesSetupZenConfig(mZenModeEventLogger.getPolicyProto(1));
+        }
     }
 
     @Test
@@ -2511,7 +2605,11 @@
         assertEquals(0, mZenModeEventLogger.getNumRulesActive(1));
         assertTrue(mZenModeEventLogger.getIsUserAction(1));
         assertEquals(Process.SYSTEM_UID, mZenModeEventLogger.getPackageUid(1));
-        checkDndProtoMatchesSetupZenConfig(mZenModeEventLogger.getPolicyProto(1));
+        if (Flags.modesApi()) {
+            assertThat(mZenModeEventLogger.getPolicyProto(1)).isNull();
+        } else {
+            checkDndProtoMatchesSetupZenConfig(mZenModeEventLogger.getPolicyProto(1));
+        }
 
         // When the system rule is enabled, this counts as an automatic action that comes from the
         // system and turns on DND
@@ -3016,6 +3114,48 @@
     }
 
     @Test
+    @EnableFlags(Flags.FLAG_MODES_API)
+    public void testZenModeEventLog_ruleWithInterruptionFilterAll_notLoggedAsDndChange() {
+        mTestFlagResolver.setFlagOverride(LOG_DND_STATE_EVENTS, true);
+        setupZenConfig();
+
+        // An app adds an automatic zen rule
+        AutomaticZenRule zenRule = new AutomaticZenRule("name",
+                null,
+                new ComponentName(CUSTOM_PKG_NAME, "cls"),
+                Uri.parse("condition"),
+                null,
+                NotificationManager.INTERRUPTION_FILTER_ALL, true);
+        String id = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule,
+                UPDATE_ORIGIN_APP, "test", CUSTOM_PKG_UID);
+
+        // Event 1: App activates the rule automatically.
+        mZenModeHelper.setAutomaticZenRuleState(id,
+                new Condition(zenRule.getConditionId(), "", STATE_TRUE, SOURCE_SCHEDULE),
+                UPDATE_ORIGIN_APP, CUSTOM_PKG_UID);
+
+        // Event 2: App deactivates the rule automatically.
+        mZenModeHelper.setAutomaticZenRuleState(id,
+                new Condition(zenRule.getConditionId(), "", STATE_FALSE, SOURCE_SCHEDULE),
+                UPDATE_ORIGIN_APP, CUSTOM_PKG_UID);
+
+        // In total, this represents 2 events.
+        assertEquals(2, mZenModeEventLogger.numLoggedChanges());
+
+        // However, they are not DND_TURNED_ON/_OFF (no notification filtering is taking place).
+        // Also, no consolidated ZenPolicy is logged (because of the same reason).
+        assertThat(mZenModeEventLogger.getEventId(0)).isEqualTo(
+                ZenModeEventLogger.ZenStateChangedEvent.DND_ACTIVE_RULES_CHANGED.getId());
+        assertThat(mZenModeEventLogger.getNumRulesActive(0)).isEqualTo(1);
+        assertThat(mZenModeEventLogger.getPolicyProto(0)).isNull();
+
+        assertThat(mZenModeEventLogger.getEventId(1)).isEqualTo(
+                ZenModeEventLogger.ZenStateChangedEvent.DND_ACTIVE_RULES_CHANGED.getId());
+        assertThat(mZenModeEventLogger.getNumRulesActive(1)).isEqualTo(0);
+        assertThat(mZenModeEventLogger.getPolicyProto(1)).isNull();
+    }
+
+    @Test
     public void testUpdateConsolidatedPolicy_defaultRulesOnly() {
         setupZenConfig();
 
@@ -3203,6 +3343,52 @@
     }
 
     @Test
+    @EnableFlags(Flags.FLAG_MODES_API)
+    public void testUpdateConsolidatedPolicy_ignoresActiveRulesWithInterruptionFilterAll() {
+        setupZenConfig();
+
+        // Rules with INTERRUPTION_FILTER_ALL are skipped when calculating consolidated policy.
+        // Note: rules with filter != PRIORITY should not have a custom policy. However, as of V
+        // this is only validated on rule addition, but not on rule update. :/
+
+        // Rule 1: PRIORITY, custom policy but not very strict (in fact, less strict than default).
+        AutomaticZenRule zenRuleWithPriority = new AutomaticZenRule("Priority",
+                null,
+                new ComponentName(CUSTOM_PKG_NAME, "cls"),
+                Uri.parse("priority"),
+                new ZenPolicy.Builder().allowMedia(true).build(),
+                NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
+        String rule1Id = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
+                zenRuleWithPriority, UPDATE_ORIGIN_APP, "test", CUSTOM_PKG_UID);
+        mZenModeHelper.setAutomaticZenRuleState(rule1Id,
+                new Condition(zenRuleWithPriority.getConditionId(), "", STATE_TRUE),
+                UPDATE_ORIGIN_APP, CUSTOM_PKG_UID);
+
+        // Rule 2: ALL, but somehow with a super strict ZenPolicy.
+        AutomaticZenRule zenRuleWithAll = new AutomaticZenRule("All",
+                null,
+                new ComponentName(CUSTOM_PKG_NAME, "cls"),
+                Uri.parse("priority"),
+                new ZenPolicy.Builder().disallowAllSounds().build(),
+                NotificationManager.INTERRUPTION_FILTER_ALL, true);
+        String rule2Id = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
+                zenRuleWithAll, UPDATE_ORIGIN_APP, "test", CUSTOM_PKG_UID);
+        mZenModeHelper.setAutomaticZenRuleState(rule2Id,
+                new Condition(zenRuleWithPriority.getConditionId(), "", STATE_TRUE),
+                UPDATE_ORIGIN_APP, CUSTOM_PKG_UID);
+
+        // Consolidated Policy should be default + rule1.
+        assertThat(mZenModeHelper.mConsolidatedPolicy.allowAlarms()).isFalse();  // default
+        assertThat(mZenModeHelper.mConsolidatedPolicy.allowMedia()).isTrue(); // priority rule
+        assertThat(mZenModeHelper.mConsolidatedPolicy.allowSystem()).isFalse();  // default
+        assertThat(mZenModeHelper.mConsolidatedPolicy.allowReminders()).isTrue();  // default
+        assertThat(mZenModeHelper.mConsolidatedPolicy.allowCalls()).isTrue();  // default
+        assertThat(mZenModeHelper.mConsolidatedPolicy.allowMessages()).isTrue(); // default
+        assertThat(mZenModeHelper.mConsolidatedPolicy.allowConversations()).isTrue();  // default
+        assertThat(mZenModeHelper.mConsolidatedPolicy.allowRepeatCallers()).isTrue(); // default
+    }
+
+    @Test
     public void zenRuleToAutomaticZenRule_allFields() {
         mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
         when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(
@@ -3225,6 +3411,7 @@
 
         rule.allowManualInvocation = ALLOW_MANUAL;
         rule.type = TYPE;
+        rule.userModifiedFields = AutomaticZenRule.FIELD_NAME;
         rule.iconResName = ICON_RES_NAME;
         rule.triggerDescription = TRIGGER_DESC;
 
@@ -3239,6 +3426,7 @@
         assertEquals(POLICY, actual.getZenPolicy());
         assertEquals(CONFIG_ACTIVITY, actual.getConfigurationActivity());
         assertEquals(TYPE, actual.getType());
+        assertEquals(AutomaticZenRule.FIELD_NAME, actual.getUserModifiedFields());
         assertEquals(ALLOW_MANUAL, actual.isManualInvocationAllowed());
         assertEquals(CREATION_TIME, actual.getCreationTime());
         assertEquals(OWNER.getPackageName(), actual.getPackageName());
@@ -3280,10 +3468,480 @@
         assertEquals(ALLOW_MANUAL, rule.allowManualInvocation);
         assertEquals(OWNER.getPackageName(), rule.getPkg());
         assertEquals(ICON_RES_NAME, rule.iconResName);
+        // Because the origin of the update is the app, we don't expect the bitmask to change.
+        assertEquals(0, rule.userModifiedFields);
         assertEquals(TRIGGER_DESC, rule.triggerDescription);
     }
 
     @Test
+    @EnableFlags(Flags.FLAG_MODES_API)
+    public void automaticZenRuleToZenRule_updatesNameUnlessUserModified() {
+        // Add a starting rule with the name OriginalName.
+        AutomaticZenRule azrBase = new AutomaticZenRule.Builder("OriginalName", CONDITION_ID)
+                .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+                .build();
+        String ruleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
+                azrBase, UPDATE_ORIGIN_APP, "reason", Process.SYSTEM_UID);
+        AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(ruleId);
+
+        // Checks the name can be changed by the app because the user has not modified it.
+        AutomaticZenRule azrUpdate = new AutomaticZenRule.Builder(rule)
+                .setName("NewName")
+                .build();
+        mZenModeHelper.updateAutomaticZenRule(ruleId, azrUpdate, UPDATE_ORIGIN_APP, "reason",
+                Process.SYSTEM_UID);
+        rule = mZenModeHelper.getAutomaticZenRule(ruleId);
+        assertThat(rule.getName()).isEqualTo("NewName");
+        assertThat(rule.canUpdate()).isTrue();
+
+        // The user modifies some other field in the rule, which makes the rule as a whole not
+        // app modifiable.
+        azrUpdate = new AutomaticZenRule.Builder(rule)
+                .setInterruptionFilter(INTERRUPTION_FILTER_ALARMS)
+                .build();
+        mZenModeHelper.updateAutomaticZenRule(ruleId, azrUpdate, UPDATE_ORIGIN_USER, "reason",
+                Process.SYSTEM_UID);
+        rule = mZenModeHelper.getAutomaticZenRule(ruleId);
+        assertThat(rule.getUserModifiedFields())
+                .isEqualTo(AutomaticZenRule.FIELD_INTERRUPTION_FILTER);
+        assertThat(rule.canUpdate()).isFalse();
+
+        // ...but the app can still modify the name, because the name itself hasn't been modified
+        // by the user.
+        azrUpdate = new AutomaticZenRule.Builder(rule)
+                .setName("NewAppName")
+                .build();
+        mZenModeHelper.updateAutomaticZenRule(ruleId, azrUpdate, UPDATE_ORIGIN_APP, "reason",
+                Process.SYSTEM_UID);
+        rule = mZenModeHelper.getAutomaticZenRule(ruleId);
+        assertThat(rule.getName()).isEqualTo("NewAppName");
+
+        // The user modifies the name.
+        azrUpdate = new AutomaticZenRule.Builder(rule)
+                .setName("UserProvidedName")
+                .build();
+        mZenModeHelper.updateAutomaticZenRule(ruleId, azrUpdate, UPDATE_ORIGIN_USER, "reason",
+                Process.SYSTEM_UID);
+        rule = mZenModeHelper.getAutomaticZenRule(ruleId);
+        assertThat(rule.getName()).isEqualTo("UserProvidedName");
+        assertThat(rule.getUserModifiedFields()).isEqualTo(AutomaticZenRule.FIELD_NAME
+                | AutomaticZenRule.FIELD_INTERRUPTION_FILTER);
+
+        // The app is no longer able to modify the name.
+        azrUpdate = new AutomaticZenRule.Builder(rule)
+                .setName("NewAppName")
+                .build();
+        mZenModeHelper.updateAutomaticZenRule(ruleId, azrUpdate, UPDATE_ORIGIN_APP, "reason",
+                Process.SYSTEM_UID);
+        rule = mZenModeHelper.getAutomaticZenRule(ruleId);
+        assertThat(rule.getName()).isEqualTo("UserProvidedName");
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_MODES_API)
+    public void automaticZenRuleToZenRule_updatesBitmaskAndValueForUserOrigin() {
+        // Adds a starting rule with empty zen policies and device effects
+        AutomaticZenRule azrBase = new AutomaticZenRule.Builder(NAME, CONDITION_ID)
+                .setZenPolicy(new ZenPolicy.Builder().build())
+                .setDeviceEffects(new ZenDeviceEffects.Builder().build())
+                .build();
+        // Adds the rule using the app, to avoid having any user modified bits set.
+        String ruleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
+                azrBase, UPDATE_ORIGIN_APP, "reason", Process.SYSTEM_UID);
+        AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(ruleId);
+
+        // Modifies the zen policy and device effects
+        ZenPolicy policy = new ZenPolicy.Builder(rule.getZenPolicy())
+                .allowChannels(ZenPolicy.CHANNEL_TYPE_PRIORITY)
+                .build();
+        ZenDeviceEffects deviceEffects =
+                new ZenDeviceEffects.Builder(rule.getDeviceEffects())
+                .setShouldDisplayGrayscale(true)
+                .build();
+        AutomaticZenRule azrUpdate = new AutomaticZenRule.Builder(rule)
+                .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+                .setZenPolicy(policy)
+                .setDeviceEffects(deviceEffects)
+                .build();
+
+        // Update the rule with the AZR from origin user.
+        mZenModeHelper.updateAutomaticZenRule(ruleId, azrUpdate, UPDATE_ORIGIN_USER, "reason",
+                Process.SYSTEM_UID);
+        rule = mZenModeHelper.getAutomaticZenRule(ruleId);
+
+        // UPDATE_ORIGIN_USER should change the bitmask and change the values.
+        assertThat(rule.getInterruptionFilter()).isEqualTo(INTERRUPTION_FILTER_PRIORITY);
+        assertThat(rule.getUserModifiedFields())
+                .isEqualTo(AutomaticZenRule.FIELD_INTERRUPTION_FILTER);
+        assertThat(rule.getZenPolicy().getUserModifiedFields())
+                .isEqualTo(ZenPolicy.FIELD_ALLOW_CHANNELS);
+        assertThat(rule.getZenPolicy().getAllowedChannels())
+                .isEqualTo(ZenPolicy.CHANNEL_TYPE_PRIORITY);
+        assertThat(rule.getDeviceEffects().getUserModifiedFields())
+                .isEqualTo(ZenDeviceEffects.FIELD_GRAYSCALE);
+        assertThat(rule.getDeviceEffects().shouldDisplayGrayscale()).isTrue();
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_MODES_API)
+    public void automaticZenRuleToZenRule_doesNotUpdateValuesForInitUserOrigin() {
+        // Adds a starting rule with empty zen policies and device effects
+        AutomaticZenRule azrBase = new AutomaticZenRule.Builder(NAME, CONDITION_ID)
+                .setInterruptionFilter(INTERRUPTION_FILTER_ALL) // Already the default, no change
+                .setZenPolicy(new ZenPolicy.Builder()
+                        .allowReminders(false)
+                        .build())
+                .setDeviceEffects(new ZenDeviceEffects.Builder()
+                        .setShouldDisplayGrayscale(false)
+                        .build())
+                .build();
+        // Adds the rule using the user, to set user-modified bits.
+        String ruleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
+                azrBase, UPDATE_ORIGIN_USER, "reason", Process.SYSTEM_UID);
+        AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(ruleId);
+        assertThat(rule.canUpdate()).isFalse();
+        assertThat(rule.getUserModifiedFields()).isEqualTo(AutomaticZenRule.FIELD_NAME);
+
+        ZenPolicy policy = new ZenPolicy.Builder(rule.getZenPolicy())
+                .allowReminders(true)
+                .build();
+        ZenDeviceEffects deviceEffects = new ZenDeviceEffects.Builder(rule.getDeviceEffects())
+                .setShouldDisplayGrayscale(true)
+                .build();
+        AutomaticZenRule azrUpdate = new AutomaticZenRule.Builder(rule)
+                .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+                .setZenPolicy(policy)
+                .setDeviceEffects(deviceEffects)
+                .build();
+
+        // Attempts to update the rule with the AZR from origin init user.
+        mZenModeHelper.updateAutomaticZenRule(ruleId, azrUpdate, UPDATE_ORIGIN_INIT_USER, "reason",
+                Process.SYSTEM_UID);
+        AutomaticZenRule unchangedRule = mZenModeHelper.getAutomaticZenRule(ruleId);
+
+        // UPDATE_ORIGIN_INIT_USER does not change the bitmask or values if rule is user modified.
+        // TODO: b/318506692 - Remove once we check that INIT origins can't call add/updateAZR.
+        assertThat(unchangedRule.getUserModifiedFields()).isEqualTo(rule.getUserModifiedFields());
+        assertThat(unchangedRule.getInterruptionFilter()).isEqualTo(INTERRUPTION_FILTER_ALL);
+        assertThat(unchangedRule.getZenPolicy().getUserModifiedFields()).isEqualTo(
+                rule.getZenPolicy().getUserModifiedFields());
+        assertThat(unchangedRule.getZenPolicy().getPriorityCategoryReminders()).isEqualTo(
+                ZenPolicy.STATE_DISALLOW);
+        assertThat(unchangedRule.getDeviceEffects().getUserModifiedFields()).isEqualTo(
+                rule.getDeviceEffects().getUserModifiedFields());
+        assertThat(unchangedRule.getDeviceEffects().shouldDisplayGrayscale()).isFalse();
+
+        // Creates a new rule with the AZR from origin init user.
+        String newRuleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
+                azrUpdate, UPDATE_ORIGIN_INIT_USER, "reason", Process.SYSTEM_UID);
+        AutomaticZenRule newRule = mZenModeHelper.getAutomaticZenRule(newRuleId);
+
+        // UPDATE_ORIGIN_INIT_USER does change the values if the rule is new,
+        // but does not update the bitmask.
+        assertThat(newRule.getUserModifiedFields()).isEqualTo(0);
+        assertThat(newRule.getInterruptionFilter()).isEqualTo(INTERRUPTION_FILTER_PRIORITY);
+        assertThat(newRule.getZenPolicy().getUserModifiedFields()).isEqualTo(0);
+        assertThat(newRule.getZenPolicy().getPriorityCategoryReminders())
+                .isEqualTo(ZenPolicy.STATE_ALLOW);
+        assertThat(newRule.getDeviceEffects().getUserModifiedFields()).isEqualTo(0);
+        assertThat(newRule.getDeviceEffects().shouldDisplayGrayscale()).isTrue();
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_MODES_API)
+    public void automaticZenRuleToZenRule_updatesValuesForSystemUiOrigin() {
+        // Adds a starting rule with empty zen policies and device effects
+        AutomaticZenRule azrBase = new AutomaticZenRule.Builder(NAME, CONDITION_ID)
+                .setInterruptionFilter(INTERRUPTION_FILTER_ALL)
+                .setZenPolicy(new ZenPolicy.Builder()
+                        .allowReminders(false)
+                        .build())
+                .setDeviceEffects(new ZenDeviceEffects.Builder()
+                        .setShouldDisplayGrayscale(false)
+                        .build())
+                .build();
+        // Adds the rule using the app, to avoid having any user modified bits set.
+        String ruleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
+                azrBase, UPDATE_ORIGIN_APP, "reason", Process.SYSTEM_UID);
+        AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(ruleId);
+
+        // Modifies the zen policy and device effects
+        ZenPolicy policy = new ZenPolicy.Builder(rule.getZenPolicy())
+                .allowReminders(true)
+                .build();
+        ZenDeviceEffects deviceEffects =
+                new ZenDeviceEffects.Builder(rule.getDeviceEffects())
+                        .setShouldDisplayGrayscale(true)
+                        .build();
+        AutomaticZenRule azrUpdate = new AutomaticZenRule.Builder(rule)
+                .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+                .setZenPolicy(policy)
+                .setDeviceEffects(deviceEffects)
+                .build();
+
+        // Update the rule with the AZR from origin systemUI.
+        mZenModeHelper.updateAutomaticZenRule(ruleId, azrUpdate, UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI,
+                "reason", Process.SYSTEM_UID);
+        rule = mZenModeHelper.getAutomaticZenRule(ruleId);
+
+        // UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI should change the value but NOT update the bitmask.
+        assertThat(rule.getUserModifiedFields()).isEqualTo(0);
+        assertThat(rule.getZenPolicy().getUserModifiedFields()).isEqualTo(0);
+        assertThat(rule.getZenPolicy().getPriorityCategoryReminders())
+                .isEqualTo(ZenPolicy.STATE_ALLOW);
+        assertThat(rule.getDeviceEffects().getUserModifiedFields()).isEqualTo(0);
+        assertThat(rule.getDeviceEffects().shouldDisplayGrayscale()).isTrue();
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_MODES_API)
+    public void automaticZenRuleToZenRule_updatesValuesIfRuleNotUserModified() {
+        // Adds a starting rule with empty zen policies and device effects
+        AutomaticZenRule azrBase = new AutomaticZenRule.Builder(NAME, CONDITION_ID)
+                .setInterruptionFilter(INTERRUPTION_FILTER_ALL)
+                .setZenPolicy(new ZenPolicy.Builder()
+                        .allowReminders(false)
+                        .build())
+                .setDeviceEffects(new ZenDeviceEffects.Builder()
+                        .setShouldDisplayGrayscale(false)
+                        .build())
+                .build();
+        // Adds the rule using the app, to avoid having any user modified bits set.
+        String ruleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
+                azrBase, UPDATE_ORIGIN_APP, "reason", Process.SYSTEM_UID);
+        AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(ruleId);
+        assertThat(rule.canUpdate()).isTrue();
+
+        ZenPolicy policy = new ZenPolicy.Builder()
+                .allowReminders(true)
+                .build();
+        ZenDeviceEffects deviceEffects = new ZenDeviceEffects.Builder()
+                .setShouldDisplayGrayscale(true)
+                .build();
+        AutomaticZenRule azrUpdate =  new AutomaticZenRule.Builder(rule)
+                .setInterruptionFilter(INTERRUPTION_FILTER_ALARMS)
+                .setZenPolicy(policy)
+                .setDeviceEffects(deviceEffects)
+                .build();
+
+        // Since the rule is not already user modified, UPDATE_ORIGIN_UNKNOWN can modify the rule.
+        // The bitmask is not modified.
+        mZenModeHelper.updateAutomaticZenRule(ruleId, azrUpdate, UPDATE_ORIGIN_UNKNOWN, "reason",
+                Process.SYSTEM_UID);
+        AutomaticZenRule unchangedRule = mZenModeHelper.getAutomaticZenRule(ruleId);
+
+        assertThat(unchangedRule.getUserModifiedFields()).isEqualTo(rule.getUserModifiedFields());
+        assertThat(unchangedRule.getInterruptionFilter()).isEqualTo(INTERRUPTION_FILTER_ALARMS);
+        assertThat(unchangedRule.getZenPolicy().getUserModifiedFields()).isEqualTo(
+                rule.getZenPolicy().getUserModifiedFields());
+        assertThat(unchangedRule.getZenPolicy().getPriorityCategoryReminders())
+                .isEqualTo(ZenPolicy.STATE_ALLOW);
+        assertThat(unchangedRule.getDeviceEffects().getUserModifiedFields()).isEqualTo(
+                rule.getDeviceEffects().getUserModifiedFields());
+        assertThat(unchangedRule.getDeviceEffects().shouldDisplayGrayscale()).isTrue();
+
+        // Creates another rule, this time from user. This will have user modified bits set.
+        String ruleIdUser = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
+                azrBase, UPDATE_ORIGIN_USER, "reason", Process.SYSTEM_UID);
+        AutomaticZenRule ruleUser = mZenModeHelper.getAutomaticZenRule(ruleIdUser);
+        assertThat(ruleUser.canUpdate()).isFalse();
+
+        // Zen rule update coming from unknown origin. This cannot fully update the rule, because
+        // the rule is already considered user modified.
+        mZenModeHelper.updateAutomaticZenRule(ruleIdUser, azrUpdate, UPDATE_ORIGIN_UNKNOWN,
+                "reason", Process.SYSTEM_UID);
+        ruleUser = mZenModeHelper.getAutomaticZenRule(ruleIdUser);
+
+        // UPDATE_ORIGIN_UNKNOWN can only change the value if the rule is not already user modified,
+        // so the rule is not changed, and neither is the bitmask.
+        assertThat(ruleUser.getInterruptionFilter()).isEqualTo(INTERRUPTION_FILTER_ALL);
+        // Interruption Filter All is the default value, so it's not included as a modified field.
+        assertThat(ruleUser.getUserModifiedFields() | AutomaticZenRule.FIELD_NAME).isGreaterThan(0);
+        assertThat(ruleUser.getZenPolicy().getUserModifiedFields()
+                | ZenPolicy.FIELD_PRIORITY_CATEGORY_REMINDERS).isGreaterThan(0);
+        assertThat(ruleUser.getZenPolicy().getPriorityCategoryReminders())
+                .isEqualTo(ZenPolicy.STATE_DISALLOW);
+        assertThat(ruleUser.getDeviceEffects().getUserModifiedFields()
+                | ZenDeviceEffects.FIELD_GRAYSCALE).isGreaterThan(0);
+        assertThat(ruleUser.getDeviceEffects().shouldDisplayGrayscale()).isFalse();
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_MODES_API)
+    public void automaticZenRuleToZenRule_updatesValuesIfRuleNew() {
+        // Adds a starting rule with empty zen policies and device effects
+        AutomaticZenRule azrBase = new AutomaticZenRule.Builder(NAME, CONDITION_ID)
+                .setInterruptionFilter(INTERRUPTION_FILTER_ALARMS)
+                .setZenPolicy(new ZenPolicy.Builder()
+                        .allowReminders(true)
+                        .build())
+                .setDeviceEffects(new ZenDeviceEffects.Builder()
+                        .setShouldDisplayGrayscale(true)
+                        .build())
+                .build();
+        // Adds the rule using origin unknown, to show that a new rule is always allowed.
+        String ruleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
+                azrBase, UPDATE_ORIGIN_UNKNOWN, "reason", Process.SYSTEM_UID);
+        AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(ruleId);
+
+        // The values are modified but the bitmask is not.
+        assertThat(rule.canUpdate()).isTrue();
+        assertThat(rule.getZenPolicy().getPriorityCategoryReminders())
+                .isEqualTo(ZenPolicy.STATE_ALLOW);
+        assertThat(rule.getDeviceEffects().shouldDisplayGrayscale()).isTrue();
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_MODES_API)
+    public void automaticZenRuleToZenRule_nullDeviceEffectsUpdate() {
+        // Adds a starting rule with empty zen policies and device effects
+        AutomaticZenRule azrBase = new AutomaticZenRule.Builder(NAME, CONDITION_ID)
+                .setDeviceEffects(new ZenDeviceEffects.Builder().build())
+                .build();
+        // Adds the rule using the app, to avoid having any user modified bits set.
+        String ruleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
+                azrBase, UPDATE_ORIGIN_APP, "reason", Process.SYSTEM_UID);
+        AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(ruleId);
+
+        AutomaticZenRule azr = new AutomaticZenRule.Builder(azrBase)
+                // Sets Device Effects to null
+                .setDeviceEffects(null)
+                .build();
+
+        // Zen rule update coming from unknown origin, but since the rule isn't already
+        // user modified, it can be updated.
+        mZenModeHelper.updateAutomaticZenRule(ruleId, azr, UPDATE_ORIGIN_UNKNOWN, "reason",
+                Process.SYSTEM_UID);
+        rule = mZenModeHelper.getAutomaticZenRule(ruleId);
+
+        // When AZR's ZenDeviceEffects is null, the updated rule's device effects will be null.
+        assertThat(rule.getDeviceEffects()).isNull();
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_MODES_API)
+    public void automaticZenRuleToZenRule_nullPolicyUpdate() {
+        // Adds a starting rule with empty zen policies and device effects
+        AutomaticZenRule azrBase = new AutomaticZenRule.Builder(NAME, CONDITION_ID)
+                .setZenPolicy(new ZenPolicy.Builder().build())
+                .build();
+        // Adds the rule using the app, to avoid having any user modified bits set.
+        String ruleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
+                azrBase, UPDATE_ORIGIN_APP, "reason", Process.SYSTEM_UID);
+        AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(ruleId);
+        assertThat(rule.canUpdate()).isTrue();
+
+        AutomaticZenRule azr = new AutomaticZenRule.Builder(azrBase)
+                // Set zen policy to null
+                .setZenPolicy(null)
+                .build();
+
+        // Zen rule update coming from unknown origin, but since the rule isn't already
+        // user modified, it can be updated.
+        mZenModeHelper.updateAutomaticZenRule(ruleId, azr, UPDATE_ORIGIN_UNKNOWN, "reason",
+                Process.SYSTEM_UID);
+        rule = mZenModeHelper.getAutomaticZenRule(ruleId);
+
+        // When AZR's ZenPolicy is null, we expect the updated rule's policy to be null.
+        assertThat(rule.getZenPolicy()).isNull();
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_MODES_API)
+    public void automaticZenRuleToZenRule_nullToNonNullPolicyUpdate() {
+        when(mContext.checkCallingPermission(anyString()))
+                .thenReturn(PackageManager.PERMISSION_GRANTED);
+        // Adds a starting rule with empty zen policies and device effects
+        AutomaticZenRule azrBase = new AutomaticZenRule.Builder(NAME, CONDITION_ID)
+                .setZenPolicy(null)
+                // .setDeviceEffects(new ZenDeviceEffects.Builder().build())
+                .build();
+        // Adds the rule using the app, to avoid having any user modified bits set.
+        String ruleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
+                azrBase, UPDATE_ORIGIN_APP, "reason", Process.SYSTEM_UID);
+        AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(ruleId);
+        assertThat(rule.canUpdate()).isTrue();
+
+        // Create a fully populated ZenPolicy.
+        ZenPolicy policy = new ZenPolicy.Builder()
+                .allowChannels(ZenPolicy.CHANNEL_TYPE_NONE) // Differs from the default
+                .allowReminders(true) // Differs from the default
+                .allowEvents(true) // Differs from the default
+                .allowConversations(ZenPolicy.CONVERSATION_SENDERS_IMPORTANT)
+                .allowMessages(PEOPLE_TYPE_STARRED)
+                .allowCalls(PEOPLE_TYPE_STARRED)
+                .allowRepeatCallers(true)
+                .allowAlarms(true)
+                .allowMedia(true)
+                .allowSystem(true) // Differs from the default
+                .showFullScreenIntent(true) // Differs from the default
+                .showLights(true) // Differs from the default
+                .showPeeking(true) // Differs from the default
+                .showStatusBarIcons(true)
+                .showBadges(true)
+                .showInAmbientDisplay(true) // Differs from the default
+                .showInNotificationList(true)
+                .build();
+        AutomaticZenRule azr = new AutomaticZenRule.Builder(azrBase)
+                .setZenPolicy(policy)
+                .build();
+
+        // Applies the update to the rule.
+        // Default config defined in getDefaultConfigParser() is used as the original rule.
+        mZenModeHelper.updateAutomaticZenRule(ruleId, azr, UPDATE_ORIGIN_USER, "reason",
+                Process.SYSTEM_UID);
+        rule = mZenModeHelper.getAutomaticZenRule(ruleId);
+
+        // New ZenPolicy differs from the default config
+        assertThat(rule.getZenPolicy()).isNotNull();
+        assertThat(rule.getZenPolicy().getAllowedChannels()).isEqualTo(ZenPolicy.CHANNEL_TYPE_NONE);
+        assertThat(rule.canUpdate()).isFalse();
+        assertThat(rule.getZenPolicy().getUserModifiedFields()).isEqualTo(
+                ZenPolicy.FIELD_ALLOW_CHANNELS
+                | ZenPolicy.FIELD_PRIORITY_CATEGORY_REMINDERS
+                | ZenPolicy.FIELD_PRIORITY_CATEGORY_EVENTS
+                | ZenPolicy.FIELD_PRIORITY_CATEGORY_SYSTEM
+                | ZenPolicy.FIELD_VISUAL_EFFECT_FULL_SCREEN_INTENT
+                | ZenPolicy.FIELD_VISUAL_EFFECT_LIGHTS
+                | ZenPolicy.FIELD_VISUAL_EFFECT_PEEK
+                | ZenPolicy.FIELD_VISUAL_EFFECT_AMBIENT
+        );
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_MODES_API)
+    public void automaticZenRuleToZenRule_nullToNonNullDeviceEffectsUpdate() {
+        // Adds a starting rule with empty zen policies and device effects
+        AutomaticZenRule azrBase = new AutomaticZenRule.Builder(NAME, CONDITION_ID)
+                .setDeviceEffects(null)
+                .build();
+        // Adds the rule using the app, to avoid having any user modified bits set.
+        String ruleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
+                azrBase, UPDATE_ORIGIN_APP, "reason", Process.SYSTEM_UID);
+        AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(ruleId);
+        assertThat(rule.canUpdate()).isTrue();
+
+        ZenDeviceEffects deviceEffects = new ZenDeviceEffects.Builder()
+                .setShouldDisplayGrayscale(true)
+                .build();
+        AutomaticZenRule azr = new AutomaticZenRule.Builder(rule)
+                .setDeviceEffects(deviceEffects)
+                .build();
+
+        // Applies the update to the rule.
+        mZenModeHelper.updateAutomaticZenRule(ruleId, azr, UPDATE_ORIGIN_USER, "reason",
+                Process.SYSTEM_UID);
+        rule = mZenModeHelper.getAutomaticZenRule(ruleId);
+
+        // New ZenDeviceEffects is used; all fields considered set, since previously were null.
+        assertThat(rule.getDeviceEffects()).isNotNull();
+        assertThat(rule.getDeviceEffects().shouldDisplayGrayscale()).isTrue();
+        assertThat(rule.canUpdate()).isFalse();
+        assertThat(rule.getDeviceEffects().getUserModifiedFields()).isEqualTo(
+                ZenDeviceEffects.FIELD_GRAYSCALE);
+    }
+
+    @Test
     public void testUpdateAutomaticRule_disabled_triggersBroadcast() throws Exception {
         setupZenConfig();
 
@@ -3669,6 +4327,324 @@
     }
 
     @Test
+    public void removeAndAddAutomaticZenRule_wasCustomized_isRestored() {
+        mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
+
+        // Start with a rule.
+        mZenModeHelper.mConfig.automaticRules.clear();
+        mTestClock.setNowMillis(1000);
+        AutomaticZenRule rule = new AutomaticZenRule.Builder("Test", CONDITION_ID)
+                .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+                .setZenPolicy(new ZenPolicy.Builder().allowRepeatCallers(false).build())
+                .build();
+        String ruleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), rule,
+                UPDATE_ORIGIN_APP, "add it", CUSTOM_PKG_UID);
+        assertThat(mZenModeHelper.getAutomaticZenRule(ruleId).getCreationTime()).isEqualTo(1000);
+        assertThat(mZenModeHelper.getAutomaticZenRule(ruleId).canUpdate()).isTrue();
+
+        // User customizes it.
+        AutomaticZenRule userUpdate = new AutomaticZenRule.Builder(rule)
+                .setInterruptionFilter(INTERRUPTION_FILTER_ALARMS)
+                .setZenPolicy(new ZenPolicy.Builder().allowRepeatCallers(true).build())
+                .build();
+        mZenModeHelper.updateAutomaticZenRule(ruleId, userUpdate, UPDATE_ORIGIN_USER, "userUpdate",
+                Process.SYSTEM_UID);
+
+        // App deletes it.
+        mTestClock.advanceByMillis(1000);
+        mZenModeHelper.removeAutomaticZenRule(ruleId, UPDATE_ORIGIN_APP, "delete it",
+                CUSTOM_PKG_UID);
+        assertThat(mZenModeHelper.mConfig.automaticRules).hasSize(0);
+        assertThat(mZenModeHelper.mConfig.deletedRules).hasSize(1);
+
+        // App adds it again.
+        mTestClock.advanceByMillis(1000);
+        String newRuleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), rule,
+                UPDATE_ORIGIN_APP, "add it again", CUSTOM_PKG_UID);
+
+        // Verify that the rule was restored:
+        // - id and creation time is the same as the original one.
+        // - ZenPolicy is the one that the user had set.
+        // - rule still has the user-modified fields.
+        AutomaticZenRule finalRule = mZenModeHelper.getAutomaticZenRule(newRuleId);
+        assertThat(finalRule.getCreationTime()).isEqualTo(1000); // And not 3000.
+        assertThat(newRuleId).isEqualTo(ruleId);
+        assertThat(finalRule.getInterruptionFilter()).isEqualTo(INTERRUPTION_FILTER_ALARMS);
+        assertThat(finalRule.getZenPolicy().getPriorityCategoryRepeatCallers()).isEqualTo(
+                ZenPolicy.STATE_ALLOW);
+        assertThat(finalRule.getUserModifiedFields()).isEqualTo(
+                AutomaticZenRule.FIELD_INTERRUPTION_FILTER);
+        assertThat(finalRule.getZenPolicy().getUserModifiedFields()).isEqualTo(
+                ZenPolicy.FIELD_PRIORITY_CATEGORY_REPEAT_CALLERS);
+
+        // Also, we discarded the "deleted rule" since we already used it for restoration.
+        assertThat(mZenModeHelper.mConfig.deletedRules).hasSize(0);
+    }
+
+    @Test
+    public void removeAndAddAutomaticZenRule_wasNotCustomized_isNotRestored() {
+        mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
+
+        // Start with a single rule.
+        mZenModeHelper.mConfig.automaticRules.clear();
+        mTestClock.setNowMillis(1000);
+        AutomaticZenRule rule = new AutomaticZenRule.Builder("Test", CONDITION_ID)
+                .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+                .setZenPolicy(new ZenPolicy.Builder().allowRepeatCallers(false).build())
+                .build();
+        String ruleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), rule,
+                UPDATE_ORIGIN_APP, "add it", CUSTOM_PKG_UID);
+        assertThat(mZenModeHelper.getAutomaticZenRule(ruleId).getCreationTime()).isEqualTo(1000);
+
+        // App deletes it.
+        mTestClock.advanceByMillis(1000);
+        mZenModeHelper.removeAutomaticZenRule(ruleId, UPDATE_ORIGIN_APP, "delete it",
+                CUSTOM_PKG_UID);
+        assertThat(mZenModeHelper.mConfig.automaticRules).hasSize(0);
+        assertThat(mZenModeHelper.mConfig.deletedRules).hasSize(0);
+
+        // App adds it again.
+        mTestClock.advanceByMillis(1000);
+        String newRuleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), rule,
+                UPDATE_ORIGIN_APP, "add it again", CUSTOM_PKG_UID);
+
+        // Verify that the rule was recreated. This means id and creation time are new.
+        AutomaticZenRule finalRule = mZenModeHelper.getAutomaticZenRule(newRuleId);
+        assertThat(finalRule.getCreationTime()).isEqualTo(3000);
+        assertThat(newRuleId).isNotEqualTo(ruleId);
+    }
+
+    @Test
+    public void removeAndAddAutomaticZenRule_recreatedButNotByApp_isNotRestored() {
+        mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
+
+        // Start with a single rule.
+        mZenModeHelper.mConfig.automaticRules.clear();
+        mTestClock.setNowMillis(1000);
+        AutomaticZenRule rule = new AutomaticZenRule.Builder("Test", CONDITION_ID)
+                .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+                .setZenPolicy(new ZenPolicy.Builder().allowRepeatCallers(false).build())
+                .build();
+        String ruleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), rule,
+                UPDATE_ORIGIN_APP, "add it", CUSTOM_PKG_UID);
+        assertThat(mZenModeHelper.getAutomaticZenRule(ruleId).getCreationTime()).isEqualTo(1000);
+
+        // User customizes it.
+        mTestClock.advanceByMillis(1000);
+        AutomaticZenRule userUpdate = new AutomaticZenRule.Builder(rule)
+                .setInterruptionFilter(INTERRUPTION_FILTER_ALARMS)
+                .setZenPolicy(new ZenPolicy.Builder().allowRepeatCallers(true).build())
+                .build();
+        mZenModeHelper.updateAutomaticZenRule(ruleId, userUpdate, UPDATE_ORIGIN_USER, "userUpdate",
+                Process.SYSTEM_UID);
+
+        // App deletes it.
+        mTestClock.advanceByMillis(1000);
+        mZenModeHelper.removeAutomaticZenRule(ruleId, UPDATE_ORIGIN_APP, "delete it",
+                CUSTOM_PKG_UID);
+        assertThat(mZenModeHelper.mConfig.automaticRules).hasSize(0);
+        assertThat(mZenModeHelper.mConfig.deletedRules).hasSize(1);
+
+        // User creates it again (unusual case, but ok).
+        mTestClock.advanceByMillis(1000);
+        String newRuleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), rule,
+                UPDATE_ORIGIN_USER, "add it anew", CUSTOM_PKG_UID);
+
+        // Verify that the rule was recreated. This means id and creation time are new, and the rule
+        // matches the latest data supplied to addAZR.
+        AutomaticZenRule finalRule = mZenModeHelper.getAutomaticZenRule(newRuleId);
+        assertThat(finalRule.getCreationTime()).isEqualTo(4000);
+        assertThat(newRuleId).isNotEqualTo(ruleId);
+        assertThat(finalRule.getInterruptionFilter()).isEqualTo(INTERRUPTION_FILTER_PRIORITY);
+        assertThat(finalRule.getZenPolicy().getPriorityCategoryRepeatCallers()).isEqualTo(
+                ZenPolicy.STATE_DISALLOW);
+
+        // Also, we discarded the "deleted rule" since we're not interested in recreating it.
+        assertThat(mZenModeHelper.mConfig.deletedRules).hasSize(0);
+    }
+
+    @Test
+    public void removeAndAddAutomaticZenRule_removedByUser_isNotRestored() {
+        mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
+
+        // Start with a single rule.
+        mZenModeHelper.mConfig.automaticRules.clear();
+        mTestClock.setNowMillis(1000);
+        AutomaticZenRule rule = new AutomaticZenRule.Builder("Test", CONDITION_ID)
+                .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+                .setZenPolicy(new ZenPolicy.Builder().allowRepeatCallers(false).build())
+                .build();
+        String ruleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), rule,
+                UPDATE_ORIGIN_APP, "add it", CUSTOM_PKG_UID);
+        assertThat(mZenModeHelper.getAutomaticZenRule(ruleId).getCreationTime()).isEqualTo(1000);
+
+        // User customizes it.
+        mTestClock.advanceByMillis(1000);
+        AutomaticZenRule userUpdate = new AutomaticZenRule.Builder(rule)
+                .setInterruptionFilter(INTERRUPTION_FILTER_ALARMS)
+                .setZenPolicy(new ZenPolicy.Builder().allowRepeatCallers(true).build())
+                .build();
+        mZenModeHelper.updateAutomaticZenRule(ruleId, userUpdate, UPDATE_ORIGIN_USER, "userUpdate",
+                Process.SYSTEM_UID);
+
+        // User deletes it.
+        mTestClock.advanceByMillis(1000);
+        mZenModeHelper.removeAutomaticZenRule(ruleId, UPDATE_ORIGIN_USER, "delete it",
+                CUSTOM_PKG_UID);
+        assertThat(mZenModeHelper.mConfig.automaticRules).hasSize(0);
+        assertThat(mZenModeHelper.mConfig.deletedRules).hasSize(0);
+
+        // App creates it again.
+        mTestClock.advanceByMillis(1000);
+        String newRuleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), rule,
+                UPDATE_ORIGIN_APP, "add it again", CUSTOM_PKG_UID);
+
+        // Verify that the rule was recreated. This means id and creation time are new.
+        AutomaticZenRule finalRule = mZenModeHelper.getAutomaticZenRule(newRuleId);
+        assertThat(finalRule.getCreationTime()).isEqualTo(4000);
+        assertThat(newRuleId).isNotEqualTo(ruleId);
+    }
+
+    @Test
+    public void removeAutomaticZenRule_preservedForRestoringByPackageAndConditionId() {
+        mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
+        mContext.getTestablePermissions().setPermission(Manifest.permission.MANAGE_NOTIFICATIONS,
+                PERMISSION_GRANTED); // So that canManageAZR passes although packages don't match.
+        mZenModeHelper.mConfig.automaticRules.clear();
+
+        // Start with a bunch of customized rules where conditionUris are not unique.
+        String id1 = mZenModeHelper.addAutomaticZenRule("pkg1",
+                new AutomaticZenRule.Builder("Test1", Uri.parse("uri1")).build(), UPDATE_ORIGIN_APP,
+                "add it", CUSTOM_PKG_UID);
+        String id2 = mZenModeHelper.addAutomaticZenRule("pkg1",
+                new AutomaticZenRule.Builder("Test2", Uri.parse("uri2")).build(), UPDATE_ORIGIN_APP,
+                "add it", CUSTOM_PKG_UID);
+        String id3 = mZenModeHelper.addAutomaticZenRule("pkg1",
+                new AutomaticZenRule.Builder("Test3", Uri.parse("uri2")).build(), UPDATE_ORIGIN_APP,
+                "add it", CUSTOM_PKG_UID);
+        String id4 = mZenModeHelper.addAutomaticZenRule("pkg2",
+                new AutomaticZenRule.Builder("Test4", Uri.parse("uri1")).build(), UPDATE_ORIGIN_APP,
+                "add it", CUSTOM_PKG_UID);
+        String id5 = mZenModeHelper.addAutomaticZenRule("pkg2",
+                new AutomaticZenRule.Builder("Test5", Uri.parse("uri1")).build(), UPDATE_ORIGIN_APP,
+                "add it", CUSTOM_PKG_UID);
+        for (ZenRule zenRule : mZenModeHelper.mConfig.automaticRules.values()) {
+            zenRule.userModifiedFields = AutomaticZenRule.FIELD_INTERRUPTION_FILTER;
+        }
+
+        mZenModeHelper.removeAutomaticZenRule(id1, UPDATE_ORIGIN_APP, "begone", CUSTOM_PKG_UID);
+        mZenModeHelper.removeAutomaticZenRule(id2, UPDATE_ORIGIN_APP, "begone", CUSTOM_PKG_UID);
+        mZenModeHelper.removeAutomaticZenRule(id3, UPDATE_ORIGIN_APP, "begone", CUSTOM_PKG_UID);
+        mZenModeHelper.removeAutomaticZenRule(id4, UPDATE_ORIGIN_APP, "begone", CUSTOM_PKG_UID);
+        mZenModeHelper.removeAutomaticZenRule(id5, UPDATE_ORIGIN_APP, "begone", CUSTOM_PKG_UID);
+
+        assertThat(mZenModeHelper.mConfig.deletedRules.keySet())
+                .containsExactly("pkg1|uri1", "pkg1|uri2", "pkg2|uri1");
+        assertThat(mZenModeHelper.mConfig.deletedRules.values().stream().map(zr -> zr.name)
+                .collect(Collectors.toList()))
+                .containsExactly("Test1", "Test3", "Test5");
+    }
+
+    @Test
+    public void removeAllZenRules_preservedForRestoring() {
+        mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
+        mZenModeHelper.mConfig.automaticRules.clear();
+
+        mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
+                new AutomaticZenRule.Builder("Test1", Uri.parse("uri1")).build(), UPDATE_ORIGIN_APP,
+                "add it", CUSTOM_PKG_UID);
+        mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
+                new AutomaticZenRule.Builder("Test2", Uri.parse("uri2")).build(), UPDATE_ORIGIN_APP,
+                "add it", CUSTOM_PKG_UID);
+
+        for (ZenRule zenRule : mZenModeHelper.mConfig.automaticRules.values()) {
+            zenRule.userModifiedFields = AutomaticZenRule.FIELD_INTERRUPTION_FILTER;
+        }
+
+        mZenModeHelper.removeAutomaticZenRules(mContext.getPackageName(), UPDATE_ORIGIN_APP,
+                "begone", CUSTOM_PKG_UID);
+
+        assertThat(mZenModeHelper.mConfig.deletedRules).hasSize(2);
+    }
+
+    @Test
+    public void removeAllZenRules_fromSystem_deletesPreservedRulesToo() {
+        mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
+        mZenModeHelper.mConfig.automaticRules.clear();
+
+        // Start with deleted rules from 2 different packages.
+        Instant now = Instant.ofEpochMilli(1701796461000L);
+        ZenRule pkg1Rule = newZenRule("pkg1", now.minus(1, ChronoUnit.DAYS), now);
+        ZenRule pkg2Rule = newZenRule("pkg2", now.minus(2, ChronoUnit.DAYS), now);
+        mZenModeHelper.mConfig.deletedRules.put(ZenModeConfig.deletedRuleKey(pkg1Rule), pkg1Rule);
+        mZenModeHelper.mConfig.deletedRules.put(ZenModeConfig.deletedRuleKey(pkg2Rule), pkg2Rule);
+
+        mZenModeHelper.removeAutomaticZenRules("pkg1",
+                UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "goodbye pkg1", Process.SYSTEM_UID);
+
+        // Preserved rules from pkg1 are gone; those from pkg2 are still there.
+        assertThat(mZenModeHelper.mConfig.deletedRules.values().stream().map(r -> r.pkg)
+                .collect(Collectors.toSet())).containsExactly("pkg2");
+    }
+
+    @Test
+    public void testRuleCleanup() throws Exception {
+        mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
+        Instant now = Instant.ofEpochMilli(1701796461000L);
+        Instant yesterday = now.minus(1, ChronoUnit.DAYS);
+        Instant aWeekAgo = now.minus(7, ChronoUnit.DAYS);
+        Instant twoMonthsAgo = now.minus(60, ChronoUnit.DAYS);
+        mTestClock.setNowMillis(now.toEpochMilli());
+
+        when(mPackageManager.getPackageInfo(eq("good_pkg"), anyInt()))
+                .thenReturn(new PackageInfo());
+        when(mPackageManager.getPackageInfo(eq("bad_pkg"), anyInt()))
+                .thenThrow(new PackageManager.NameNotFoundException("bad_pkg is not here"));
+
+        // Set up a config for another user containing:
+        ZenModeConfig config = new ZenModeConfig();
+        config.user = 42;
+        mZenModeHelper.mConfigs.put(42, config);
+        // okay rules (not deleted, package exists, with a range of creation dates).
+        config.automaticRules.put("ar1", newZenRule("good_pkg", now, null));
+        config.automaticRules.put("ar2", newZenRule("good_pkg", yesterday, null));
+        config.automaticRules.put("ar3", newZenRule("good_pkg", twoMonthsAgo, null));
+        // newish rules for a missing package
+        config.automaticRules.put("ar4", newZenRule("bad_pkg", yesterday, null));
+        // oldish rules belonging to a missing package
+        config.automaticRules.put("ar5", newZenRule("bad_pkg", aWeekAgo, null));
+        // rules deleted recently
+        config.deletedRules.put("del1", newZenRule("good_pkg", twoMonthsAgo, yesterday));
+        config.deletedRules.put("del2", newZenRule("good_pkg", twoMonthsAgo, aWeekAgo));
+        // rules deleted a long time ago
+        config.deletedRules.put("del3", newZenRule("good_pkg", twoMonthsAgo, twoMonthsAgo));
+        // rules for a missing package, created recently and deleted recently
+        config.deletedRules.put("del4", newZenRule("bad_pkg", yesterday, now));
+        // rules for a missing package, created a long time ago and deleted recently
+        config.deletedRules.put("del5", newZenRule("bad_pkg", twoMonthsAgo, now));
+        // rules for a missing package, created a long time ago and deleted a long time ago
+        config.deletedRules.put("del6", newZenRule("bad_pkg", twoMonthsAgo, twoMonthsAgo));
+
+        mZenModeHelper.onUserUnlocked(42); // copies config and cleans it up.
+
+        assertThat(mZenModeHelper.mConfig.automaticRules.keySet())
+                .containsExactly("ar1", "ar2", "ar3", "ar4");
+        assertThat(mZenModeHelper.mConfig.deletedRules.keySet())
+                .containsExactly("del1", "del2", "del4");
+    }
+
+    private static ZenRule newZenRule(String pkg, Instant createdAt, @Nullable Instant deletedAt) {
+        ZenRule rule = new ZenRule();
+        rule.pkg = pkg;
+        rule.creationTime = createdAt.toEpochMilli();
+        rule.deletionInstant = deletedAt;
+        // Plus stuff so that isValidAutomaticRule() passes
+        rule.name = "A rule from " + pkg + " created on " + createdAt;
+        rule.conditionId = Uri.parse(rule.name);
+        return rule;
+    }
+
+    @Test
     public void applyGlobalZenModeAsImplicitZenRule_createsImplicitRuleAndActivatesIt() {
         mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
         mZenModeHelper.mConfig.automaticRules.clear();
@@ -3780,6 +4756,7 @@
                 .allowCalls(PEOPLE_TYPE_CONTACTS)
                 .allowConversations(CONVERSATION_SENDERS_IMPORTANT)
                 .hideAllVisualEffects()
+                .allowChannels(ZenPolicy.CHANNEL_TYPE_PRIORITY)
                 .build();
         assertThat(mZenModeHelper.mConfig.automaticRules.values())
                 .comparingElementsUsing(IGNORE_TIMESTAMPS)
@@ -3811,6 +4788,7 @@
                 .allowCalls(PEOPLE_TYPE_STARRED)
                 .allowConversations(CONVERSATION_SENDERS_IMPORTANT)
                 .hideAllVisualEffects()
+                .allowChannels(ZenPolicy.CHANNEL_TYPE_PRIORITY)
                 .build();
         assertThat(mZenModeHelper.mConfig.automaticRules.values())
                 .comparingElementsUsing(IGNORE_TIMESTAMPS)
@@ -4268,4 +5246,25 @@
             return parser.nextTag();
         }
     }
+
+    private static class TestClock extends SimpleClock {
+        private long mNowMillis = 441644400000L;
+
+        private TestClock() {
+            super(ZoneOffset.UTC);
+        }
+
+        @Override
+        public long millis() {
+            return mNowMillis;
+        }
+
+        private void setNowMillis(long millis) {
+            mNowMillis = millis;
+        }
+
+        private void advanceByMillis(long millis) {
+            mNowMillis += millis;
+        }
+    }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenPolicyTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenPolicyTest.java
index 2f4f891c..21c96d6 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenPolicyTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenPolicyTest.java
@@ -34,6 +34,7 @@
 
 import com.google.protobuf.nano.InvalidProtocolBufferNanoException;
 
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -49,6 +50,11 @@
     @Rule
     public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
 
+    @Before
+    public final void setUp() {
+        mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
+    }
+
     @Test
     public void testZenPolicyApplyAllowedToDisallowed() {
         ZenPolicy.Builder builder = new ZenPolicy.Builder();
@@ -640,6 +646,54 @@
     }
 
     @Test
+    public void testFromParcel() {
+        ZenPolicy.Builder builder = new ZenPolicy.Builder();
+        builder.setUserModifiedFields(10);
+
+        ZenPolicy policy = builder.build();
+        assertThat(policy.getUserModifiedFields()).isEqualTo(10);
+
+        Parcel parcel = Parcel.obtain();
+        policy.writeToParcel(parcel, 0);
+        parcel.setDataPosition(0);
+
+        ZenPolicy fromParcel = ZenPolicy.CREATOR.createFromParcel(parcel);
+        assertThat(fromParcel.getUserModifiedFields()).isEqualTo(10);
+    }
+
+    @Test
+    public void testPolicy_userModifiedFields() {
+        ZenPolicy.Builder builder = new ZenPolicy.Builder();
+        builder.setUserModifiedFields(10);
+        assertThat(builder.build().getUserModifiedFields()).isEqualTo(10);
+
+        builder.setUserModifiedFields(0);
+        assertThat(builder.build().getUserModifiedFields()).isEqualTo(0);
+    }
+
+    @Test
+    public void testPolicyBuilder_constructFromPolicy() {
+        ZenPolicy.Builder builder = new ZenPolicy.Builder();
+        ZenPolicy policy = builder.allowRepeatCallers(true).allowAlarms(false)
+                .showLights(true).showBadges(false)
+                .allowChannels(ZenPolicy.CHANNEL_TYPE_PRIORITY)
+                .setUserModifiedFields(20).build();
+
+        ZenPolicy newPolicy = new ZenPolicy.Builder(policy).build();
+
+        assertThat(newPolicy.getPriorityCategoryAlarms()).isEqualTo(ZenPolicy.STATE_DISALLOW);
+        assertThat(newPolicy.getPriorityCategoryCalls()).isEqualTo(ZenPolicy.STATE_UNSET);
+        assertThat(newPolicy.getPriorityCategoryRepeatCallers()).isEqualTo(ZenPolicy.STATE_ALLOW);
+
+        assertThat(newPolicy.getVisualEffectLights()).isEqualTo(ZenPolicy.STATE_ALLOW);
+        assertThat(newPolicy.getVisualEffectBadge()).isEqualTo(ZenPolicy.STATE_DISALLOW);
+        assertThat(newPolicy.getVisualEffectPeek()).isEqualTo(ZenPolicy.STATE_UNSET);
+
+        assertThat(newPolicy.getAllowedChannels()).isEqualTo(ZenPolicy.CHANNEL_TYPE_PRIORITY);
+        assertThat(newPolicy.getUserModifiedFields()).isEqualTo(20);
+    }
+
+    @Test
     public void testTooLongLists_fromParcel() {
         ArrayList<Integer> longList = new ArrayList<Integer>(50);
         for (int i = 0; i < 50; i++) {
diff --git a/services/tests/vibrator/Android.bp b/services/tests/vibrator/Android.bp
index 6f37967..66dcaff 100644
--- a/services/tests/vibrator/Android.bp
+++ b/services/tests/vibrator/Android.bp
@@ -31,13 +31,13 @@
         "frameworks-base-testutils",
         "frameworks-services-vibrator-testutils",
         "junit",
-        "mockito-target-minus-junit4",
+        "mockito-target-inline-minus-junit4",
         "platform-test-annotations",
         "service-permission.stubs.system_server",
         "services.core",
         "flag-junit",
     ],
-
+    jni_libs: ["libdexmakerjvmtiagent"],
     platform_apis: true,
     certificate: "platform",
     dxflags: ["--multi-dex"],
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibrationScalerTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibrationScalerTest.java
index bbca704e..f9fe6a9 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibrationScalerTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibrationScalerTest.java
@@ -43,12 +43,17 @@
 import android.os.VibrationEffect;
 import android.os.Vibrator;
 import android.os.test.TestLooper;
+import android.os.vibrator.Flags;
 import android.os.vibrator.PrebakedSegment;
 import android.os.vibrator.PrimitiveSegment;
 import android.os.vibrator.StepSegment;
 import android.os.vibrator.VibrationConfig;
 import android.os.vibrator.VibrationEffectSegment;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 import android.provider.Settings;
+import android.util.SparseArray;
 
 import androidx.test.InstrumentationRegistry;
 
@@ -68,6 +73,9 @@
 
     @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
     @Rule public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule();
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule =
+            DeviceFlagsValueProvider.createCheckFlagsRule();
 
     @Mock private PowerManagerInternal mPowerManagerInternalMock;
     @Mock private PackageManagerInternal mPackageManagerInternalMock;
@@ -256,6 +264,29 @@
         assertEquals(0.5, scaled.getScale(), 1e-5);
     }
 
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED)
+    public void scale_withAdaptiveHaptics_scalesVibrationsCorrectly() {
+        setDefaultIntensity(USAGE_RINGTONE, VIBRATION_INTENSITY_HIGH);
+        setDefaultIntensity(USAGE_NOTIFICATION, VIBRATION_INTENSITY_HIGH);
+
+        SparseArray<Float> adaptiveHapticsScales = new SparseArray<>();
+        adaptiveHapticsScales.put(USAGE_RINGTONE, 0.5f);
+        adaptiveHapticsScales.put(USAGE_NOTIFICATION, 0.5f);
+        mVibrationScaler.updateAdaptiveHapticsScales(adaptiveHapticsScales);
+
+        StepSegment scaled = getFirstSegment(mVibrationScaler.scale(
+                VibrationEffect.createOneShot(128, 128), USAGE_RINGTONE));
+        // Ringtone scales down.
+        assertTrue(scaled.getAmplitude() < 0.5);
+
+        scaled = getFirstSegment(mVibrationScaler.scale(
+                VibrationEffect.createWaveform(new long[]{128}, new int[]{128}, -1),
+                USAGE_NOTIFICATION));
+        // Notification scales down.
+        assertTrue(scaled.getAmplitude() < 0.5);
+    }
+
     private void setDefaultIntensity(@VibrationAttributes.Usage int usage,
             @Vibrator.VibrationIntensity int intensity) {
         when(mVibrationConfigMock.getDefaultVibrationIntensity(eq(usage))).thenReturn(intensity);
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java
index 49efd1b..1e0b1df 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java
@@ -16,21 +16,49 @@
 
 package com.android.server.vibrator;
 
+import static android.os.VibrationAttributes.USAGE_ALARM;
+import static android.os.VibrationAttributes.USAGE_COMMUNICATION_REQUEST;
+import static android.os.VibrationAttributes.USAGE_NOTIFICATION;
+
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+
+import android.frameworks.vibrator.ScaleParam;
+import android.frameworks.vibrator.VibrationParam;
 import android.os.RemoteException;
+import android.util.SparseArray;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.util.ArrayList;
+import java.util.List;
 
 public class VibratorControlServiceTest {
 
+    @Rule
+    public MockitoRule rule = MockitoJUnit.rule();
+
+    @Mock
+    private VibrationScaler mMockVibrationScaler;
+    @Captor
+    private ArgumentCaptor<SparseArray<Float>> mVibrationScalesCaptor;
+
     private VibratorControlService mVibratorControlService;
     private final Object mLock = new Object();
 
     @Before
     public void setUp() throws Exception {
-        mVibratorControlService = new VibratorControlService(new VibratorControllerHolder(), mLock);
+        mVibratorControlService = new VibratorControlService(new VibratorControllerHolder(),
+                mMockVibrationScaler, mLock);
     }
 
     @Test
@@ -47,6 +75,8 @@
         FakeVibratorController fakeController = new FakeVibratorController();
         mVibratorControlService.registerVibratorController(fakeController);
         mVibratorControlService.unregisterVibratorController(fakeController);
+
+        verify(mMockVibrationScaler).updateAdaptiveHapticsScales(null);
         assertThat(fakeController.isLinkedToDeath).isFalse();
     }
 
@@ -56,8 +86,91 @@
         FakeVibratorController fakeController1 = new FakeVibratorController();
         FakeVibratorController fakeController2 = new FakeVibratorController();
         mVibratorControlService.registerVibratorController(fakeController1);
-
         mVibratorControlService.unregisterVibratorController(fakeController2);
+
+        verifyZeroInteractions(mMockVibrationScaler);
         assertThat(fakeController1.isLinkedToDeath).isTrue();
     }
+
+    @Test
+    public void testSetVibrationParams_cachesAdaptiveHapticsScalesCorrectly()
+            throws RemoteException {
+        FakeVibratorController fakeController = new FakeVibratorController();
+        mVibratorControlService.registerVibratorController(fakeController);
+        SparseArray<Float> vibrationScales = new SparseArray<>();
+        vibrationScales.put(ScaleParam.TYPE_ALARM, 0.7f);
+        vibrationScales.put(ScaleParam.TYPE_NOTIFICATION, 0.4f);
+
+        mVibratorControlService.setVibrationParams(generateVibrationParams(vibrationScales),
+                fakeController);
+
+        verify(mMockVibrationScaler).updateAdaptiveHapticsScales(mVibrationScalesCaptor.capture());
+        SparseArray<Float> cachedVibrationScales = mVibrationScalesCaptor.getValue();
+        assertThat(cachedVibrationScales.size()).isEqualTo(3);
+        assertThat(cachedVibrationScales.keyAt(0)).isEqualTo(USAGE_ALARM);
+        assertThat(cachedVibrationScales.valueAt(0)).isEqualTo(0.7f);
+        assertThat(cachedVibrationScales.keyAt(1)).isEqualTo(USAGE_NOTIFICATION);
+        assertThat(cachedVibrationScales.valueAt(1)).isEqualTo(0.4f);
+        // Setting ScaleParam.TYPE_NOTIFICATION will update vibration scaling for both
+        // notification and communication request usages.
+        assertThat(cachedVibrationScales.keyAt(2)).isEqualTo(USAGE_COMMUNICATION_REQUEST);
+        assertThat(cachedVibrationScales.valueAt(2)).isEqualTo(0.4f);
+    }
+
+    @Test
+    public void testSetVibrationParams_withUnregisteredController_ignoresRequest()
+            throws RemoteException {
+        FakeVibratorController fakeController = new FakeVibratorController();
+
+        SparseArray<Float> vibrationScales = new SparseArray<>();
+        vibrationScales.put(ScaleParam.TYPE_ALARM, 0.7f);
+        vibrationScales.put(ScaleParam.TYPE_NOTIFICATION, 0.4f);
+
+        mVibratorControlService.setVibrationParams(generateVibrationParams(vibrationScales),
+                fakeController);
+
+        verifyZeroInteractions(mMockVibrationScaler);
+    }
+
+    @Test
+    public void testClearVibrationParams_clearsCachedAdaptiveHapticsScales()
+            throws RemoteException {
+        FakeVibratorController fakeController = new FakeVibratorController();
+        mVibratorControlService.registerVibratorController(fakeController);
+        mVibratorControlService.clearVibrationParams(ScaleParam.TYPE_ALARM, fakeController);
+
+        verify(mMockVibrationScaler).updateAdaptiveHapticsScales(null);
+    }
+
+    @Test
+    public void testClearVibrationParams_withUnregisteredController_ignoresRequest()
+            throws RemoteException {
+        FakeVibratorController fakeController = new FakeVibratorController();
+
+        mVibratorControlService.clearVibrationParams(ScaleParam.TYPE_ALARM, fakeController);
+
+        verifyZeroInteractions(mMockVibrationScaler);
+    }
+
+    private VibrationParam[] generateVibrationParams(SparseArray<Float> vibrationScales) {
+        List<VibrationParam> vibrationParamList = new ArrayList<>();
+        for (int i = 0; i < vibrationScales.size(); i++) {
+            int type = vibrationScales.keyAt(i);
+            float scale = vibrationScales.valueAt(i);
+
+            vibrationParamList.add(generateVibrationParam(type, scale));
+        }
+
+        return vibrationParamList.toArray(new VibrationParam[0]);
+    }
+
+    private VibrationParam generateVibrationParam(int type, float scale) {
+        ScaleParam scaleParam = new ScaleParam();
+        scaleParam.typesMask = type;
+        scaleParam.scale = scale;
+        VibrationParam vibrationParam = new VibrationParam();
+        vibrationParam.setScale(scaleParam);
+
+        return vibrationParam;
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTests.java b/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTests.java
index 5363583..29467f2 100644
--- a/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTests.java
@@ -16,6 +16,10 @@
 
 package com.android.server.policy;
 
+import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
+import static android.view.WindowManagerGlobal.ADD_OKAY;
+
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
@@ -26,11 +30,16 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
 
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertEquals;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
 
 import android.app.ActivityManager;
+import android.app.AppOpsManager;
+import android.platform.test.flag.junit.SetFlagsRule;
 
 import androidx.test.filters.SmallTest;
 
@@ -39,6 +48,7 @@
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 
 /**
@@ -50,6 +60,9 @@
 @SmallTest
 public class PhoneWindowManagerTests {
 
+    @Rule
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
     PhoneWindowManager mPhoneWindowManager;
 
     @Before
@@ -85,6 +98,36 @@
         verify(mPhoneWindowManager).createHomeDockIntent();
     }
 
+    @Test
+    public void testCheckAddPermission_withoutAccessibilityOverlay_noAccessibilityAppOpLogged() {
+        mSetFlagsRule.enableFlags(android.view.contentprotection.flags.Flags
+                .FLAG_CREATE_ACCESSIBILITY_OVERLAY_APP_OP_ENABLED);
+        int[] outAppOp = new int[1];
+        assertEquals(ADD_OKAY, mPhoneWindowManager.checkAddPermission(TYPE_WALLPAPER,
+                /* isRoundedCornerOverlay= */ false, "test.pkg", outAppOp));
+        assertThat(outAppOp[0]).isEqualTo(AppOpsManager.OP_NONE);
+    }
+
+    @Test
+    public void testCheckAddPermission_withAccessibilityOverlay() {
+        mSetFlagsRule.enableFlags(android.view.contentprotection.flags.Flags
+                .FLAG_CREATE_ACCESSIBILITY_OVERLAY_APP_OP_ENABLED);
+        int[] outAppOp = new int[1];
+        assertEquals(ADD_OKAY, mPhoneWindowManager.checkAddPermission(TYPE_ACCESSIBILITY_OVERLAY,
+                /* isRoundedCornerOverlay= */ false, "test.pkg", outAppOp));
+        assertThat(outAppOp[0]).isEqualTo(AppOpsManager.OP_CREATE_ACCESSIBILITY_OVERLAY);
+    }
+
+    @Test
+    public void testCheckAddPermission_withAccessibilityOverlay_flagDisabled() {
+        mSetFlagsRule.disableFlags(android.view.contentprotection.flags.Flags
+                .FLAG_CREATE_ACCESSIBILITY_OVERLAY_APP_OP_ENABLED);
+        int[] outAppOp = new int[1];
+        assertEquals(ADD_OKAY, mPhoneWindowManager.checkAddPermission(TYPE_ACCESSIBILITY_OVERLAY,
+                /* isRoundedCornerOverlay= */ false, "test.pkg", outAppOp));
+        assertThat(outAppOp[0]).isEqualTo(AppOpsManager.OP_NONE);
+    }
+
     private void mockStartDockOrHome() throws Exception {
         doNothing().when(ActivityManager.getService()).stopAppSwitches();
         ActivityTaskManagerInternal mMockActivityTaskManagerInternal =
diff --git a/services/tests/wmtests/src/com/android/server/policy/WindowWakeUpPolicyTests.java b/services/tests/wmtests/src/com/android/server/policy/WindowWakeUpPolicyTests.java
new file mode 100644
index 0000000..c3da903
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/policy/WindowWakeUpPolicyTests.java
@@ -0,0 +1,354 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.policy;
+
+import static android.os.PowerManager.WAKE_REASON_CAMERA_LAUNCH;
+import static android.os.PowerManager.WAKE_REASON_LID;
+import static android.os.PowerManager.WAKE_REASON_GESTURE;
+import static android.os.PowerManager.WAKE_REASON_POWER_BUTTON;
+import static android.os.PowerManager.WAKE_REASON_WAKE_KEY;
+import static android.os.PowerManager.WAKE_REASON_WAKE_MOTION;
+import static android.view.InputDevice.SOURCE_ROTARY_ENCODER;
+import static android.view.InputDevice.SOURCE_TOUCHSCREEN;
+import static android.view.KeyEvent.KEYCODE_HOME;
+import static android.view.KeyEvent.KEYCODE_POWER;
+import static android.view.KeyEvent.KEYCODE_STEM_PRIMARY;
+
+import static com.android.internal.R.bool.config_allowTheaterModeWakeFromKey;
+import static com.android.internal.R.bool.config_allowTheaterModeWakeFromPowerKey;
+import static com.android.internal.R.bool.config_allowTheaterModeWakeFromMotion;
+import static com.android.internal.R.bool.config_allowTheaterModeWakeFromCameraLens;
+import static com.android.internal.R.bool.config_allowTheaterModeWakeFromLidSwitch;
+import static com.android.internal.R.bool.config_allowTheaterModeWakeFromGesture;
+import static com.android.server.policy.Flags.FLAG_SUPPORT_INPUT_WAKEUP_DELEGATE;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+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.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.res.Resources;
+import android.os.PowerManager;
+import android.platform.test.flag.junit.SetFlagsRule;
+import android.provider.Settings;
+
+import androidx.test.InstrumentationRegistry;
+
+import com.android.internal.os.Clock;
+import com.android.internal.util.test.FakeSettingsProvider;
+import com.android.internal.util.test.FakeSettingsProviderRule;
+import com.android.server.LocalServices;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.util.function.BooleanSupplier;
+/**
+ * Test class for {@link WindowWakeUpPolicy}.
+ *
+ * <p>Build/Install/Run: atest WmTests:WindowWakeUpPolicyTests
+ */
+public final class WindowWakeUpPolicyTests {
+    @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
+    @Rule public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule();
+    @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
+    @Mock PowerManager mPowerManager;
+    @Mock Clock mClock;
+    @Mock WindowWakeUpPolicyInternal.InputWakeUpDelegate mInputWakeUpDelegate;
+
+    private Context mContextSpy;
+    private Resources mResourcesSpy;
+
+    private WindowWakeUpPolicy mPolicy;
+
+    @Before
+    public void setUp() {
+        mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getContext()));
+        mResourcesSpy = spy(mContextSpy.getResources());
+        when(mContextSpy.getResources()).thenReturn(mResourcesSpy);
+        when(mContextSpy.getSystemService(PowerManager.class)).thenReturn(mPowerManager);
+        LocalServices.removeServiceForTest(WindowWakeUpPolicyInternal.class);
+    }
+
+    @Test
+    public void testSupportsInputWakeDelegatse_publishesLocalService() {
+        mSetFlagsRule.enableFlags(FLAG_SUPPORT_INPUT_WAKEUP_DELEGATE);
+
+        mPolicy = new WindowWakeUpPolicy(mContextSpy, mClock);
+
+        assertThat(LocalServices.getService(WindowWakeUpPolicyInternal.class)).isNotNull();
+    }
+
+    @Test
+    public void testDoesNotSupportInputWakeDelegatse_doesNotPublishLocalService() {
+        mSetFlagsRule.disableFlags(FLAG_SUPPORT_INPUT_WAKEUP_DELEGATE);
+
+        mPolicy = new WindowWakeUpPolicy(mContextSpy, mClock);
+
+        assertThat(LocalServices.getService(WindowWakeUpPolicyInternal.class)).isNull();
+    }
+
+    @Test
+    public void testMotionWakeUpDelegation_wakePowerManagerIfDelegateDoesNotHandleWake() {
+        setTheaterModeEnabled(false);
+        mSetFlagsRule.enableFlags(FLAG_SUPPORT_INPUT_WAKEUP_DELEGATE);
+        mPolicy = new WindowWakeUpPolicy(mContextSpy, mClock);
+        LocalServices.getService(WindowWakeUpPolicyInternal.class)
+                .setInputWakeUpDelegate(mInputWakeUpDelegate);
+
+        setDelegatedMotionWakeUpResult(true);
+
+        // Verify the policy wake up call succeeds because of the call on the delegate, and not
+        // because of a PowerManager wake up.
+        assertThat(mPolicy.wakeUpFromMotion(200, SOURCE_TOUCHSCREEN, true)).isTrue();
+        verify(mInputWakeUpDelegate).wakeUpFromMotion(200, SOURCE_TOUCHSCREEN, true);
+        verifyNoPowerManagerWakeUp();
+
+        setDelegatedMotionWakeUpResult(false);
+
+        // Verify the policy wake up call succeeds because of the PowerManager wake up, since the
+        // delegate would not handle the wake up request.
+        assertThat(mPolicy.wakeUpFromMotion(300, SOURCE_ROTARY_ENCODER, false)).isTrue();
+        verify(mInputWakeUpDelegate).wakeUpFromMotion(300, SOURCE_ROTARY_ENCODER, false);
+        verify(mPowerManager).wakeUp(300, WAKE_REASON_WAKE_MOTION, "android.policy:MOTION");
+    }
+
+    @Test
+    public void testKeyWakeUpDelegation_wakePowerManagerIfDelegateDoesNotHandleWake() {
+        setTheaterModeEnabled(false);
+        mSetFlagsRule.enableFlags(FLAG_SUPPORT_INPUT_WAKEUP_DELEGATE);
+        mPolicy = new WindowWakeUpPolicy(mContextSpy, mClock);
+        LocalServices.getService(WindowWakeUpPolicyInternal.class)
+                .setInputWakeUpDelegate(mInputWakeUpDelegate);
+
+        setDelegatedKeyWakeUpResult(true);
+
+        // Verify the policy wake up call succeeds because of the call on the delegate, and not
+        // because of a PowerManager wake up.
+        assertThat(mPolicy.wakeUpFromKey(200, KEYCODE_POWER, true)).isTrue();
+        verify(mInputWakeUpDelegate).wakeUpFromKey(200, KEYCODE_POWER, true);
+        verifyNoPowerManagerWakeUp();
+
+        setDelegatedKeyWakeUpResult(false);
+
+        // Verify the policy wake up call succeeds because of the PowerManager wake up, since the
+        // delegate would not handle the wake up request.
+        assertThat(mPolicy.wakeUpFromKey(300, KEYCODE_STEM_PRIMARY, false)).isTrue();
+        verify(mInputWakeUpDelegate).wakeUpFromKey(300, KEYCODE_STEM_PRIMARY, false);
+        verify(mPowerManager).wakeUp(300, WAKE_REASON_WAKE_KEY, "android.policy:KEY");
+    }
+
+    @Test
+    public void testDelegatedKeyWakeIsSubjectToPolicyChecks() {
+        mSetFlagsRule.enableFlags(FLAG_SUPPORT_INPUT_WAKEUP_DELEGATE);
+        setDelegatedKeyWakeUpResult(true);
+        setTheaterModeEnabled(true);
+        setBooleanRes(config_allowTheaterModeWakeFromKey, false);
+        setBooleanRes(config_allowTheaterModeWakeFromPowerKey, false);
+        mPolicy = new WindowWakeUpPolicy(mContextSpy, mClock);
+        LocalServices.getService(WindowWakeUpPolicyInternal.class)
+                .setInputWakeUpDelegate(mInputWakeUpDelegate);
+
+        // Check that the wake up does not happen because the theater mode policy check fails.
+        assertThat(mPolicy.wakeUpFromKey(200, KEYCODE_POWER, true)).isFalse();
+        verify(mInputWakeUpDelegate, never()).wakeUpFromKey(anyLong(), anyInt(), anyBoolean());
+    }
+
+    @Test
+    public void testDelegatedMotionWakeIsSubjectToPolicyChecks() {
+        mSetFlagsRule.enableFlags(FLAG_SUPPORT_INPUT_WAKEUP_DELEGATE);
+        setDelegatedMotionWakeUpResult(true);
+        setTheaterModeEnabled(true);
+        setBooleanRes(config_allowTheaterModeWakeFromMotion, false);
+        mPolicy = new WindowWakeUpPolicy(mContextSpy, mClock);
+        LocalServices.getService(WindowWakeUpPolicyInternal.class)
+                .setInputWakeUpDelegate(mInputWakeUpDelegate);
+
+        // Check that the wake up does not happen because the theater mode policy check fails.
+        assertThat(mPolicy.wakeUpFromMotion(200, SOURCE_TOUCHSCREEN, true)).isFalse();
+        verify(mInputWakeUpDelegate, never()).wakeUpFromMotion(anyLong(), anyInt(), anyBoolean());
+    }
+
+    @Test
+    public void testWakeUpFromMotion() {
+        runPowerManagerUpChecks(
+                () -> mPolicy.wakeUpFromMotion(mClock.uptimeMillis(), SOURCE_TOUCHSCREEN, true),
+                config_allowTheaterModeWakeFromMotion,
+                WAKE_REASON_WAKE_MOTION,
+                "android.policy:MOTION");
+    }
+
+    @Test
+    public void testWakeUpFromKey_nonPowerKey() {
+        runPowerManagerUpChecks(
+                () -> mPolicy.wakeUpFromKey(mClock.uptimeMillis(), KEYCODE_HOME, true),
+                config_allowTheaterModeWakeFromKey,
+                WAKE_REASON_WAKE_KEY,
+                "android.policy:KEY");
+    }
+
+    @Test
+    public void testWakeUpFromKey_powerKey() {
+        // Disable the resource affecting all wake keys because it affects power key as well.
+        // That way, power key wake during theater mode will solely be controlled by
+        // `config_allowTheaterModeWakeFromPowerKey` in the checks.
+        setBooleanRes(config_allowTheaterModeWakeFromKey, false);
+
+        // Test with power key
+        runPowerManagerUpChecks(
+                () -> mPolicy.wakeUpFromKey(mClock.uptimeMillis(), KEYCODE_POWER, true),
+                config_allowTheaterModeWakeFromPowerKey,
+                WAKE_REASON_POWER_BUTTON,
+                "android.policy:POWER");
+
+        // Test that power key wake ups happen during theater mode as long as wake-keys are allowed
+        // even if the power-key specific theater mode config is disabled.
+        setBooleanRes(config_allowTheaterModeWakeFromPowerKey, false);
+        runPowerManagerUpChecks(
+                () -> mPolicy.wakeUpFromKey(mClock.uptimeMillis(), KEYCODE_POWER, false),
+                config_allowTheaterModeWakeFromKey,
+                WAKE_REASON_POWER_BUTTON,
+                "android.policy:POWER");
+    }
+
+    @Test
+    public void testWakeUpFromLid() {
+        runPowerManagerUpChecks(
+                () -> mPolicy.wakeUpFromLid(),
+                config_allowTheaterModeWakeFromLidSwitch,
+                WAKE_REASON_LID,
+                "android.policy:LID");
+    }
+
+    @Test
+    public void testWakeUpFromWakeGesture() {
+        runPowerManagerUpChecks(
+                () -> mPolicy.wakeUpFromWakeGesture(),
+                config_allowTheaterModeWakeFromGesture,
+                WAKE_REASON_GESTURE,
+                "android.policy:GESTURE");
+    }
+
+    @Test
+    public void testwakeUpFromCameraCover() {
+        runPowerManagerUpChecks(
+                () -> mPolicy.wakeUpFromCameraCover(mClock.uptimeMillis()),
+                config_allowTheaterModeWakeFromCameraLens,
+                WAKE_REASON_CAMERA_LAUNCH,
+                "android.policy:CAMERA_COVER");
+    }
+
+    @Test
+    public void testWakeUpFromPowerKeyCameraGesture() {
+        // Disable the resource affecting all wake keys because it affects power key as well.
+        // That way, power key wake during theater mode will solely be controlled by
+        // `config_allowTheaterModeWakeFromPowerKey` in the checks.
+        setBooleanRes(config_allowTheaterModeWakeFromKey, false);
+
+        runPowerManagerUpChecks(
+                () -> mPolicy.wakeUpFromPowerKeyCameraGesture(),
+                config_allowTheaterModeWakeFromPowerKey,
+                WAKE_REASON_CAMERA_LAUNCH,
+                "android.policy:CAMERA_GESTURE_PREVENT_LOCK");
+    }
+
+    private void runPowerManagerUpChecks(
+            BooleanSupplier wakeUpCall,
+            int theatherModeWakeResId,
+            int expectedWakeReason,
+            String expectedWakeDetails) {
+        // Test under theater mode enabled.
+        setTheaterModeEnabled(true);
+
+        Mockito.reset(mPowerManager);
+        setBooleanRes(theatherModeWakeResId, true);
+        mPolicy = new WindowWakeUpPolicy(mContextSpy, mClock);
+        setUptimeMillis(200);
+        assertWithMessage("Wake should happen in theater mode when config allows it.")
+                .that(wakeUpCall.getAsBoolean()).isTrue();
+        verify(mPowerManager).wakeUp(200L, expectedWakeReason, expectedWakeDetails);
+
+        Mockito.reset(mPowerManager);
+        setBooleanRes(theatherModeWakeResId, false);
+        mPolicy = new WindowWakeUpPolicy(mContextSpy, mClock);
+        setUptimeMillis(250);
+        assertWithMessage("Wake should not happen in theater mode when config disallows it.")
+                .that(wakeUpCall.getAsBoolean()).isFalse();
+        verifyNoPowerManagerWakeUp();
+
+        // Cases when theater mode is disabled.
+        setTheaterModeEnabled(false);
+
+        Mockito.reset(mPowerManager);
+        setBooleanRes(theatherModeWakeResId, true);
+        mPolicy = new WindowWakeUpPolicy(mContextSpy, mClock);
+        setUptimeMillis(300);
+        assertWithMessage("Wake should happen when not in theater mode.")
+                .that(wakeUpCall.getAsBoolean()).isTrue();
+        verify(mPowerManager).wakeUp(300L, expectedWakeReason, expectedWakeDetails);
+
+        Mockito.reset(mPowerManager);
+        setBooleanRes(theatherModeWakeResId, false);
+        mPolicy = new WindowWakeUpPolicy(mContextSpy, mClock);
+        setUptimeMillis(350);
+        assertWithMessage("Wake should happen when not in theater mode.")
+                .that(wakeUpCall.getAsBoolean()).isTrue();
+        verify(mPowerManager).wakeUp(350L, expectedWakeReason, expectedWakeDetails);
+    }
+
+    private void verifyNoPowerManagerWakeUp() {
+        verify(mPowerManager, never()).wakeUp(anyLong(), anyInt(), anyString());
+    }
+
+    private void setBooleanRes(int resId, boolean val) {
+        when(mResourcesSpy.getBoolean(resId)).thenReturn(val);
+    }
+
+    private void setUptimeMillis(long uptimeMillis) {
+        when(mClock.uptimeMillis()).thenReturn(uptimeMillis);
+    }
+
+    private void setTheaterModeEnabled(boolean enabled) {
+        Settings.Global.putInt(
+                mContextSpy.getContentResolver(), Settings.Global.THEATER_MODE_ON, enabled ? 1 : 0);
+    }
+
+    private void setDelegatedMotionWakeUpResult(boolean result) {
+        when(mInputWakeUpDelegate.wakeUpFromMotion(anyLong(), anyInt(), anyBoolean()))
+                .thenReturn(result);
+    }
+
+    private void setDelegatedKeyWakeUpResult(boolean result) {
+        when(mInputWakeUpDelegate.wakeUpFromKey(anyLong(), anyInt(), anyBoolean()))
+                .thenReturn(result);
+    }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 718c598..9bb2da0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -143,7 +143,6 @@
 import android.os.RemoteException;
 import android.platform.test.annotations.Presubmit;
 import android.provider.DeviceConfig;
-import android.util.MergedConfiguration;
 import android.util.MutableBoolean;
 import android.view.DisplayInfo;
 import android.view.IRemoteAnimationFinishedCallback;
@@ -161,6 +160,7 @@
 
 import com.android.internal.R;
 import com.android.server.wm.ActivityRecord.State;
+import com.android.window.flags.Flags;
 
 import org.junit.Assert;
 import org.junit.Before;
@@ -320,7 +320,7 @@
     }
 
     private void ensureActivityConfiguration(ActivityRecord activity) {
-        activity.ensureActivityConfiguration(0 /* globalChanges */, false /* preserveWindow */);
+        activity.ensureActivityConfiguration();
     }
 
     @Test
@@ -394,8 +394,7 @@
         activity.setState(RESUMED, "Testing");
 
         task.onRequestedOverrideConfigurationChanged(task.getConfiguration());
-        activity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(),
-                activity.getConfiguration()));
+        activity.setLastReportedConfiguration(new Configuration(), activity.getConfiguration());
 
         activity.info.configChanges &= ~CONFIG_ORIENTATION;
         final Configuration newConfig = new Configuration(task.getConfiguration());
@@ -419,8 +418,7 @@
         activity.setState(RESUMED, "Testing");
 
         task.onRequestedOverrideConfigurationChanged(task.getConfiguration());
-        activity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(),
-                activity.getConfiguration()));
+        activity.setLastReportedConfiguration(new Configuration(), activity.getConfiguration());
 
         activity.info.configChanges &= ~CONFIG_ORIENTATION;
         final Configuration newConfig = new Configuration(task.getConfiguration());
@@ -446,8 +444,7 @@
         activity.setState(RESUMED, "Testing");
 
         task.onRequestedOverrideConfigurationChanged(task.getConfiguration());
-        activity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(),
-                activity.getConfiguration()));
+        activity.setLastReportedConfiguration(new Configuration(), activity.getConfiguration());
 
         activity.info.configChanges &= ~CONFIG_ORIENTATION;
         final Configuration newConfig = new Configuration(task.getConfiguration());
@@ -467,8 +464,7 @@
         activity.setState(RESUMED, "Testing");
 
         task.onRequestedOverrideConfigurationChanged(task.getConfiguration());
-        activity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(),
-                activity.getConfiguration()));
+        activity.setLastReportedConfiguration(new Configuration(), activity.getConfiguration());
 
         activity.info.configChanges &= ~ActivityInfo.CONFIG_FONT_SCALE;
         final Configuration newConfig = new Configuration(task.getConfiguration());
@@ -570,8 +566,7 @@
                 .build();
         activity.setState(RESUMED, "Testing");
 
-        activity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(),
-                activity.getConfiguration()));
+        activity.setLastReportedConfiguration(new Configuration(), activity.getConfiguration());
 
         clearInvocations(mClientLifecycleManager);
 
@@ -718,7 +713,7 @@
 
         // Clear size compat.
         activity.clearSizeCompatMode();
-        activity.ensureActivityConfiguration(0 /* globalChanges */, false /* preserveWindow */);
+        activity.ensureActivityConfiguration();
         mDisplayContent.sendNewConfiguration();
 
         // Relaunching the app should still respect the orientation request.
@@ -798,8 +793,7 @@
             doReturn(false).when(stack).isTranslucent(any());
             assertTrue(task.shouldBeVisible(null /* starting */));
 
-            activity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(),
-                    activity.getConfiguration()));
+            activity.setLastReportedConfiguration(new Configuration(), activity.getConfiguration());
 
             final Configuration newConfig = new Configuration(activity.getConfiguration());
             final int shortSide = newConfig.screenWidthDp == newConfig.screenHeightDp
@@ -819,8 +813,7 @@
 
             task.onConfigurationChanged(newConfig);
 
-            activity.ensureActivityConfiguration(0 /* globalChanges */,
-                    false /* preserveWindow */, true /* ignoreVisibility */);
+            activity.ensureActivityConfiguration(true /* ignoreVisibility */);
 
             final ActivityConfigurationChangeItem expected =
                     ActivityConfigurationChangeItem.obtain(activity.token,
@@ -846,7 +839,7 @@
     }
 
     @Test
-    public void testTakeOptions() {
+    public void testTakeSceneTransitionInfo() {
         final ActivityRecord activity = createActivityWithTask();
         ActivityOptions opts = ActivityOptions.makeRemoteAnimation(
                 new RemoteAnimationAdapter(new Stub() {
@@ -864,7 +857,9 @@
                     }
                 }, 0, 0));
         activity.updateOptionsLocked(opts);
-        assertNotNull(activity.takeOptions());
+        // Ensure the SceneTransitionInfo is null (since the ActivityOptions is for remote
+        // animation and AR#takeSceneTransitionInfo also clear the AR#mPendingOptions
+        assertNull(activity.takeSceneTransitionInfo());
         assertNull(activity.getOptions());
 
         final AppTransition appTransition = activity.mDisplayContent.mAppTransition;
@@ -1563,8 +1558,7 @@
         topActivity.nowVisible = true;
         topActivity.setState(RESUMED, "true");
         doCallRealMethod().when(mRootWindowContainer).ensureActivitiesVisible(
-                any() /* starting */, anyInt() /* configChanges */,
-                anyBoolean() /* preserveWindows */, anyBoolean() /* notifyClients */);
+                any() /* starting */, anyBoolean() /* notifyClients */);
         topActivity.setShowWhenLocked(true);
 
         // Verify the stack-top activity is occluded keyguard.
@@ -1624,7 +1618,6 @@
         secondActivity.finishing = true;
         secondActivity.completeFinishing("test");
         verify(secondActivity.mDisplayContent).ensureActivitiesVisible(null /* starting */,
-                0 /* configChanges */ , false /* preserveWindows */,
                 true /* notifyClients */);
 
         // Finish the first activity
@@ -1632,7 +1625,6 @@
         firstActivity.setVisibleRequested(true);
         firstActivity.completeFinishing("test");
         verify(firstActivity.mDisplayContent, times(2)).ensureActivitiesVisible(null /* starting */,
-                0 /* configChanges */ , false /* preserveWindows */,
                 true /* notifyClients */);
 
         // Remove the translucent activity and clear invocations for next test
@@ -1960,6 +1952,7 @@
         display.continueUpdateOrientationForDiffOrienLaunchingApp();
         assertTrue(display.isFixedRotationLaunchingApp(activity));
 
+        activity.stopFreezingScreen(true /* unfreezeSurfaceNow */, true /* force */);
         // Simulate the rotation has been updated to previous one, e.g. sensor updates before the
         // remote rotation is completed.
         doReturn(originalRotation).when(displayRotation).rotationForOrientation(
@@ -1970,14 +1963,12 @@
         activity.finishFixedRotationTransform();
         final ScreenRotationAnimation rotationAnim = display.getRotationAnimation();
         assertNotNull(rotationAnim);
-        rotationAnim.setRotation(display.getPendingTransaction(), originalRotation);
 
         // Because the display doesn't rotate, the rotated activity needs to cancel the fixed
         // rotation. There should be a rotation animation to cover the change of activity.
         verify(activity).onCancelFixedRotationTransform(rotatedInfo.rotation);
         assertTrue(activity.isFreezingScreen());
         assertFalse(displayRotation.isRotatingSeamlessly());
-        assertTrue(rotationAnim.isRotating());
 
         // Simulate the remote rotation has completed and the configuration doesn't change, then
         // the rotated activity should also be restored by clearing the transform.
@@ -2644,6 +2635,9 @@
         // Can specify orientation if the current orientation candidate is orientation behind.
         assertEquals(SCREEN_ORIENTATION_LANDSCAPE,
                 activity.getOrientation(SCREEN_ORIENTATION_BEHIND));
+        activity.makeFinishingLocked();
+        assertEquals("Finishing activity must not report orientation",
+                SCREEN_ORIENTATION_UNSET, activity.getOrientation(SCREEN_ORIENTATION_BEHIND));
 
         final ActivityRecord translucentActivity = new ActivityBuilder(mAtm)
                 .setActivityTheme(android.R.style.Theme_Translucent)
@@ -3315,7 +3309,7 @@
         // keyguard to back to the app, expect IME insets is not frozen
         app.mActivityRecord.commitVisibility(true, false);
         mDisplayContent.updateImeInputAndControlTarget(app);
-        mDisplayContent.mWmService.mRoot.performSurfacePlacement();
+        performSurfacePlacementAndWaitForWindowAnimator();
 
         assertFalse(app.mActivityRecord.mImeInsetsFrozenUntilStartInput);
 
@@ -3364,13 +3358,13 @@
         mDisplayContent.setImeLayeringTarget(app2);
         app2.mActivityRecord.commitVisibility(true, false);
         mDisplayContent.updateImeInputAndControlTarget(app2);
-        mDisplayContent.mWmService.mRoot.performSurfacePlacement();
+        performSurfacePlacementAndWaitForWindowAnimator();
 
         // Verify after unfreezing app2's IME insets state, we won't dispatch visible IME insets
         // to client if the app didn't request IME visible.
         assertFalse(app2.mActivityRecord.mImeInsetsFrozenUntilStartInput);
 
-        if (mWm.mFlags.mWindowStateResizeItemFlag) {
+        if (Flags.bundleClientTransactionFlag()) {
             verify(app2.getProcess()).scheduleClientTransactionItem(
                     isA(WindowStateResizeItem.class));
         } else {
@@ -3418,7 +3412,7 @@
         // frozen until the input started.
         mDisplayContent.setImeLayeringTarget(app1);
         mDisplayContent.updateImeInputAndControlTarget(app1);
-        mDisplayContent.mWmService.mRoot.performSurfacePlacement();
+        performSurfacePlacementAndWaitForWindowAnimator();
 
         assertEquals(app1, mDisplayContent.getImeInputTarget());
         assertFalse(activity1.mImeInsetsFrozenUntilStartInput);
@@ -3697,7 +3691,7 @@
         doReturn(false).when(activity).showToCurrentUser();
         spyOn(taskFragment);
         doReturn(false).when(taskFragment).shouldBeVisible(any());
-        display.ensureActivitiesVisible(null, 0, false, false);
+        display.ensureActivitiesVisible(null /* starting */, false /* notifyClients */);
         assertFalse(activity.isVisibleRequested());
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivitySnapshotControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivitySnapshotControllerTests.java
index 98f1843..03d3029 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivitySnapshotControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivitySnapshotControllerTests.java
@@ -17,13 +17,31 @@
 package com.android.server.wm;
 
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
 
+import android.content.ComponentName;
+import android.graphics.ColorSpace;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.hardware.HardwareBuffer;
 import android.platform.test.annotations.Presubmit;
+import android.util.ArraySet;
+import android.view.Surface;
+import android.window.TaskSnapshot;
 
 import androidx.test.filters.SmallTest;
 
@@ -32,6 +50,7 @@
 import org.junit.runner.RunWith;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 
 /**
  * Test class for {@link ActivitySnapshotController}.
@@ -45,6 +64,7 @@
 public class ActivitySnapshotControllerTests extends WindowTestsBase {
 
     private ActivitySnapshotController mActivitySnapshotController;
+
     @Before
     public void setUp() throws Exception {
         spyOn(mWm.mSnapshotController.mActivitySnapshotController);
@@ -154,4 +174,90 @@
         assertEquals(openingWindowBelow.mActivityRecord,
                 mActivitySnapshotController.mPendingLoadActivity.valueAt(0));
     }
+
+    /**
+     * Simulate multiple TaskFragments inside a task.
+     */
+    @Test
+    public void testMultipleActivitiesLoadSnapshot() {
+        final Task testTask = createTask(mDisplayContent);
+        final ActivityRecord activityA = createActivityRecord(testTask);
+        final ActivityRecord activityB = createActivityRecord(testTask);
+        final ActivityRecord activityC = createActivityRecord(testTask);
+        final TaskSnapshot taskSnapshot = createSnapshot();
+
+        final int[] mixedCode = new int[3];
+        mixedCode[0] = ActivitySnapshotController.getSystemHashCode(activityA);
+        mixedCode[1] = ActivitySnapshotController.getSystemHashCode(activityB);
+        mixedCode[2] = ActivitySnapshotController.getSystemHashCode(activityC);
+
+        mActivitySnapshotController.addUserSavedFile(testTask.mUserId, taskSnapshot, mixedCode);
+        mActivitySnapshotController.mCache.putSnapshot(activityA, taskSnapshot);
+        mActivitySnapshotController.mCache.putSnapshot(activityB, taskSnapshot);
+        mActivitySnapshotController.mCache.putSnapshot(activityC, taskSnapshot);
+
+        assertTrue(mActivitySnapshotController.hasRecord(activityA));
+        assertTrue(mActivitySnapshotController.hasRecord(activityB));
+
+        // If A is removed, B and C should also be removed because they share the same snapshot.
+        mActivitySnapshotController.onAppRemoved(activityA);
+        assertFalse(mActivitySnapshotController.hasRecord(activityA));
+        assertFalse(mActivitySnapshotController.hasRecord(activityB));
+        final ActivityRecord[] singleActivityList = new ActivityRecord[1];
+        singleActivityList[0] = activityA;
+        assertNull(mActivitySnapshotController.getSnapshot(singleActivityList));
+        singleActivityList[0] = activityB;
+        assertNull(mActivitySnapshotController.getSnapshot(singleActivityList));
+        final ActivityRecord[] activities = new ActivityRecord[3];
+        activities[0] = activityA;
+        activities[1] = activityB;
+        activities[2] = activityC;
+        assertNull(mActivitySnapshotController.getSnapshot(activities));
+
+        // Reset and test load snapshot
+        mActivitySnapshotController.addUserSavedFile(testTask.mUserId, taskSnapshot, mixedCode);
+        // Request to load by B, nothing will be loaded because the snapshot was [A,B,C].
+        mActivitySnapshotController.mPendingLoadActivity.add(activityB);
+        mActivitySnapshotController.loadActivitySnapshot();
+        verify(mActivitySnapshotController, never()).loadSnapshotInner(any(), any());
+
+        // Able to load snapshot when requesting for all A, B, C
+        mActivitySnapshotController.mPendingLoadActivity.clear();
+        mActivitySnapshotController.mPendingLoadActivity.add(activityA);
+        mActivitySnapshotController.mPendingLoadActivity.add(activityB);
+        mActivitySnapshotController.mPendingLoadActivity.add(activityC);
+        final ArraySet<ActivityRecord> verifyList = new ArraySet<>();
+        verifyList.add(activityA);
+        verifyList.add(activityB);
+        verifyList.add(activityC);
+        mActivitySnapshotController.loadActivitySnapshot();
+        verify(mActivitySnapshotController).loadSnapshotInner(argThat(
+                argument -> {
+                    final ArrayList<ActivityRecord> argumentList = new ArrayList<>(
+                            Arrays.asList(argument));
+                    return verifyList.containsAll(argumentList)
+                            && argumentList.containsAll(verifyList);
+                }),
+                any());
+
+        for (int i = activities.length - 1; i >= 0; --i) {
+            mActivitySnapshotController.mCache.putSnapshot(activities[i], taskSnapshot);
+        }
+        // The loaded snapshot can be retrieved only if the activities match exactly.
+        singleActivityList[0] = activityB;
+        assertNull(mActivitySnapshotController.getSnapshot(singleActivityList));
+        assertEquals(taskSnapshot, mActivitySnapshotController.getSnapshot(activities));
+    }
+
+    private TaskSnapshot createSnapshot() {
+        HardwareBuffer buffer = mock(HardwareBuffer.class);
+        doReturn(100).when(buffer).getWidth();
+        doReturn(100).when(buffer).getHeight();
+        return new TaskSnapshot(1, 0 /* captureTime */, new ComponentName("", ""), buffer,
+                ColorSpace.get(ColorSpace.Named.SRGB), ORIENTATION_PORTRAIT,
+                Surface.ROTATION_0, new Point(100, 100), new Rect() /* contentInsets */,
+                new Rect() /* letterboxInsets*/, false /* isLowResolution */,
+                true /* isRealSnapshot */, WINDOWING_MODE_FULLSCREEN, 0 /* mSystemUiVisibility */,
+                false /* isTranslucent */, false /* hasImeSurface */);
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index 8a9c05d..c82f751 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -88,6 +88,7 @@
 import android.app.ActivityOptions;
 import android.app.AppOpsManager;
 import android.app.BackgroundStartPrivileges;
+import android.app.ComponentOptions.BackgroundActivityStartMode;
 import android.app.IApplicationThread;
 import android.app.PictureInPictureParams;
 import android.content.ComponentName;
@@ -914,24 +915,78 @@
                 .mockStatic(FrameworkStatsLog.class)
                 .strictness(Strictness.LENIENT)
                 .startMocking();
-        doReturn(PERMISSION_GRANTED).when(() -> ActivityTaskManagerService.checkPermission(
-                eq(START_ACTIVITIES_FROM_BACKGROUND),
-                anyInt(), anyInt()));
-        runAndVerifyBackgroundActivityStartsSubtest(
-                "allowed_notAborted", false,
-                UNIMPORTANT_UID, false, PROCESS_STATE_BOUND_TOP,
-                UNIMPORTANT_UID2, false, PROCESS_STATE_BOUND_TOP,
-                false, true, false, false, false, false, false, false);
-        verify(() -> FrameworkStatsLog.write(FrameworkStatsLog.BAL_ALLOWED,
-                "",  // activity name
-                BackgroundActivityStartController.BAL_ALLOW_PERMISSION,
-                UNIMPORTANT_UID,
-                UNIMPORTANT_UID2));
-        mockingSession.finishMocking();
+        try {
+            doReturn(PERMISSION_GRANTED).when(() -> ActivityTaskManagerService.checkPermission(
+                    eq(START_ACTIVITIES_FROM_BACKGROUND),
+                    anyInt(), anyInt()));
+            runAndVerifyBackgroundActivityStartsSubtest(
+                    "allowed_notAborted", false,
+                    UNIMPORTANT_UID, false, PROCESS_STATE_BOUND_TOP,
+                    UNIMPORTANT_UID2, false, PROCESS_STATE_BOUND_TOP,
+                    false, true, false, false, false, false, false, false);
+            verify(() -> FrameworkStatsLog.write(FrameworkStatsLog.BAL_ALLOWED,
+                    "",  // activity name
+                    BackgroundActivityStartController.BAL_ALLOW_PERMISSION,
+                    UNIMPORTANT_UID,
+                    UNIMPORTANT_UID2,
+                    BackgroundActivityStartController.BAL_ALLOW_PERMISSION,
+                    true, // opt in
+                    false, // but no explicit opt in
+                    BackgroundActivityStartController.BAL_BLOCK,
+                    true, // opt in
+                    false // but no explicit opt in
+            ));
+        } finally {
+            mockingSession.finishMocking();
+        }
     }
 
     /**
-     * This test ensures proper logging for BAL_ALLOW_PENDING_INTENT.
+     * This test ensures proper logging for BAL_ALLOW_PENDING_INTENT, when the PendingIntent sender
+     * is the only reason BAL is allowed.
+     */
+    @Test
+    public void testBackgroundActivityStartsAllowed_loggingOnlyPendingIntentAllowed() {
+        doReturn(false).when(mAtm).isBackgroundActivityStartsEnabled();
+        MockitoSession mockingSession = mockitoSession()
+                .mockStatic(ActivityTaskManagerService.class)
+                .mockStatic(FrameworkStatsLog.class)
+                .mockStatic(PendingIntentRecord.class)
+                .strictness(Strictness.LENIENT)
+                .startMocking();
+        try {
+            doReturn(PERMISSION_GRANTED).when(() -> ActivityTaskManagerService.checkPermission(
+                    eq(START_ACTIVITIES_FROM_BACKGROUND),
+                    anyInt(), anyInt()));
+            doReturn(BackgroundStartPrivileges.allowBackgroundActivityStarts(null)).when(
+                    () -> PendingIntentRecord.getBackgroundStartPrivilegesAllowedByCaller(
+                            anyObject(), anyInt(), anyObject()));
+            runAndVerifyBackgroundActivityStartsSubtest(
+                    "allowed_notAborted", false,
+                    UNIMPORTANT_UID, false, PROCESS_STATE_BOUND_TOP,
+                    Process.SYSTEM_UID, true, PROCESS_STATE_BOUND_TOP,
+                    false, true, false, false, false, false, false, false,
+                    ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED);
+            verify(() -> FrameworkStatsLog.write(FrameworkStatsLog.BAL_ALLOWED,
+                    DEFAULT_COMPONENT_PACKAGE_NAME + "/" + DEFAULT_COMPONENT_PACKAGE_NAME,
+                    BackgroundActivityStartController.BAL_ALLOW_PENDING_INTENT,
+                    UNIMPORTANT_UID,
+                    Process.SYSTEM_UID,
+                    BackgroundActivityStartController.BAL_ALLOW_PERMISSION,
+                    false, // opt in
+                    true, // explicit opt out
+                    BackgroundActivityStartController.BAL_ALLOW_VISIBLE_WINDOW,
+                    true, // opt in
+                    false // but no explicit opt in
+            ));
+        } finally {
+            mockingSession.finishMocking();
+        }
+    }
+
+    /**
+     * This test ensures proper logging for BAL_ALLOW_PENDING_INTENT, when the PendingIntent sender
+     * is not the primary reason to allow BAL (but the creator).
      */
     @Test
     public void testBackgroundActivityStartsAllowed_loggingPendingIntentAllowed() {
@@ -942,23 +997,34 @@
                 .mockStatic(PendingIntentRecord.class)
                 .strictness(Strictness.LENIENT)
                 .startMocking();
-        doReturn(PERMISSION_GRANTED).when(() -> ActivityTaskManagerService.checkPermission(
-                eq(START_ACTIVITIES_FROM_BACKGROUND),
-                anyInt(), anyInt()));
-        doReturn(BackgroundStartPrivileges.allowBackgroundActivityStarts(null)).when(
-                () -> PendingIntentRecord.getBackgroundStartPrivilegesAllowedByCaller(
-                anyObject(), anyInt(), anyObject()));
-        runAndVerifyBackgroundActivityStartsSubtest(
-                "allowed_notAborted", false,
-                UNIMPORTANT_UID, false, PROCESS_STATE_BOUND_TOP,
-                Process.SYSTEM_UID, true, PROCESS_STATE_BOUND_TOP,
-                false, true, false, false, false, false, false, false);
-        verify(() -> FrameworkStatsLog.write(FrameworkStatsLog.BAL_ALLOWED,
-                DEFAULT_COMPONENT_PACKAGE_NAME + "/" + DEFAULT_COMPONENT_PACKAGE_NAME,
-                BackgroundActivityStartController.BAL_ALLOW_PENDING_INTENT,
-                UNIMPORTANT_UID,
-                Process.SYSTEM_UID));
-        mockingSession.finishMocking();
+        try {
+            doReturn(PERMISSION_GRANTED).when(() -> ActivityTaskManagerService.checkPermission(
+                    eq(START_ACTIVITIES_FROM_BACKGROUND),
+                    anyInt(), anyInt()));
+            doReturn(BackgroundStartPrivileges.allowBackgroundActivityStarts(null)).when(
+                    () -> PendingIntentRecord.getBackgroundStartPrivilegesAllowedByCaller(
+                            anyObject(), anyInt(), anyObject()));
+            runAndVerifyBackgroundActivityStartsSubtest(
+                    "allowed_notAborted", false,
+                    UNIMPORTANT_UID, false, PROCESS_STATE_BOUND_TOP,
+                    Process.SYSTEM_UID, true, PROCESS_STATE_BOUND_TOP,
+                    false, true, false, false, false, false, false, false,
+                    ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
+            verify(() -> FrameworkStatsLog.write(FrameworkStatsLog.BAL_ALLOWED,
+                    "",
+                    BackgroundActivityStartController.BAL_ALLOW_PERMISSION,
+                    UNIMPORTANT_UID,
+                    Process.SYSTEM_UID,
+                    BackgroundActivityStartController.BAL_ALLOW_PERMISSION,
+                    true, // opt in
+                    true, // explicit opt in
+                    BackgroundActivityStartController.BAL_ALLOW_VISIBLE_WINDOW,
+                    true, // opt in
+                    false // but no explicit opt in
+            ));
+        } finally {
+            mockingSession.finishMocking();
+        }
     }
 
     private void runAndVerifyBackgroundActivityStartsSubtest(String name, boolean shouldHaveAborted,
@@ -971,6 +1037,27 @@
             boolean isCallingUidAffiliatedProfileOwner,
             boolean isPinnedSingleInstance,
             boolean hasSystemExemptAppOp) {
+        runAndVerifyBackgroundActivityStartsSubtest(name, shouldHaveAborted, callingUid,
+                callingUidHasVisibleWindow, callingUidProcState, realCallingUid,
+                realCallingUidHasVisibleWindow,  realCallingUidProcState, hasForegroundActivities,
+                callerIsRecents, callerIsTempAllowed,
+                callerIsInstrumentingWithBackgroundActivityStartPrivileges,
+                isCallingUidDeviceOwner, isCallingUidAffiliatedProfileOwner, isPinnedSingleInstance,
+                hasSystemExemptAppOp,
+                ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED);
+    }
+
+    private void runAndVerifyBackgroundActivityStartsSubtest(String name, boolean shouldHaveAborted,
+            int callingUid, boolean callingUidHasVisibleWindow, int callingUidProcState,
+            int realCallingUid, boolean realCallingUidHasVisibleWindow, int realCallingUidProcState,
+            boolean hasForegroundActivities, boolean callerIsRecents,
+            boolean callerIsTempAllowed,
+            boolean callerIsInstrumentingWithBackgroundActivityStartPrivileges,
+            boolean isCallingUidDeviceOwner,
+            boolean isCallingUidAffiliatedProfileOwner,
+            boolean isPinnedSingleInstance,
+            boolean hasSystemExemptAppOp,
+            @BackgroundActivityStartMode int pendingIntentCreatorBackgroundActivityStartMode) {
         // window visibility
         doReturn(callingUidHasVisibleWindow).when(mAtm).hasActiveVisibleWindow(callingUid);
         doReturn(realCallingUidHasVisibleWindow).when(mAtm).hasActiveVisibleWindow(realCallingUid);
@@ -1022,7 +1109,10 @@
             launchMode = LAUNCH_SINGLE_INSTANCE;
         }
 
-        final ActivityOptions options = spy(ActivityOptions.makeBasic());
+        ActivityOptions rawOptions = ActivityOptions.makeBasic()
+                .setPendingIntentCreatorBackgroundActivityStartMode(
+                        pendingIntentCreatorBackgroundActivityStartMode);
+        final ActivityOptions options = spy(rawOptions);
         ActivityRecord[] outActivity = new ActivityRecord[1];
         ActivityStarter starter = prepareStarter(
                 FLAG_ACTIVITY_NEW_TASK, true, launchMode)
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
index a11079b..0c1fbf3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
@@ -1221,7 +1221,7 @@
     public void testTransitionGoodToGoForTaskFragments_detachedApp() {
         final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
         final ITaskFragmentOrganizer iOrganizer = getITaskFragmentOrganizer(organizer);
-        mAtm.mTaskFragmentOrganizerController.registerOrganizer(iOrganizer);
+        registerTaskFragmentOrganizer(iOrganizer);
         final Task task = createTask(mDisplayContent);
         final TaskFragment changeTaskFragment =
                 createTaskFragmentWithEmbeddedActivity(task, organizer);
@@ -1265,7 +1265,7 @@
         definition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_CLOSE, adapter);
         definition.addRemoteAnimation(TRANSIT_OLD_ACTIVITY_OPEN, adapter);
         definition.addRemoteAnimation(TRANSIT_OLD_ACTIVITY_CLOSE, adapter);
-        mAtm.mTaskFragmentOrganizerController.registerOrganizer(iOrganizer);
+        registerTaskFragmentOrganizer(iOrganizer);
         mAtm.mTaskFragmentOrganizerController.registerRemoteAnimations(iOrganizer, definition);
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
index ba8c94d..9950541 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
@@ -439,7 +439,7 @@
         final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
         final ITaskFragmentOrganizer iOrganizer =
                 ITaskFragmentOrganizer.Stub.asInterface(organizer.getOrganizerToken().asBinder());
-        mAtm.mTaskFragmentOrganizerController.registerOrganizer(iOrganizer);
+        registerTaskFragmentOrganizer(iOrganizer);
         final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm)
                 .setParentTask(task)
                 .setOrganizer(organizer)
diff --git a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
index ac18f80..402cbcc 100644
--- a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
@@ -464,9 +464,10 @@
         // Simulate ActivityOptions#makeSceneTransitionAnimation
         final Bundle myBundle = new Bundle();
         myBundle.putInt(ActivityOptions.KEY_ANIM_TYPE, ANIM_SCENE_TRANSITION);
-        myBundle.putParcelable("android:activity.transitionCompleteListener",
-                mock(android.os.ResultReceiver.class));
         final ActivityOptions options = new ActivityOptions(myBundle);
+        final ActivityOptions.SceneTransitionInfo info = new ActivityOptions.SceneTransitionInfo();
+        info.setResultReceiver(mock(android.os.ResultReceiver.class));
+        options.setSceneTransitionInfo(info);
 
         final ActivityRecord testActivity = new ActivityBuilder(mAtm)
                 .setCreateTask(true)
@@ -741,7 +742,7 @@
 
         MockitoSession mockitoSession = mockitoSession().mockStatic(BackNavigationController.class)
                 .strictness(Strictness.LENIENT).startMocking();
-        doReturn(taskSnapshot).when(() -> BackNavigationController.getSnapshot(any()));
+        doReturn(taskSnapshot).when(() -> BackNavigationController.getSnapshot(any(), any()));
         when(resourcesSpy.getBoolean(
                 com.android.internal.R.bool.config_predictShowStartingSurface))
                 .thenReturn(preferWindowlessSurface);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ClientLifecycleManagerTests.java b/services/tests/wmtests/src/com/android/server/wm/ClientLifecycleManagerTests.java
index c757457..09f677e7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ClientLifecycleManagerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ClientLifecycleManagerTests.java
@@ -172,7 +172,7 @@
     @Test
     public void testScheduleTransactionItemUnlocked() throws RemoteException {
         // Use non binder client to get non-recycled ClientTransaction.
-        mLifecycleManager.scheduleTransactionItemUnlocked(mNonBinderClient, mTransactionItem);
+        mLifecycleManager.scheduleTransactionItemNow(mNonBinderClient, mTransactionItem);
 
         // Dispatch immediately.
         assertTrue(mLifecycleManager.mPendingTransactions.isEmpty());
@@ -217,6 +217,8 @@
 
     @Test
     public void testDispatchPendingTransactions() throws RemoteException {
+        mSetFlagsRule.enableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
+
         mLifecycleManager.mPendingTransactions.put(mClientBinder, mTransaction);
 
         mLifecycleManager.dispatchPendingTransactions();
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentDeferredUpdateTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentDeferredUpdateTests.java
index dfa595c..99d354a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentDeferredUpdateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentDeferredUpdateTests.java
@@ -55,6 +55,11 @@
 @RunWith(WindowTestRunner.class)
 public class DisplayContentDeferredUpdateTests extends WindowTestsBase {
 
+    // The fields to override the current DisplayInfo.
+    private String mUniqueId;
+    private int mColorMode;
+    private int mLogicalDensityDpi;
+
     @Override
     protected void onBeforeSystemServicesCreated() {
         // Set other flags to their default values
@@ -73,7 +78,7 @@
     public void testUpdate_deferrableFieldChangedTransitionStarted_deferrableFieldUpdated() {
         performInitialDisplayUpdate();
 
-        givenDisplayInfo(/* uniqueId= */ "old");
+        mUniqueId = "old";
         Runnable onUpdated = mock(Runnable.class);
         mDisplayContent.requestDisplayUpdate(onUpdated);
 
@@ -82,11 +87,21 @@
         verify(onUpdated).run();
         clearInvocations(mDisplayContent.mTransitionController, onUpdated);
 
-        givenDisplayInfo(/* uniqueId= */ "new");
+        mUniqueId = "new";
         mDisplayContent.requestDisplayUpdate(onUpdated);
         captureStartTransitionCollection().getValue().onCollectStarted(/* deferred= */ true);
         verify(onUpdated).run();
+        verify(mDisplayContent.mTransitionController).requestStartTransition(
+                any(), any(), any(), any());
         assertThat(mDisplayContent.getDisplayInfo().uniqueId).isEqualTo("new");
+        clearInvocations(mDisplayContent.mTransitionController, onUpdated);
+
+        mLogicalDensityDpi += 100;
+        mDisplayContent.requestDisplayUpdate(onUpdated);
+        captureStartTransitionCollection().getValue().onCollectStarted(/* deferred= */ true);
+        verify(onUpdated).run();
+        verify(mDisplayContent.mTransitionController).requestStartTransition(
+                any(), any(), any(), any());
     }
 
     @Test
@@ -94,7 +109,8 @@
         performInitialDisplayUpdate();
 
         // Update only color mode (non-deferrable field) and keep the same unique id
-        givenDisplayInfo(/* uniqueId= */ "initial_unique_id", /* colorMode= */ 123);
+        mUniqueId = "initial_unique_id";
+        mColorMode = 123;
         Runnable onUpdated = mock(Runnable.class);
         mDisplayContent.requestDisplayUpdate(onUpdated);
 
@@ -107,7 +123,8 @@
         performInitialDisplayUpdate();
 
         // Update only color mode (non-deferrable field) and keep the same unique id
-        givenDisplayInfo(/* uniqueId= */ "initial_unique_id", /* colorMode= */ 123);
+        mUniqueId = "initial_unique_id";
+        mColorMode = 123;
         mDisplayContent.requestDisplayUpdate(mock(Runnable.class));
 
         assertThat(mDisplayContent.getDisplayInfo().colorMode).isEqualTo(123);
@@ -116,7 +133,7 @@
 
         // Update unique id (deferrable field), keep the same color mode,
         // this update should be deferred
-        givenDisplayInfo(/* uniqueId= */ "new_unique_id", /* colorMode= */ 123);
+        mUniqueId = "new_unique_id";
         mDisplayContent.requestDisplayUpdate(mock(Runnable.class));
 
         assertThat(mDisplayContent.getDisplayInfo().colorMode).isEqualTo(123);
@@ -126,7 +143,7 @@
         // Update color mode again and keep the same unique id, color mode update
         // should not be deferred, unique id update is still deferred as transition
         // has not started collecting yet
-        givenDisplayInfo(/* uniqueId= */ "new_unique_id", /* colorMode= */ 456);
+        mColorMode = 456;
         Runnable onUpdated = mock(Runnable.class);
         mDisplayContent.requestDisplayUpdate(onUpdated);
 
@@ -146,14 +163,14 @@
     @Test
     public void testUpdate_deferrableFieldUpdatedTransitionPending_fieldNotUpdated() {
         performInitialDisplayUpdate();
-        givenDisplayInfo(/* uniqueId= */ "old");
+        mUniqueId = "old";
         Runnable onUpdated = mock(Runnable.class);
         mDisplayContent.requestDisplayUpdate(onUpdated);
         captureStartTransitionCollection().getValue().onCollectStarted(/* deferred= */ true);
         verify(onUpdated).run();
         clearInvocations(mDisplayContent.mTransitionController, onUpdated);
 
-        givenDisplayInfo(/* uniqueId= */ "new");
+        mUniqueId = "new";
         mDisplayContent.requestDisplayUpdate(onUpdated);
 
         captureStartTransitionCollection(); // do not continue by not starting the collection
@@ -164,7 +181,7 @@
     @Test
     public void testTwoDisplayUpdates_transitionStarted_displayUpdated() {
         performInitialDisplayUpdate();
-        givenDisplayInfo(/* uniqueId= */ "old");
+        mUniqueId = "old";
         Runnable onUpdated = mock(Runnable.class);
         mDisplayContent.requestDisplayUpdate(onUpdated);
         captureStartTransitionCollection().getValue()
@@ -173,10 +190,10 @@
         clearInvocations(mDisplayContent.mTransitionController, onUpdated);
 
         // Perform two display updates while WM is 'busy'
-        givenDisplayInfo(/* uniqueId= */ "new1");
+        mUniqueId = "new1";
         Runnable onUpdated1 = mock(Runnable.class);
         mDisplayContent.requestDisplayUpdate(onUpdated1);
-        givenDisplayInfo(/* uniqueId= */ "new2");
+        mUniqueId = "new2";
         Runnable onUpdated2 = mock(Runnable.class);
         mDisplayContent.requestDisplayUpdate(onUpdated2);
 
@@ -215,22 +232,19 @@
         return callbackCaptor;
     }
 
-    private void givenDisplayInfo(String uniqueId) {
-        givenDisplayInfo(uniqueId, /* colorMode= */ 0);
-    }
+    private void performInitialDisplayUpdate() {
+        mUniqueId = "initial_unique_id";
+        mColorMode = 0;
+        mLogicalDensityDpi = 400;
 
-    private void givenDisplayInfo(String uniqueId, int colorMode) {
         spyOn(mDisplayContent.mDisplay);
         doAnswer(invocation -> {
             DisplayInfo info = invocation.getArgument(0);
-            info.uniqueId = uniqueId;
-            info.colorMode = colorMode;
+            info.uniqueId = mUniqueId;
+            info.colorMode = mColorMode;
+            info.logicalDensityDpi = mLogicalDensityDpi;
             return null;
         }).when(mDisplayContent.mDisplay).getDisplayInfo(any());
-    }
-
-    private void performInitialDisplayUpdate() {
-        givenDisplayInfo(/* uniqueId= */ "initial_unique_id", /* colorMode= */ 0);
         Runnable onUpdated = mock(Runnable.class);
         mDisplayContent.requestDisplayUpdate(onUpdated);
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index dfe79bf..782d89c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -75,7 +75,6 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.same;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
 import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP;
@@ -116,6 +115,9 @@
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.platform.test.annotations.Presubmit;
+import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 import android.util.ArraySet;
 import android.util.DisplayMetrics;
 import android.view.Display;
@@ -147,6 +149,7 @@
 import com.android.server.policy.WindowManagerPolicy;
 import com.android.server.wm.utils.WmDisplayCutout;
 
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
@@ -173,6 +176,10 @@
 @RunWith(WindowTestRunner.class)
 public class DisplayContentTests extends WindowTestsBase {
 
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule =
+            DeviceFlagsValueProvider.createCheckFlagsRule();
+
     @SetupWindows(addAllCommonWindows = true)
     @Test
     public void testForAllWindows() {
@@ -490,7 +497,7 @@
         newOverrideConfig.fontScale += 0.3;
 
         defaultDisplay.updateDisplayOverrideConfigurationLocked(newOverrideConfig,
-                null /* starting */, false /* deferResume */, null /* result */);
+                null /* starting */, false /* deferResume */);
 
         // Check that global configuration is updated, as we've updated default display's config.
         Configuration globalConfig = mWm.mRoot.getConfiguration();
@@ -499,7 +506,7 @@
 
         // Return back to original values.
         defaultDisplay.updateDisplayOverrideConfigurationLocked(currentConfig,
-                null /* starting */, false /* deferResume */, null /* result */);
+                null /* starting */, false /* deferResume */);
         globalConfig = mWm.mRoot.getConfiguration();
         assertEquals(currentConfig.densityDpi, globalConfig.densityDpi);
         assertEquals(currentConfig.fontScale, globalConfig.fontScale, 0.1 /* delta */);
@@ -509,6 +516,7 @@
      * Tests tapping on a root task in different display results in window gaining focus.
      */
     @Test
+    @RequiresFlagsDisabled(com.android.input.flags.Flags.FLAG_REMOVE_POINTER_EVENT_TRACKING_IN_WM)
     public void testInputEventBringsCorrectDisplayInFocus() {
         DisplayContent dc0 = mWm.getDefaultDisplayContentLocked();
         // Create a second display
@@ -1176,7 +1184,7 @@
         activity.setRequestedOrientation(newOrientation);
 
         verify(dc, never()).updateDisplayOverrideConfigurationLocked(any(), eq(activity),
-                anyBoolean(), same(null));
+                anyBoolean());
         assertEquals(ROTATION_180, dc.getRotation());
     }
 
@@ -2123,10 +2131,8 @@
         // Once transition starts, rotation is applied and transition shows DC rotating.
         testPlayer.startTransition();
         waitUntilHandlersIdle();
-        verify(activity1).ensureActivityConfiguration(anyInt(), anyBoolean(), anyBoolean(),
-                anyBoolean());
-        verify(activity2).ensureActivityConfiguration(anyInt(), anyBoolean(), anyBoolean(),
-                anyBoolean());
+        verify(activity1).ensureActivityConfiguration(anyBoolean(), anyBoolean());
+        verify(activity2).ensureActivityConfiguration(anyBoolean(), anyBoolean());
         assertNotEquals(origRot, dc.getConfiguration().windowConfiguration.getRotation());
         assertNotNull(testPlayer.mLastReady);
         assertTrue(testPlayer.mController.isPlaying());
@@ -2248,11 +2254,11 @@
             // The assertion will fail if DisplayArea#ensureActivitiesVisible is called twice.
             assertFalse(called[0]);
             called[0] = true;
-            mDisplayContent.ensureActivitiesVisible(null, 0, false, false);
+            mDisplayContent.ensureActivitiesVisible(null, false);
             return null;
-        }).when(mockTda).ensureActivitiesVisible(any(), anyInt(), anyBoolean(), anyBoolean());
+        }).when(mockTda).ensureActivitiesVisible(any(), anyBoolean());
 
-        mDisplayContent.ensureActivitiesVisible(null, 0, false, false);
+        mDisplayContent.ensureActivitiesVisible(null, false);
     }
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
index 985be42..4e4bbfe 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
@@ -697,6 +697,31 @@
 
     @Test
     @EnableCompatChanges({OVERRIDE_ANY_ORIENTATION_TO_USER})
+    public void testOverrideOrientationIfNeeded_fullscreenOverrides_optOutSystem_returnsUser()
+            throws Exception {
+        mockThatProperty(PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE, /* value */ false);
+        prepareActivityThatShouldApplyUserFullscreenOverride();
+
+        // fullscreen override still applied
+        assertEquals(SCREEN_ORIENTATION_USER, mController.overrideOrientationIfNeeded(
+                /* candidate */ SCREEN_ORIENTATION_PORTRAIT));
+    }
+
+    @Test
+    @EnableCompatChanges({OVERRIDE_ANY_ORIENTATION_TO_USER})
+    public void testOverrideOrientationIfNeeded_fullscreenOverrides_optOutUser_returnsUser()
+            throws Exception {
+        mockThatProperty(PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE,
+                /* value */ false);
+        prepareActivityThatShouldApplyUserFullscreenOverride();
+
+        // fullscreen override still applied
+        assertEquals(SCREEN_ORIENTATION_USER, mController.overrideOrientationIfNeeded(
+                /* candidate */ SCREEN_ORIENTATION_PORTRAIT));
+    }
+
+    @Test
+    @EnableCompatChanges({OVERRIDE_ANY_ORIENTATION_TO_USER})
     public void testOverrideOrientationIfNeeded_fullscreenOverrideEnabled_returnsUnchanged()
             throws Exception {
         mDisplayContent.setIgnoreOrientationRequest(false);
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
index 8de45b0..32b3558 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
@@ -106,8 +106,7 @@
         topActivity.getRootTask().moveToFront("testRecentsActivityVisiblility");
 
         doCallRealMethod().when(mRootWindowContainer).ensureActivitiesVisible(
-                any() /* starting */, anyInt() /* configChanges */,
-                anyBoolean() /* preserveWindows */, anyBoolean() /* notifyClients */);
+                any() /* starting */, anyBoolean() /* notifyClients */);
 
         RecentsAnimationCallbacks recentsAnimation = startRecentsActivity(
                 mRecentsComponent, true /* getRecentsAnimation */);
@@ -178,8 +177,7 @@
         mAtm.startRecentsActivity(recentsIntent, 0 /* eventTime */,
                 null /* recentsAnimationRunner */);
 
-        verify(recentsActivity).ensureActivityConfiguration(anyInt() /* globalChanges */,
-                anyBoolean() /* preserveWindow */, eq(true) /* ignoreVisibility */);
+        verify(recentsActivity).ensureActivityConfiguration(eq(true) /* ignoreVisibility */);
         assertThat(mSupervisor.mStoppingActivities).contains(recentsActivity);
     }
 
@@ -199,8 +197,7 @@
                 "testRestartRecentsActivity");
 
         doCallRealMethod().when(mRootWindowContainer).ensureActivitiesVisible(
-                any() /* starting */, anyInt() /* configChanges */,
-                anyBoolean() /* preserveWindows */, anyBoolean() /* notifyClients */);
+                any() /* starting */, anyBoolean() /* notifyClients */);
         doReturn(app).when(mAtm).getProcessController(eq(recentActivity.processName), anyInt());
         doNothing().when(mClientLifecycleManager).scheduleTransaction(any());
 
@@ -354,8 +351,7 @@
 
         doReturn(TEST_USER_ID).when(mAtm).getCurrentUserId();
         doCallRealMethod().when(mRootWindowContainer).ensureActivitiesVisible(
-                any() /* starting */, anyInt() /* configChanges */,
-                anyBoolean() /* preserveWindows */, anyBoolean() /* notifyClients */);
+                any() /* starting */, anyBoolean() /* notifyClients */);
 
         startRecentsActivity(otherUserHomeActivity.getTask().getBaseIntent().getComponent(),
                 true);
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java b/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java
index 89cd726..527ea0d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java
@@ -140,7 +140,7 @@
 
         final WindowContainer parent = activity1.getTask().getParent();
         assertEquals(SCREEN_ORIENTATION_PORTRAIT, parent.getOrientation());
-        mDisplayContent.mClosingApps.add(activity2);
+        activity2.setVisibleRequested(false);
         assertEquals(SCREEN_ORIENTATION_LANDSCAPE, parent.getOrientation());
     }
 
@@ -823,8 +823,7 @@
                 .build();
 
         doReturn(false).when(secondActivity).occludesParent();
-        homeRootTask.ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
-                false /* preserveWindows */);
+        homeRootTask.ensureActivitiesVisible(null /* starting */);
 
         assertTrue(firstActivity.shouldBeVisible());
     }
@@ -1419,8 +1418,7 @@
 
         // Any common path that updates activity visibility should clear the unknown visibility
         // records that are no longer visible according to hierarchy.
-        task.ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
-                false /* preserveWindows */);
+        task.ensureActivitiesVisible(null /* starting */);
         // Assume the top activity relayouted, just remove it directly.
         unknownAppVisibilityController.appRemovedOrHidden(activities[1]);
         // All unresolved records should be removed.
@@ -1441,8 +1439,7 @@
         doNothing().when(mSupervisor).startSpecificActivity(any(), anyBoolean(),
                 anyBoolean());
 
-        task.ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
-                false /* preserveWindows */);
+        task.ensureActivitiesVisible(null /* starting */);
         verify(mSupervisor).startSpecificActivity(any(), eq(false) /* andResume */,
                 anyBoolean());
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
index 28fecd6..6013063 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -76,7 +76,6 @@
 import android.os.PowerManager;
 import android.os.UserHandle;
 import android.platform.test.annotations.Presubmit;
-import android.util.MergedConfiguration;
 import android.util.Pair;
 
 import androidx.test.filters.MediumTest;
@@ -545,8 +544,7 @@
         assertNotEquals(activity.getConfiguration().orientation, rotatedConfig.orientation);
         // Assume the activity was shown in different orientation. For example, the top activity is
         // landscape and the portrait lockscreen is shown.
-        activity.setLastReportedConfiguration(
-                new MergedConfiguration(mAtm.getGlobalConfiguration(), rotatedConfig));
+        activity.setLastReportedConfiguration(mAtm.getGlobalConfiguration(), rotatedConfig);
         activity.setState(STOPPED, "sleep");
 
         display.setIsSleeping(true);
diff --git a/services/tests/wmtests/src/com/android/server/wm/SensitiveContentPackagesTest.java b/services/tests/wmtests/src/com/android/server/wm/SensitiveContentPackagesTest.java
new file mode 100644
index 0000000..71dbc57
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/SensitiveContentPackagesTest.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.server.wm.SensitiveContentPackages.PackageInfo;
+
+import org.junit.After;
+import org.junit.Test;
+
+import java.util.Collections;
+import java.util.Set;
+
+/**
+ * Build/Install/Run:
+ *  atest WmTests:SensitiveContentPackagesTest
+ */
+@SmallTest
+@Presubmit
+public class SensitiveContentPackagesTest {
+    private static final String APP_PKG_1 = "com.android.server.wm.one";
+    private static final String APP_PKG_2 = "com.android.server.wm.two";
+    private static final String APP_PKG_3 = "com.android.server.wm.three";
+
+    private static final int APP_UID_1 = 5;
+    private static final int APP_UID_2 = 6;
+    private static final int APP_UID_3 = 7;
+
+
+    private final SensitiveContentPackages mSensitiveContentPackages =
+            new SensitiveContentPackages();
+
+    @After
+    public void tearDown() {
+        mSensitiveContentPackages.setShouldBlockScreenCaptureForApp(Collections.emptySet());
+    }
+
+    @Test
+    public void setShouldBlockScreenCaptureForApp() {
+        Set<PackageInfo> blockedApps =
+                Set.of(new PackageInfo(APP_PKG_1, APP_UID_1),
+                        new PackageInfo(APP_PKG_1, APP_UID_2),
+                        new PackageInfo(APP_PKG_2, APP_UID_1),
+                        new PackageInfo(APP_PKG_2, APP_UID_2));
+
+        mSensitiveContentPackages.setShouldBlockScreenCaptureForApp(blockedApps);
+
+        assertTrue(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_1, APP_UID_1));
+        assertTrue(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_1, APP_UID_2));
+        assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_1, APP_UID_3));
+
+        assertTrue(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_2, APP_UID_1));
+        assertTrue(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_2, APP_UID_2));
+        assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_2, APP_UID_3));
+
+        assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_3, APP_UID_1));
+        assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_3, APP_UID_2));
+        assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_3, APP_UID_3));
+    }
+
+    @Test
+    public void setShouldBlockScreenCaptureForApp_empty() {
+        Set<PackageInfo> blockedApps =
+                Set.of(new PackageInfo(APP_PKG_1, APP_UID_1),
+                        new PackageInfo(APP_PKG_1, APP_UID_2),
+                        new PackageInfo(APP_PKG_2, APP_UID_1),
+                        new PackageInfo(APP_PKG_2, APP_UID_2));
+
+        mSensitiveContentPackages.setShouldBlockScreenCaptureForApp(blockedApps);
+        mSensitiveContentPackages.setShouldBlockScreenCaptureForApp(Collections.emptySet());
+
+        assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_1, APP_UID_1));
+        assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_1, APP_UID_2));
+        assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_1, APP_UID_3));
+
+        assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_2, APP_UID_1));
+        assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_2, APP_UID_2));
+        assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_2, APP_UID_3));
+
+        assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_3, APP_UID_1));
+        assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_3, APP_UID_2));
+        assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_3, APP_UID_3));
+    }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index c3102e0..5518c60 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -947,7 +947,7 @@
 
         // Recompute the natural configuration in the new display.
         mActivity.clearSizeCompatMode();
-        mActivity.ensureActivityConfiguration(0 /* globalChanges */, false /* preserveWindow */);
+        mActivity.ensureActivityConfiguration();
         // Because the display cannot rotate, the portrait activity will fit the short side of
         // display with keeping portrait bounds [200, 0 - 700, 1000] in center.
         assertEquals(newDisplayBounds.height(), currentBounds.height());
@@ -4858,7 +4858,7 @@
         }
         // Make sure to use the provided configuration to construct the size compat fields.
         activity.clearSizeCompatMode();
-        activity.ensureActivityConfiguration(0 /* globalChanges */, false /* preserveWindow */);
+        activity.ensureActivityConfiguration();
         // Make sure the display configuration reflects the change of activity.
         if (activity.mDisplayContent.updateOrientation()) {
             activity.mDisplayContent.sendNewConfiguration();
diff --git a/services/tests/wmtests/src/com/android/server/wm/SplashScreenExceptionListTest.java b/services/tests/wmtests/src/com/android/server/wm/SplashScreenExceptionListTest.java
index 8bd5473..2d3c4bb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SplashScreenExceptionListTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SplashScreenExceptionListTest.java
@@ -28,6 +28,7 @@
 import android.os.Looper;
 import android.platform.test.annotations.Presubmit;
 import android.provider.DeviceConfig;
+import android.util.Log;
 
 import androidx.test.filters.MediumTest;
 
@@ -147,6 +148,8 @@
     private void setExceptionListAndWaitForCallback(String commaSeparatedList) {
         CountDownLatch latch = new CountDownLatch(1);
         mOnUpdateDeviceConfig = rawList -> {
+            Log.i(getClass().getSimpleName(), "updateDeviceConfig expected="
+                    + commaSeparatedList + " actual=" + rawList);
             if (commaSeparatedList.equals(rawList)) {
                 latch.countDown();
             }
@@ -155,7 +158,7 @@
                 KEY_SPLASH_SCREEN_EXCEPTION_LIST, commaSeparatedList, false);
         try {
             assertTrue("Timed out waiting for DeviceConfig to be updated.",
-                    latch.await(1, TimeUnit.SECONDS));
+                    latch.await(5, TimeUnit.SECONDS));
         } catch (InterruptedException e) {
             Assert.fail(e.getMessage());
         }
diff --git a/services/tests/wmtests/src/com/android/server/wm/SyncEngineTests.java b/services/tests/wmtests/src/com/android/server/wm/SyncEngineTests.java
index 810cbe8..6c5f975 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SyncEngineTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SyncEngineTests.java
@@ -16,6 +16,7 @@
 
 package com.android.server.wm;
 
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
@@ -134,6 +135,33 @@
         assertFalse(r.isSyncFinished(r.getSyncGroup()));
         r.finishRelaunching();
         assertTrue(r.isSyncFinished(r.getSyncGroup()));
+        assertEquals(SYNC_STATE_READY, r.mSyncState);
+
+        // If the container has finished the sync, isSyncFinished should not change the sync state.
+        final BLASTSyncEngine.SyncGroup syncGroup = r.getSyncGroup();
+        r.finishSync(mTransaction, syncGroup, false /* cancel */);
+        assertEquals(SYNC_STATE_NONE, r.mSyncState);
+        assertTrue(r.isSyncFinished(syncGroup));
+        assertEquals(SYNC_STATE_NONE, r.mSyncState);
+    }
+
+    @Test
+    public void testFinishSyncByStartingWindow() {
+        final ActivityRecord taskRoot = new ActivityBuilder(mAtm).setCreateTask(true).build();
+        final Task task = taskRoot.getTask();
+        final ActivityRecord translucentTop = new ActivityBuilder(mAtm).setTask(task)
+                .setActivityTheme(android.R.style.Theme_Translucent).build();
+        createWindow(null, TYPE_BASE_APPLICATION, taskRoot, "win");
+        final WindowState startingWindow = createWindow(null, TYPE_APPLICATION_STARTING,
+                translucentTop, "starting");
+        startingWindow.mStartingData = new SnapshotStartingData(mWm, null, 0);
+        task.mSharedStartingData = startingWindow.mStartingData;
+        task.prepareSync();
+
+        final BLASTSyncEngine.SyncGroup group = mock(BLASTSyncEngine.SyncGroup.class);
+        assertFalse(task.isSyncFinished(group));
+        startingWindow.onSyncFinishedDrawing();
+        assertTrue(task.isSyncFinished(group));
     }
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServiceTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/SystemServiceTestsBase.java
index e65a9fe..b45fa31 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServiceTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServiceTestsBase.java
@@ -16,6 +16,8 @@
 
 package com.android.server.wm;
 
+import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
+
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 
 import android.os.Handler;
@@ -35,7 +37,7 @@
             new DexmakerShareClassLoaderRule();
 
     @Rule(order = 1)
-    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT);
 
     @Rule(order = 2)
     public final SystemServicesTestRule mSystemServicesTestRule = new SystemServicesTestRule(
diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
index 8cd9ff3..90493d4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
@@ -375,8 +375,7 @@
         // Always keep things awake.
         doReturn(true).when(mWmService.mRoot).hasAwakeDisplay();
         // Called when moving activity to pinned stack.
-        doNothing().when(mWmService.mRoot).ensureActivitiesVisible(any(),
-                anyInt(), anyBoolean(), anyBoolean());
+        doNothing().when(mWmService.mRoot).ensureActivitiesVisible(any(), anyBoolean());
         spyOn(mWmService.mDisplayWindowSettings);
         spyOn(mWmService.mDisplayWindowSettingsProvider);
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
index 06f29c2..a88285a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
@@ -34,6 +34,7 @@
 import static android.window.TaskFragmentOperation.OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT;
 import static android.window.TaskFragmentOperation.OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS;
 import static android.window.TaskFragmentOperation.OP_TYPE_SET_ANIMATION_PARAMS;
+import static android.window.TaskFragmentOperation.OP_TYPE_SET_DIM_ON_TASK;
 import static android.window.TaskFragmentOperation.OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT;
 import static android.window.TaskFragmentOrganizer.KEY_ERROR_CALLBACK_OP_TYPE;
 import static android.window.TaskFragmentOrganizer.KEY_ERROR_CALLBACK_THROWABLE;
@@ -74,6 +75,7 @@
 import static org.mockito.Mockito.verify;
 
 import android.annotation.NonNull;
+import android.app.IApplicationThread;
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
@@ -104,6 +106,8 @@
 
 import androidx.test.filters.SmallTest;
 
+import com.android.window.flags.Flags;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -123,7 +127,6 @@
 @Presubmit
 @RunWith(WindowTestRunner.class)
 public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
-    private static final int TASK_ID = 10;
 
     private TaskFragmentOrganizerController mController;
     private WindowOrganizerController mWindowOrganizerController;
@@ -143,6 +146,8 @@
     private TaskFragmentInfo mTaskFragmentInfo;
     @Mock
     private Task mTask;
+    @Mock
+    private IApplicationThread mAppThread;
     @Captor
     private ArgumentCaptor<TaskFragmentTransaction> mTransactionCaptor;
 
@@ -178,12 +183,21 @@
         doReturn(new SurfaceControl()).when(mTaskFragment).getSurfaceControl();
         doReturn(mFragmentToken).when(mTaskFragment).getFragmentToken();
         doReturn(new Configuration()).when(mTaskFragmentInfo).getConfiguration();
+        doReturn(mAppThread).when(mController).getAppThread(anyInt(), anyInt());
+        doAnswer(invocation -> {
+            final ITaskFragmentOrganizer organizer =
+                    (ITaskFragmentOrganizer) invocation.getArguments()[0];
+            final TaskFragmentTransaction taskFragmentTransaction =
+                    (TaskFragmentTransaction) invocation.getArguments()[1];
+            organizer.onTransactionReady(taskFragmentTransaction);
+            return null;
+        }).when(mAppThread).scheduleTaskFragmentTransaction(any(), any());
 
         // To prevent it from calling the real server.
         doNothing().when(mOrganizer).applyTransaction(any(), anyInt(), anyBoolean());
         doNothing().when(mOrganizer).onTransactionHandled(any(), any(), anyInt(), anyBoolean());
 
-        mController.registerOrganizer(mIOrganizer);
+        registerTaskFragmentOrganizer(mIOrganizer);
     }
 
     @Test
@@ -204,12 +218,15 @@
     }
 
     @Test
-    public void testOnTaskFragmentAppeared() {
+    public void testOnTaskFragmentAppeared_throughTaskFragmentOrganizer() throws RemoteException {
+        mSetFlagsRule.disableFlags(Flags.FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
+
         // No-op when the TaskFragment is not attached.
         mController.onTaskFragmentAppeared(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
         mController.dispatchPendingEvents();
 
         verify(mOrganizer, never()).onTransactionReady(any());
+        verify(mAppThread, never()).scheduleTaskFragmentTransaction(any(), any());
 
         // Send callback when the TaskFragment is attached.
         setupMockParent(mTaskFragment, mTask);
@@ -219,12 +236,40 @@
 
         assertTaskFragmentParentInfoChangedTransaction(mTask);
         assertTaskFragmentAppearedTransaction(false /* hasSurfaceControl */);
+        verify(mAppThread, never()).scheduleTaskFragmentTransaction(any(), any());
+    }
+
+    @Test
+    public void testOnTaskFragmentAppeared_throughApplicationThread() throws RemoteException  {
+        mSetFlagsRule.enableFlags(Flags.FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
+        // Re-register the organizer in case the flag was disabled during setup.
+        mController.unregisterOrganizer(mIOrganizer);
+        registerTaskFragmentOrganizer(mIOrganizer);
+
+        // No-op when the TaskFragment is not attached.
+        mController.onTaskFragmentAppeared(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
+        mController.dispatchPendingEvents();
+
+        verify(mOrganizer, never()).onTransactionReady(any());
+        verify(mAppThread, never()).scheduleTaskFragmentTransaction(any(), any());
+
+        // Send callback when the TaskFragment is attached.
+        setupMockParent(mTaskFragment, mTask);
+
+        mController.onTaskFragmentAppeared(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
+        mController.dispatchPendingEvents();
+
+        verify(mAppThread).scheduleTaskFragmentTransaction(eq(mIOrganizer), any());
+        assertTaskFragmentParentInfoChangedTransaction(mTask);
+        assertTaskFragmentAppearedTransaction(false /* hasSurfaceControl */);
     }
 
     @Test
     public void testOnTaskFragmentAppeared_systemOrganizer() {
+        mSetFlagsRule.enableFlags(Flags.FLAG_TASK_FRAGMENT_SYSTEM_ORGANIZER_FLAG);
+
         mController.unregisterOrganizer(mIOrganizer);
-        mController.registerOrganizerInternal(mIOrganizer, true /* isSystemOrganizer */);
+        registerTaskFragmentOrganizer(mIOrganizer, true /* isSystemOrganizer */);
 
         // No-op when the TaskFragment is not attached.
         mController.onTaskFragmentAppeared(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
@@ -565,8 +610,10 @@
 
     @Test
     public void testApplyTransaction_allowRemoteTransitionForSystemOrganizer() {
+        mSetFlagsRule.enableFlags(Flags.FLAG_TASK_FRAGMENT_SYSTEM_ORGANIZER_FLAG);
+
         mController.unregisterOrganizer(mIOrganizer);
-        mController.registerOrganizerInternal(mIOrganizer, true /* isSystemOrganizer */);
+        registerTaskFragmentOrganizer(mIOrganizer, true /* isSystemOrganizer */);
 
         mTransaction.setRelativeBounds(mFragmentWindowToken, new Rect(0, 0, 100, 100));
         mTaskFragment.setTaskFragmentOrganizer(mOrganizerToken, 10 /* uid */,
@@ -824,12 +871,19 @@
                 .setAnimationParams(animationParams)
                 .build();
         mTransaction.addTaskFragmentOperation(mFragmentToken, operation);
+        final TaskFragmentOperation dimOperation = new TaskFragmentOperation.Builder(
+                OP_TYPE_SET_DIM_ON_TASK)
+                .setDimOnTask(true)
+                .build();
+        mTransaction.addTaskFragmentOperation(mFragmentToken, dimOperation);
         mOrganizer.applyTransaction(mTransaction, TASK_FRAGMENT_TRANSIT_CHANGE,
                 false /* shouldApplyIndependently */);
         assertApplyTransactionAllowed(mTransaction);
 
         assertEquals(animationParams, mTaskFragment.getAnimationParams());
         assertEquals(Color.GREEN, mTaskFragment.getAnimationParams().getAnimationBackgroundColor());
+
+        assertTrue(mTaskFragment.isDimmingOnParentTask());
     }
 
     @Test
@@ -1056,7 +1110,7 @@
         // Nothing should happen as the organizer is not registered.
         assertNull(mWindowOrganizerController.getTaskFragment(fragmentToken));
 
-        mController.registerOrganizer(mIOrganizer);
+        registerTaskFragmentOrganizer(mIOrganizer);
         assertApplyTransactionAllowed(mTransaction);
 
         // Successfully created when the organizer is registered.
@@ -1692,8 +1746,10 @@
 
     @Test
     public void testApplyTransaction_reorderToBottomOfTask() {
+        mSetFlagsRule.enableFlags(Flags.FLAG_TASK_FRAGMENT_SYSTEM_ORGANIZER_FLAG);
+
         mController.unregisterOrganizer(mIOrganizer);
-        mController.registerOrganizerInternal(mIOrganizer, true /* isSystemOrganizer */);
+        registerTaskFragmentOrganizer(mIOrganizer, true /* isSystemOrganizer */);
         final Task task = createTask(mDisplayContent);
         // Create a non-embedded Activity at the bottom.
         final ActivityRecord bottomActivity = new ActivityBuilder(mAtm)
@@ -1727,8 +1783,10 @@
 
     @Test
     public void testApplyTransaction_reorderToTopOfTask() {
+        mSetFlagsRule.enableFlags(Flags.FLAG_TASK_FRAGMENT_SYSTEM_ORGANIZER_FLAG);
+
         mController.unregisterOrganizer(mIOrganizer);
-        mController.registerOrganizerInternal(mIOrganizer, true /* isSystemOrganizer */);
+        registerTaskFragmentOrganizer(mIOrganizer, true /* isSystemOrganizer */);
         final Task task = createTask(mDisplayContent);
         // Create a non-embedded Activity at the bottom.
         final ActivityRecord bottomActivity = new ActivityBuilder(mAtm)
@@ -1762,9 +1820,11 @@
 
     @Test
     public void testApplyTransaction_createTaskFragmentDecorSurface() {
+        mSetFlagsRule.enableFlags(Flags.FLAG_TASK_FRAGMENT_SYSTEM_ORGANIZER_FLAG);
+
         // TODO(b/293654166) remove system organizer requirement once security review is cleared.
         mController.unregisterOrganizer(mIOrganizer);
-        mController.registerOrganizerInternal(mIOrganizer, true /* isSystemOrganizer */);
+        registerTaskFragmentOrganizer(mIOrganizer, true /* isSystemOrganizer */);
         final Task task = createTask(mDisplayContent);
 
         final TaskFragment tf = createTaskFragment(task);
@@ -1779,9 +1839,11 @@
 
     @Test
     public void testApplyTransaction_removeTaskFragmentDecorSurface() {
+        mSetFlagsRule.enableFlags(Flags.FLAG_TASK_FRAGMENT_SYSTEM_ORGANIZER_FLAG);
+
         // TODO(b/293654166) remove system organizer requirement once security review is cleared.
         mController.unregisterOrganizer(mIOrganizer);
-        mController.registerOrganizerInternal(mIOrganizer, true /* isSystemOrganizer */);
+        registerTaskFragmentOrganizer(mIOrganizer, true /* isSystemOrganizer */);
         final Task task = createTask(mDisplayContent);
         final TaskFragment tf = createTaskFragment(task);
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
index ec068be..22ddf84 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
@@ -53,6 +53,7 @@
 import android.os.Binder;
 import android.platform.test.annotations.Presubmit;
 import android.view.SurfaceControl;
+import android.view.View;
 import android.window.ITaskFragmentOrganizer;
 import android.window.TaskFragmentAnimationParams;
 import android.window.TaskFragmentInfo;
@@ -90,8 +91,7 @@
         mOrganizer = new TaskFragmentOrganizer(Runnable::run);
         mIOrganizer = ITaskFragmentOrganizer.Stub.asInterface(mOrganizer.getOrganizerToken()
                 .asBinder());
-        mAtm.mWindowOrganizerController.mTaskFragmentOrganizerController
-                .registerOrganizer(mIOrganizer);
+        registerTaskFragmentOrganizer(mIOrganizer);
         mTaskFragment = new TaskFragmentBuilder(mAtm)
                 .setCreateParentTask()
                 .setOrganizer(mOrganizer)
@@ -269,8 +269,7 @@
         mTaskFragment.getTask().addChild(activityBelow, 0);
 
         // Ensure the activity below is visible
-        mTaskFragment.getTask().ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
-                false /* preserveWindows */);
+        mTaskFragment.getTask().ensureActivitiesVisible(null /* starting */);
         assertEquals(true, activityBelow.isVisibleRequested());
     }
 
@@ -686,6 +685,9 @@
         // Return Task bounds if dimming on parent Task.
         final Rect dimBounds = new Rect();
         mTaskFragment.setEmbeddedDimArea(EMBEDDED_DIM_AREA_PARENT_TASK);
+        final Dimmer dimmer = mTaskFragment.getDimmer();
+        spyOn(dimmer);
+        doReturn(taskBounds).when(dimmer).getDimBounds();
         mTaskFragment.getDimBounds(dimBounds);
         assertEquals(taskBounds, dimBounds);
 
@@ -694,4 +696,75 @@
         mTaskFragment.getDimBounds(dimBounds);
         assertEquals(taskFragmentBounds, dimBounds);
     }
+
+    @Test
+    public void testMoveFocusToAdjacentWindow() {
+        // Setup two activities in ActivityEmbedding split.
+        final Task task = createTask(mDisplayContent);
+        final TaskFragment taskFragmentLeft = new TaskFragmentBuilder(mAtm)
+                .setParentTask(task)
+                .createActivityCount(2)
+                .setOrganizer(mOrganizer)
+                .setFragmentToken(new Binder())
+                .build();
+        final TaskFragment taskFragmentRight = new TaskFragmentBuilder(mAtm)
+                .setParentTask(task)
+                .createActivityCount(1)
+                .setOrganizer(mOrganizer)
+                .setFragmentToken(new Binder())
+                .build();
+        taskFragmentLeft.setAdjacentTaskFragment(taskFragmentRight);
+        taskFragmentLeft.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+        taskFragmentRight.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+        task.setBounds(0, 0, 1200, 1000);
+        taskFragmentLeft.setBounds(0, 0, 600, 1000);
+        taskFragmentRight.setBounds(600, 0, 1200, 1000);
+        final ActivityRecord appLeftTop = taskFragmentLeft.getTopMostActivity();
+        final ActivityRecord appLeftBottom = taskFragmentLeft.getBottomMostActivity();
+        final ActivityRecord appRightTop = taskFragmentRight.getTopMostActivity();
+        appLeftTop.setVisibleRequested(true);
+        appRightTop.setVisibleRequested(true);
+        final WindowState winLeftTop = createAppWindow(appLeftTop, "winLeftTop");
+        final WindowState winLeftBottom = createAppWindow(appLeftBottom, "winLeftBottom");
+        final WindowState winRightTop = createAppWindow(appRightTop, "winRightTop");
+        winLeftTop.setHasSurface(true);
+        winRightTop.setHasSurface(true);
+
+        taskFragmentLeft.setResumedActivity(appLeftTop, "test");
+        taskFragmentRight.setResumedActivity(appRightTop, "test");
+        appLeftTop.setState(RESUMED, "test");
+        appRightTop.setState(RESUMED, "test");
+        mDisplayContent.mFocusedApp = appRightTop;
+
+        // Make the appLeftTop be the focused activity and ensure the focused app is updated.
+        appLeftTop.moveFocusableActivityToTop("test");
+        assertEquals(winLeftTop, mDisplayContent.mCurrentFocus);
+
+        // Send request from a non-focused window with valid direction.
+        assertFalse(mWm.moveFocusToAdjacentWindow(null, winLeftBottom.mClient, View.FOCUS_RIGHT));
+        // The focus should remain the same.
+        assertEquals(winLeftTop, mDisplayContent.mCurrentFocus);
+
+        // Send request from the focused window with valid direction.
+        assertTrue(mWm.moveFocusToAdjacentWindow(null, winLeftTop.mClient, View.FOCUS_RIGHT));
+        // The focus should change.
+        assertEquals(winRightTop, mDisplayContent.mCurrentFocus);
+
+        // Send request from the focused window with invalid direction.
+        assertFalse(mWm.moveFocusToAdjacentWindow(null, winRightTop.mClient, View.FOCUS_UP));
+        // The focus should remain the same.
+        assertEquals(winRightTop, mDisplayContent.mCurrentFocus);
+
+        // Send request from the focused window with valid direction.
+        assertTrue(mWm.moveFocusToAdjacentWindow(null, winRightTop.mClient, View.FOCUS_BACKWARD));
+        // The focus should change.
+        assertEquals(winLeftTop, mDisplayContent.mCurrentFocus);
+    }
+
+    private WindowState createAppWindow(ActivityRecord app, String name) {
+        final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, app, name,
+                0 /* ownerId */, false /* ownerCanAddInternalSystemWindow */, new TestIWindow());
+        mWm.mWindowMap.put(win.mClient.asBinder(), win);
+        return win;
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
index da7612b..b360800 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
@@ -43,6 +43,7 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
 import static com.android.server.policy.WindowManagerPolicy.USER_ROTATION_FREE;
 import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_TASK_ORG;
+import static com.android.server.wm.TaskFragment.EMBEDDED_DIM_AREA_PARENT_TASK;
 import static com.android.server.wm.TaskFragment.TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT;
 
 import static com.google.common.truth.Truth.assertThat;
@@ -339,16 +340,14 @@
         // Check visibility of occluded tasks
         doReturn(false).when(leafTask1).shouldBeVisible(any());
         doReturn(true).when(leafTask2).shouldBeVisible(any());
-        rootTask.ensureActivitiesVisible(
-                null /* starting */ , 0 /* configChanges */, false /* preserveWindows */);
+        rootTask.ensureActivitiesVisible(null /* starting */);
         assertFalse(activity1.isVisible());
         assertTrue(activity2.isVisible());
 
         // Check visibility of not occluded tasks
         doReturn(true).when(leafTask1).shouldBeVisible(any());
         doReturn(true).when(leafTask2).shouldBeVisible(any());
-        rootTask.ensureActivitiesVisible(
-                null /* starting */ , 0 /* configChanges */, false /* preserveWindows */);
+        rootTask.ensureActivitiesVisible(null /* starting */);
         assertTrue(activity1.isVisible());
         assertTrue(activity2.isVisible());
     }
@@ -1622,6 +1621,29 @@
     }
 
     @Test
+    public void testBoostDimmingTaskFragmentOnTask() {
+        final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
+        final Task task = createTask(mDisplayContent);
+        final TaskFragment primary = createTaskFragmentWithEmbeddedActivity(task, organizer);
+        final TaskFragment secondary = createTaskFragmentWithEmbeddedActivity(task, organizer);
+        final SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class);
+
+        primary.mVisibleRequested = true;
+        secondary.mVisibleRequested = true;
+        primary.setAdjacentTaskFragment(secondary);
+        secondary.setAdjacentTaskFragment(primary);
+        primary.setEmbeddedDimArea(EMBEDDED_DIM_AREA_PARENT_TASK);
+        doReturn(true).when(primary).shouldBoostDimmer();
+        task.assignChildLayers(t);
+
+        // The layers are initially assigned via the hierarchy, but the primary will be boosted and
+        // assigned again to above of the secondary.
+        verify(primary).assignLayer(t, 0);
+        verify(secondary).assignLayer(t, 1);
+        verify(primary).assignLayer(t, 2);
+    }
+
+    @Test
     public void testMoveOrCreateDecorSurface() {
         final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
         final Task task =  new TaskBuilder(mSupervisor).setCreateActivity(true).build();
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
index bd111ad..7551b165 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -89,8 +89,8 @@
     }
 
     @Override
-    public int interceptMotionBeforeQueueingNonInteractive(int displayId, long whenNanos,
-            int policyFlags) {
+    public int interceptMotionBeforeQueueingNonInteractive(int displayId, int source, int action,
+            long whenNanos, int policyFlags) {
         return 0;
     }
 
@@ -330,6 +330,10 @@
     }
 
     @Override
+    public void showDismissibleKeyguard() {
+    }
+
+    @Override
     public void setPipVisibilityLw(boolean visible) {
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
index 71447e7..51df1d4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -195,6 +195,36 @@
     }
 
     @Test
+    public void testCreateInfo_Activity() {
+        final Transition transition = createTestTransition(TRANSIT_OPEN);
+        ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
+        ArraySet<WindowContainer> participants = transition.mParticipants;
+
+        final Task theTask = createTask(mDisplayContent);
+        final ActivityRecord closing = createActivityRecord(theTask);
+        final ActivityRecord opening = createActivityRecord(theTask);
+        // Start states.
+        changes.put(theTask, new Transition.ChangeInfo(theTask, true /* vis */, false /* exChg */));
+        changes.put(opening, new Transition.ChangeInfo(opening, false /* vis */, true /* exChg */));
+        changes.put(closing, new Transition.ChangeInfo(closing, true /* vis */, false /* exChg */));
+        fillChangeMap(changes, theTask);
+        // End states.
+        closing.setVisibleRequested(false);
+        opening.setVisibleRequested(true);
+
+        final int transit = transition.mType;
+        int flags = 0;
+
+        participants.add(opening);
+        participants.add(closing);
+        ArrayList<Transition.ChangeInfo> targets =
+                Transition.calculateTargets(participants, changes);
+        TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, mMockT);
+        assertEquals(2, info.getChanges().size());
+        assertEquals(info.getChanges().get(1).getActivityComponent(), closing.mActivityComponent);
+    }
+
+    @Test
     public void testCreateInfo_NestedTasks() {
         final Transition transition = createTestTransition(TRANSIT_OPEN);
         ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
@@ -1495,8 +1525,7 @@
         verify(taskSnapshotController, times(0)).recordSnapshot(eq(task1));
 
         enteringAnimReports.clear();
-        doCallRealMethod().when(mWm.mRoot).ensureActivitiesVisible(any(),
-                anyInt(), anyBoolean(), anyBoolean());
+        doCallRealMethod().when(mWm.mRoot).ensureActivitiesVisible(any(), anyBoolean());
         final boolean[] wasInFinishingTransition = { false };
         controller.registerLegacyListener(new WindowManagerInternal.AppTransitionListener() {
             @Override
@@ -1640,7 +1669,7 @@
         final ActivityRecord nonEmbeddedActivity = createActivityRecord(task);
         assertFalse(nonEmbeddedActivity.isEmbedded());
         final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
-        mAtm.mTaskFragmentOrganizerController.registerOrganizer(
+        registerTaskFragmentOrganizer(
                 ITaskFragmentOrganizer.Stub.asInterface(organizer.getOrganizerToken().asBinder()));
         final TaskFragment embeddedTf = new TaskFragmentBuilder(mAtm)
                 .setParentTask(task)
@@ -1690,7 +1719,7 @@
         task.getConfiguration().windowConfiguration.setBounds(taskBounds);
         final ActivityRecord nonEmbeddedActivity = createActivityRecord(task);
         final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
-        mAtm.mTaskFragmentOrganizerController.registerOrganizer(
+        registerTaskFragmentOrganizer(
                 ITaskFragmentOrganizer.Stub.asInterface(organizer.getOrganizerToken().asBinder()));
         final TaskFragment embeddedTf = new TaskFragmentBuilder(mAtm)
                 .setParentTask(task)
@@ -1819,7 +1848,7 @@
         // Skip manipulate the SurfaceControl.
         doNothing().when(activity).setDropInputMode(anyInt());
         final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
-        mAtm.mTaskFragmentOrganizerController.registerOrganizer(
+        registerTaskFragmentOrganizer(
                 ITaskFragmentOrganizer.Stub.asInterface(organizer.getOrganizerToken().asBinder()));
         final TaskFragment embeddedTf = new TaskFragmentBuilder(mAtm)
                 .setParentTask(task)
@@ -1850,7 +1879,7 @@
 
         // Test background color for Activity and embedded TaskFragment.
         final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
-        mAtm.mTaskFragmentOrganizerController.registerOrganizer(
+        registerTaskFragmentOrganizer(
                 ITaskFragmentOrganizer.Stub.asInterface(organizer.getOrganizerToken().asBinder()));
         final Task task = createTask(mDisplayContent);
         final TaskFragment embeddedTf = createTaskFragmentWithEmbeddedActivity(task, organizer);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
index 8bf4833..a1cc8d5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
@@ -37,6 +37,7 @@
 import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
 import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST;
 
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
 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.never;
@@ -79,12 +80,14 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.platform.test.annotations.Presubmit;
+import android.util.ArraySet;
 import android.util.MergedConfiguration;
 import android.view.ContentRecordingSession;
 import android.view.IWindow;
 import android.view.InputChannel;
 import android.view.InsetsSourceControl;
 import android.view.InsetsState;
+import android.view.Surface;
 import android.view.SurfaceControl;
 import android.view.View;
 import android.view.WindowInsets;
@@ -100,16 +103,19 @@
 import com.android.compatibility.common.util.AdoptShellPermissionsRule;
 import com.android.internal.os.IResultReceiver;
 import com.android.server.LocalServices;
+import com.android.server.wm.SensitiveContentPackages.PackageInfo;
 import com.android.server.wm.WindowManagerService.WindowContainerInfo;
 
 import com.google.common.truth.Expect;
 
+import org.junit.After;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
 
 import java.util.ArrayList;
+import java.util.Collections;
 
 /**
  * Build/Install/Run:
@@ -131,6 +137,11 @@
     @Rule
     public Expect mExpect = Expect.create();
 
+    @After
+    public void tearDown() {
+        mWm.mSensitiveContentPackages.setShouldBlockScreenCaptureForApp(Collections.emptySet());
+    }
+
     @Test
     public void testIsRequestedOrientationMapped() {
         mWm.setOrientationRequestPolicy(/* isIgnoreOrientationRequestDisabled*/ true,
@@ -813,6 +824,42 @@
     }
 
     @Test
+    public void setShouldBlockScreenCaptureForApp() {
+        String testPackage = "test";
+        int ownerId1 = 20;
+        int ownerId2 = 21;
+        PackageInfo blockedPackage = new PackageInfo(testPackage, ownerId1);
+        ArraySet<PackageInfo> blockedPackages = new ArraySet();
+        blockedPackages.add(blockedPackage);
+
+        WindowManagerInternal wmInternal = LocalServices.getService(WindowManagerInternal.class);
+        wmInternal.setShouldBlockScreenCaptureForApp(blockedPackages);
+
+        assertTrue(mWm.mSensitiveContentPackages
+                .shouldBlockScreenCaptureForApp(testPackage, ownerId1));
+        assertFalse(mWm.mSensitiveContentPackages
+                .shouldBlockScreenCaptureForApp(testPackage, ownerId2));
+        verify(mWm).refreshScreenCaptureDisabled();
+    }
+
+    @Test
+    public void setShouldBlockScreenCaptureForApp_emptySet_clearsCache() {
+        String testPackage = "test";
+        int ownerId1 = 20;
+        PackageInfo blockedPackage = new PackageInfo(testPackage, ownerId1);
+        ArraySet<PackageInfo> blockedPackages = new ArraySet();
+        blockedPackages.add(blockedPackage);
+
+        WindowManagerInternal wmInternal = LocalServices.getService(WindowManagerInternal.class);
+        wmInternal.setShouldBlockScreenCaptureForApp(blockedPackages);
+        wmInternal.setShouldBlockScreenCaptureForApp(Collections.emptySet());
+
+        assertFalse(mWm.mSensitiveContentPackages
+                .shouldBlockScreenCaptureForApp(testPackage, ownerId1));
+        verify(mWm, times(2)).refreshScreenCaptureDisabled();
+    }
+
+    @Test
     public void testisLetterboxBackgroundMultiColored() {
         assertThat(setupLetterboxConfigurationWithBackgroundType(
                 LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING)).isTrue();
@@ -952,6 +999,57 @@
     }
 
     @Test
+    public void testDrawMagnifiedViewport() {
+        final int displayId = mDisplayContent.mDisplayId;
+        // Use real surface, so ViewportWindow's BlastBufferQueue can be created.
+        final ArrayList<SurfaceControl> surfaceControls = new ArrayList<>();
+        mWm.mSurfaceControlFactory = s -> new SurfaceControl.Builder() {
+            @Override
+            public SurfaceControl build() {
+                final SurfaceControl sc = super.build();
+                surfaceControls.add(sc);
+                return sc;
+            }
+        };
+        mWm.mAccessibilityController.setMagnificationCallbacks(displayId,
+                mock(WindowManagerInternal.MagnificationCallbacks.class));
+        final boolean[] lockCanvasInWmLock = { false };
+        final Surface surface = mWm.mAccessibilityController.forceShowMagnifierSurface(displayId);
+        spyOn(surface);
+        doAnswer(invocationOnMock -> {
+            lockCanvasInWmLock[0] |= Thread.holdsLock(mWm.mGlobalLock);
+            invocationOnMock.callRealMethod();
+            return null;
+        }).when(surface).lockCanvas(any());
+        mWm.mAccessibilityController.drawMagnifiedRegionBorderIfNeeded(displayId, mTransaction);
+        waitUntilHandlersIdle();
+        try {
+            verify(surface).lockCanvas(any());
+
+            clearInvocations(surface);
+            // Invalidate and redraw.
+            mWm.mAccessibilityController.onDisplaySizeChanged(mDisplayContent);
+            mWm.mAccessibilityController.drawMagnifiedRegionBorderIfNeeded(displayId, mTransaction);
+            // Turn off magnification to release surface.
+            mWm.mAccessibilityController.setMagnificationCallbacks(displayId, null);
+            if (!com.android.window.flags.Flags.drawMagnifierBorderOutsideWmlock()) {
+                verify(surface).release();
+                assertTrue(lockCanvasInWmLock[0]);
+                return;
+            }
+            waitUntilHandlersIdle();
+            // lockCanvas must not be called after releasing.
+            verify(surface, never()).lockCanvas(any());
+            verify(surface).release();
+            assertFalse(lockCanvasInWmLock[0]);
+        } finally {
+            for (int i = surfaceControls.size() - 1; i >= 0; --i) {
+                surfaceControls.get(i).release();
+            }
+        }
+    }
+
+    @Test
     public void testRequestKeyboardShortcuts_noWindow() {
         doNothing().when(mWm.mContext).enforceCallingOrSelfPermission(anyString(), anyString());
         doReturn(null).when(mWm).getFocusedWindowLocked();
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
index 28e0c6b..74aabe1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
@@ -40,11 +40,11 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
-import static com.android.server.wm.testing.Assert.assertThrows;
 import static com.android.server.wm.ActivityRecord.State.RESUMED;
 import static com.android.server.wm.WindowContainer.POSITION_TOP;
 import static com.android.server.wm.WindowContainer.SYNC_STATE_READY;
 import static com.android.server.wm.WindowState.BLAST_TIMEOUT_DURATION;
+import static com.android.server.wm.testing.Assert.assertThrows;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -92,6 +92,7 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.server.wm.TaskOrganizerController.PendingTaskEvent;
+import com.android.window.flags.Flags;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -585,6 +586,8 @@
 
     @Test
     public void testTaskFragmentHiddenFocusableTranslucentChanges() {
+        mSetFlagsRule.enableFlags(Flags.FLAG_TASK_FRAGMENT_SYSTEM_ORGANIZER_FLAG);
+
         removeGlobalMinSizeRestriction();
         final Task rootTask = new TaskBuilder(mSupervisor).setCreateActivity(true)
                 .setWindowingMode(WINDOWING_MODE_FULLSCREEN).build();
@@ -660,6 +663,8 @@
 
     private void testTaskFragmentChangesWithoutSystemOrganizerThrowException(
             BiConsumer<WindowContainerTransaction, WindowContainerToken> addOp) {
+        mSetFlagsRule.enableFlags(Flags.FLAG_TASK_FRAGMENT_SYSTEM_ORGANIZER_FLAG);
+
         removeGlobalMinSizeRestriction();
         final Task rootTask = new TaskBuilder(mSupervisor).setCreateActivity(true)
                 .setWindowingMode(WINDOWING_MODE_FULLSCREEN).build();
@@ -1735,11 +1740,9 @@
         final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
         final ITaskFragmentOrganizer organizerInterface =
                 ITaskFragmentOrganizer.Stub.asInterface(organizer.getOrganizerToken().asBinder());
-        mWm.mAtmService.mWindowOrganizerController.mTaskFragmentOrganizerController
-                .registerOrganizerInternal(
-                        ITaskFragmentOrganizer.Stub.asInterface(
-                                organizer.getOrganizerToken().asBinder()),
-                        isSystemOrganizer);
+        registerTaskFragmentOrganizer(
+                ITaskFragmentOrganizer.Stub.asInterface(organizer.getOrganizerToken().asBinder()),
+                isSystemOrganizer);
         t.setTaskFragmentOrganizer(organizerInterface);
 
         return organizer;
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
index e31ee11..400e4b6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
@@ -38,7 +38,6 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.clearInvocations;
@@ -306,10 +305,12 @@
 
     @Test
     public void testCachedStateConfigurationChange() throws RemoteException {
-        doNothing().when(mClientLifecycleManager).scheduleTransactionItemUnlocked(any(), any());
+        doNothing().when(mClientLifecycleManager).scheduleTransactionItemNow(any(), any());
         final IApplicationThread thread = mWpc.getThread();
         final Configuration newConfig = new Configuration(mWpc.getConfiguration());
         newConfig.densityDpi += 100;
+        mWpc.mWindowSession = getTestSession();
+        mWpc.mWindowSession.onWindowAdded(mock(WindowState.class));
         // Non-cached state will send the change directly.
         mWpc.setReportedProcState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND);
         clearInvocations(mClientLifecycleManager);
@@ -322,13 +323,13 @@
         newConfig.densityDpi += 100;
         mWpc.onConfigurationChanged(newConfig);
         verify(mClientLifecycleManager, never()).scheduleTransactionItem(eq(thread), any());
-        verify(mClientLifecycleManager, never()).scheduleTransactionItemUnlocked(eq(thread), any());
+        verify(mClientLifecycleManager, never()).scheduleTransactionItemNow(eq(thread), any());
 
         // Cached -> non-cached will send the previous deferred config immediately.
         mWpc.setReportedProcState(ActivityManager.PROCESS_STATE_RECEIVER);
         final ArgumentCaptor<ConfigurationChangeItem> captor =
                 ArgumentCaptor.forClass(ConfigurationChangeItem.class);
-        verify(mClientLifecycleManager).scheduleTransactionItemUnlocked(
+        verify(mClientLifecycleManager).scheduleTransactionItemNow(
                 eq(thread), captor.capture());
         final ClientTransactionHandler client = mock(ClientTransactionHandler.class);
         captor.getValue().preExecute(client);
@@ -432,7 +433,7 @@
         mWpc.updateAppSpecificSettingsForAllActivitiesInPackage(DEFAULT_COMPONENT_PACKAGE_NAME,
                 Configuration.UI_MODE_NIGHT_YES, LocaleList.forLanguageTags("en-XA"),
                 GRAMMATICAL_GENDER_NOT_SPECIFIED);
-        verify(activity).ensureActivityConfiguration(anyInt(), anyBoolean());
+        verify(activity).ensureActivityConfiguration();
     }
 
     @Test
@@ -443,7 +444,7 @@
                 Configuration.UI_MODE_NIGHT_YES, LocaleList.forLanguageTags("en-XA"),
                 GRAMMATICAL_GENDER_NOT_SPECIFIED);
         verify(activity, never()).applyAppSpecificConfig(anyInt(), any(), anyInt());
-        verify(activity, never()).ensureActivityConfiguration(anyInt(), anyBoolean());
+        verify(activity, never()).ensureActivityConfiguration();
     }
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index 75e252f..fb4edfa 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -55,6 +55,7 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.server.notification.Flags.FLAG_SENSITIVE_NOTIFICATION_APP_PROTECTION;
 import static com.android.server.wm.DisplayContent.IME_TARGET_CONTROL;
 import static com.android.server.wm.DisplayContent.IME_TARGET_LAYERING;
 import static com.android.server.wm.WindowContainer.SYNC_STATE_WAITING_FOR_DRAW;
@@ -90,6 +91,7 @@
 import android.os.InputConfig;
 import android.os.RemoteException;
 import android.platform.test.annotations.Presubmit;
+import android.platform.test.annotations.RequiresFlagsEnabled;
 import android.util.ArraySet;
 import android.util.MergedConfiguration;
 import android.view.Gravity;
@@ -109,7 +111,9 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.server.testutils.StubTransaction;
+import com.android.server.wm.SensitiveContentPackages.PackageInfo;
 
+import org.junit.After;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -130,6 +134,11 @@
 @RunWith(WindowTestRunner.class)
 public class WindowStateTests extends WindowTestsBase {
 
+    @After
+    public void tearDown() {
+        mWm.mSensitiveContentPackages.setShouldBlockScreenCaptureForApp(Collections.emptySet());
+    }
+
     @Test
     public void testIsParentWindowHidden() {
         final WindowState parentWindow = createWindow(null, TYPE_APPLICATION, "parentWindow");
@@ -814,7 +823,7 @@
     @Test
     public void testEmbeddedActivityResizing_clearAllDrawn() {
         final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
-        mAtm.mTaskFragmentOrganizerController.registerOrganizer(
+        registerTaskFragmentOrganizer(
                 ITaskFragmentOrganizer.Stub.asInterface(organizer.getOrganizerToken().asBinder()));
         final Task task = createTask(mDisplayContent);
         final TaskFragment embeddedTf = createTaskFragmentWithEmbeddedActivity(task, organizer);
@@ -1373,6 +1382,28 @@
         assertThat(listener.mIsVisibleForImeTargetOverlay).isFalse();
     }
 
+    @Test
+    @RequiresFlagsEnabled(FLAG_SENSITIVE_NOTIFICATION_APP_PROTECTION)
+    public void testIsSecureLocked_sensitiveContentProtectionManagerEnabled() {
+        String testPackage = "test";
+        int ownerId1 = 20;
+        int ownerId2 = 21;
+        final WindowState window1 = createWindow(null, TYPE_APPLICATION, "window1", ownerId1);
+        final WindowState window2 = createWindow(null, TYPE_APPLICATION, "window2", ownerId2);
+
+        // Setting packagename for targeted feature
+        window1.mAttrs.packageName = testPackage;
+        window2.mAttrs.packageName = testPackage;
+
+        PackageInfo blockedPackage = new PackageInfo(testPackage, ownerId1);
+        ArraySet<PackageInfo> blockedPackages = new ArraySet();
+        blockedPackages.add(blockedPackage);
+        mWm.mSensitiveContentPackages.setShouldBlockScreenCaptureForApp(blockedPackages);
+
+        assertTrue(window1.isSecureLocked());
+        assertFalse(window2.isSecureLocked());
+    }
+
     private static class TestImeTargetChangeListener implements ImeTargetChangeListener {
         private IBinder mImeTargetToken;
         private boolean mIsRemoved;
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index a5f6190f..9c421ba 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -107,6 +107,7 @@
 import android.view.WindowManager.DisplayImePolicy;
 import android.view.inputmethod.ImeTracker;
 import android.window.ClientWindowFrames;
+import android.window.ITaskFragmentOrganizer;
 import android.window.ITransitionPlayer;
 import android.window.ScreenCapture;
 import android.window.StartingWindowInfo;
@@ -891,6 +892,22 @@
         return taskFragment;
     }
 
+    /** @see TaskFragmentOrganizerController#registerOrganizer */
+    void registerTaskFragmentOrganizer(@NonNull ITaskFragmentOrganizer organizer) {
+        registerTaskFragmentOrganizer(organizer, false /* isSystemOrganizer */);
+    }
+
+    /** @see TaskFragmentOrganizerController#registerOrganizer */
+    void registerTaskFragmentOrganizer(@NonNull ITaskFragmentOrganizer organizer,
+            boolean isSystemOrganizer) {
+        // Ensure there is an IApplicationThread to dispatch TaskFragmentTransaction.
+        if (mAtm.mProcessMap.getProcess(WindowManagerService.MY_PID) == null) {
+            mSystemServicesTestRule.addProcess("pkgName", "procName",
+                    WindowManagerService.MY_PID, WindowManagerService.MY_UID);
+        }
+        mAtm.mTaskFragmentOrganizerController.registerOrganizer(organizer, isSystemOrganizer);
+    }
+
     /** Creates a {@link DisplayContent} that supports IME and adds it to the system. */
     DisplayContent createNewDisplay() {
         return createNewDisplayWithImeSupport(DISPLAY_IME_POLICY_LOCAL);
@@ -1037,6 +1054,19 @@
     }
 
     /**
+     * Performs surface placement and waits for WindowAnimator to complete the frame. It is used
+     * to execute the callbacks if the surface placement is expected to add some callbacks via
+     * {@link WindowAnimator#addAfterPrepareSurfacesRunnable}.
+     */
+    void performSurfacePlacementAndWaitForWindowAnimator() {
+        mWm.mAnimator.ready();
+        if (!mWm.mWindowPlacerLocked.isTraversalScheduled()) {
+            mRootWindowContainer.performSurfacePlacement();
+        }
+        waitUntilWindowAnimatorIdle();
+    }
+
+    /**
      * Avoids rotating screen disturbed by some conditions. It is usually used for the default
      * display that is not the instance of {@link TestDisplayContent} (it bypasses the conditions).
      *
diff --git a/services/usage/java/com/android/server/usage/BroadcastResponseStatsLogger.java b/services/usage/java/com/android/server/usage/BroadcastResponseStatsLogger.java
index bfc1771..336bfdd 100644
--- a/services/usage/java/com/android/server/usage/BroadcastResponseStatsLogger.java
+++ b/services/usage/java/com/android/server/usage/BroadcastResponseStatsLogger.java
@@ -35,6 +35,7 @@
 import android.util.TimeUtils;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.Keep;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.RingBuffer;
 import com.android.server.usage.BroadcastResponseStatsTracker.NotificationEventType;
@@ -178,6 +179,7 @@
         }
     }
 
+    @Keep
     public static final class BroadcastEvent implements Data {
         public int sourceUid;
         public int targetUserId;
@@ -198,6 +200,7 @@
         }
     }
 
+    @Keep
     public static final class NotificationEvent implements Data {
         public int type;
         public String packageName;
diff --git a/services/usb/Android.bp b/services/usb/Android.bp
index 1dc5dcf..3a0a6ab 100644
--- a/services/usb/Android.bp
+++ b/services/usb/Android.bp
@@ -35,4 +35,7 @@
         "android.hardware.usb-V1.3-java",
         "android.hardware.usb-V3-java",
     ],
+    lint: {
+        baseline_filename: "lint-baseline.xml",
+    },
 }
diff --git a/services/usb/lint-baseline.xml b/services/usb/lint-baseline.xml
index c2c0a35..62a2ee5 100644
--- a/services/usb/lint-baseline.xml
+++ b/services/usb/lint-baseline.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 7.1.0-dev" type="baseline" client="" dependencies="true" name="" variant="all" version="7.1.0-dev">
+<issues format="6" by="lint 8.4.0-alpha01" type="baseline" client="" dependencies="true" name="" variant="all" version="8.4.0-alpha01">
 
     <issue
         id="NonUserGetterCalled"
@@ -12,4 +12,4 @@
             column="42"/>
     </issue>
 
-</issues>
+</issues>
\ No newline at end of file
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/PhoneCallStateHandler.java b/services/voiceinteraction/java/com/android/server/soundtrigger/PhoneCallStateHandler.java
index 8773cab..1df7012 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/PhoneCallStateHandler.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/PhoneCallStateHandler.java
@@ -24,6 +24,7 @@
 import android.util.Slog;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.telephony.flags.Flags;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -117,12 +118,28 @@
     private boolean checkCallStatus() {
         List<SubscriptionInfo> infoList = mSubscriptionManager.getActiveSubscriptionInfoList();
         if (infoList == null) return false;
-        return infoList.stream()
-                .filter(s -> (s.getSubscriptionId() != SubscriptionManager.INVALID_SUBSCRIPTION_ID))
-                .anyMatch(s -> isCallOngoingFromState(
-                                        mTelephonyManager
-                                                .createForSubscriptionId(s.getSubscriptionId())
-                                                .getCallStateForSubscription()));
+        if (!Flags.enforceTelephonyFeatureMapping()) {
+            return infoList.stream()
+                    .filter(s -> (s.getSubscriptionId()
+                            != SubscriptionManager.INVALID_SUBSCRIPTION_ID))
+                    .anyMatch(s -> isCallOngoingFromState(
+                            mTelephonyManager
+                                    .createForSubscriptionId(s.getSubscriptionId())
+                                    .getCallStateForSubscription()));
+        } else {
+            return infoList.stream()
+                    .filter(s -> (s.getSubscriptionId()
+                            != SubscriptionManager.INVALID_SUBSCRIPTION_ID))
+                    .anyMatch(s -> {
+                        try {
+                            return isCallOngoingFromState(mTelephonyManager
+                                    .createForSubscriptionId(s.getSubscriptionId())
+                                    .getCallStateForSubscription());
+                        } catch (UnsupportedOperationException e) {
+                            return false;
+                        }
+                    });
+        }
     }
 
     private void updateTelephonyListeners() {
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index d05eb5c..57b13e9 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -422,8 +422,8 @@
             "android.telecom.extra.CALL_CREATED_TIME_MILLIS";
 
     /**
-     * The extra for call log uri that was used to mark missed calls as read when dialer gets the
-     * notification on reboot.
+     * Extra URI that is used by a dialer to query the {@link android.provider.CallLog} content
+     * provider and associate a missed call notification with a call log entry.
      */
     @FlaggedApi(Flags.FLAG_ADD_CALL_URI_FOR_MISSED_CALLS)
     public static final String EXTRA_CALL_LOG_URI =
diff --git a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
index 250c3a5..2a6ac98 100644
--- a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
+++ b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
@@ -196,7 +196,7 @@
         // We have READ_PHONE_STATE permission, so return true as long as the AppOps bit hasn't been
         // revoked.
         AppOpsManager appOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
-        return appOps.noteOp(AppOpsManager.OPSTR_READ_PHONE_STATE, uid, callingPackage,
+        return appOps.noteOpNoThrow(AppOpsManager.OPSTR_READ_PHONE_STATE, uid, callingPackage,
                 callingFeatureId, null) == AppOpsManager.MODE_ALLOWED;
     }
 
@@ -249,7 +249,7 @@
         // We have READ_PHONE_STATE permission, so return true as long as the AppOps bit hasn't been
         // revoked.
         AppOpsManager appOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
-        return appOps.noteOp(AppOpsManager.OPSTR_READ_PHONE_STATE, uid, callingPackage,
+        return appOps.noteOpNoThrow(AppOpsManager.OPSTR_READ_PHONE_STATE, uid, callingPackage,
                 callingFeatureId, null) == AppOpsManager.MODE_ALLOWED;
     }
 
@@ -521,7 +521,7 @@
         // We have READ_CALL_LOG permission, so return true as long as the AppOps bit hasn't been
         // revoked.
         AppOpsManager appOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
-        return appOps.noteOp(AppOpsManager.OPSTR_READ_CALL_LOG, uid, callingPackage,
+        return appOps.noteOpNoThrow(AppOpsManager.OPSTR_READ_CALL_LOG, uid, callingPackage,
                 callingPackageName, null) == AppOpsManager.MODE_ALLOWED;
     }
 
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index c7b84a3..79e4ad0 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -10955,6 +10955,9 @@
      * @return A {@link PersistableBundle} containing the config for the given subId, or default
      *         values for an invalid subId.
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     *
      * @deprecated Use {@link #getConfigForSubId(int, String...)} instead.
      */
     @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
@@ -11002,6 +11005,9 @@
      * @return A {@link PersistableBundle} with key/value mapping for the specified configuration
      * on success, or an empty (but never null) bundle on failure (for example, when the calling app
      * has no permission).
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     @RequiresPermission(anyOf = {
             Manifest.permission.READ_PHONE_STATE,
@@ -11047,6 +11053,9 @@
      * @param overrideValues Key-value pairs of the values that are to be overridden. If set to
      *                       {@code null}, this will remove all previous overrides and set the
      *                       carrier configuration back to production values.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
@@ -11104,6 +11113,10 @@
      *
      * @see #getConfigForSubId
      * @see #getConfig(String...)
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     *
      * @deprecated use {@link #getConfig(String...)} instead.
      */
     @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
@@ -11138,6 +11151,9 @@
      * configs on success, or an empty (but never null) bundle on failure.
      * @see #getConfigForSubId(int, String...)
      * @see SubscriptionManager#getDefaultSubscriptionId()
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     @RequiresPermission(anyOf = {
             Manifest.permission.READ_PHONE_STATE,
@@ -11189,6 +11205,9 @@
      *
      * <p>This method returns before the reload has completed, and {@link
      * android.service.carrier.CarrierService#onLoadConfig} will be called from an arbitrary thread.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
@@ -11212,6 +11231,8 @@
      * <p>Depending on simState, the config may be cleared or loaded from config app. This is only
      * used by SubscriptionInfoUpdater.
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @SystemApi
@@ -11234,6 +11255,8 @@
      * Gets the package name for a default carrier service.
      * @return the package name for a default carrier service; empty string if not available.
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @NonNull
@@ -11287,6 +11310,9 @@
      * @param subId the subscription ID, normally obtained from {@link SubscriptionManager}.
      *
      * @see #getConfigForSubId
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
diff --git a/telephony/java/android/telephony/SecurityAlgorithmUpdate.java b/telephony/java/android/telephony/SecurityAlgorithmUpdate.java
index 61d7ead..c902016 100644
--- a/telephony/java/android/telephony/SecurityAlgorithmUpdate.java
+++ b/telephony/java/android/telephony/SecurityAlgorithmUpdate.java
@@ -169,6 +169,7 @@
     public static final int SECURITY_ALGORITHM_NEA1 = 56;
     public static final int SECURITY_ALGORITHM_NEA2 = 57;
     public static final int SECURITY_ALGORITHM_NEA3 = 58;
+    public static final int SECURITY_ALGORITHM_IMS_NULL = 67;
     public static final int SECURITY_ALGORITHM_SIP_NULL = 68;
     public static final int SECURITY_ALGORITHM_AES_GCM = 69;
     public static final int SECURITY_ALGORITHM_AES_GMAC = 70;
@@ -176,9 +177,8 @@
     public static final int SECURITY_ALGORITHM_DES_EDE3_CBC = 72;
     public static final int SECURITY_ALGORITHM_AES_EDE3_CBC = 73;
     public static final int SECURITY_ALGORITHM_HMAC_SHA1_96 = 74;
-    public static final int SECURITY_ALGORITHM_HMAC_SHA1_96_NULL = 75;
-    public static final int SECURITY_ALGORITHM_HMAC_MD5_96 = 76;
-    public static final int SECURITY_ALGORITHM_HMAC_MD5_96_NULL = 77;
+    public static final int SECURITY_ALGORITHM_HMAC_MD5_96 = 75;
+    public static final int SECURITY_ALGORITHM_SRTP_NULL = 86;
     public static final int SECURITY_ALGORITHM_SRTP_AES_COUNTER = 87;
     public static final int SECURITY_ALGORITHM_SRTP_AES_F8 = 88;
     public static final int SECURITY_ALGORITHM_SRTP_HMAC_SHA1 = 89;
@@ -199,15 +199,15 @@
             SECURITY_ALGORITHM_UEA2, SECURITY_ALGORITHM_EEA0, SECURITY_ALGORITHM_EEA1,
             SECURITY_ALGORITHM_EEA2, SECURITY_ALGORITHM_EEA3, SECURITY_ALGORITHM_NEA0,
             SECURITY_ALGORITHM_NEA1, SECURITY_ALGORITHM_NEA2, SECURITY_ALGORITHM_NEA3,
-            SECURITY_ALGORITHM_SIP_NULL, SECURITY_ALGORITHM_AES_GCM,
+            SECURITY_ALGORITHM_IMS_NULL, SECURITY_ALGORITHM_SIP_NULL, SECURITY_ALGORITHM_AES_GCM,
             SECURITY_ALGORITHM_AES_GMAC, SECURITY_ALGORITHM_AES_CBC,
             SECURITY_ALGORITHM_DES_EDE3_CBC, SECURITY_ALGORITHM_AES_EDE3_CBC,
-            SECURITY_ALGORITHM_HMAC_SHA1_96, SECURITY_ALGORITHM_HMAC_SHA1_96_NULL,
-            SECURITY_ALGORITHM_HMAC_MD5_96, SECURITY_ALGORITHM_HMAC_MD5_96_NULL,
-            SECURITY_ALGORITHM_SRTP_AES_COUNTER, SECURITY_ALGORITHM_SRTP_AES_F8,
-            SECURITY_ALGORITHM_SRTP_HMAC_SHA1, SECURITY_ALGORITHM_ENCR_AES_GCM_16,
-            SECURITY_ALGORITHM_ENCR_AES_CBC, SECURITY_ALGORITHM_AUTH_HMAC_SHA2_256_128,
-            SECURITY_ALGORITHM_UNKNOWN, SECURITY_ALGORITHM_OTHER, SECURITY_ALGORITHM_ORYX})
+            SECURITY_ALGORITHM_HMAC_SHA1_96, SECURITY_ALGORITHM_HMAC_MD5_96,
+            SECURITY_ALGORITHM_SRTP_NULL, SECURITY_ALGORITHM_SRTP_AES_COUNTER,
+            SECURITY_ALGORITHM_SRTP_AES_F8, SECURITY_ALGORITHM_SRTP_HMAC_SHA1,
+            SECURITY_ALGORITHM_ENCR_AES_GCM_16, SECURITY_ALGORITHM_ENCR_AES_CBC,
+            SECURITY_ALGORITHM_AUTH_HMAC_SHA2_256_128, SECURITY_ALGORITHM_UNKNOWN,
+            SECURITY_ALGORITHM_OTHER, SECURITY_ALGORITHM_ORYX})
     public @interface SecurityAlgorithm {
     }
 
diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java
index df349f8..c958aba 100644
--- a/telephony/java/android/telephony/SmsManager.java
+++ b/telephony/java/android/telephony/SmsManager.java
@@ -563,7 +563,10 @@
      *  raw pdu of the status report is in the extended data ("pdu").
      *
      * @throws IllegalArgumentException if destinationAddress or text are empty
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
      */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
     public void sendTextMessage(
             String destinationAddress, String scAddress, String text,
             PendingIntent sentIntent, PendingIntent deliveryIntent) {
@@ -581,8 +584,11 @@
      * Used for logging and diagnostics purposes. The id may be 0.
      *
      * @throws IllegalArgumentException if destinationAddress or text are empty
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
      *
      */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
     public void sendTextMessage(
             @NonNull String destinationAddress, @Nullable String scAddress, @NonNull String text,
             @Nullable PendingIntent sentIntent, @Nullable PendingIntent deliveryIntent,
@@ -788,12 +794,16 @@
      * </p>
      *
      * @see #sendTextMessage(String, String, String, PendingIntent, PendingIntent)
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
      */
     @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(allOf = {
             android.Manifest.permission.MODIFY_PHONE_STATE,
             android.Manifest.permission.SEND_SMS
     })
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
     public void sendTextMessageWithoutPersisting(
             String destinationAddress, String scAddress, String text,
             PendingIntent sentIntent, PendingIntent deliveryIntent) {
@@ -908,7 +918,10 @@
      *  {@link #RESULT_REMOTE_EXCEPTION} for error.
      *
      * @throws IllegalArgumentException if the format is invalid.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
      */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
     public void injectSmsPdu(
             byte[] pdu, @SmsMessage.Format String format, PendingIntent receivedIntent) {
         if (!format.equals(SmsMessage.FORMAT_3GPP) && !format.equals(SmsMessage.FORMAT_3GPP2)) {
@@ -940,6 +953,7 @@
      * @return an <code>ArrayList</code> of strings that, in order, comprise the original message.
      * @throws IllegalArgumentException if text is null.
      */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
     public ArrayList<String> divideMessage(String text) {
         if (null == text) {
             throw new IllegalArgumentException("text is null");
@@ -1046,7 +1060,10 @@
      *  extended data ("pdu").
      *
      * @throws IllegalArgumentException if destinationAddress or data are empty
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
      */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
     public void sendMultipartTextMessage(
             String destinationAddress, String scAddress, ArrayList<String> parts,
             ArrayList<PendingIntent> sentIntents, ArrayList<PendingIntent> deliveryIntents) {
@@ -1062,8 +1079,10 @@
      * Used for logging and diagnostics purposes. The id may be 0.
      *
      * @throws IllegalArgumentException if destinationAddress or data are empty
-     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
      */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
     public void sendMultipartTextMessage(
             @NonNull String destinationAddress, @Nullable String scAddress,
             @NonNull List<String> parts, @Nullable List<PendingIntent> sentIntents,
@@ -1089,7 +1108,11 @@
      *
      * @param packageName serves as the default package name if the package name that is
      *        associated with the user id is null.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
      */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
     public void sendMultipartTextMessage(
             @NonNull String destinationAddress, @Nullable String scAddress,
             @NonNull List<String> parts, @Nullable List<PendingIntent> sentIntents,
@@ -1191,10 +1214,14 @@
      * </p>
      *
      * @see #sendMultipartTextMessage(String, String, ArrayList, ArrayList, ArrayList)
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
      * @hide
      **/
     @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
     public void sendMultipartTextMessageWithoutPersisting(
             String destinationAddress, String scAddress, List<String> parts,
             List<PendingIntent> sentIntents, List<PendingIntent> deliveryIntents) {
@@ -1498,7 +1525,10 @@
      *  raw pdu of the status report is in the extended data ("pdu").
      *
      * @throws IllegalArgumentException if destinationAddress or data are empty
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
      */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
     public void sendDataMessage(
             String destinationAddress, String scAddress, short destinationPort,
             byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) {
@@ -1609,6 +1639,7 @@
      * .{@link #createForSubscriptionId createForSubscriptionId(subId)} instead
      */
     @Deprecated
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
     public static SmsManager getSmsManagerForSubscriptionId(int subId) {
         return getSmsManagerForContextAndSubscriptionId(null, subId);
     }
@@ -1626,6 +1657,7 @@
      * @see SubscriptionManager#getActiveSubscriptionInfoList()
      * @see SubscriptionManager#getDefaultSmsSubscriptionId()
      */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
     public @NonNull SmsManager createForSubscriptionId(int subId) {
         return getSmsManagerForContextAndSubscriptionId(mContext, subId);
     }
@@ -1651,7 +1683,11 @@
      * @return associated subscription ID or {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID} if
      * the default subscription id cannot be determined or the device has multiple active
      * subscriptions and and no default is set ("ask every time") by the user.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
      */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
     public int getSubscriptionId() {
         try {
             return (mSubId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID)
@@ -2018,10 +2054,14 @@
      *
      * @throws IllegalArgumentException if endMessageId < startMessageId
      * @deprecated Use {@link TelephonyManager#setCellBroadcastIdRanges} instead.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
      * {@hide}
      */
     @Deprecated
     @SystemApi
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
     public boolean enableCellBroadcastRange(int startMessageId, int endMessageId,
             @android.telephony.SmsCbMessage.MessageFormat int ranType) {
         boolean success = false;
@@ -2079,11 +2119,15 @@
      * @see #enableCellBroadcastRange(int, int, int)
      *
      * @throws IllegalArgumentException if endMessageId < startMessageId
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
+     *
      * @deprecated Use {@link TelephonyManager#setCellBroadcastIdRanges} instead.
      * {@hide}
      */
     @Deprecated
     @SystemApi
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
     public boolean disableCellBroadcastRange(int startMessageId, int endMessageId,
             @android.telephony.SmsCbMessage.MessageFormat int ranType) {
         boolean success = false;
@@ -2223,7 +2267,11 @@
      * @return the user-defined default SMS subscription id, or the active subscription id if
      * there's only one active subscription available, otherwise
      * {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID}.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
      */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
     public static int getDefaultSmsSubscriptionId() {
         try {
             return getISmsService().getPreferredSmsSubscription();
@@ -2271,10 +2319,14 @@
      * </p>
      *
      * @return the total number of SMS records which can be stored on the SIM card.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
      */
     @RequiresPermission(anyOf = {android.Manifest.permission.READ_PHONE_STATE,
             android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE})
     @IntRange(from = 0)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
     public int getSmsCapacityOnIcc() {
         int ret = 0;
         try {
@@ -2819,7 +2871,10 @@
      * <code>MMS_ERROR_DATA_DISABLED</code><br>
      * <code>MMS_ERROR_MMS_DISABLED_BY_CARRIER</code><br>
      * @throws IllegalArgumentException if contentUri is empty
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
      */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
     public void sendMultimediaMessage(Context context, Uri contentUri, String locationUrl,
             Bundle configOverrides, PendingIntent sentIntent) {
         sendMultimediaMessage(context, contentUri, locationUrl, configOverrides, sentIntent,
@@ -2863,7 +2918,10 @@
      * @param messageId an id that uniquely identifies the message requested to be sent.
      * Used for logging and diagnostics purposes. The id may be 0.
      * @throws IllegalArgumentException if contentUri is empty
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
      */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
     public void sendMultimediaMessage(@NonNull Context context, @NonNull Uri contentUri,
             @Nullable String locationUrl,
             @SuppressWarnings("NullableCollection") @Nullable Bundle configOverrides,
@@ -2922,7 +2980,10 @@
      * <code>MMS_ERROR_DATA_DISABLED</code><br>
      * <code>MMS_ERROR_MMS_DISABLED_BY_CARRIER</code><br>
      * @throws IllegalArgumentException if locationUrl or contentUri is empty
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
      */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
     public void downloadMultimediaMessage(Context context, String locationUrl, Uri contentUri,
             Bundle configOverrides, PendingIntent downloadedIntent) {
         downloadMultimediaMessage(context, locationUrl, contentUri, configOverrides,
@@ -2968,7 +3029,10 @@
      * @param messageId an id that uniquely identifies the message requested to be downloaded.
      * Used for logging and diagnostics purposes. The id may be 0.
      * @throws IllegalArgumentException if locationUrl or contentUri is empty
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
      */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
     public void downloadMultimediaMessage(@NonNull Context context, @NonNull String locationUrl,
             @NonNull Uri contentUri,
             @SuppressWarnings("NullableCollection") @Nullable Bundle configOverrides,
@@ -3079,7 +3143,11 @@
      *
      * @return the bundle key/values pairs that contains MMS configuration values
      *  or an empty Bundle if they cannot be found.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
      */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
     @NonNull public Bundle getCarrierConfigValues() {
         try {
             ISms iSms = getISmsService();
@@ -3115,7 +3183,11 @@
      *
      * @return Token to include in an SMS message. The token will be 11 characters long.
      * @see android.provider.Telephony.Sms.Intents#getMessagesFromIntent
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
      */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
     public String createAppSpecificSmsToken(PendingIntent intent) {
         try {
             ISms iccSms = getISmsServiceOrThrow();
@@ -3233,7 +3305,11 @@
      *  message.
      * @param intent this intent is sent when the matching SMS message is received.
      * @return Token to include in an SMS message.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
      */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
     @Nullable
     public String createAppSpecificSmsTokenWithPackageInfo(
             @Nullable String prefixes, @NonNull PendingIntent intent) {
@@ -3393,9 +3469,13 @@
      * </p>
      *
      * @return the SMSC address string, null if failed.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
      */
     @SuppressAutoDoc // for carrier privileges and default SMS application.
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
     @Nullable
     public String getSmscAddress() {
         String smsc = null;
@@ -3430,9 +3510,13 @@
      *
      * @param smsc the SMSC address string.
      * @return true for success, false otherwise. Failure can be due modem returning an error.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
      */
     @SuppressAutoDoc // for carrier privileges and default SMS application.
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
     public boolean setSmscAddress(@NonNull String smsc) {
         try {
             ISms iSms = getISmsService();
@@ -3455,10 +3539,14 @@
      *  {@link SmsManager#PREMIUM_SMS_CONSENT_ASK_USER},
      *  {@link SmsManager#PREMIUM_SMS_CONSENT_NEVER_ALLOW}, or
      *  {@link SmsManager#PREMIUM_SMS_CONSENT_ALWAYS_ALLOW}
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
      * @hide
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
     public @PremiumSmsConsent int getPremiumSmsConsent(@NonNull String packageName) {
         int permission = 0;
         try {
@@ -3479,10 +3567,14 @@
      * @param permission one of {@link SmsManager#PREMIUM_SMS_CONSENT_ASK_USER},
      *  {@link SmsManager#PREMIUM_SMS_CONSENT_NEVER_ALLOW}, or
      *  {@link SmsManager#PREMIUM_SMS_CONSENT_ALWAYS_ALLOW}
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
      * @hide
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
     public void setPremiumSmsConsent(
             @NonNull String packageName, @PremiumSmsConsent int permission) {
         try {
@@ -3498,11 +3590,15 @@
     /**
      * Reset all cell broadcast ranges. Previously enabled ranges will become invalid after this.
      * @deprecated Use {@link TelephonyManager#setCellBroadcastIdRanges} with empty list instead
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
      * @hide
      */
     @Deprecated
     @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_CELL_BROADCASTS)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
     public void resetAllCellBroadcastRanges() {
         try {
             ISms iSms = getISmsService();
@@ -3530,6 +3626,8 @@
      * available.
      * @throws SecurityException if the caller does not have the required permission/privileges.
      * @throws IllegalStateException in case of telephony service is not available.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @NonNull
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 6c8663a..a5c6d57 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -1315,7 +1315,7 @@
      * A source of phone number: the EF-MSISDN (see 3GPP TS 31.102),
      * or EF-MDN for CDMA (see 3GPP2 C.P0065-B), from UICC application.
      *
-     * <p>The availability and a of the number depends on the carrier.
+     * <p>The availability and accuracy of the number depends on the carrier.
      * The number may be updated by over-the-air update to UICC applications
      * from the carrier, or by other means with physical access to the SIM.
      */
@@ -1557,12 +1557,21 @@
      * caller can see all subscription across user profiles as it does today today even if it's
      * {@code false}.
      */
-    private boolean mIsForAllUserProfiles = false;
+    private final boolean mIsForAllUserProfiles;
 
     /** @hide */
     @UnsupportedAppUsage
     public SubscriptionManager(Context context) {
-        if (DBG) logd("SubscriptionManager created");
+        this(context, false /*isForAllUserProfiles*/);
+    }
+
+    /**  Constructor */
+    private SubscriptionManager(Context context, boolean isForAllUserProfiles) {
+        if (DBG) {
+            logd("SubscriptionManager created "
+                    + (isForAllUserProfiles ? "for all user profile" : ""));
+        }
+        mIsForAllUserProfiles = isForAllUserProfiles;
         mContext = context;
     }
 
@@ -1757,6 +1766,9 @@
      *
      * @param subId The unique SubscriptionInfo key in database.
      * @return SubscriptionInfo, maybe null if its not active.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
@@ -1790,6 +1802,8 @@
      * @param iccId the IccId of SIM card
      * @return SubscriptionInfo, maybe null if its not active
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@@ -1826,6 +1840,9 @@
      *
      * @param slotIndex the slot which the subscription is inserted
      * @return SubscriptionInfo, maybe null if its not active
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
@@ -1870,6 +1887,8 @@
      * {@link SubscriptionInfo#getSubscriptionId()}.
      *
      * @throws SecurityException if callers do not hold the required permission.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     @NonNull
     @RequiresPermission(anyOf = {
@@ -1929,6 +1948,9 @@
      * then by {@link SubscriptionInfo#getSubscriptionId}.
      * </li>
      * </ul>
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     // @RequiresPermission(TODO(b/308809058))
@@ -1972,6 +1994,8 @@
      * This is similar to {@link #getActiveSubscriptionInfoList} except that it will return
      * both active and hidden SubscriptionInfos.
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     public @NonNull List<SubscriptionInfo> getCompleteActiveSubscriptionInfoList() {
         List<SubscriptionInfo> completeList = getActiveSubscriptionInfoList(
@@ -1983,7 +2007,7 @@
     }
 
     /**
-     * Convert this subscription manager instance into one that can see all subscriptions across
+     * Create a new subscription manager instance that can see all subscriptions across
      * user profiles.
      *
      * @return a SubscriptionManager that can see all subscriptions regardless its user profile
@@ -1993,13 +2017,12 @@
      * @see #getActiveSubscriptionInfoCount
      * @see UserHandle
      */
-    @FlaggedApi(Flags.FLAG_WORK_PROFILE_API_SPLIT)
+    @FlaggedApi(Flags.FLAG_ENFORCE_SUBSCRIPTION_USER_FILTER)
     // @RequiresPermission(TODO(b/308809058))
     // The permission check for accessing all subscriptions will be enforced upon calling the
     // individual APIs linked above.
     @NonNull public SubscriptionManager createForAllUserProfiles() {
-        mIsForAllUserProfiles = true;
-        return this;
+        return new SubscriptionManager(mContext, true/*isForAllUserProfiles*/);
     }
 
     /**
@@ -2056,6 +2079,9 @@
      * <p>
      * Permissions android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE is required
      * for #getAvailableSubscriptionInfoList to be invoked.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @SystemApi
@@ -2097,6 +2123,9 @@
      * if the list is non-empty the list is sorted by {@link SubscriptionInfo#getSimSlotIndex}
      * then by {@link SubscriptionInfo#getSubscriptionId}.
      * </ul>
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
      */
     public List<SubscriptionInfo> getAccessibleSubscriptionInfoList() {
         List<SubscriptionInfo> result = null;
@@ -2125,6 +2154,8 @@
      *
      * @see TelephonyManager#getCardIdForDefaultEuicc() for more information on the card ID.
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
      * @hide
      */
     @SystemApi
@@ -2155,6 +2186,8 @@
      *
      * @see TelephonyManager#getCardIdForDefaultEuicc() for more information on the card ID.
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
      * @hide
      */
     @SystemApi
@@ -2177,6 +2210,9 @@
      * @return The current number of active subscriptions.
      *
      * @see #getActiveSubscriptionInfoList()
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     // @RequiresPermission(TODO(b/308809058))
@@ -2247,6 +2283,9 @@
      * @param slotIndex the slot assigned to this subscription. It is ignored for subscriptionType
      *                  of {@link #SUBSCRIPTION_TYPE_REMOTE_SIM}.
      * @param subscriptionType the {@link #SUBSCRIPTION_TYPE}
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
@@ -2289,6 +2328,8 @@
      * @throws NullPointerException if {@code uniqueId} is {@code null}.
      * @throws SecurityException if callers do not hold the required permission.
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
@@ -2435,6 +2476,7 @@
      * @deprecated Use {@link #getSubscriptionId(int)} instead.
      * @hide
      */
+    @Deprecated
     public static int[] getSubId(int slotIndex) {
         if (!isValidSlotIndex(slotIndex)) {
             return null;
@@ -2489,6 +2531,9 @@
      * On a data only device or on error, will return INVALID_SUBSCRIPTION_ID.
      *
      * @return the default voice subscription Id.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     public static int getDefaultVoiceSubscriptionId() {
         int subId = INVALID_SUBSCRIPTION_ID;
@@ -2516,6 +2561,9 @@
      *
      * @param subscriptionId A valid subscription ID to set as the system default, or
      *                       {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID}
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @SystemApi
@@ -2535,6 +2583,9 @@
     /**
      * Same as {@link #setDefaultVoiceSubscriptionId(int)}, but preserved for backwards
      * compatibility.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     public void setDefaultVoiceSubId(int subId) {
@@ -2578,6 +2629,8 @@
      *
      * @param subscriptionId the supplied subscription ID
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @SystemApi
@@ -2612,6 +2665,8 @@
      *
      * @param subscriptionId the supplied subscription ID
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @SystemApi
@@ -2634,6 +2689,9 @@
      * Will return null on voice only devices, or on error.
      *
      * @return the SubscriptionInfo for the default data subscription.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @UnsupportedAppUsage
@@ -2720,6 +2778,9 @@
      *
      * @return the list of subId's that are active,
      *         is never null but the length may be 0.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @SystemApi
@@ -2738,6 +2799,9 @@
      *
      * @return the list of subId's that are active,
      *         is never null but the length may be 0.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @SystemApi
@@ -2987,6 +3051,9 @@
      * @param context Context object
      * @param subId Subscription Id of Subscription whose resources are required
      * @return Resources associated with Subscription.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @NonNull
@@ -3069,6 +3136,9 @@
      * @return {@code true} if the supplied subscription ID corresponds to an active subscription;
      * {@code false} if it does not correspond to an active subscription; or throw a
      * SecurityException if the caller hasn't got the right permission.
+     *i
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public boolean isActiveSubscriptionId(int subscriptionId) {
@@ -3377,6 +3447,8 @@
      *
      * @throws IllegalStateException when subscription manager service is not available.
      * @throws SecurityException when clients do not have MODIFY_PHONE_STATE permission.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @SystemApi
@@ -3454,6 +3526,9 @@
      * {@link TelephonyManager#hasCarrierPrivileges}).
      *
      * @return the list of opportunistic subscription info. If none exists, an empty list.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
@@ -3489,8 +3564,12 @@
      *  PendingIntent)} and does not support Multiple Enabled Profile(MEP). Apps should use
      *  {@link EuiccManager#switchToSubscription(int, PendingIntent)} or
      *  {@link EuiccManager#switchToSubscription(int, int, PendingIntent)} instead.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
      */
     @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_EUICC)
     @Deprecated
     public void switchToSubscription(int subId, @NonNull PendingIntent callbackIntent) {
         Preconditions.checkNotNull(callbackIntent, "callbackIntent cannot be null");
@@ -3518,6 +3597,9 @@
      * @param opportunistic whether it’s opportunistic subscription.
      * @param subId the unique SubscriptionInfo index in database
      * @return {@code true} if the operation is succeed, {@code false} otherwise.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
@@ -3554,6 +3636,8 @@
      *             outlined above.
      * @throws IllegalArgumentException if any of the subscriptions in the list doesn't exist.
      * @throws IllegalStateException if Telephony service is in bad state.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      *
      * @param subIdList list of subId that will be in the same group
      * @return groupUUID a UUID assigned to the subscription group.
@@ -3598,6 +3682,8 @@
      *             outlined above.
      * @throws IllegalArgumentException if the some subscriptions in the list doesn't exist.
      * @throws IllegalStateException if Telephony service is in bad state.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      *
      * @param subIdList list of subId that need adding into the group
      * @param groupUuid the groupUuid the subscriptions are being added to.
@@ -3647,6 +3733,8 @@
      * @throws IllegalArgumentException if the some subscriptions in the list doesn't belong the
      * specified group.
      * @throws IllegalStateException if Telephony service is in bad state.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      *
      * @see #createSubscriptionGroup(List)
      */
@@ -3696,6 +3784,8 @@
      * @throws IllegalStateException if Telephony service is in bad state.
      * @throws SecurityException if the caller doesn't meet the requirements
      *             outlined above.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      *
      * @param groupUuid of which list of subInfo will be returned.
      * @return list of subscriptionInfo that belong to the same group, including the given
@@ -3785,9 +3875,9 @@
             Map<ParcelUuid, SubscriptionInfo> groupMap = new HashMap<>();
 
             for (SubscriptionInfo info : availableList) {
-                // Opportunistic subscriptions are considered invisible
+                // Grouped opportunistic subscriptions are considered invisible
                 // to users so they should never be returned.
-                if (!isSubscriptionVisible(info)) continue;
+                if (info.getGroupUuid() != null && info.isOpportunistic()) continue;
 
                 ParcelUuid groupUuid = info.getGroupUuid();
                 if (groupUuid == null) {
@@ -3817,6 +3907,8 @@
      *
      * @return whether the operation is successful.
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @SystemApi
@@ -3844,6 +3936,9 @@
      *
      * @param subscriptionId which subscription to operate on.
      * @param enabled whether uicc applications are enabled or disabled.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @SystemApi
@@ -3872,6 +3967,8 @@
      *
      * @return whether can disable subscriptions on physical SIMs.
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @SystemApi
@@ -3897,6 +3994,8 @@
      *
      * @param subscriptionId The subscription id.
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @SystemApi
@@ -3923,6 +4022,8 @@
      * @param sharing The status sharing preference.
      *
      * @throws SecurityException if the caller doesn't have permissions required.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
     public void setDeviceToDeviceStatusSharingPreference(int subscriptionId,
@@ -3941,6 +4042,8 @@
      * @return The device to device status sharing preference
      *
      * @throws SecurityException if the caller doesn't have permissions required.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     public @DeviceToDeviceStatusSharingPreference int getDeviceToDeviceStatusSharingPreference(
             int subscriptionId) {
@@ -3960,6 +4063,8 @@
      * @param contacts The list of contacts that allow device to device status sharing.
      *
      * @throws SecurityException if the caller doesn't have permissions required.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
     public void setDeviceToDeviceStatusSharingContacts(int subscriptionId,
@@ -3980,6 +4085,9 @@
      * @param subscriptionId Subscription id.
      *
      * @return The list of contacts that allow device to device status sharing.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     public @NonNull List<Uri> getDeviceToDeviceStatusSharingContacts(int subscriptionId) {
         String result = getStringSubscriptionProperty(mContext, subscriptionId,
@@ -4012,6 +4120,8 @@
      *
      * @throws IllegalArgumentException if the provided slot index is invalid.
      * @throws SecurityException if callers do not hold the required permission.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      *
      * @hide
      */
@@ -4152,6 +4262,8 @@
      *
      * @param data with the sim specific configs to be backed up.
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @SystemApi
@@ -4206,6 +4318,8 @@
      * @throws IllegalArgumentException if {@code source} is invalid.
      * @throws IllegalStateException if the telephony process is not currently available.
      * @throws SecurityException if the caller doesn't have permissions required.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      *
      * @see #PHONE_NUMBER_SOURCE_UICC
      * @see #PHONE_NUMBER_SOURCE_CARRIER
@@ -4266,6 +4380,8 @@
      *
      * @throws IllegalStateException if the telephony process is not currently available.
      * @throws SecurityException if the caller doesn't have permissions required.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      *
      * @see #getPhoneNumber(int, int)
      */
@@ -4309,6 +4425,8 @@
      * @throws IllegalStateException if the telephony process is not currently available.
      * @throws NullPointerException if {@code number} is {@code null}.
      * @throws SecurityException if the caller doesn't have permissions required.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     @RequiresPermission("carrier privileges")
     public void setCarrierPhoneNumber(int subscriptionId, @NonNull String number) {
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 22a5cd7..0dce084 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -510,7 +510,7 @@
     /** @hide */
     @UnsupportedAppUsage
     public TelephonyManager(Context context) {
-      this(context, SubscriptionManager.DEFAULT_SUBSCRIPTION_ID);
+        this(context, SubscriptionManager.DEFAULT_SUBSCRIPTION_ID);
     }
 
     /** @hide */
@@ -2115,6 +2115,20 @@
     public static final String ACTION_REQUEST_OMADM_CONFIGURATION_UPDATE =
             "com.android.omadm.service.CONFIGURATION_UPDATE";
 
+    /**
+     * Activity action: Show setting to reset mobile networks.
+     *
+     * <p>On devices with a settings activity to reset mobile networks, the activity should be
+     * launched without additional permissions.
+     *
+     * <p>On some devices, this settings activity may not exist. Callers should ensure that this
+     * case is appropriately handled.
+     */
+    @FlaggedApi(Flags.FLAG_RESET_MOBILE_NETWORK_SETTINGS)
+    @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_RESET_MOBILE_NETWORK_SETTINGS =
+            "android.telephony.action.RESET_MOBILE_NETWORK_SETTINGS";
+
     //
     //
     // Device Info
@@ -2126,10 +2140,14 @@
      * the IMEI/SV for GSM phones. Return null if the software version is
      * not available.
      * <p>
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY}.
      */
     @RequiresPermission(anyOf = {
             android.Manifest.permission.READ_PHONE_STATE,
             android.Manifest.permission.READ_BASIC_PHONE_STATE})
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY)
     @Nullable
     public String getDeviceSoftwareVersion() {
         return getDeviceSoftwareVersion(getSlotIndex());
@@ -2144,10 +2162,13 @@
      *
      * @param slotIndex of which deviceID is returned
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY}.
      * @hide
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY)
     @Nullable
     public String getDeviceSoftwareVersion(int slotIndex) {
         ITelephony telephony = getITelephony();
@@ -2274,6 +2295,9 @@
      *
      * See {@link #getImei(int)} for details on the required permissions and behavior
      * when the caller does not hold sufficient permissions.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_GSM}.
      */
     @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@@ -2316,6 +2340,9 @@
      * </ul>
      *
      * @param slotIndex of which IMEI is returned
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_GSM}.
      */
     @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@@ -2336,6 +2363,9 @@
     /**
      * Returns the Type Allocation Code from the IMEI. Return null if Type Allocation Code is not
      * available.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_GSM}.
      */
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_GSM)
     @Nullable
@@ -2348,6 +2378,9 @@
      * available.
      *
      * @param slotIndex of which Type Allocation Code is returned
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_GSM}.
      */
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_GSM)
     @Nullable
@@ -2393,6 +2426,9 @@
      *     the READ_PHONE_STATE permission, or if the calling app is targeting API level 29 or
      *     higher, then a SecurityException is thrown.</li>
      * </ul>
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CDMA}.
      */
     @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@@ -2432,6 +2468,9 @@
      * </ul>
      *
      * @param slotIndex of which MEID is returned
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CDMA}.
      */
     @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@@ -2458,6 +2497,9 @@
     /**
      * Returns the Manufacturer Code from the MEID. Return null if Manufacturer Code is not
      * available.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CDMA}.
      */
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA)
     @Nullable
@@ -2470,6 +2512,9 @@
      * available.
      *
      * @param slotIndex of which Type Allocation Code is returned
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CDMA}.
      */
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA)
     @Nullable
@@ -2514,6 +2559,9 @@
      *     the READ_PHONE_STATE permission, or if the calling app is targeting API level 29 or
      *     higher, then a SecurityException is thrown.</li>
      * </ul>
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@@ -2549,10 +2597,14 @@
      *<p>
      * @return Current location of the device or null if not available.
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
+     *
      * @deprecated use {@link #getAllCellInfo} instead, which returns a superset of this API.
      */
     @Deprecated
     @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
     public CellLocation getCellLocation() {
         try {
             ITelephony telephony = getITelephony();
@@ -2582,12 +2634,15 @@
      *
      * @return List of NeighboringCellInfo or null if info unavailable.
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      * @removed
      * @deprecated Use {@link #getAllCellInfo} which returns a superset of the information
      *             from NeighboringCellInfo, including LTE cell information.
      */
     @Deprecated
     @RequiresPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
     public List<NeighboringCellInfo> getNeighboringCellInfo() {
         try {
             ITelephony telephony = getITelephony();
@@ -2634,9 +2689,12 @@
      * @see #PHONE_TYPE_CDMA
      * @see #PHONE_TYPE_SIP
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY}.
      * {@hide}
      */
     @SystemApi
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY)
     public int getCurrentPhoneType() {
         return getCurrentPhoneType(getSubId());
     }
@@ -2649,9 +2707,13 @@
      * @see #PHONE_TYPE_CDMA
      *
      * @param subId for which phone type is returned
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY}.
      * @hide
      */
     @SystemApi
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY)
     public int getCurrentPhoneType(int subId) {
         int phoneId;
         if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
@@ -2698,7 +2760,11 @@
      * @see #PHONE_TYPE_GSM
      * @see #PHONE_TYPE_CDMA
      * @see #PHONE_TYPE_SIP
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY}.
      */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY)
     public int getPhoneType() {
         if (!isVoiceCapable()) {
             return PHONE_TYPE_NONE;
@@ -2898,6 +2964,9 @@
      * @see CarrierConfigManager#getConfigForSubId(int)
      * @see #createForSubscriptionId(int)
      * @see #createForPhoneAccountHandle(PhoneAccountHandle)
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @WorkerThread
@@ -2944,6 +3013,9 @@
      * <p>
      * @return the lowercase 2 character ISO-3166-1 alpha-2 country code, or empty string if not
      * available.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      */
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
     public String getNetworkCountryIso() {
@@ -2966,6 +3038,8 @@
      * available.
      *
      * @throws IllegalArgumentException when the slotIndex is invalid.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      *
      */
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
@@ -3094,9 +3168,13 @@
      *
      * @deprecated use {@link #getDataNetworkType()}
      * @return the NETWORK_TYPE_xxxx for current data connection.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      */
     @Deprecated
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
     public @NetworkType int getNetworkType() {
         return getNetworkType(getSubId(SubscriptionManager.getActiveDataSubscriptionId()));
     }
@@ -3185,12 +3263,15 @@
      * @see #NETWORK_TYPE_EHRPD
      * @see #NETWORK_TYPE_HSPAP
      * @see #NETWORK_TYPE_NR
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      */
     @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(anyOf = {
             android.Manifest.permission.READ_PHONE_STATE,
             android.Manifest.permission.READ_BASIC_PHONE_STATE})
-    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
     public @NetworkType int getDataNetworkType() {
         return getDataNetworkType(getSubId(SubscriptionManager.getActiveDataSubscriptionId()));
     }
@@ -3231,6 +3312,9 @@
      * or {@link android.Manifest.permission#READ_BASIC_PHONE_STATE
      * READ_BASIC_PHONE_STATE} or that the calling app has carrier privileges
      * (see {@link #hasCarrierPrivileges}).
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
      */
     @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(anyOf = {
@@ -3583,6 +3667,9 @@
      * of whether an active SIM profile is present or not so this API would always return true.
      *
      * @return true if a ICC card is present.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
     public boolean hasIccCard() {
@@ -3626,6 +3713,9 @@
      * @see #SIM_STATE_PERM_DISABLED
      * @see #SIM_STATE_CARD_IO_ERROR
      * @see #SIM_STATE_CARD_RESTRICTED
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
     public @SimState int getSimState() {
@@ -3667,6 +3757,8 @@
      * @see #SIM_STATE_CARD_RESTRICTED
      * @see #SIM_STATE_PRESENT
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @SystemApi
@@ -3687,11 +3779,14 @@
      * @see #SIM_STATE_CARD_RESTRICTED
      * @see #SIM_STATE_PRESENT
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      * @deprecated instead use {@link #getSimCardState(int, int)}
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
     @Deprecated
     public @SimState int getSimCardState(int physicalSlotIndex) {
         int activePort = getFirstActivePortIndex(physicalSlotIndex);
@@ -3713,6 +3808,8 @@
      * @see #SIM_STATE_CARD_RESTRICTED
      * @see #SIM_STATE_PRESENT
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @SystemApi
@@ -3771,6 +3868,8 @@
      * @see #SIM_STATE_PERM_DISABLED
      * @see #SIM_STATE_LOADED
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @SystemApi
@@ -3794,11 +3893,14 @@
      * @see #SIM_STATE_PERM_DISABLED
      * @see #SIM_STATE_LOADED
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      * @deprecated instead use {@link #getSimApplicationState(int, int)}
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
     @Deprecated
     public @SimState int getSimApplicationState(int physicalSlotIndex) {
         int activePort = getFirstActivePortIndex(physicalSlotIndex);
@@ -3822,6 +3924,8 @@
      * @see #SIM_STATE_PERM_DISABLED
      * @see #SIM_STATE_LOADED
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @SystemApi
@@ -3862,6 +3966,9 @@
      *
      * @param appType the uicc app type like {@link APPTYPE_CSIM}
      * @return true if the specified type of application in UICC CARD or false if no uicc or error.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @SystemApi
@@ -3894,6 +4001,9 @@
      * @see #SIM_STATE_PERM_DISABLED
      * @see #SIM_STATE_CARD_IO_ERROR
      * @see #SIM_STATE_CARD_RESTRICTED
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
     public @SimState int getSimState(int slotIndex) {
@@ -4091,6 +4201,9 @@
      *     the READ_PHONE_STATE permission, or if the calling app is targeting API level 29 or
      *     higher, then a SecurityException is thrown.</li>
      * </ul>
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@@ -4158,6 +4271,9 @@
      *
      * @return {@code true} if 3GPP and 3GPP2 radio technologies can be supported at the same time
      *         {@code false} if not supported or unknown
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      * @hide
      */
     @SystemApi
@@ -4205,6 +4321,9 @@
      * through a factory reset.
      *
      * @return card ID of the default eUICC card, if loaded.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
      */
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_EUICC)
     public int getCardIdForDefaultEuicc() {
@@ -4238,6 +4357,9 @@
      * @return a list of UiccCardInfo objects, representing information on the currently inserted
      * UICCs and eUICCs. Each UiccCardInfo in the list will have private information filtered out if
      * the caller does not have adequate permissions for that card.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
@@ -4262,6 +4384,8 @@
      *
      * @return UiccSlotInfo array.
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @SystemApi
@@ -4305,6 +4429,9 @@
      * @param physicalSlots The content of the array represents the physical slot index. The array
      *        size should be same as {@link #getUiccSlotsInfo()}.
      * @return boolean Return true if the switch succeeds, false if the switch fails.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      * @deprecated {@link #setSimSlotMapping(Collection, Executor, Consumer)}
      */
@@ -4314,6 +4441,7 @@
     @SystemApi
     @Deprecated
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
     public boolean switchSlots(int[] physicalSlots) {
         try {
             ITelephony telephony = getITelephony();
@@ -4406,6 +4534,8 @@
      * @throws IllegalArgumentException if the caller passes in an invalid collection of
      *         UiccSlotMapping like duplicate data, etc
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @SystemApi
@@ -4438,11 +4568,14 @@
      * @return a map indicates the mapping from logical slots to physical slots. The size of the map
      * should be {@link #getPhoneCount()} if success, otherwise return an empty map.
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      * @deprecated use {@link #getSimSlotMapping()} instead.
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
     @NonNull
     @Deprecated
     public Map<Integer, Integer> getLogicalToPhysicalSlotMapping() {
@@ -4470,6 +4603,9 @@
      *
      * @return a collection of {@link UiccSlotMapping} which indicates the mapping from logical
      *         slots to ports and physical slots.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @SystemApi
@@ -4527,6 +4663,9 @@
      *     the READ_PHONE_STATE permission, or if the calling app is targeting API level 29 or
      *     higher, then a SecurityException is thrown.</li>
      * </ul>
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@@ -4579,6 +4718,8 @@
      *         found, and the carrier does not require a key.
      * @throws IllegalArgumentException when an invalid key is found or when key is required but
      *         not found.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@@ -4624,6 +4765,9 @@
      * Requires Permission: MODIFY_PHONE_STATE.
      *
      * @see #getCarrierInfoForImsiEncryption
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
@@ -4826,6 +4970,9 @@
      *                 from disk, as well as on which {@code callback} will be called.
      * @param callback A callback called when the upload operation terminates, either in success
      *                 or in error.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
      */
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
     public void uploadCallComposerPicture(@NonNull Path pictureToUpload,
@@ -4933,6 +5080,9 @@
      *                 read, as well as on which the callback will be called.
      * @param callback A callback called when the upload operation terminates, either in success
      *                 or in error.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
      */
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
     public void uploadCallComposerPicture(@NonNull InputStream pictureToUpload,
@@ -5067,6 +5217,9 @@
      *
      * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
      * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
@@ -5125,6 +5278,8 @@
      *     {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
      *     for apps targeting SDK API level 29 and below.
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @deprecated use {@link SubscriptionManager#getPhoneNumber(int)} instead.
      */
     @Deprecated
@@ -5134,6 +5289,7 @@
             android.Manifest.permission.READ_SMS,
             android.Manifest.permission.READ_PHONE_NUMBERS
     })
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
     public String getLine1Number() {
         return getLine1Number(getSubId());
     }
@@ -5200,9 +5356,13 @@
      * @param alphaTag alpha-tagging of the dailing nubmer
      * @param number The dialing number
      * @return true if the operation was executed correctly.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @deprecated use {@link SubscriptionManager#setCarrierPhoneNumber(int, String)} instead.
      */
     @Deprecated
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
     public boolean setLine1NumberForDisplay(String alphaTag, String number) {
         return setLine1NumberForDisplay(getSubId(), alphaTag, number);
     }
@@ -5322,6 +5482,8 @@
      * {@link SubscriptionManager#createSubscriptionGroup(List)} for the definition of a group,
      * otherwise return an empty array if there is a failure.
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @SystemApi
@@ -5407,6 +5569,9 @@
      *
      * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
      * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
      */
     @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
@@ -5445,6 +5610,9 @@
      *
      * @param alphaTag The alpha tag to display.
      * @param number The voicemail number.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
      */
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
     public boolean setVoiceMailNumber(String alphaTag, String number) {
@@ -5519,6 +5687,8 @@
      * @see #KEY_VISUAL_VOICEMAIL_ENABLED_BY_USER_BOOL
      * @see #KEY_VOICEMAIL_SCRAMBLED_PIN_STRING
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
      * @hide
      */
     @SystemApi
@@ -5549,6 +5719,9 @@
      * @see #createForSubscriptionId(int)
      * @see #createForPhoneAccountHandle(PhoneAccountHandle)
      * @see VisualVoicemailService
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
      */
     @Nullable
     @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
@@ -5579,6 +5752,9 @@
      *
      * @see TelecomManager#getDefaultDialerPackage()
      * @see CarrierConfigManager#KEY_CARRIER_VVM_PACKAGE_NAME_STRING_ARRAY
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
      */
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
     public void setVisualVoicemailSmsFilterSettings(VisualVoicemailSmsFilterSettings settings) {
@@ -5609,6 +5785,9 @@
      *
      * @see SmsManager#sendDataMessage(String, String, short, byte[], PendingIntent, PendingIntent)
      * @see SmsManager#sendTextMessage(String, String, String, PendingIntent, PendingIntent)
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
      */
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
     public void sendVisualVoicemailSms(String number, int port, String text,
@@ -5794,6 +5973,9 @@
       * @see #SIM_ACTIVATION_STATE_ACTIVATING
       * @see #SIM_ACTIVATION_STATE_ACTIVATED
       * @see #SIM_ACTIVATION_STATE_DEACTIVATED
+      *
+      * @throws UnsupportedOperationException If the device does not have
+      *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}
       * @hide
       */
     @SystemApi
@@ -5842,6 +6024,9 @@
      * @see #SIM_ACTIVATION_STATE_ACTIVATED
      * @see #SIM_ACTIVATION_STATE_DEACTIVATED
      * @see #SIM_ACTIVATION_STATE_RESTRICTED
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_DATA}.
      * @hide
      */
     @SystemApi
@@ -5890,6 +6075,9 @@
      * @see #SIM_ACTIVATION_STATE_ACTIVATING
      * @see #SIM_ACTIVATION_STATE_ACTIVATED
      * @see #SIM_ACTIVATION_STATE_DEACTIVATED
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
      * @hide
      */
     @SystemApi
@@ -5940,6 +6128,9 @@
      * @see #SIM_ACTIVATION_STATE_ACTIVATED
      * @see #SIM_ACTIVATION_STATE_DEACTIVATED
      * @see #SIM_ACTIVATION_STATE_RESTRICTED
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_DATA}.
      * @hide
      */
     @SystemApi
@@ -6018,6 +6209,9 @@
      *
      * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
      * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
      */
     @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
@@ -6058,7 +6252,10 @@
      *
      * @throws SecurityException if the caller does not have carrier privileges or is not the
      *         current default dialer
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
      */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
     public void sendDialerSpecialCode(String inputCode) {
         try {
             final ITelephony telephony = getITelephony();
@@ -6129,6 +6326,9 @@
      *
      * <p>Requires Permission:
      * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @Nullable
@@ -6154,6 +6354,9 @@
      * Returns the IMS public user identities (IMPU) that were loaded from the ISIM.
      * @return an array of IMPU strings, with one IMPU per string, or null if
      *      not present or not loaded
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      * @deprecated use {@link #getImsPublicUserIdentities()}
      */
@@ -6185,6 +6388,8 @@
      *         EF_IMPU is not available.
      * @throws IllegalStateException in case the ISIM hasn’t been loaded
      * @throws SecurityException if the caller does not have the required permission/privilege
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @NonNull
@@ -6240,6 +6445,10 @@
      * targeting API level 31+.
      *
      * @return the current call state.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELECOM}.
+     *
      * @deprecated Use {@link #getCallStateForSubscription} to retrieve the call state for a
      * specific telephony subscription (which allows carrier privileged apps),
      * {@link TelephonyCallback.CallStateListener} for real-time call state updates, or
@@ -6247,6 +6456,7 @@
      * device.
      */
     @RequiresPermission(value = android.Manifest.permission.READ_PHONE_STATE, conditional = true)
+    @RequiresFeature(PackageManager.FEATURE_TELECOM)
     @Deprecated
     public @CallState int getCallState() {
         if (mContext != null) {
@@ -6267,6 +6477,9 @@
      * @see TelephonyManager#createForSubscriptionId(int)
      * @see TelephonyManager#createForPhoneAccountHandle(PhoneAccountHandle)
      * @return The call state of the subscription associated with this TelephonyManager instance.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
      */
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
@@ -6327,6 +6540,9 @@
      * @see #DATA_ACTIVITY_OUT
      * @see #DATA_ACTIVITY_INOUT
      * @see #DATA_ACTIVITY_DORMANT
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_DATA}.
      */
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
     public int getDataActivity() {
@@ -6400,6 +6616,9 @@
      * @see #DATA_SUSPENDED
      * @see #DATA_DISCONNECTING
      * @see #DATA_HANDOVER_IN_PROGRESS
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_DATA}.
      */
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
     public int getDataState() {
@@ -6585,10 +6804,14 @@
      * Returns the CDMA ERI icon display number. The number is assigned by
      * 3GPP2 C.R1001-H v1.0 Table 8.1-1. Additionally carriers define their own ERI display numbers.
      * Defined values are {@link #ERI_ON}, {@link #ERI_OFF}, and {@link #ERI_FLASH}.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CDMA}.
      * @hide
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA)
     public @EriIconIndex int getCdmaEnhancedRoamingIndicatorDisplayNumber() {
         return getCdmaEriIconIndex(getSubId());
     }
@@ -6796,6 +7019,9 @@
      *
      * @return List of {@link android.telephony.CellInfo}; null if cell
      * information is unavailable.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      */
     @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION)
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
@@ -6891,6 +7117,9 @@
      *
      * @param executor the executor on which callback will be invoked.
      * @param callback a callback to receive CellInfo.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      */
     @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION)
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
@@ -6952,6 +7181,9 @@
      * @param workSource the requestor to whom the power consumption for this should be attributed.
      * @param executor the executor on which callback will be invoked.
      * @param callback a callback to receive CellInfo.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      * @hide
      */
     @SystemApi
@@ -7038,6 +7270,9 @@
 
     /**
      * Returns the MMS user agent.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
      */
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
     public String getMmsUserAgent() {
@@ -7054,6 +7289,9 @@
 
     /**
      * Returns the MMS user agent profile URL.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
      */
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
     public String getMmsUAProfUrl() {
@@ -7097,8 +7335,12 @@
      *
      * @param AID Application id. See ETSI 102.221 and 101.220.
      * @return an IccOpenLogicalChannelResponse object.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @deprecated Replaced by {@link #iccOpenLogicalChannel(String, int)}
      */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
     @Deprecated
     public IccOpenLogicalChannelResponse iccOpenLogicalChannel(String AID) {
         return iccOpenLogicalChannel(getSubId(), AID, -1);
@@ -7131,6 +7373,8 @@
      * @param aid Application id. See ETSI 102.221 and 101.220.
      * @param p2 P2 parameter (described in ISO 7816-4).
      * @return an IccOpenLogicalChannelResponse object.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      * @deprecated This API is not compatible on eUICC supporting Multiple Enabled Profile(MEP),
      * instead use {@link #iccOpenLogicalChannelByPort(int, int, String, int)}
@@ -7186,9 +7430,13 @@
      * @param aid Application id. See ETSI 102.221 and 101.220.
      * @param p2 P2 parameter (described in ISO 7816-4).
      * @return an IccOpenLogicalChannelResponse object.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
     @SystemApi
     @NonNull
     public IccOpenLogicalChannelResponse iccOpenLogicalChannelByPort(int slotIndex,
@@ -7241,6 +7489,9 @@
      * @param AID Application id. See ETSI 102.221 and 101.220.
      * @param p2 P2 parameter (described in ISO 7816-4).
      * @return an IccOpenLogicalChannelResponse object.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
     public IccOpenLogicalChannelResponse iccOpenLogicalChannel(String AID, int p2) {
@@ -7307,6 +7558,9 @@
      * @param channel is the channel id to be closed as returned by a successful
      *            iccOpenLogicalChannel.
      * @return true if the channel was closed successfully.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      * @deprecated This API is not compatible on eUICC supporting Multiple Enabled Profile(MEP),
      * instead use {@link #iccCloseLogicalChannelByPort(int, int, int)}
@@ -7351,9 +7605,12 @@
      * @throws IllegalStateException if the Telephony process is not currently available or modem
      *                               currently can't process this command.
      * @throws IllegalArgumentException if invalid arguments are passed.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
     @SystemApi
     public void iccCloseLogicalChannelByPort(int slotIndex, int portIndex, int channel) {
         try {
@@ -7389,6 +7646,8 @@
      *            iccOpenLogicalChannel.
      * @return true if the channel was closed successfully.
      * @throws IllegalArgumentException if input parameters are wrong. e.g., invalid channel
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
     public boolean iccCloseLogicalChannel(int channel) {
@@ -7455,6 +7714,8 @@
      * @param data Data to be sent with the APDU.
      * @return The APDU response from the ICC card with the status appended at the end, or null if
      * there is an issue connecting to the Telephony service.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      * @deprecated This API is not compatible on eUICC supporting Multiple Enabled Profile(MEP),
      * instead use
@@ -7502,9 +7763,13 @@
      * @param data Data to be sent with the APDU.
      * @return The APDU response from the ICC card with the status appended at the end, or null if
      * there is an issue connecting to the Telephony service.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
     @SystemApi
     @NonNull
     public String iccTransmitApduLogicalChannelByPort(int slotIndex, int portIndex, int channel,
@@ -7549,6 +7814,9 @@
      * @param data Data to be sent with the APDU.
      * @return The APDU response from the ICC card with the status appended at
      *            the end.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
     public String iccTransmitApduLogicalChannel(int channel, int cla,
@@ -7614,6 +7882,9 @@
      * @param data Data to be sent with the APDU.
      * @return The APDU response from the ICC card with the status appended at
      *            the end.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      * @deprecated This API is not compatible on eUICC supporting Multiple Enabled Profile(MEP),
      * instead use
@@ -7659,9 +7930,13 @@
      * @param data Data to be sent with the APDU.
      * @return The APDU response from the ICC card with the status appended at
      *            the end.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
     @SystemApi
     @NonNull
     public String iccTransmitApduBasicChannelByPort(int slotIndex, int portIndex, int cla,
@@ -7698,6 +7973,9 @@
      * @param data Data to be sent with the APDU.
      * @return The APDU response from the ICC card with the status appended at
      *            the end.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
     public String iccTransmitApduBasicChannel(int cla,
@@ -7754,6 +8032,9 @@
      * @param p3 P3 value of the APDU command.
      * @param filePath
      * @return The APDU response.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
     public byte[] iccExchangeSimIO(int fileID, int command, int p1, int p2, int p3,
@@ -7803,6 +8084,9 @@
      * @return The APDU response from the ICC card in hexadecimal format
      *         with the last 4 bytes being the status word. If the command fails,
      *         returns an empty string.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
     public String sendEnvelopeWithStatus(String content) {
@@ -7964,6 +8248,8 @@
      *
      * @return {@code true} on success; {@code false} on any failure.
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      * @hide
      */
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
@@ -7994,6 +8280,8 @@
      *
      * @deprecated  Using {@link #rebootModem()} instead.
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      * @hide
      */
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
@@ -8021,6 +8309,8 @@
      * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      * @throws IllegalStateException if the Telephony process is not currently available.
      * @throws RuntimeException
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      */
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
@@ -8152,6 +8442,9 @@
      *                      {@link #getMaxNumberVerificationTimeoutMillis()}, whichever is lesser.
      * @param executor The {@link Executor} that callbacks should be executed on.
      * @param callback The callback to use for delivering results.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
      */
     @SystemApi
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
@@ -8364,6 +8657,9 @@
      * See 3GPP TS 31.103 (Section 4.2.7) for the definition and more information on this table.
      *
      * @return IMS Service Table or null if not present or not loaded
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @Nullable
@@ -8482,6 +8778,9 @@
      *   Key freshness failure
      *   Authentication error, no memory space available
      *   Authentication error, no memory space available in EFMUK
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     // TODO(b/73660190): This should probably require MODIFY_PHONE_STATE, not
     // READ_PRIVILEGED_PHONE_STATE. It certainly shouldn't reference the permission in Javadoc since
@@ -8538,6 +8837,9 @@
      * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @return an array of forbidden PLMNs or null if not available
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
@@ -8588,6 +8890,9 @@
      * @return number of PLMNs that were successfully written to the SIM FPLMN list.
      * This may be less than the number of PLMNs passed in where the SIM file does not have enough
      * room for all of the values passed in. Return -1 in the event of an unexpected failure
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
@@ -8623,6 +8928,8 @@
      * @param appType of type int of either {@link #APPTYPE_USIM} or {@link #APPTYPE_ISIM}.
      * @return HexString represents sim service table else null.
      * @throws SecurityException if the caller does not have the required permission/privileges
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
 
@@ -8659,6 +8966,9 @@
      * state.
      *
      * @param slotIndex the sim slot to reset the IMS stack on.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
      * @hide */
     @SystemApi
     @WorkerThread
@@ -9070,12 +9380,15 @@
      *
      * @return The bitmask of preferred network types.
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      * @hide
      * @deprecated Use {@link #getAllowedNetworkTypesBitmask} instead.
      */
     @Deprecated
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     @SystemApi
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
     public @NetworkTypeBitMask long getPreferredNetworkTypeBitmask() {
         return getAllowedNetworkTypesBitmask();
     }
@@ -9094,6 +9407,8 @@
      *
      * @return The bitmask of allowed network types.
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      * @hide
      */
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@@ -9119,10 +9434,14 @@
      * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @return the allowed network type bitmask
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      * @hide
      * @deprecated Use {@link #getAllowedNetworkTypesForReason} instead.
      */
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
     @SystemApi
     @Deprecated
     public @NetworkTypeBitMask long getAllowedNetworkTypes() {
@@ -9147,6 +9466,9 @@
      * <p>Requires Permission:
      * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
      * app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      */
     @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
@@ -9233,6 +9555,9 @@
      *        tasks one at a time in serial order.
      * @param callback Returns network scan results or errors.
      * @return A NetworkScan obj which contains a callback which can be used to stop the scan.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      */
     @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(allOf = {
@@ -9281,11 +9606,15 @@
      *        tasks one at a time in serial order.
      * @param callback Returns network scan results or errors.
      * @return A NetworkScan obj which contains a callback which can be used to stop the scan.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      */
     @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(allOf = {
             android.Manifest.permission.MODIFY_PHONE_STATE
     })
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
     public @Nullable NetworkScan requestNetworkScan(
             @IncludeLocationData int includeLocationData,
             @NonNull NetworkScanRequest request,
@@ -9303,6 +9632,9 @@
     }
 
     /**
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
+     *
      * @deprecated
      * Use {@link
      * #requestNetworkScan(NetworkScanRequest, Executor, TelephonyScanManager.NetworkScanCallback)}
@@ -9313,6 +9645,7 @@
             android.Manifest.permission.MODIFY_PHONE_STATE,
             Manifest.permission.ACCESS_FINE_LOCATION
     })
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
     public NetworkScan requestNetworkScan(
         NetworkScanRequest request, TelephonyScanManager.NetworkScanCallback callback) {
         return requestNetworkScan(request, AsyncTask.SERIAL_EXECUTOR, callback);
@@ -9333,6 +9666,9 @@
      * attaching to the selected PLMN until reboot; otherwise, attach to the chosen PLMN and resume
      * normal network selection next time.
      * @return {@code true} on success; {@code false} on any failure.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      */
     @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
@@ -9364,6 +9700,9 @@
      *         {@link AccessNetworkConstants.AccessNetworkType#UNKNOWN}, modem will select
      *         the next best RAN for network registration.
      * @return {@code true} on success; {@code false} on any failure.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      */
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
@@ -9416,6 +9755,9 @@
      * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @return the network selection mode.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      */
     @SuppressAutoDoc // No support for carrier privileges (b/72967236).
     @RequiresPermission(anyOf = {
@@ -9445,6 +9787,9 @@
      * (see {@link #hasCarrierPrivileges})
      *
      * @return manually selected network info on success or empty string on failure
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      */
     @SuppressAutoDoc // No support carrier privileges (b/72967236).
     @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
@@ -9474,6 +9819,8 @@
      *
      * @return {@code true} if this device is in emergency SMS mode, {@code false} otherwise.
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
      * @hide
      */
     @SystemApi
@@ -9543,11 +9890,15 @@
      *
      * @param networkTypeBitmask The bitmask of preferred network types.
      * @return true on success; false on any failure.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      * @hide
      * @deprecated Use {@link #setAllowedNetworkTypesForReason} instead.
      */
     @Deprecated
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
     @SystemApi
     public boolean setPreferredNetworkTypeBitmask(@NetworkTypeBitMask long networkTypeBitmask) {
         try {
@@ -9589,6 +9940,10 @@
      *
      * @param allowedNetworkTypes The bitmask of allowed network types.
      * @return true on success; false on any failure.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
+     *
      * @hide
      * @deprecated Use {@link #setAllowedNetworkTypesForReason} instead with reason
      * {@link #ALLOWED_NETWORK_TYPES_REASON_CARRIER}.
@@ -9676,11 +10031,16 @@
      * @throws IllegalStateException if the Telephony process is not currently available.
      * @throws IllegalArgumentException if invalid AllowedNetworkTypesReason is passed.
      * @throws SecurityException if the caller does not have the required privileges or if the
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      * caller tries to use one of the following security-based reasons without
      * {@link android.Manifest.permission#MODIFY_PHONE_STATE} permissions.
      * <ol>
      *     <li>{@code TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G}</li>
      * </ol>
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      */
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     @RequiresFeature(
@@ -9720,6 +10080,8 @@
      * @throws IllegalStateException    if the Telephony process is not currently available.
      * @throws IllegalArgumentException if invalid AllowedNetworkTypesReason is passed.
      * @throws SecurityException if the caller does not have the required permission/privileges
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      */
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     @RequiresFeature(
@@ -9794,6 +10156,9 @@
      * <p>Requires that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @return true on success; false on any failure.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      */
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
     public boolean setPreferredNetworkTypeToGlobal() {
@@ -9819,6 +10184,9 @@
      * Requires Permission: MODIFY_PHONE_STATE.
      *
      * @return {@code true} if DUN APN is required for tethering.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_DATA}.
      * @hide
      */
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
@@ -9872,6 +10240,9 @@
      * is a superset of the checks done in SubscriptionManager#canManageSubscription.
      *
      * @return true if the app has carrier privileges.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
     public boolean hasCarrierPrivileges() {
@@ -9919,6 +10290,9 @@
      *
      * @param brand The brand name to display/set.
      * @return true if the operation was executed correctly.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
     public boolean setOperatorBrandOverride(String brand) {
@@ -10020,7 +10394,11 @@
      * Expose the rest of ITelephony to @SystemApi
      */
 
-    /** @hide */
+    /**
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CDMA}.
+     * @hide
+     */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA)
@@ -10028,7 +10406,11 @@
         return getCdmaMdn(getSubId());
     }
 
-    /** @hide */
+    /**
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CDMA}.
+     * @hide
+     */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA)
@@ -10045,7 +10427,11 @@
         }
     }
 
-    /** @hide */
+    /**
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CDMA}.
+     * @hide
+     */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA)
@@ -10053,7 +10439,11 @@
         return getCdmaMin(getSubId());
     }
 
-    /** @hide */
+    /**
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CDMA}.
+     * @hide
+     */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA)
@@ -10070,7 +10460,11 @@
         }
     }
 
-    /** @hide */
+    /**
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * @hide
+     */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
@@ -10087,7 +10481,11 @@
         return CARRIER_PRIVILEGE_STATUS_NO_ACCESS;
     }
 
-    /** @hide */
+    /**
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * @hide
+     */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
@@ -10104,14 +10502,22 @@
         return CARRIER_PRIVILEGE_STATUS_NO_ACCESS;
     }
 
-    /** @hide */
+    /**
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * @hide
+     */
     @SystemApi
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
     public List<String> getCarrierPackageNamesForIntent(Intent intent) {
         return getCarrierPackageNamesForIntentAndPhone(intent, getPhoneId());
     }
 
-    /** @hide */
+    /**
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * @hide
+     */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
@@ -10138,10 +10544,13 @@
      * @return The system-selected package that provides the {@link CarrierService} implementation
      * for the current subscription, or {@code null} if none is resolved
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
     public @Nullable String getCarrierServicePackageName() {
         return getCarrierServicePackageNameForLogicalSlot(getPhoneId());
     }
@@ -10155,10 +10564,13 @@
      * @return The system-selected package that provides the {@link CarrierService} implementation
      * for the slot, or {@code null} if none is resolved
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
     public @Nullable String getCarrierServicePackageNameForLogicalSlot(int logicalSlotIndex) {
         try {
             ITelephony telephony = getITelephony();
@@ -10192,6 +10604,8 @@
     /**
      * Get the names of packages with carrier privileges for all the active subscriptions.
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @SystemApi
@@ -10242,6 +10656,8 @@
      *
      * @throws IllegalArgumentException if requested state is invalid.
      * @throws SecurityException if the caller does not have the permission.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
      */
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
@@ -10271,6 +10687,9 @@
      *
      * @return the user-set status for enriched calling with call composer, either of
      * {@link #CALL_COMPOSER_STATUS_ON} or {@link #CALL_COMPOSER_STATUS_OFF}.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
      */
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
@@ -10287,7 +10706,11 @@
         return CALL_COMPOSER_STATUS_OFF;
     }
 
-    /** @hide */
+    /**
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
+     * @hide
+     */
     @SystemApi
     @SuppressLint("RequiresPermission")
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
@@ -10302,6 +10725,9 @@
     }
 
     /**
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
+     *
      * @deprecated Use  {@link android.telecom.TelecomManager#placeCall(Uri address,
      * Bundle extras)} instead.
      * @hide
@@ -10309,6 +10735,7 @@
     @Deprecated
     @SystemApi
     @RequiresPermission(android.Manifest.permission.CALL_PHONE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
     public void call(String callingPackage, String number) {
         try {
             ITelephony telephony = getITelephony();
@@ -10355,6 +10782,8 @@
     }
 
     /**
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELECOM}.
      * @deprecated Use {@link android.telecom.TelecomManager#isInCall} instead
      * @hide
      */
@@ -10364,12 +10793,15 @@
             android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
             android.Manifest.permission.READ_PHONE_STATE
     })
+    @RequiresFeature(PackageManager.FEATURE_TELECOM)
     public boolean isOffhook() {
         TelecomManager tm = (TelecomManager) mContext.getSystemService(TELECOM_SERVICE);
         return tm.isInCall();
     }
 
     /**
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELECOM}.
      * @deprecated Use {@link android.telecom.TelecomManager#isRinging} instead
      * @hide
      */
@@ -10379,12 +10811,15 @@
             android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
             android.Manifest.permission.READ_PHONE_STATE
     })
+    @RequiresFeature(PackageManager.FEATURE_TELECOM)
     public boolean isRinging() {
         TelecomManager tm = (TelecomManager) mContext.getSystemService(TELECOM_SERVICE);
         return tm.isRinging();
     }
 
     /**
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELECOM}.
      * @deprecated Use {@link android.telecom.TelecomManager#isInCall} instead
      * @hide
      */
@@ -10394,12 +10829,15 @@
             android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
             android.Manifest.permission.READ_PHONE_STATE
     })
+    @RequiresFeature(PackageManager.FEATURE_TELECOM)
     public boolean isIdle() {
         TelecomManager tm = (TelecomManager) mContext.getSystemService(TELECOM_SERVICE);
         return !tm.isInCall();
     }
 
     /**
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      * @deprecated Use {@link android.telephony.TelephonyManager#getServiceState} instead
      * @hide
      */
@@ -10409,6 +10847,7 @@
             android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
             android.Manifest.permission.READ_PHONE_STATE
     })
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
     public boolean isRadioOn() {
         try {
             ITelephony telephony = getITelephony();
@@ -10420,7 +10859,11 @@
         return false;
     }
 
-    /** @hide */
+    /**
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * @hide
+     */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
@@ -10435,7 +10878,11 @@
         return false;
     }
 
-    /** @hide */
+    /**
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * @hide
+     */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
@@ -10451,11 +10898,15 @@
     }
 
     /**
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     *
      * @deprecated use {@link #supplyIccLockPin(String)} instead.
      * @hide
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
     @Deprecated
     public int[] supplyPinReportResult(String pin) {
         try {
@@ -10469,11 +10920,15 @@
     }
 
     /**
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     *
      * @deprecated use {@link #supplyIccLockPuk(String, String)} instead.
      * @hide
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
     @Deprecated
     public int[] supplyPukReportResult(String puk, String pin) {
         try {
@@ -10499,6 +10954,8 @@
      * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
      * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @SystemApi
@@ -10535,6 +10992,8 @@
      * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
      * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @SystemApi
@@ -10608,6 +11067,9 @@
      * @param callback called by the framework to inform the caller of the result of executing the
      *                 USSD request (see {@link UssdResponseCallback}).
      * @param handler the {@link Handler} to run the request on.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      */
     @RequiresPermission(android.Manifest.permission.CALL_PHONE)
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
@@ -10652,6 +11114,9 @@
      * voice and data simultaneously. This can change based on location or network condition.
      *
      * @return {@code true} if simultaneous voice and data supported, and {@code false} otherwise.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_DATA}.
      */
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
     public boolean isConcurrentVoiceAndDataSupported() {
@@ -10665,9 +11130,13 @@
         return false;
     }
 
-    /** @hide */
+    /**
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
+     * @hide */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
     public boolean handlePinMmi(String dialString) {
         try {
             ITelephony telephony = getITelephony();
@@ -10679,9 +11148,14 @@
         return false;
     }
 
-    /** @hide */
+    /**
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
+     * @hide
+     */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
     public boolean handlePinMmiForSubscriber(int subId, String dialString) {
         try {
             ITelephony telephony = getITelephony();
@@ -10693,7 +11167,11 @@
         return false;
     }
 
-    /** @hide */
+    /**
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
+     * @hide
+     */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
@@ -10711,6 +11189,8 @@
      * @deprecated - use the APIs {@link requestRadioPowerOffForReason} and
      * {@link clearRadioPowerOffForReason}.
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      * @hide
      */
     @Deprecated
@@ -10738,6 +11218,8 @@
      * @deprecated - use the APIs {@link requestRadioPowerOffForReason} and
      * {@link clearRadioPowerOffForReason}.
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      * @hide
      */
     @Deprecated
@@ -10785,6 +11267,8 @@
      * @throws SecurityException if the caller does not have MODIFY_PHONE_STATE permission.
      * @throws IllegalStateException if the Telephony service is not currently available.
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      * @hide
      */
     @SystemApi
@@ -10814,6 +11298,8 @@
      * @throws SecurityException if the caller does not have MODIFY_PHONE_STATE permission.
      * @throws IllegalStateException if the Telephony service is not currently available.
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      * @hide
      */
     @SystemApi
@@ -10843,6 +11329,8 @@
      * @throws SecurityException if the caller does not have READ_PRIVILEGED_PHONE_STATE permission.
      * @throws IllegalStateException if the Telephony service is not currently available.
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      * @hide
      */
     @SystemApi
@@ -10872,6 +11360,8 @@
      * <p>To know when the radio has completed powering off, use
      * {@link PhoneStateListener#LISTEN_SERVICE_STATE LISTEN_SERVICE_STATE}.
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      * @hide
      */
     @SystemApi
@@ -10893,6 +11383,9 @@
      * Check if any radio is on over all the slot indexes.
      *
      * @return {@code true} if any radio is on over any slot index.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      * @hide
      */
     @SystemApi
@@ -10939,6 +11432,8 @@
      * {@link android.Manifest.permission#READ_PHONE_STATE} or that the calling
      * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      * @hide
      */
     @SystemApi
@@ -10968,7 +11463,11 @@
         Log.e(TAG, "Do not call TelephonyManager#updateServiceLocation()");
     }
 
-    /** @hide */
+    /**
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_DATA}.
+     * @hide
+     */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
@@ -10983,7 +11482,11 @@
         return false;
     }
 
-    /** @hide */
+    /**
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_DATA}.
+     * @hide
+     */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
@@ -10998,7 +11501,11 @@
         return false;
     }
 
-    /** @hide */
+    /**
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_DATA}.
+     * @hide
+     */
     @SystemApi
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
     public boolean isDataConnectivityPossible() {
@@ -11013,7 +11520,11 @@
         return false;
     }
 
-    /** @hide */
+    /**
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
+     * @hide
+     */
     @SystemApi
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
     public boolean needsOtaServiceProvisioning() {
@@ -11064,23 +11575,29 @@
      * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param enable Whether to enable mobile data.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_DATA}.
      * @deprecated use setDataEnabledForReason with reason DATA_ENABLED_REASON_USER instead.
      *
      */
     @Deprecated
     @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
     public void setDataEnabled(boolean enable) {
         setDataEnabled(getSubId(SubscriptionManager.getDefaultDataSubscriptionId()), enable);
     }
 
     /**
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_DATA}.
      * @hide
      * @deprecated use {@link #setDataEnabledForReason(int, boolean)} instead.
     */
     @SystemApi
     @Deprecated
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
     public void setDataEnabled(int subId, boolean enable) {
         try {
             setDataEnabledForReason(subId, DATA_ENABLED_REASON_USER, enable);
@@ -11091,10 +11608,14 @@
 
     /**
      * @deprecated use {@link #isDataEnabled()} instead.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_DATA}.
      * @hide
      */
     @SystemApi
     @Deprecated
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
     public boolean getDataEnabled() {
         return isDataEnabled();
     }
@@ -11118,6 +11639,9 @@
      * {@link ConnectivityManager#getRestrictBackgroundStatus}.
      *
      * @return true if mobile data is enabled.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_DATA}.
      */
     @RequiresPermission(anyOf = {android.Manifest.permission.ACCESS_NETWORK_STATE,
             android.Manifest.permission.MODIFY_PHONE_STATE,
@@ -11148,6 +11672,9 @@
      * has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @return {@code true} if the data roaming is enabled on the subscription, otherwise return
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_DATA}.
      * {@code false}.
      */
     @RequiresPermission(anyOf = {android.Manifest.permission.ACCESS_NETWORK_STATE,
@@ -11187,6 +11714,8 @@
      * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
      * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CDMA}.
      * @hide
      */
     @SystemApi
@@ -11229,6 +11758,8 @@
      * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
      * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CDMA}.
      * @hide
      */
     @SystemApi
@@ -11297,6 +11828,8 @@
      * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
      * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CDMA}.
      * @hide
      */
     @SystemApi
@@ -11335,6 +11868,8 @@
      * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
      * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CDMA}.
      * @hide
      */
     @SystemApi
@@ -11370,6 +11905,8 @@
      *
      * @param isEnabled {@code true} to enable mobile data roaming, otherwise disable it.
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_DATA}.
      * @hide
      */
     @SystemApi
@@ -11388,11 +11925,15 @@
     }
 
     /**
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_DATA}.
+     *
      * @deprecated use {@link #isDataEnabled()} instead.
      * @hide
      */
     @Deprecated
     @SystemApi
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
     public boolean getDataEnabled(int subId) {
         try {
             return isDataEnabledForReason(subId, DATA_ENABLED_REASON_USER);
@@ -11403,6 +11944,8 @@
     }
 
     /**
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
      * @deprecated Use {@link android.telephony.ims.ImsMmTelManager#setVtSettingEnabled(boolean)}
      * instead.
      * @hide
@@ -11410,6 +11953,7 @@
     @Deprecated
     @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_IMS)
     public void enableVideoCalling(boolean enable) {
         try {
             ITelephony telephony = getITelephony();
@@ -11421,6 +11965,8 @@
     }
 
     /**
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
      * @deprecated Use {@link ImsMmTelManager#isVtSettingEnabled()} instead to check if the user
      * has enabled the Video Calling setting, {@link ImsMmTelManager#isAvailable(int, int)} to
      * determine if video calling is available, or {@link ImsMmTelManager#isCapable(int, int)} to
@@ -11433,6 +11979,7 @@
             android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
             android.Manifest.permission.READ_PHONE_STATE
     })
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_IMS)
     public boolean isVideoCallingEnabled() {
         try {
             ITelephony telephony = getITelephony();
@@ -11448,6 +11995,9 @@
      * Whether the device supports configuring the DTMF tone length.
      *
      * @return {@code true} if the DTMF tone length can be changed, and {@code false} otherwise.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
      */
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
     public boolean canChangeDtmfToneLength() {
@@ -11469,7 +12019,11 @@
      * Whether the device is a world phone.
      *
      * @return {@code true} if the device is a world phone, and {@code false} otherwise.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY}.
      */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY)
     public boolean isWorldPhone() {
         try {
             ITelephony telephony = getITelephony();
@@ -11490,8 +12044,11 @@
      *
      * @return {@code true} if the device supports TTY mode, and {@code false} otherwise.
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELECOM}.
      */
     @Deprecated
+    @RequiresFeature(PackageManager.FEATURE_TELECOM)
     public boolean isTtyModeSupported() {
         try {
             TelecomManager telecomManager = null;
@@ -11512,6 +12069,9 @@
      * support for the feature and device firmware support.
      *
      * @return {@code true} if the device and carrier both support RTT, {@code false} otherwise.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
      */
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_IMS)
     public boolean isRttSupported() {
@@ -11532,6 +12092,9 @@
      *
      * @return {@code true} if the device supports hearing aid compatibility, and {@code false}
      * otherwise.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
      */
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
     public boolean isHearingAidCompatibilitySupported() {
@@ -11794,11 +12357,14 @@
      * <p>Requires Permission:
      *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * {@hide}
      **/
     @SystemApi
     @Deprecated
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
     public void setSimPowerState(int state) {
         setSimPowerStateForSlot(getSlotIndex(), state);
     }
@@ -11820,11 +12386,14 @@
      * <p>Requires Permission:
      *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * {@hide}
      **/
     @SystemApi
     @Deprecated
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
     public void setSimPowerStateForSlot(int slotIndex, int state) {
         try {
             ITelephony telephony = getITelephony();
@@ -11857,6 +12426,8 @@
      * <p>Requires Permission:
      *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * {@hide}
      **/
     @SystemApi
@@ -11887,6 +12458,8 @@
      * <p>Requires Permission:
      *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * {@hide}
      **/
     @SystemApi
@@ -12041,6 +12614,9 @@
      * application currently configured for this user.
      * @return component name of the app and class to direct Respond Via Message intent to, or
      * {@code null} if the functionality is not supported.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
      * @hide
      */
     @SystemApi
@@ -12063,6 +12639,9 @@
      * user associated with this subscription.
      * @return component name of the app and class to direct Respond Via Message intent to, or
      * {@code null} if the functionality is not supported.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
      * @hide
      */
     @SystemApi
@@ -12199,9 +12778,13 @@
      * @return The {@link PhoneAccountHandle} associated with the TelphonyManager, or {@code null}
      * if there is no associated {@link PhoneAccountHandle}; this can happen if the subscription is
      * data-only or an opportunistic subscription.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
      */
     @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
     public @Nullable PhoneAccountHandle getPhoneAccountHandle() {
         return getPhoneAccountHandleForSubscriptionId(getSubId());
     }
@@ -12263,10 +12846,14 @@
      * Resets Telephony and IMS settings back to factory defaults only for the subscription
      * associated with this instance.
      * @see #createForSubscriptionId(int)
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY}.
      * @hide
      */
     @SystemApi
     @RequiresPermission(Manifest.permission.CONNECTIVITY_INTERNAL)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY)
     public void resetSettings() {
         try {
             Log.d(TAG, "resetSettings: subId=" + getSubId());
@@ -12288,6 +12875,8 @@
      *
      * @see Locale#toLanguageTag()
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @SystemApi
@@ -12380,10 +12969,14 @@
      * @param callback A callback object to which the result will be delivered. If there was an
      *                 error processing the request, {@link OutcomeReceiver#onError} will be called
      *                 with more details about the error.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY}.
      * @hide
      */
     @SystemApi
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY)
     public void requestModemActivityInfo(@NonNull @CallbackExecutor Executor executor,
             @NonNull OutcomeReceiver<ModemActivityInfo, ModemActivityInfoException> callback) {
         Objects.requireNonNull(executor);
@@ -12470,6 +13063,9 @@
      * and {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}.
      * May return {@code null} when the subscription is inactive or when there was an error
      * communicating with the phone process.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      */
     @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(allOf = {
@@ -12502,12 +13098,16 @@
      * location related information.
      * May return {@code null} when the subscription is inactive or when there was an error
      * communicating with the phone process.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      */
     @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(allOf = {
             Manifest.permission.READ_PHONE_STATE,
             Manifest.permission.ACCESS_COARSE_LOCATION
     })
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
     public @Nullable ServiceState getServiceState(@IncludeLocationData int includeLocationData) {
         return getServiceStateForSubscriber(getSubId(),
                 includeLocationData != INCLUDE_LOCATION_DATA_FINE,
@@ -12566,6 +13166,9 @@
      * voicemail ringtone.
      * @return The URI for the ringtone to play when receiving a voicemail from a specific
      * PhoneAccount. May be {@code null} if no ringtone is set.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
      */
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
     public @Nullable Uri getVoicemailRingtoneUri(PhoneAccountHandle accountHandle) {
@@ -12592,10 +13195,13 @@
      * @param uri The URI for the ringtone to play when receiving a voicemail from a specific
      * PhoneAccount.
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
      * @deprecated Use {@link android.provider.Settings#ACTION_CHANNEL_NOTIFICATION_SETTINGS}
      * instead.
      */
     @Deprecated
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
     public void setVoicemailRingtoneUri(PhoneAccountHandle phoneAccountHandle, Uri uri) {
         try {
             ITelephony service = getITelephony();
@@ -12613,6 +13219,9 @@
      * @param accountHandle The handle for the {@link PhoneAccount} for which to retrieve the
      * voicemail vibration setting.
      * @return {@code true} if the vibration is set for this PhoneAccount, {@code false} otherwise.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
      */
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
     public boolean isVoicemailVibrationEnabled(PhoneAccountHandle accountHandle) {
@@ -12639,10 +13248,13 @@
      * @param enabled Whether to enable or disable vibration for voicemail notifications from a
      * specific PhoneAccount.
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
      * @deprecated Use {@link android.provider.Settings#ACTION_CHANNEL_NOTIFICATION_SETTINGS}
      * instead.
      */
     @Deprecated
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
     public void setVoicemailVibrationEnabled(PhoneAccountHandle phoneAccountHandle,
             boolean enabled) {
         try {
@@ -12668,6 +13280,9 @@
      *
      * @return Carrier id of the current subscription. Return {@link #UNKNOWN_CARRIER_ID} if the
      * subscription is unavailable or the carrier cannot be identified.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
     public int getSimCarrierId() {
@@ -12693,6 +13308,9 @@
      *
      * @return Carrier name of the current subscription. Return {@code null} if the subscription is
      * unavailable or the carrier cannot be identified.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
     public @Nullable CharSequence getSimCarrierIdName() {
@@ -12731,6 +13349,9 @@
      * @return Returns fine-grained carrier id of the current subscription.
      * Return {@link #UNKNOWN_CARRIER_ID} if the subscription is unavailable or the carrier cannot
      * be identified.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
     public int getSimSpecificCarrierId() {
@@ -12757,6 +13378,9 @@
      *
      * @return user-facing name of the subscription specific carrier id. Return {@code null} if the
      * subscription is unavailable or the carrier cannot be identified.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
     public @Nullable CharSequence getSimSpecificCarrierIdName() {
@@ -12785,6 +13409,9 @@
      *
      * @return matching carrier id from sim MCCMNC. Return {@link #UNKNOWN_CARRIER_ID} if the
      * subscription is unavailable or the carrier cannot be identified.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
     public int getCarrierIdFromSimMccMnc() {
@@ -12860,6 +13487,9 @@
      *
      * @param appType the uicc app type.
      * @return Application ID for specified app type or {@code null} if no uicc or error.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @Nullable
@@ -12925,6 +13555,9 @@
      * Requires that the calling app has READ_PRIVILEGED_PHONE_STATE permission
      *
      * @return PRLVersion or null if error.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CDMA}.
      * @hide
      */
     @SystemApi
@@ -12990,6 +13623,9 @@
      *
      * @return The number of carriers set successfully. Should be length of
      * carrierList on success; -1 if carrierList null or on error.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CARRIERLOCK}.
      * @hide
      */
     @SystemApi
@@ -13116,6 +13752,9 @@
      * @return {@link #SET_CARRIER_RESTRICTION_SUCCESS} in case of success.
      * {@link #SET_CARRIER_RESTRICTION_NOT_SUPPORTED} if the modem does not support the
      * configuration. {@link #SET_CARRIER_RESTRICTION_ERROR} in all other error cases.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CARRIERLOCK}.
      * @hide
      */
     @SystemApi
@@ -13149,11 +13788,15 @@
      *
      * @return List of {@link android.telephony.CarrierIdentifier}; empty list
      * means all carriers are allowed.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CARRIERLOCK}.
      * @hide
      */
     @Deprecated
     @SystemApi
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CARRIERLOCK)
     public List<CarrierIdentifier> getAllowedCarriers(int slotIndex) {
         if (SubscriptionManager.isValidPhoneId(slotIndex)) {
             CarrierRestrictionRules carrierRestrictionRule = getCarrierRestrictionRules();
@@ -13175,6 +13818,9 @@
      * @return {@link CarrierRestrictionRules} which contains the allowed carrier list and the
      * excluded carrier list with the priority between the two lists. Returns {@code null}
      * in case of error.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CARRIERLOCK}.
      * @hide
      */
     @SystemApi
@@ -13243,6 +13889,8 @@
      *                       status result fetched from the radio
      * @throws SecurityException if the caller does not have the required permission/privileges or
      *                           if the caller is not pre-registered.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
@@ -13309,11 +13957,15 @@
      * @see #resetAllCarrierActions()
      * @deprecated use {@link #setDataEnabledForReason(int, boolean) with
      * reason {@link #DATA_ENABLED_REASON_CARRIER}} instead.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_DATA}.
      * @hide
      */
     @Deprecated
     @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
     public void setCarrierDataEnabled(boolean enabled) {
         try {
             setDataEnabledForReason(DATA_ENABLED_REASON_CARRIER, enabled);
@@ -13337,6 +13989,8 @@
      * @deprecated - use the APIs {@link requestRadioPowerOffForReason} and
      * {@link clearRadioPowerOffForReason}.
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      * @hide
      */
     @Deprecated
@@ -13456,6 +14110,9 @@
      *
      * @param report control start/stop reporting network status.
      * @see #resetAllCarrierActions()
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      * @hide
      */
     @SystemApi
@@ -13482,6 +14139,8 @@
      *
      * <p>Requires Permission:
      * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @SystemApi
@@ -13604,6 +14263,8 @@
      * has {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} irrespective of
      * the reason.
      * @throws IllegalStateException if the Telephony process is not currently available.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_DATA}.
      */
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
@@ -13647,6 +14308,8 @@
      * {@link android.Manifest.permission#MODIFY_PHONE_STATE}
      * {@link android.Manifest.permission#READ_BASIC_PHONE_STATE}
      * @throws IllegalStateException if the Telephony process is not currently available.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_DATA}.
      */
     @RequiresPermission(anyOf = {android.Manifest.permission.ACCESS_NETWORK_STATE,
             android.Manifest.permission.READ_PHONE_STATE,
@@ -13703,6 +14366,9 @@
      * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
      *
      * @return true if phone is in emergency callback mode.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
      * @hide
      */
     @SystemApi
@@ -13742,6 +14408,9 @@
      * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}.
      *
      * @return {@code true} if manual network selection is allowed, otherwise return {@code false}.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      */
     @SuppressAutoDoc // No support carrier privileges (b/72967236).
     @RequiresPermission(anyOf = {android.Manifest.permission.READ_PRECISE_PHONE_STATE,
@@ -13765,6 +14434,9 @@
      * Get the most recent SignalStrength information reported by the modem. Due
      * to power saving this information may not always be current.
      * @return the most recent cached signal strength info from the modem
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      */
     @Nullable
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
@@ -13791,6 +14463,9 @@
      *   <LI>And possibly others.</LI>
      * </UL>
      * @return {@code true} if the overall data connection is allowed; {@code false} if not.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_DATA}.
      */
     @RequiresPermission(anyOf = {android.Manifest.permission.ACCESS_NETWORK_STATE,
             android.Manifest.permission.READ_PHONE_STATE,
@@ -13961,6 +14636,9 @@
      *
      * @param enable enable(True) or disable(False)
      * @return returns true if successfully set.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      * @hide
      */
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
@@ -13990,6 +14668,9 @@
      * <p>
      * Requires Permission:
      *   {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      * @hide
      */
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@@ -14191,6 +14872,8 @@
      * that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @throws SecurityException if the caller does not have the required permission
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      */
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
@@ -14226,6 +14909,8 @@
      * <p> Requires permission:
      * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
      * @hide
      */
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
@@ -14253,6 +14938,8 @@
      * <p> Requires permission:
      * {@link android.Manifest.permission#READ_ACTIVE_EMERGENCY_SESSION}
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
      * @hide
      */
     @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION)
@@ -14279,6 +14966,8 @@
      * <p> Requires permission:
      * {@link android.Manifest.permission#READ_ACTIVE_EMERGENCY_SESSION}
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
      * @hide
      */
     @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION)
@@ -14339,6 +15028,9 @@
      * subscription, the key is {@link SubscriptionManager#getDefaultSubscriptionId}) and the value
      * as the list of {@link EmergencyNumber}; empty Map if this information is not available;
      * or throw a SecurityException if the caller does not have the permission.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
      */
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     @NonNull
@@ -14395,6 +15087,8 @@
      * as the list of {@link EmergencyNumber}; empty Map if this information is not available;
      * or throw a SecurityException if the caller does not have the permission.
      * @throws IllegalStateException if the Telephony process is not currently available.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
      */
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     @NonNull
@@ -14463,6 +15157,8 @@
      * @return {@code true} if the given number is an emergency number based on current locale,
      * SIM card(s), Android database, modem, network or defaults; {@code false} otherwise.
      * @throws IllegalStateException if the Telephony process is not currently available.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
      */
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
     public boolean isEmergencyNumber(@NonNull String number) {
@@ -14500,6 +15196,8 @@
      * network; {@code false} if it is not; or throw an SecurityException if the caller does not
      * have the required permission/privileges
      * @throws IllegalStateException if the Telephony process is not currently available.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
      *
      * @deprecated Please use {@link TelephonyManager#isEmergencyNumber(String)} instead.
      * @hide
@@ -14529,6 +15227,8 @@
      * <p>Requires Permission:
      *   {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
      * @hide
      */
     @SystemApi
@@ -14680,6 +15380,9 @@
      * @param callback Callback will be triggered once it succeeds or failed.
      *                 See the {@code SET_OPPORTUNISTIC_SUB_*} constants
      *                 for more details. Pass null if don't care about the result.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_DATA}.
      */
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
     public void setPreferredOpportunisticDataSubscription(int subId, boolean needValidation,
@@ -14740,6 +15443,8 @@
      * {@link SubscriptionManager#DEFAULT_SUBSCRIPTION_ID} if there are no preferred
      * subscription id
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_DATA}.
      */
     @RequiresPermission(anyOf = {
             android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
@@ -14779,6 +15484,8 @@
      * @param executor The executor of where the callback will execute.
      * @param callback Callback will be triggered once it succeeds or failed.
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      */
     @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
@@ -14839,10 +15546,13 @@
      * @param enable whether to enable or disable the modem stack.
      * @return whether the operation is successful.
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY}.
      * @hide
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY)
     public boolean enableModemForSlot(int slotIndex, boolean enable) {
         boolean ret = false;
         try {
@@ -14865,10 +15575,14 @@
      * {@link #hasCarrierPrivileges()}).
      *
      * @param slotIndex which slot it's checking.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY}.
      */
     @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(anyOf = {android.Manifest.permission.READ_PHONE_STATE,
             android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE})
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY)
     public boolean isModemEnabledForSlot(int slotIndex) {
         try {
             ITelephony telephony = getITelephony();
@@ -14931,6 +15645,8 @@
      * @param isMultiSimCarrierRestricted true if usage of multiple SIMs is restricted, false
      * otherwise.
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CARRIERLOCK}.
      * @hide
      */
     @SystemApi
@@ -14986,6 +15702,9 @@
      * {@link #MULTISIM_NOT_SUPPORTED_BY_HARDWARE} if the device does not support multiple SIMs.
      * {@link #MULTISIM_NOT_SUPPORTED_BY_CARRIER} in the device supports multiple SIMs, but the
      * functionality is restricted by the carrier.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
@@ -15017,6 +15736,8 @@
      *
      * @param numOfSims number of live SIMs we want to switch to
      * @throws android.os.RemoteException
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
@@ -15044,6 +15765,9 @@
      *
      * @return {@code true} if reboot will be triggered after making changes to modem
      * configurations, otherwise return {@code false}.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
@@ -15206,6 +15930,8 @@
      *         {@link #CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED}, or
      *         {@link #CARRIER_PRIVILEGE_STATUS_ERROR_LOADING_RULES}
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @SystemApi
@@ -15320,6 +16046,8 @@
      * @param apnType Value indicating the apn type. Apn types are defined in {@link ApnSetting}.
      * @return whether data is enabled for a apn type.
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_DATA}.
      * @hide
      */
     @SystemApi
@@ -15342,6 +16070,8 @@
      * Whether an APN type is metered or not. It will be evaluated with the subId associated
      * with the TelephonyManager instance.
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_DATA}.
      * @hide
      */
     @SystemApi
@@ -15371,6 +16101,9 @@
      * @param executor The executor to execute the callback on
      * @param callback The callback that gets invoked when the radio responds to the request. Called
      *                 with {@code true} if the request succeeded, {@code false} otherwise.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      * @hide
      */
     @SystemApi
@@ -15389,6 +16122,9 @@
      * Same as {@link #setSystemSelectionChannels(List, Executor, Consumer<Boolean>)}, but to be
      * used when the caller does not need feedback on the results of the operation.
      * @param specifiers which bands to scan.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      * @hide
      */
     @SystemApi
@@ -15436,6 +16172,8 @@
      * @return a list of {@link RadioAccessSpecifier}, or an empty list if no bands are specified.
      * @throws IllegalStateException if the Telephony process is not currently available.
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      * @hide
      */
     @SystemApi
@@ -15464,6 +16202,8 @@
      * @return {@code true} if input mccmnc and mvno matches with data from sim operator.
      * {@code false} otherwise.
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * {@hide}
      */
     @SystemApi
@@ -15554,6 +16294,8 @@
      * {@link CallForwardingInfo#REASON_UNCONDITIONAL}, {@link CallForwardingInfo#REASON_BUSY},
      * {@link CallForwardingInfo#REASON_NO_REPLY}, {@link CallForwardingInfo#REASON_NOT_REACHABLE},
      * {@link CallForwardingInfo#REASON_ALL}, or {@link CallForwardingInfo#REASON_ALL_CONDITIONAL}
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
      *
      * @hide
      */
@@ -15631,6 +16373,8 @@
      * <li>{@link CallForwardingInfo#getTimeoutSeconds()} returns a non-positive value when
      * enabling call forwarding</li>
      * </ul>
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
      * @hide
      */
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
@@ -15755,6 +16499,9 @@
      *                          <li>{@link #CALL_WAITING_STATUS_NOT_SUPPORTED}}</li>
      *                          <li>{@link #CALL_WAITING_STATUS_FDN_CHECK_FAILURE}}</li>
      *                       </ul>
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
      * @hide
      */
     @SystemApi
@@ -15805,6 +16552,9 @@
      *                       {@link #CALL_WAITING_STATUS_NOT_SUPPORTED} or
      *                       {@link #CALL_WAITING_STATUS_UNKNOWN_ERROR} or
      *                       {@link #CALL_WAITING_STATUS_FDN_CHECK_FAILURE} if it failed.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
      * @hide
      */
     @SystemApi
@@ -15905,6 +16655,9 @@
      *
      * @param policy The data policy to enable.
      * @param enabled Whether to enable or disable the policy.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_DATA}.
      * @hide
      */
     @SystemApi
@@ -15926,6 +16679,9 @@
      *
      * @param policy The data policy that you want the status for.
      * @return {@code true} if enabled, {@code false} otherwise.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_DATA}.
      * @hide
      */
     @SystemApi
@@ -15961,6 +16717,8 @@
      * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
      * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @WorkerThread
@@ -15995,6 +16753,8 @@
      * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
      * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @SystemApi
@@ -16037,6 +16797,8 @@
      * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
      * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @SystemApi
@@ -16185,6 +16947,8 @@
      * </ol>
      * @return operation result.
      * @throws IllegalStateException if the Telephony process is not currently available.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      * @hide
      */
     @SystemApi
@@ -16219,6 +16983,8 @@
      * connectivity is active. It means the device is allowed to connect to both primary and
      * secondary cell.
      * @throws IllegalStateException if the Telephony process is not currently available.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      * @hide
      */
     @SystemApi
@@ -16456,6 +17222,8 @@
      *
      * @throws IllegalStateException if the Telephony process is not currently available.
      * @throws SecurityException if the caller doesn't have the permission.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      *
      */
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
@@ -16572,6 +17340,9 @@
      *
      * @param capability the name of the capability to check for
      * @return the availability of the capability
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      */
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
     public boolean isRadioInterfaceCapabilitySupported(
@@ -16691,6 +17462,8 @@
      * @throws IllegalArgumentException if the thermalMitigationRequest had invalid parameters or
      * if the device's modem does not support data throttling.
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      * @hide
      */
     @SystemApi
@@ -17023,6 +17796,9 @@
      * contain the GBA Ks_NAF/Ks_ext_NAF when available. If the NAF keys are
      * available and valid at the time of call and bootstrapping is not requested,
      * then the callback shall be invoked with the available keys.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @SystemApi
@@ -17121,6 +17897,8 @@
      * @param request the SignalStrengthUpdateRequest to be set into the System
      *
      * @throws IllegalStateException if a new request is set with same subId from the same caller
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      */
     @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
@@ -17151,6 +17929,9 @@
      * @see #setSignalStrengthUpdateRequest(SignalStrengthUpdateRequest)
      *
      * @param request the SignalStrengthUpdateRequest to be cleared from the System
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      */
     @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
@@ -17174,10 +17955,14 @@
      * @return the PhoneCapability which describes the data connection capability of modem.
      * It's used to evaluate possible phone config change, for example from single
      * SIM device to multi-SIM device.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
     public @NonNull PhoneCapability getPhoneCapability() {
         try {
             ITelephony telephony = getITelephony();
@@ -17242,11 +18027,15 @@
      * at least one SIM card for which the user needs to manually enter the PIN
      * code after the reboot. {@link #PREPARE_UNATTENDED_REBOOT_ERROR} in case
      * of error.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.REBOOT)
     @PrepareUnattendedRebootResult
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
     public int prepareForUnattendedReboot() {
         try {
             ITelephony service = getITelephony();
@@ -17349,6 +18138,9 @@
      *
      * @param executor the executor on which callback will be invoked.
      * @param callback a callback to receive the current slicing configuration.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      */
     @RequiresFeature(
             enforcement = "android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported",
@@ -17430,8 +18222,11 @@
      * @param capability The premium capability to check.
      * @return Whether the given premium capability is available to purchase.
      * @throws SecurityException if the caller does not hold permission READ_BASIC_PHONE_STATE.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_DATA}.
      */
     @RequiresPermission(android.Manifest.permission.READ_BASIC_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
     public boolean isPremiumCapabilityAvailableForPurchase(@PremiumCapability int capability) {
         try {
             ITelephony telephony = getITelephony();
@@ -17671,10 +18466,13 @@
      * @param callback The result of the purchase request.
      * @throws SecurityException if the caller does not hold permissions
      *         READ_BASIC_PHONE_STATE or INTERNET.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_DATA}.
      * @see #isPremiumCapabilityAvailableForPurchase(int) to check whether the capability is valid.
      */
     @RequiresPermission(allOf = {android.Manifest.permission.READ_BASIC_PHONE_STATE,
             android.Manifest.permission.INTERNET})
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
     public void purchasePremiumCapability(@PremiumCapability int capability,
             @NonNull @CallbackExecutor Executor executor,
             @NonNull @PurchasePremiumCapabilityResult Consumer<Integer> callback) {
@@ -18125,13 +18923,73 @@
     }
 
     /**
+     * Enables or disables notifications sent when cellular null cipher or integrity algorithms
+     * are in use by the cellular modem.
+     *
+     * @throws IllegalStateException if the Telephony process is not currently available
+     * @throws SecurityException if the caller does not have the required privileges
+     * @throws UnsupportedOperationException if the modem does not support reporting on ciphering
+     * and integrity algorithms in use
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_MODEM_CIPHER_TRANSPARENCY)
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    @SystemApi
+    public void setEnableNullCipherNotifications(boolean enable) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                telephony.setEnableNullCipherNotifications(enable);
+            } else {
+                throw new IllegalStateException("telephony service is null.");
+            }
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "setEnableNullCipherNotifications RemoteException", ex);
+            ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Get whether notifications are enabled for null cipher or integrity algorithms in use by the
+     * cellular modem.
+     *
+     * @throws IllegalStateException if the Telephony process is not currently available
+     * @throws SecurityException if the caller does not have the required privileges
+     * @throws UnsupportedOperationException if the modem does not support reporting on ciphering
+     * and integrity algorithms in use
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_MODEM_CIPHER_TRANSPARENCY)
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @SystemApi
+    public boolean isNullCipherNotificationsEnabled() {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.isNullCipherNotificationsEnabled();
+            } else {
+                throw new IllegalStateException("telephony service is null.");
+            }
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "isNullCipherNotificationsEnabled RemoteException", ex);
+            ex.rethrowFromSystemServer();
+        }
+        return false;
+    }
+
+
+    /**
      * Get current cell broadcast message identifier ranges.
      *
      * @throws SecurityException if the caller does not have the required permission
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
+     *
      * @hide
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_CELL_BROADCASTS)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
     @NonNull
     public List<CellBroadcastIdRange> getCellBroadcastIdRanges() {
         try {
@@ -18285,10 +19143,13 @@
      * the result when the operation completes.
      * @throws SecurityException if the caller does not have the required permission
      * @throws IllegalArgumentException when the ranges are invalid.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
      * @hide
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_CELL_BROADCASTS)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
     public void setCellBroadcastIdRanges(@NonNull List<CellBroadcastIdRange> ranges,
             @NonNull @CallbackExecutor Executor executor,
             @NonNull Consumer<Integer> callback) {
@@ -18364,7 +19225,8 @@
      * </ul>
      *
      * @return Primary IMEI of type string
-     * @throws UnsupportedOperationException if the radio doesn't support this feature.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_GSM}.
      * @throws SecurityException if the caller does not have the required permission/privileges
      */
     @NonNull
diff --git a/telephony/java/android/telephony/data/DataCallResponse.java b/telephony/java/android/telephony/data/DataCallResponse.java
index 9dd83d1..b6f9e1f 100644
--- a/telephony/java/android/telephony/data/DataCallResponse.java
+++ b/telephony/java/android/telephony/data/DataCallResponse.java
@@ -451,7 +451,7 @@
 
     /**
      * Return the network validation status that was initiated by {@link
-     * DataService.DataServiceProvider#requestValidation}
+     * DataService.DataServiceProvider#requestNetworkValidation}
      *
      * @return The network validation status of data connection.
      */
@@ -931,7 +931,7 @@
 
         /**
          * Set the network validation status that corresponds to the state of the network validation
-         * request started by {@link DataService.DataServiceProvider#requestValidation}
+         * request started by {@link DataService.DataServiceProvider#requestNetworkValidation}
          *
          * @param status The network validation status.
          * @return The same instance of the builder.
diff --git a/telephony/java/android/telephony/data/DataService.java b/telephony/java/android/telephony/data/DataService.java
index 80e91a3..f04e1c9 100644
--- a/telephony/java/android/telephony/data/DataService.java
+++ b/telephony/java/android/telephony/data/DataService.java
@@ -415,13 +415,13 @@
          *     request validation to the DataService and checks if the request has been submitted.
          */
         @FlaggedApi(Flags.FLAG_NETWORK_VALIDATION)
-        public void requestValidation(int cid,
+        public void requestNetworkValidation(int cid,
                 @NonNull @CallbackExecutor Executor executor,
                 @NonNull @DataServiceCallback.ResultCode Consumer<Integer> resultCodeCallback) {
             Objects.requireNonNull(executor, "executor cannot be null");
             Objects.requireNonNull(resultCodeCallback, "resultCodeCallback cannot be null");
 
-            Log.d(TAG, "requestValidation: " + cid);
+            Log.d(TAG, "requestNetworkValidation: " + cid);
 
             // The default implementation is to return unsupported.
             executor.execute(() -> resultCodeCallback
@@ -741,7 +741,7 @@
                 case DATA_SERVICE_REQUEST_VALIDATION:
                     if (serviceProvider == null) break;
                     ValidationRequest validationRequest = (ValidationRequest) message.obj;
-                    serviceProvider.requestValidation(
+                    serviceProvider.requestNetworkValidation(
                             validationRequest.cid,
                             validationRequest.executor,
                             FunctionalUtils
@@ -924,9 +924,10 @@
         }
 
         @Override
-        public void requestValidation(int slotIndex, int cid, IIntegerConsumer resultCodeCallback) {
+        public void requestNetworkValidation(int slotIndex, int cid,
+                IIntegerConsumer resultCodeCallback) {
             if (resultCodeCallback == null) {
-                loge("requestValidation: resultCodeCallback is null");
+                loge("requestNetworkValidation: resultCodeCallback is null");
                 return;
             }
             ValidationRequest validationRequest =
diff --git a/telephony/java/android/telephony/data/IDataService.aidl b/telephony/java/android/telephony/data/IDataService.aidl
index 15f8881..c36c302 100644
--- a/telephony/java/android/telephony/data/IDataService.aidl
+++ b/telephony/java/android/telephony/data/IDataService.aidl
@@ -48,5 +48,5 @@
     void cancelHandover(int slotId, int cid, IDataServiceCallback callback);
     void registerForUnthrottleApn(int slotIndex, IDataServiceCallback callback);
     void unregisterForUnthrottleApn(int slotIndex, IDataServiceCallback callback);
-    void requestValidation(int slotId, int cid, IIntegerConsumer callback);
+    void requestNetworkValidation(int slotId, int cid, IIntegerConsumer callback);
 }
diff --git a/telephony/java/android/telephony/euicc/EuiccCardManager.java b/telephony/java/android/telephony/euicc/EuiccCardManager.java
index e981e1f..69594f2 100644
--- a/telephony/java/android/telephony/euicc/EuiccCardManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccCardManager.java
@@ -183,6 +183,9 @@
      * @param cardId   The Id of the eUICC.
      * @param executor The executor through which the callback should be invoked.
      * @param callback The callback to get the result code and all the profiles.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
      */
     public void requestAllProfiles(String cardId, @CallbackExecutor Executor executor,
             ResultCallback<EuiccProfileInfo[]> callback) {
@@ -212,6 +215,9 @@
      * @param iccid    The iccid of the profile.
      * @param executor The executor through which the callback should be invoked.
      * @param callback The callback to get the result code and profile.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
      */
     public void requestProfile(String cardId, String iccid, @CallbackExecutor Executor executor,
             ResultCallback<EuiccProfileInfo> callback) {
@@ -244,6 +250,9 @@
      *                  ICCID is known, an APDU will be sent through to read the enabled profile.
      * @param executor  The executor through which the callback should be invoked.
      * @param callback  The callback to get the result code and the profile.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
      */
     public void requestEnabledProfileForPort(@NonNull String cardId, int portIndex,
             @NonNull @CallbackExecutor Executor executor,
@@ -276,6 +285,9 @@
      * @param refresh  Whether sending the REFRESH command to modem.
      * @param executor The executor through which the callback should be invoked.
      * @param callback The callback to get the result code.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
      */
     public void disableProfile(String cardId, String iccid, boolean refresh,
             @CallbackExecutor Executor executor, ResultCallback<Void> callback) {
@@ -307,6 +319,9 @@
      * @param refresh  Whether sending the REFRESH command to modem.
      * @param executor The executor through which the callback should be invoked.
      * @param callback The callback to get the result code and the EuiccProfileInfo enabled.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
      * @deprecated instead use {@link #switchToProfile(String, String, int, boolean, Executor,
      * ResultCallback)}
      */
@@ -344,6 +359,9 @@
      * @param refresh   Whether sending the REFRESH command to modem.
      * @param executor  The executor through which the callback should be invoked.
      * @param callback  The callback to get the result code and the EuiccProfileInfo enabled.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
      */
     public void switchToProfile(@Nullable String cardId, @Nullable String iccid, int portIndex,
             boolean refresh, @NonNull @CallbackExecutor Executor executor,
@@ -375,6 +393,9 @@
      * @param nickname The nickname of the profile.
      * @param executor The executor through which the callback should be invoked.
      * @param callback The callback to get the result code.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
      */
     public void setNickname(String cardId, String iccid, String nickname,
             @CallbackExecutor Executor executor, ResultCallback<Void> callback) {
@@ -404,6 +425,9 @@
      * @param iccid The iccid of the profile.
      * @param executor The executor through which the callback should be invoked.
      * @param callback The callback to get the result code.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
      */
     public void deleteProfile(String cardId, String iccid, @CallbackExecutor Executor executor,
             ResultCallback<Void> callback) {
@@ -434,6 +458,9 @@
      *     EuiccCard for details.
      * @param executor The executor through which the callback should be invoked.
      * @param callback The callback to get the result code.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
      */
     public void resetMemory(String cardId, @ResetOption int options,
             @CallbackExecutor Executor executor, ResultCallback<Void> callback) {
@@ -462,6 +489,9 @@
      * @param cardId The Id of the eUICC.
      * @param executor The executor through which the callback should be invoked.
      * @param callback The callback to get the result code and the default SM-DP+ address.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
      */
     public void requestDefaultSmdpAddress(String cardId, @CallbackExecutor Executor executor,
             ResultCallback<String> callback) {
@@ -490,6 +520,9 @@
      * @param cardId The Id of the eUICC.
      * @param executor The executor through which the callback should be invoked.
      * @param callback The callback to get the result code and the SM-DS address.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
      */
     public void requestSmdsAddress(String cardId, @CallbackExecutor Executor executor,
             ResultCallback<String> callback) {
@@ -519,6 +552,9 @@
      * @param defaultSmdpAddress The default SM-DP+ address to set.
      * @param executor The executor through which the callback should be invoked.
      * @param callback The callback to get the result code.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
      */
     public void setDefaultSmdpAddress(String cardId, String defaultSmdpAddress,
             @CallbackExecutor Executor executor, ResultCallback<Void> callback) {
@@ -548,6 +584,9 @@
      * @param cardId The Id of the eUICC.
      * @param executor The executor through which the callback should be invoked.
      * @param callback the callback to get the result code and the rule authorisation table.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
      */
     public void requestRulesAuthTable(String cardId, @CallbackExecutor Executor executor,
             ResultCallback<EuiccRulesAuthTable> callback) {
@@ -576,6 +615,9 @@
      * @param cardId The Id of the eUICC.
      * @param executor The executor through which the callback should be invoked.
      * @param callback the callback to get the result code and the challenge.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
      */
     public void requestEuiccChallenge(String cardId, @CallbackExecutor Executor executor,
             ResultCallback<byte[]> callback) {
@@ -604,6 +646,9 @@
      * @param cardId The Id of the eUICC.
      * @param executor The executor through which the callback should be invoked.
      * @param callback the callback to get the result code and the info1.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
      */
     public void requestEuiccInfo1(String cardId, @CallbackExecutor Executor executor,
             ResultCallback<byte[]> callback) {
@@ -632,6 +677,9 @@
      * @param cardId The Id of the eUICC.
      * @param executor The executor through which the callback should be invoked.
      * @param callback the callback to get the result code and the info2.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
      */
     public void requestEuiccInfo2(String cardId, @CallbackExecutor Executor executor,
             ResultCallback<byte[]> callback) {
@@ -671,6 +719,9 @@
      * @param executor The executor through which the callback should be invoked.
      * @param callback the callback to get the result code and a byte array which represents a
      *     {@code AuthenticateServerResponse} defined in GSMA RSP v2.0+.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
      */
     public void authenticateServer(String cardId, String matchingId, byte[] serverSigned1,
             byte[] serverSignature1, byte[] euiccCiPkIdToBeUsed, byte[] serverCertificate,
@@ -716,6 +767,9 @@
      * @param executor The executor through which the callback should be invoked.
      * @param callback the callback to get the result code and a byte array which represents a
      *     {@code PrepareDownloadResponse} defined in GSMA RSP v2.0+
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
      */
     public void prepareDownload(String cardId, @Nullable byte[] hashCc, byte[] smdpSigned2,
             byte[] smdpSignature2, byte[] smdpCertificate, @CallbackExecutor Executor executor,
@@ -753,6 +807,9 @@
      * @param executor The executor through which the callback should be invoked.
      * @param callback the callback to get the result code and a byte array which represents a
      *     {@code LoadBoundProfilePackageResponse} defined in GSMA RSP v2.0+.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
      */
     public void loadBoundProfilePackage(String cardId, byte[] boundProfilePackage,
             @CallbackExecutor Executor executor, ResultCallback<byte[]> callback) {
@@ -787,6 +844,9 @@
      * @param executor The executor through which the callback should be invoked.
      * @param callback the callback to get the result code and an byte[] which represents a
      *     {@code CancelSessionResponse} defined in GSMA RSP v2.0+.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
      */
     public void cancelSession(String cardId, byte[] transactionId, @CancelReason int reason,
             @CallbackExecutor Executor executor, ResultCallback<byte[]> callback) {
@@ -820,6 +880,9 @@
      * @param events bits of the event types ({@link EuiccNotification.Event}) to list.
      * @param executor The executor through which the callback should be invoked.
      * @param callback the callback to get the result code and the list of notifications.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
      */
     public void listNotifications(String cardId, @EuiccNotification.Event int events,
             @CallbackExecutor Executor executor, ResultCallback<EuiccNotification[]> callback) {
@@ -850,6 +913,9 @@
      * @param events bits of the event types ({@link EuiccNotification.Event}) to list.
      * @param executor The executor through which the callback should be invoked.
      * @param callback the callback to get the result code and the list of notifications.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
      */
     public void retrieveNotificationList(String cardId, @EuiccNotification.Event int events,
             @CallbackExecutor Executor executor, ResultCallback<EuiccNotification[]> callback) {
@@ -880,6 +946,9 @@
      * @param seqNumber the sequence number of the notification.
      * @param executor The executor through which the callback should be invoked.
      * @param callback the callback to get the result code and the notification.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
      */
     public void retrieveNotification(String cardId, int seqNumber,
             @CallbackExecutor Executor executor, ResultCallback<EuiccNotification> callback) {
@@ -910,6 +979,9 @@
      * @param seqNumber the sequence number of the notification.
      * @param executor The executor through which the callback should be invoked.
      * @param callback the callback to get the result code.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
      */
     public void removeNotificationFromList(String cardId, int seqNumber,
             @CallbackExecutor Executor executor, ResultCallback<Void> callback) {
diff --git a/telephony/java/android/telephony/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java
index 86fbb04..09d2108 100644
--- a/telephony/java/android/telephony/euicc/EuiccManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccManager.java
@@ -927,6 +927,9 @@
      * subscription APIs.
      *
      * @return true if embedded subscriptions are currently enabled.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
      */
     public boolean isEnabled() {
         // In the future, this may reach out to IEuiccController (if non-null) to check any dynamic
@@ -942,6 +945,9 @@
      * access to the EID of another eUICC.
      *
      * @return the EID. May be null if the eUICC is not ready.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
      */
     @Nullable
     public String getEid() {
@@ -963,6 +969,8 @@
      * @return the status of eUICC OTA. If the eUICC is not ready,
      *         {@link OtaStatus#EUICC_OTA_STATUS_UNAVAILABLE} will be returned.
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
      * @hide
      */
     @SystemApi
@@ -1014,6 +1022,9 @@
      * @param subscription the subscription to download.
      * @param switchAfterDownload if true, the profile will be activated upon successful download.
      * @param callbackIntent a PendingIntent to launch when the operation completes.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
      */
     @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
     public void downloadSubscription(DownloadableSubscription subscription,
@@ -1075,6 +1086,9 @@
      * @param resolutionExtras Resolution-specific extras depending on the result of the resolution.
      *     For example, this may indicate whether the user has consented or may include the input
      *     they provided.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
      * @hide
      */
     @SystemApi
@@ -1111,6 +1125,9 @@
      *
      * @param subscription the subscription which needs metadata filled in
      * @param callbackIntent a PendingIntent to launch when the operation completes.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
      * @hide
      */
     @SystemApi
@@ -1142,6 +1159,9 @@
      * internal system use only.
      *
      * @param callbackIntent a PendingIntent to launch when the operation completes.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
      * @hide
      */
     @SystemApi
@@ -1163,6 +1183,9 @@
      * Returns information about the eUICC chip/device.
      *
      * @return the {@link EuiccInfo}. May be null if the eUICC is not ready.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
      */
     @Nullable
     public EuiccInfo getEuiccInfo() {
@@ -1188,6 +1211,9 @@
      *
      * @param subscriptionId the ID of the subscription to delete.
      * @param callbackIntent a PendingIntent to launch when the operation completes.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
      */
     @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
     public void deleteSubscription(int subscriptionId, PendingIntent callbackIntent) {
@@ -1251,6 +1277,9 @@
      *     {@code android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission, or the
      *     calling app must be authorized to manage the active subscription on the target eUICC.
      * @param callbackIntent a PendingIntent to launch when the operation completes.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
      */
     @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
     public void switchToSubscription(int subscriptionId, PendingIntent callbackIntent) {
@@ -1312,6 +1341,9 @@
      *     {@link SubscriptionInfo#getPortIndex()}.
      * @param portIndex the index of the port to target for the enabled subscription
      * @param callbackIntent a PendingIntent to launch when the operation completes.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
      */
     @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
     public void switchToSubscription(int subscriptionId, int portIndex,
@@ -1349,6 +1381,9 @@
      * @param subscriptionId the ID of the subscription to update.
      * @param nickname the new nickname to apply.
      * @param callbackIntent a PendingIntent to launch when the operation completes.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
      */
     @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
     public void updateSubscriptionNickname(
@@ -1376,6 +1411,8 @@
      * @deprecated From R, callers should specify a flag for specific set of subscriptions to erase
      * and use {@link #eraseSubscriptions(int, PendingIntent)} instead
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
      * @hide
      */
     @SystemApi
@@ -1402,6 +1439,8 @@
      * @param options flag indicating specific set of subscriptions to erase
      * @param callbackIntent a PendingIntent to launch when the operation completes.
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
      * @hide
      */
     @SystemApi
@@ -1459,6 +1498,9 @@
      * determine whether a country is supported please check {@link #isSupportedCountry}.
      *
      * @param supportedCountries is a list of strings contains country ISO codes in uppercase.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
      * @hide
      */
     @SystemApi
@@ -1487,6 +1529,9 @@
      * determine whether a country is supported please check {@link #isSupportedCountry}.
      *
      * @param unsupportedCountries is a list of strings contains country ISO codes in uppercase.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
      * @hide
      */
     @SystemApi
@@ -1512,6 +1557,9 @@
      * {@code android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission.
      *
      * @return list of strings contains country ISO codes in uppercase.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
      * @hide
      */
     @SystemApi
@@ -1535,6 +1583,9 @@
      * {@code android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission.
      *
      * @return list of strings contains country ISO codes in uppercase.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
      * @hide
      */
     @SystemApi
@@ -1566,6 +1617,9 @@
      * @param countryIso should be the ISO-3166 country code is provided in uppercase 2 character
      * format.
      * @return whether the given country supports eUICC or not.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
      * @hide
      */
     @SystemApi
@@ -1630,6 +1684,9 @@
      *
      * @param portIndex is an enumeration of the ports available on the UICC.
      * @return {@code true} if port is available
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
      */
     public boolean isSimPortAvailable(int portIndex) {
         try {
diff --git a/telephony/java/android/telephony/ims/ImsMmTelManager.java b/telephony/java/android/telephony/ims/ImsMmTelManager.java
index 71bb329..551057f 100644
--- a/telephony/java/android/telephony/ims/ImsMmTelManager.java
+++ b/telephony/java/android/telephony/ims/ImsMmTelManager.java
@@ -779,6 +779,8 @@
      * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_AVAILABLE_BOOL
      * @throws IllegalArgumentException if the subscription associated with this operation is not
      * active (SIM is not inserted, ESIM inactive) or invalid.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
      * @return true if the user's setting for advanced calling is enabled, false otherwise.
      */
     @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
@@ -827,6 +829,8 @@
      * @see #isAdvancedCallingSettingEnabled()
      * @throws IllegalArgumentException if the subscription associated with this operation is not
      * active (SIM is not inserted, ESIM inactive) or invalid.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
      * @hide
      */
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
@@ -865,6 +869,8 @@
      * @param capability The IMS MmTel capability to query.
      * @return {@code true} if the MmTel IMS capability is capable for this subscription, false
      *         otherwise.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
      * @hide
      */
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@@ -893,6 +899,8 @@
      * @param capability The IMS MmTel capability to query.
      * @return {@code true} if the MmTel IMS capability is available for this subscription, false
      *         otherwise.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
      * @hide
      */
     @SystemApi
@@ -986,6 +994,8 @@
      *
      * @throws IllegalArgumentException if the subscription associated with this operation is not
      * active (SIM is not inserted, ESIM inactive) or invalid.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
      * @return true if the user’s “Video Calling” setting is currently enabled.
      */
     @RequiresPermission(anyOf = {
@@ -1017,6 +1027,8 @@
      *
      * @throws IllegalArgumentException if the subscription associated with this operation is not
      * active (SIM is not inserted, ESIM inactive) or invalid.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
      * @see #isVtSettingEnabled()
      * @hide
      */
@@ -1060,6 +1072,8 @@
      *
      * @throws IllegalArgumentException if the subscription associated with this operation is not
      * active (SIM is not inserted, ESIM inactive) or invalid.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
      */
     @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
     @RequiresPermission(anyOf = {
@@ -1090,6 +1104,8 @@
      *
      * @throws IllegalArgumentException if the subscription associated with this operation is not
      * active (SIM is not inserted, ESIM inactive) or invalid.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
      * @param isEnabled true if the user's setting for Voice over WiFi is enabled, false otherwise=
      * @see #isVoWiFiSettingEnabled()
      * @hide
@@ -1148,6 +1164,8 @@
      *
      * @throws ImsException if the IMS service associated with this subscription is not available or
      * the IMS service is not available.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
      * @return true if the user's setting for Voice over Cross SIM is enabled and false if it is not
      */
     @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
@@ -1192,6 +1210,8 @@
      * </ul>
      * @throws ImsException if the IMS service associated with this subscription is not available or
      * the IMS service is not available.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
      * @param isEnabled true if the user's setting for Voice over Cross SIM is enabled,
      *                 false otherwise
      * @see #isCrossSimCallingEnabled()
@@ -1233,6 +1253,8 @@
      *
      * @throws IllegalArgumentException if the subscription associated with this operation is not
      * active (SIM is not inserted, ESIM inactive) or invalid.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
      * @return true if the user's setting for Voice over WiFi while roaming is enabled, false
      * if disabled.
      */
@@ -1267,6 +1289,8 @@
      *     false otherwise.
      * @throws IllegalArgumentException if the subscription associated with this operation is not
      * active (SIM is not inserted, ESIM inactive) or invalid.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
      * @see #isVoWiFiRoamingSettingEnabled()
      * @hide
      */
@@ -1304,6 +1328,8 @@
      * - {@link #WIFI_MODE_WIFI_PREFERRED}
      * @throws IllegalArgumentException if the subscription associated with this operation is not
      * active (SIM is not inserted, ESIM inactive) or invalid.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
      * @see #setVoWiFiSettingEnabled(boolean)
      * @hide
      */
@@ -1347,6 +1373,8 @@
      *
      * @throws IllegalArgumentException if the subscription associated with this operation is not
      * active (SIM is not inserted, ESIM inactive) or invalid.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
      * @return The Voice over WiFi Mode preference set by the user, which can be one of the
      * following:
      * - {@link #WIFI_MODE_WIFI_ONLY}
@@ -1386,6 +1414,8 @@
      * - {@link #WIFI_MODE_WIFI_PREFERRED}
      * @throws IllegalArgumentException if the subscription associated with this operation is not
      * active (SIM is not inserted, ESIM inactive) or invalid.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
      * @see #getVoWiFiModeSetting()
      * @hide
      */
@@ -1422,6 +1452,8 @@
      *     - {@link #WIFI_MODE_WIFI_PREFERRED}
      * @throws IllegalArgumentException if the subscription associated with this operation is not
      * active (SIM is not inserted, ESIM inactive) or invalid.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
      * @see #setVoWiFiRoamingSettingEnabled(boolean)
      * @hide
      */
@@ -1458,6 +1490,8 @@
      *     - {@link #WIFI_MODE_WIFI_PREFERRED}
      * @throws IllegalArgumentException if the subscription associated with this operation is not
      * active (SIM is not inserted, ESIM inactive) or invalid.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
      * @see #getVoWiFiRoamingModeSetting()
      * @hide
      */
@@ -1492,6 +1526,8 @@
      * settings.
      * @throws IllegalArgumentException if the subscription associated with this operation is not
      * active (SIM is not inserted, ESIM inactive) or invalid.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
      * @param isEnabled if true RTT should be enabled during calls made on this subscription.
      * @hide
      */
@@ -1535,6 +1571,8 @@
      *
      * @throws IllegalArgumentException if the subscription associated with this operation is not
      * active (SIM is not inserted, ESIM inactive) or invalid.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
      * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL
      */
     @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
diff --git a/telephony/java/android/telephony/ims/ImsRcsManager.java b/telephony/java/android/telephony/ims/ImsRcsManager.java
index 2b49bcd..62d4263 100644
--- a/telephony/java/android/telephony/ims/ImsRcsManager.java
+++ b/telephony/java/android/telephony/ims/ImsRcsManager.java
@@ -250,6 +250,8 @@
      * the {@code ImsService} associated with the subscription is not available. This can happen if
      * the service crashed, for example. See {@link ImsException#getCode()} for a more detailed
      * reason.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
      */
     @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
     public void registerImsRegistrationCallback(
@@ -294,6 +296,8 @@
      * @param c The {@link RegistrationManager.RegistrationCallback} to be removed.
      * @see android.telephony.SubscriptionManager.OnSubscriptionsChangedListener
      * @see #registerImsRegistrationCallback(Executor, RegistrationCallback)
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
      */
     @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
     public void unregisterImsRegistrationCallback(
@@ -329,6 +333,8 @@
      * following: {@link RegistrationManager#REGISTRATION_STATE_NOT_REGISTERED},
      * {@link RegistrationManager#REGISTRATION_STATE_REGISTERING}, or
      * {@link RegistrationManager#REGISTRATION_STATE_REGISTERED}.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
      */
     @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
     public void getRegistrationState(@NonNull @CallbackExecutor Executor executor,
@@ -378,6 +384,8 @@
      * {@see AccessNetworkConstants#TRANSPORT_TYPE_WWAN},
      * {@see AccessNetworkConstants#TRANSPORT_TYPE_WLAN}, or
      * {@see AccessNetworkConstants#TRANSPORT_TYPE_INVALID}.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
      */
     @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
     public void getRegistrationTransportType(@NonNull @CallbackExecutor Executor executor,
@@ -435,6 +443,8 @@
      * {@link ImsRcsManager} is valid, but the ImsService associated with the subscription is not
      * available. This can happen if the ImsService has crashed, for example, or if the subscription
      * becomes inactive. See {@link ImsException#getCode()} for more information on the error codes.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
      * @hide
      */
     @SystemApi
@@ -479,6 +489,8 @@
      * @see #addOnAvailabilityChangedListener(Executor, OnAvailabilityChangedListener)
      * @throws ImsException if the IMS service is not available when calling this method.
      * See {@link ImsException#getCode()} for more information on the error codes.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
      * @hide
      */
     @SystemApi
@@ -525,6 +537,8 @@
      * @see android.telephony.CarrierConfigManager.Ims#KEY_ENABLE_PRESENCE_CAPABILITY_EXCHANGE_BOOL
      * @throws ImsException if the IMS service is not available when calling this method.
      * See {@link ImsException#getCode()} for more information on the error codes.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
      * @hide
      */
     @SystemApi
@@ -563,6 +577,8 @@
      * @see #isCapable(int, int)
      * @throws ImsException if the IMS service is not available when calling this method.
      * See {@link ImsException#getCode()} for more information on the error codes.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
      * @hide
      */
     @SystemApi
diff --git a/telephony/java/android/telephony/ims/ProvisioningManager.java b/telephony/java/android/telephony/ims/ProvisioningManager.java
index 1c5d1e9..62b8420 100644
--- a/telephony/java/android/telephony/ims/ProvisioningManager.java
+++ b/telephony/java/android/telephony/ims/ProvisioningManager.java
@@ -1300,8 +1300,10 @@
      * @param executor The executor that the callback methods will be called on.
      * @param callback The callback instance being registered.
      * @throws ImsException if the subscription associated with this callback is
-     * valid, but the {@link ImsService the service crashed, for example. See
+     * valid, but the service crashed, for example. See
      * {@link ImsException#getCode()} for a more detailed reason.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
      */
     @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
     public void registerFeatureProvisioningChangedCallback(
@@ -1327,6 +1329,8 @@
      *
      * @param callback The existing {@link FeatureProvisioningCallback} to be removed.
      * @see #registerFeatureProvisioningChangedCallback(Executor, FeatureProvisioningCallback)
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
      */
     public void unregisterFeatureProvisioningChangedCallback(
             @NonNull FeatureProvisioningCallback callback) {
@@ -1347,6 +1351,8 @@
      * @return an integer value for the provided key, or
      * {@link ImsConfigImplBase#CONFIG_RESULT_UNKNOWN} if the key doesn't exist.
      * @throws IllegalArgumentException if the key provided was invalid.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
      * @hide
      */
     @SystemApi
@@ -1369,6 +1375,8 @@
      * @return a String value for the provided key, {@code null} if the key doesn't exist, or
      * {@link StringResultError} if there was an error getting the value for the provided key.
      * @throws IllegalArgumentException if the key provided was invalid.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
      * @hide
      */
     @SystemApi
@@ -1392,6 +1400,8 @@
      * @param key An integer that represents the provisioning key, which is defined by the OEM.
      * @param value a integer value for the provided key.
      * @return the result of setting the configuration value.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
      * @hide
      *
      * Note: For compatibility purposes, the integer values [0 - 99] used in
@@ -1420,6 +1430,8 @@
      *     should be appropriately namespaced to avoid collision.
      * @param value a String value for the provided key.
      * @return the result of setting the configuration value.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
      * @hide
      */
     @SystemApi
@@ -1451,6 +1463,9 @@
      *
      * @see CarrierConfigManager.Ims#KEY_MMTEL_REQUIRES_PROVISIONING_BUNDLE
      * @param isProvisioned true if the device is provisioned for UT over IMS, false otherwise.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
      */
     @WorkerThread
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
@@ -1485,6 +1500,9 @@
      * @return true if the device is provisioned for the capability or does not require
      * provisioning, false if the capability does require provisioning and has not been
      * provisioned yet.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
      */
     @WorkerThread
     @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
@@ -1509,6 +1527,9 @@
      * @return true if the device is provisioned for the capability or does not require
      * provisioning, false if the capability does require provisioning and has not been
      * provisioned yet.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
+     *
      * @deprecated Use {@link #getRcsProvisioningStatusForCapability(int, int)} instead,
      * as this only retrieves provisioning information for
      * {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE}
@@ -1546,6 +1567,9 @@
      * @return true if the device is provisioned for the capability or does not require
      * provisioning, false if the capability does require provisioning and has not been
      * provisioned yet.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
      */
     @WorkerThread
     @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
@@ -1577,6 +1601,9 @@
      * @see CarrierConfigManager#KEY_CARRIER_RCS_PROVISIONING_REQUIRED_BOOL
      * @param isProvisioned true if the device is provisioned for the RCS capability specified,
      *                      false otherwise.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
+     *
      * @deprecated Use {@link #setRcsProvisioningStatusForCapability(int, int, boolean)} instead,
      * as this method only sets provisioning information for
      * {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE}
@@ -1615,6 +1642,9 @@
      * @see CarrierConfigManager.Ims#KEY_RCS_REQUIRES_PROVISIONING_BUNDLE
      * @param isProvisioned true if the device is provisioned for the RCS capability specified,
      *                      false otherwise.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
      */
     @WorkerThread
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
@@ -1644,6 +1674,9 @@
      * @return true if provisioning is required for the MMTEL capability and IMS
      * registration technology specified, false if it is not required or if the device does not
      * support IMS.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
      */
     @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
     public boolean isProvisioningRequiredForCapability(
@@ -1672,6 +1705,9 @@
      * @return true if provisioning is required for the RCS capability and IMS
      * registration technology specified, false if it is not required or if the device does not
      * support IMS.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
      */
     @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
     public boolean isRcsProvisioningRequiredForCapability(
@@ -1700,10 +1736,14 @@
      * @param config The XML file to be read. ASCII/UTF8 encoded text if not compressed.
      * @param isCompressed The XML file is compressed in gzip format and must be decompressed
      *         before being read.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION}.
      * @hide
      */
     @SystemApi
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION)
     public void notifyRcsAutoConfigurationReceived(@NonNull byte[] config, boolean isCompressed) {
         if (config == null) {
             throw new IllegalArgumentException("Must include a non-null config XML file.");
@@ -1714,7 +1754,6 @@
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
-
     }
 
     /**
@@ -1787,10 +1826,14 @@
      * When the IMS/RCS service receives the RCS client configuration, it will detect
      * the change in the configuration, and trigger the auto-configuration as needed.
      * @param rcc RCS client configuration {@link RcsClientConfiguration}
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION}.
      * @hide
      */
     @SystemApi
     @RequiresPermission(Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION)
     public void setRcsClientConfiguration(
             @NonNull RcsClientConfiguration rcc) throws ImsException {
         try {
@@ -1826,6 +1869,7 @@
     @RequiresPermission(anyOf = {
             Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
             Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION})
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION)
     public boolean isRcsVolteSingleRegistrationCapable() throws ImsException {
         try {
             return getITelephony().isRcsVolteSingleRegistrationCapable(mSubId);
@@ -1870,12 +1914,15 @@
     * params (See {@link #setRcsClientConfiguration}) and re register the
     * callback.
     * See {@link ImsException#getCode()} for a more detailed reason.
+    * @throws UnsupportedOperationException If the device does not have
+    *          {@link PackageManager#FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION}.
     * @hide
     */
     @SystemApi
     @RequiresPermission(anyOf = {
             Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
             Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION})
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION)
     public void registerRcsProvisioningCallback(
             @NonNull @CallbackExecutor Executor executor,
             @NonNull RcsProvisioningCallback callback) throws ImsException {
@@ -1908,12 +1955,15 @@
      * @see #registerRcsProvisioningCallback(Executor, RcsProvisioningCallback)
      * @throws IllegalArgumentException if the subscription associated with
      * this callback is invalid.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION}.
      * @hide
      */
     @SystemApi
     @RequiresPermission(anyOf = {
             Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
             Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION})
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION)
     public void unregisterRcsProvisioningCallback(
             @NonNull RcsProvisioningCallback callback) {
         try {
@@ -1935,10 +1985,14 @@
      * {@link RcsProvisioningCallback#onConfigurationReset}, then
      * {@link RcsProvisioningCallback#onConfigurationChanged} when the new
      * RCS configuration is received and notified by {@link #notifyRcsAutoConfigurationReceived}
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION}.
      * @hide
      */
     @SystemApi
     @RequiresPermission(Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION)
     public void triggerRcsReconfiguration() {
         try {
             getITelephony().triggerRcsReconfiguration(mSubId);
diff --git a/telephony/java/android/telephony/ims/RcsUceAdapter.java b/telephony/java/android/telephony/ims/RcsUceAdapter.java
index 3bb9be0..8925a9e 100644
--- a/telephony/java/android/telephony/ims/RcsUceAdapter.java
+++ b/telephony/java/android/telephony/ims/RcsUceAdapter.java
@@ -21,9 +21,11 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.RequiresFeature;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.IBinder;
@@ -49,6 +51,7 @@
  *
  * @see ImsRcsManager#getUceAdapter() for information on creating an instance of this class.
  */
+@RequiresFeature(PackageManager.FEATURE_TELEPHONY_IMS)
 public class RcsUceAdapter {
     private static final String TAG = "RcsUceAdapter";
 
@@ -585,6 +588,8 @@
      * {@link RcsUceAdapter} is valid, but the ImsService associated with the subscription is not
      * available. This can happen if the ImsService has crashed, for example, or if the subscription
      * becomes inactive. See {@link ImsException#getCode()} for more information on the error codes.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
      * @hide
      */
     @SystemApi
@@ -682,6 +687,8 @@
      * {@link RcsUceAdapter} is valid, but the ImsService associated with the subscription is not
      * available. This can happen if the ImsService has crashed, for example, or if the subscription
      * becomes inactive. See {@link ImsException#getCode()} for more information on the error codes.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
      * @hide
      */
     @SystemApi
@@ -759,6 +766,8 @@
      * {@link RcsUceAdapter} is valid, but the ImsService associated with the subscription is not
      * available. This can happen if the ImsService has crashed, for example, or if the subscription
      * becomes inactive. See {@link ImsException#getCode()} for more information on the error codes.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
      * @hide
      */
     @SystemApi
@@ -800,6 +809,8 @@
      * the {@link ImsService} associated with the subscription is not available. This can happen if
      * the service crashed, for example. See {@link ImsException#getCode()} for a more detailed
      * reason.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
      * @hide
      */
     @SystemApi
@@ -845,6 +856,8 @@
      * the {@link ImsService} associated with the subscription is not available. This can happen if
      * the service crashed, for example. See {@link ImsException#getCode()} for a more detailed
      * reason.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
      * @hide
      */
     @SystemApi
@@ -901,6 +914,8 @@
      * {@link RcsUceAdapter} is valid, but the ImsService associated with the subscription is not
      * available. This can happen if the ImsService has crashed, for example, or if the subscription
      * becomes inactive. See {@link ImsException#getCode()} for more information on the error codes.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
      */
     @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
     public boolean isUceSettingEnabled() throws ImsException {
@@ -954,6 +969,8 @@
      * {@link RcsUceAdapter} is valid, but the ImsService associated with the subscription is not
      * available. This can happen if the ImsService has crashed, for example, or if the subscription
      * becomes inactive. See {@link ImsException#getCode()} for more information on the error codes.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
      * @hide
      */
     @SystemApi
diff --git a/telephony/java/android/telephony/ims/RegistrationManager.java b/telephony/java/android/telephony/ims/RegistrationManager.java
index 54ceaed..9f83da9 100644
--- a/telephony/java/android/telephony/ims/RegistrationManager.java
+++ b/telephony/java/android/telephony/ims/RegistrationManager.java
@@ -84,7 +84,7 @@
                 SUGGESTED_ACTION_TRIGGER_PLMN_BLOCK,
                 SUGGESTED_ACTION_TRIGGER_PLMN_BLOCK_WITH_TIMEOUT,
                 SUGGESTED_ACTION_TRIGGER_RAT_BLOCK,
-                SUGGESTED_ACTION_TRIGGER_CLEAR_RAT_BLOCK
+                SUGGESTED_ACTION_TRIGGER_CLEAR_RAT_BLOCKS
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface SuggestedAction {}
@@ -116,9 +116,10 @@
 
     /**
      * Indicates that the IMS registration on current RAT failed multiple times.
-     * The radio shall block the current RAT and search for other available RATs in the
-     * background. If no other RAT is available that meets the carrier requirements, the
-     * radio may remain on the current RAT for internet service. The radio clears all
+     * The radio shall block the {@link ImsRegistrationImplBase.ImsRegistrationTech}
+     * included with this and search for other available RATs in the background.
+     * If no other RAT is available that meets the carrier requirements, the
+     * radio may remain on the blocked RAT for internet service. The radio clears all
      * RATs marked as unavailable if the IMS service is registered to the carrier network.
      * @hide
      */
@@ -133,7 +134,7 @@
      */
     @SystemApi
     @FlaggedApi(Flags.FLAG_ADD_RAT_RELATED_SUGGESTED_ACTION_TO_IMS_REGISTRATION)
-    int SUGGESTED_ACTION_TRIGGER_CLEAR_RAT_BLOCK = 4;
+    int SUGGESTED_ACTION_TRIGGER_CLEAR_RAT_BLOCKS = 4;
 
     /**@hide*/
     // Translate ImsRegistrationImplBase API to new AccessNetworkConstant because WLAN
diff --git a/telephony/java/android/telephony/ims/SipDelegateManager.java b/telephony/java/android/telephony/ims/SipDelegateManager.java
index 25ebdd0..abf2105 100644
--- a/telephony/java/android/telephony/ims/SipDelegateManager.java
+++ b/telephony/java/android/telephony/ims/SipDelegateManager.java
@@ -525,6 +525,8 @@
      * @param callback The callback instance being registered.
      * @throws ImsException in the case that the callback can not be registered.
      * See {@link ImsException#getCode} for more information on when this is called.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION}.
      */
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public void registerSipDialogStateCallback(@NonNull Executor executor,
@@ -557,6 +559,9 @@
      * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE}
      *
      * @param callback The callback instance to be unregistered.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION}.
      */
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public void unregisterSipDialogStateCallback(@NonNull SipDialogStateCallback callback)
diff --git a/telephony/java/android/telephony/satellite/AntennaPosition.java b/telephony/java/android/telephony/satellite/AntennaPosition.java
index 8842886..d6440fc 100644
--- a/telephony/java/android/telephony/satellite/AntennaPosition.java
+++ b/telephony/java/android/telephony/satellite/AntennaPosition.java
@@ -35,10 +35,10 @@
 @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
 public final class AntennaPosition implements Parcelable {
     /** Antenna direction used for satellite communication. */
-    @NonNull AntennaDirection mAntennaDirection;
+    @NonNull private AntennaDirection mAntennaDirection;
 
     /** Enum corresponding to device hold position to be used by the end user. */
-    @SatelliteManager.DeviceHoldPosition int mSuggestedHoldPosition;
+    @SatelliteManager.DeviceHoldPosition private int mSuggestedHoldPosition;
 
     /**
      * @hide
diff --git a/telephony/java/android/telephony/satellite/ISatelliteStateCallback.aidl b/telephony/java/android/telephony/satellite/ISatelliteModemStateCallback.aidl
similarity index 94%
rename from telephony/java/android/telephony/satellite/ISatelliteStateCallback.aidl
rename to telephony/java/android/telephony/satellite/ISatelliteModemStateCallback.aidl
index cd9d81e..9ff73e2 100644
--- a/telephony/java/android/telephony/satellite/ISatelliteStateCallback.aidl
+++ b/telephony/java/android/telephony/satellite/ISatelliteModemStateCallback.aidl
@@ -20,7 +20,7 @@
  * Interface for satellite state change callback.
  * @hide
  */
-oneway interface ISatelliteStateCallback {
+oneway interface ISatelliteModemStateCallback {
     /**
      * Indicates that the satellite modem state has changed.
      *
diff --git a/telephony/java/android/telephony/satellite/PointingInfo.java b/telephony/java/android/telephony/satellite/PointingInfo.java
index 022a856..9440b65 100644
--- a/telephony/java/android/telephony/satellite/PointingInfo.java
+++ b/telephony/java/android/telephony/satellite/PointingInfo.java
@@ -17,6 +17,7 @@
 package android.telephony.satellite;
 
 import android.annotation.FlaggedApi;
+import android.annotation.FloatRange;
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
 import android.os.Parcel;
@@ -108,11 +109,19 @@
         return sb.toString();
     }
 
+    /**
+     * Returns the azimuth of the satellite, in degrees.
+     */
+    @FloatRange(from = -180, to = 180)
     @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
     public float getSatelliteAzimuthDegrees() {
         return mSatelliteAzimuthDegrees;
     }
 
+    /**
+     * Returns the elevation of the satellite, in degrees.
+     */
+    @FloatRange(from = -90, to = 90)
     @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
     public float getSatelliteElevationDegrees() {
         return mSatelliteElevationDegrees;
diff --git a/telephony/java/android/telephony/satellite/SatelliteDatagramCallback.java b/telephony/java/android/telephony/satellite/SatelliteDatagramCallback.java
index b5763c3..8e79ca5 100644
--- a/telephony/java/android/telephony/satellite/SatelliteDatagramCallback.java
+++ b/telephony/java/android/telephony/satellite/SatelliteDatagramCallback.java
@@ -22,10 +22,15 @@
 
 import com.android.internal.telephony.flags.Flags;
 
+import java.util.concurrent.Executor;
 import java.util.function.Consumer;
 
 /**
  * A callback class for listening to satellite datagrams.
+ * {@link SatelliteDatagramCallback} is registered to telephony when an app which invokes
+ * {@link SatelliteManager#registerForSatelliteDatagram(Executor, SatelliteDatagramCallback)},
+ * and {@link #onSatelliteDatagramReceived(long, SatelliteDatagram, int, Consumer)} will be invoked
+ * when a new datagram is received from satellite.
  *
  * @hide
  */
diff --git a/telephony/java/android/telephony/satellite/SatelliteManager.java b/telephony/java/android/telephony/satellite/SatelliteManager.java
index e09bd20..2a69703 100644
--- a/telephony/java/android/telephony/satellite/SatelliteManager.java
+++ b/telephony/java/android/telephony/satellite/SatelliteManager.java
@@ -75,8 +75,9 @@
     private static final ConcurrentHashMap<SatelliteProvisionStateCallback,
             ISatelliteProvisionStateCallback> sSatelliteProvisionStateCallbackMap =
             new ConcurrentHashMap<>();
-    private static final ConcurrentHashMap<SatelliteStateCallback, ISatelliteStateCallback>
-            sSatelliteStateCallbackMap = new ConcurrentHashMap<>();
+    private static final ConcurrentHashMap<SatelliteModemStateCallback,
+            ISatelliteModemStateCallback>
+            sSatelliteModemStateCallbackMap = new ConcurrentHashMap<>();
     private static final ConcurrentHashMap<SatelliteTransmissionUpdateCallback,
             ISatelliteTransmissionUpdateCallback> sSatelliteTransmissionUpdateCallbackMap =
             new ConcurrentHashMap<>();
@@ -624,6 +625,11 @@
     /**
      * Request to get whether the satellite service is supported on the device.
      *
+     * <p>
+     * Note: This API only checks whether the device supports the satellite feature. The result will
+     * not be affected by whether the device is provisioned.
+     * </p>
+     *
      * @param executor The executor on which the callback will be called.
      * @param callback The callback object to which the result will be delivered.
      *                 If the request is successful, {@link OutcomeReceiver#onResult(Object)}
@@ -1301,21 +1307,22 @@
     @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
     @SatelliteResult public int registerForSatelliteModemStateChanged(
             @NonNull @CallbackExecutor Executor executor,
-            @NonNull SatelliteStateCallback callback) {
+            @NonNull SatelliteModemStateCallback callback) {
         Objects.requireNonNull(executor);
         Objects.requireNonNull(callback);
 
         try {
             ITelephony telephony = getITelephony();
             if (telephony != null) {
-                ISatelliteStateCallback internalCallback = new ISatelliteStateCallback.Stub() {
+                ISatelliteModemStateCallback internalCallback =
+                        new ISatelliteModemStateCallback.Stub() {
                     @Override
                     public void onSatelliteModemStateChanged(int state) {
                         executor.execute(() -> Binder.withCleanCallingIdentity(() ->
                                 callback.onSatelliteModemStateChanged(state)));
                     }
                 };
-                sSatelliteStateCallbackMap.put(callback, internalCallback);
+                sSatelliteModemStateCallbackMap.put(callback, internalCallback);
                 return telephony.registerForSatelliteModemStateChanged(mSubId, internalCallback);
             } else {
                 throw new IllegalStateException("telephony service is null.");
@@ -1332,16 +1339,18 @@
      * If callback was not registered before, the request will be ignored.
      *
      * @param callback The callback that was passed to
-     * {@link #registerForSatelliteModemStateChanged(Executor, SatelliteStateCallback)}.
+     * {@link #registerForSatelliteModemStateChanged(Executor, SatelliteModemStateCallback)}.
      *
      * @throws SecurityException if the caller doesn't have required permission.
      * @throws IllegalStateException if the Telephony process is not currently available.
      */
     @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
     @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
-    public void unregisterForSatelliteModemStateChanged(@NonNull SatelliteStateCallback callback) {
+    public void unregisterForSatelliteModemStateChanged(
+            @NonNull SatelliteModemStateCallback callback) {
         Objects.requireNonNull(callback);
-        ISatelliteStateCallback internalCallback = sSatelliteStateCallbackMap.remove(callback);
+        ISatelliteModemStateCallback internalCallback = sSatelliteModemStateCallbackMap.remove(
+                callback);
 
         try {
             ITelephony telephony = getITelephony();
diff --git a/telephony/java/android/telephony/satellite/SatelliteStateCallback.java b/telephony/java/android/telephony/satellite/SatelliteModemStateCallback.java
similarity index 96%
rename from telephony/java/android/telephony/satellite/SatelliteStateCallback.java
rename to telephony/java/android/telephony/satellite/SatelliteModemStateCallback.java
index bfe6e10..8d33c88 100644
--- a/telephony/java/android/telephony/satellite/SatelliteStateCallback.java
+++ b/telephony/java/android/telephony/satellite/SatelliteModemStateCallback.java
@@ -28,7 +28,7 @@
  */
 @SystemApi
 @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
-public interface SatelliteStateCallback {
+public interface SatelliteModemStateCallback {
     /**
      * Called when satellite modem state changes.
      * @param state The new satellite modem state.
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 84777c9..3ea86c7 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -72,7 +72,7 @@
 import android.telephony.satellite.ISatelliteDatagramCallback;
 import android.telephony.satellite.ISatelliteTransmissionUpdateCallback;
 import android.telephony.satellite.ISatelliteProvisionStateCallback;
-import android.telephony.satellite.ISatelliteStateCallback;
+import android.telephony.satellite.ISatelliteModemStateCallback;
 import android.telephony.satellite.NtnSignalStrength;
 import android.telephony.satellite.SatelliteCapabilities;
 import android.telephony.satellite.SatelliteDatagram;
@@ -2896,7 +2896,7 @@
      */
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
             + "android.Manifest.permission.SATELLITE_COMMUNICATION)")
-    int registerForSatelliteModemStateChanged(int subId, ISatelliteStateCallback callback);
+    int registerForSatelliteModemStateChanged(int subId, ISatelliteModemStateCallback callback);
 
     /**
      * Unregisters for modem state changed from satellite modem.
@@ -2907,7 +2907,7 @@
      */
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
             + "android.Manifest.permission.SATELLITE_COMMUNICATION)")
-    void unregisterForSatelliteModemStateChanged(int subId, ISatelliteStateCallback callback);
+    void unregisterForSatelliteModemStateChanged(int subId, ISatelliteModemStateCallback callback);
 
    /**
      * Register to receive incoming datagrams over satellite.
@@ -3055,6 +3055,29 @@
     boolean setEmergencyCallToSatelliteHandoverType(int handoverType, int delaySeconds);
 
     /**
+     * This API should be used by only CTS tests to forcefully set the country codes.
+     *
+     * @param reset {@code true} mean the overridden country codes should not be used, {@code false}
+     *              otherwise.
+     * @return {@code true} if the country code is set successfully, {@code false} otherwise.
+     */
+    boolean setCountryCodes(in boolean reset, in List<String> currentNetworkCountryCodes,
+            in Map cachedNetworkCountryCodes, in String locationCountryCode,
+            in long locationCountryCodeTimestampNanos);
+
+    /**
+     * This API should be used by only CTS tests to override the overlay configs of satellite
+     * access controller.
+     *
+     * @param reset {@code true} mean the overridden configs should not be used, {@code false}
+     *              otherwise.
+     * @return {@code true} if the overlay configs are set successfully, {@code false} otherwise.
+     */
+    boolean setSatelliteAccessControlOverlayConfigs(in boolean reset, in boolean isAllowed,
+            in String s2CellFile, in long locationFreshDurationNanos,
+            in List<String> satelliteCountryCodes);
+
+    /**
      * Test method to confirm the file contents are not altered.
      */
      @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
@@ -3207,4 +3230,32 @@
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
         + "android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)")
     boolean isCellularIdentifierDisclosureNotificationsEnabled();
+
+    /**
+     * Enables or disables notifications sent when cellular null cipher or integrity algorithms
+     * are in use by the cellular modem.
+     *
+     * @throws IllegalStateException if the Telephony process is not currently available
+     * @throws SecurityException if the caller does not have the required privileges
+     * @throws UnsupportedOperationException if the modem does not support reporting on ciphering
+     * and integrity algorithms in use
+     * @hide
+     */
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+        + "android.Manifest.permission.MODIFY_PHONE_STATE)")
+    void setEnableNullCipherNotifications(boolean enable);
+
+    /**
+     * Get whether notifications are enabled for null cipher or integrity algorithms in use by the
+     * cellular modem.
+     *
+     * @throws IllegalStateException if the Telephony process is not currently available
+     * @throws SecurityException if the caller does not have the required privileges
+     * @throws UnsupportedOperationException if the modem does not support reporting on ciphering
+     * and integrity algorithms in use
+     * @hide
+     */
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+        + "android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)")
+    boolean isNullCipherNotificationsEnabled();
 }
diff --git a/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/GraphicsActivity.java b/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/GraphicsActivity.java
index 60b5ce7..217659e 100644
--- a/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/GraphicsActivity.java
+++ b/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/GraphicsActivity.java
@@ -43,9 +43,7 @@
 import java.io.StringWriter;
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.Comparator;
 import java.util.List;
-import java.util.Optional;
 import java.util.stream.Collectors;
 
 /**
@@ -178,8 +176,14 @@
             this.deviceFrameRate = deviceFrameRate;
         }
 
+        FrameRateTimeoutException(List<Float> expectedFrameRates, float deviceFrameRate) {
+            this.expectedFrameRates = expectedFrameRates;
+            this.deviceFrameRate = deviceFrameRate;
+        }
+
         public float expectedFrameRate;
         public float deviceFrameRate;
+        public List<Float> expectedFrameRates;
     }
 
     public enum Api {
@@ -502,31 +506,37 @@
     }
 
     private void waitForStableFrameRate(TestSurface... surfaces) throws InterruptedException {
-        verifyCompatibleAndStableFrameRate(0, surfaces);
+        verifyFrameRates(List.of(), surfaces);
     }
 
     private void verifyExactAndStableFrameRate(
             float expectedFrameRate,
             TestSurface... surfaces) throws InterruptedException {
-        verifyFrameRate(expectedFrameRate, false, surfaces);
+        verifyFrameRate(List.of(expectedFrameRate), false, surfaces);
     }
 
     private void verifyCompatibleAndStableFrameRate(
             float expectedFrameRate,
             TestSurface... surfaces) throws InterruptedException {
-        verifyFrameRate(expectedFrameRate, true, surfaces);
+        verifyFrameRate(List.of(expectedFrameRate), true, surfaces);
     }
 
-    // Set expectedFrameRate to 0.0 to verify only stable frame rate.
-    private void verifyFrameRate(
-            float expectedFrameRate, boolean multiplesAllowed,
+    /** Verify stable frame rate at one of the expectedFrameRates. */
+    private void verifyFrameRates(List<Float> expectedFrameRates, TestSurface... surfaces)
+            throws InterruptedException {
+        verifyFrameRate(expectedFrameRates, true, surfaces);
+    }
+
+    // Set expectedFrameRates to empty to verify only stable frame rate.
+    private void verifyFrameRate(List<Float> expectedFrameRates, boolean multiplesAllowed,
             TestSurface... surfaces) throws InterruptedException {
         Log.i(TAG, "Verifying compatible and stable frame rate");
         long nowNanos = System.nanoTime();
         long gracePeriodEndTimeNanos =
                 nowNanos + FRAME_RATE_SWITCH_GRACE_PERIOD_SECONDS * 1_000_000_000L;
         while (true) {
-            if (expectedFrameRate > FRAME_RATE_TOLERANCE) { // expectedFrameRate > 0
+            if (expectedFrameRates.size() == 1) {
+                float expectedFrameRate = expectedFrameRates.get(0);
                 // Wait until we switch to a compatible frame rate.
                 Log.i(TAG,
                         String.format(
@@ -557,11 +567,25 @@
             while (endTimeNanos > nowNanos) {
                 int numModeChangedEvents = mModeChangedEvents.size();
                 if (waitForEvents(endTimeNanos, surfaces)) {
-                    Log.i(TAG,
-                            String.format("Stable frame rate %.2f verified",
-                                    multiplesAllowed ? mDisplayModeRefreshRate
-                                                     : mDisplayRefreshRate));
-                    return;
+                    // Verify any expected frame rate since there are multiple that will suffice.
+                    // Mainly to account for running tests on real devices, where other non-test
+                    // layers may affect the outcome.
+                    if (expectedFrameRates.size() > 1) {
+                        for (float expectedFrameRate : expectedFrameRates) {
+                            if (isFrameRateMultiple(mDisplayModeRefreshRate, expectedFrameRate)) {
+                                return;
+                            }
+                        }
+                        // The frame rate is stable but it is not one of the expected frame rates.
+                        throw new FrameRateTimeoutException(
+                                expectedFrameRates, mDisplayModeRefreshRate);
+                    } else {
+                        Log.i(TAG,
+                                String.format("Stable frame rate %.2f verified",
+                                        multiplesAllowed ? mDisplayModeRefreshRate
+                                                         : mDisplayRefreshRate));
+                        return;
+                    }
                 }
                 nowNanos = System.nanoTime();
                 if (mModeChangedEvents.size() > numModeChangedEvents) {
@@ -623,12 +647,28 @@
                             // caused the timeout failure. Wait for a bit to see if we get notified
                             // of a precondition violation, and if so, retry the test. Otherwise
                             // fail.
-                            assertTrue(String.format(
-                                               "Timed out waiting for a stable and compatible frame"
-                                                       + " rate. expected=%.2f received=%.2f."
-                                                       + " Stack trace: " + stackTrace,
-                                               exc.expectedFrameRate, exc.deviceFrameRate),
-                                    waitForPreconditionViolation());
+                            if (exc.expectedFrameRates.isEmpty()) {
+                                assertTrue(
+                                        String.format(
+                                                "Timed out waiting for a stable and compatible"
+                                                        + " frame rate."
+                                                        + " expected=%.2f received=%.2f."
+                                                        + " Stack trace: " + stackTrace,
+                                                exc.expectedFrameRate, exc.deviceFrameRate),
+                                        waitForPreconditionViolation());
+                            } else {
+                                assertTrue(
+                                        String.format(
+                                                "Timed out waiting for a stable and compatible"
+                                                        + " frame rate."
+                                                        + " expected={%.2f} received=%.2f."
+                                                        + " Stack trace: " + stackTrace,
+                                                exc.expectedFrameRates.stream()
+                                                        .map(Object::toString)
+                                                        .collect(Collectors.joining(", ")),
+                                                exc.deviceFrameRate),
+                                        waitForPreconditionViolation());
+                            }
                         }
 
                         if (!testPassed) {
@@ -679,10 +719,15 @@
                     "**** Running testSurfaceControlFrameRateCompatibility with compatibility "
                             + compatibility);
 
-            float expectedFrameRate = getExpectedFrameRateForCompatibility(compatibility);
+            List<Float> expectedFrameRates = getExpectedFrameRateForCompatibility(compatibility);
+            Log.i(TAG,
+                    "Expected frame rates: "
+                            + expectedFrameRates.stream()
+                                      .map(Object::toString)
+                                      .collect(Collectors.joining(", ")));
             int initialNumEvents = mModeChangedEvents.size();
             surface.setFrameRate(30.f, compatibility);
-            verifyExactAndStableFrameRate(expectedFrameRate, surface);
+            verifyFrameRates(expectedFrameRates, surface);
             verifyModeSwitchesDontChangeResolution(initialNumEvents, mModeChangedEvents.size());
         });
     }
@@ -699,10 +744,10 @@
         runOneSurfaceTest((TestSurface surface) -> {
             Log.i(TAG, "**** Running testSurfaceControlFrameRateCategory for category " + category);
 
-            float expectedFrameRate = getExpectedFrameRateForCategory(category);
+            List<Float> expectedFrameRates = getExpectedFrameRateForCategory(category);
             int initialNumEvents = mModeChangedEvents.size();
             surface.setFrameRateCategory(category);
-            verifyCompatibleAndStableFrameRate(expectedFrameRate, surface);
+            verifyFrameRates(expectedFrameRates, surface);
             verifyModeSwitchesDontChangeResolution(initialNumEvents, mModeChangedEvents.size());
         });
     }
@@ -771,45 +816,46 @@
                 "frame rate strategy=" + parentStrategy);
     }
 
-    private float getExpectedFrameRateForCompatibility(int compatibility) {
+    private List<Float> getExpectedFrameRateForCompatibility(int compatibility) {
         assumeTrue("**** testSurfaceControlFrameRateCompatibility SKIPPED for compatibility "
                         + compatibility,
                 compatibility == Surface.FRAME_RATE_COMPATIBILITY_GTE);
 
         Display display = getDisplay();
-        Optional<Float> expectedFrameRate = getRefreshRates(display.getMode(), display)
-                                                    .stream()
-                                                    .filter(rate -> rate >= 30.f)
-                                                    .min(Comparator.naturalOrder());
+        List<Float> expectedFrameRates = getRefreshRates(display.getMode(), display)
+                                                 .stream()
+                                                 .filter(rate -> rate >= 30.f)
+                                                 .collect(Collectors.toList());
 
         assumeTrue("**** testSurfaceControlFrameRateCompatibility SKIPPED because no refresh rate "
                         + "is >= 30",
-                expectedFrameRate.isPresent());
-        return expectedFrameRate.get();
+                !expectedFrameRates.isEmpty());
+        return expectedFrameRates;
     }
 
-    private float getExpectedFrameRateForCategory(int category) {
+    private List<Float> getExpectedFrameRateForCategory(int category) {
         Display display = getDisplay();
         List<Float> frameRates = getRefreshRates(display.getMode(), display);
 
         if (category == Surface.FRAME_RATE_CATEGORY_DEFAULT) {
             // Max due to default vote and no other frame rate specifications.
-            return Collections.max(frameRates);
+            return List.of(Collections.max(frameRates));
         } else if (category == Surface.FRAME_RATE_CATEGORY_NO_PREFERENCE) {
-            return Collections.min(frameRates);
+            return frameRates;
         }
 
         FpsRange categoryRange = convertCategory(category);
-        Optional<Float> expectedFrameRate = frameRates.stream()
-                                                    .filter(fps -> categoryRange.includes(fps))
-                                                    .min(Comparator.naturalOrder());
+        List<Float> expectedFrameRates = frameRates.stream()
+                                                 .filter(fps -> categoryRange.includes(fps))
+                                                 .collect(Collectors.toList());
         assumeTrue("**** testSurfaceControlFrameRateCategory SKIPPED for category " + category,
-                expectedFrameRate.isPresent());
-        return expectedFrameRate.get();
+                !expectedFrameRates.isEmpty());
+        return expectedFrameRates;
     }
 
     private FpsRange convertCategory(int category) {
         switch (category) {
+            case Surface.FRAME_RATE_CATEGORY_HIGH_HINT:
             case Surface.FRAME_RATE_CATEGORY_HIGH:
                 return FRAME_RATE_CATEGORY_HIGH;
             case Surface.FRAME_RATE_CATEGORY_NORMAL:
diff --git a/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/SurfaceControlTest.java b/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/SurfaceControlTest.java
index 4b56c10..caaee63 100644
--- a/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/SurfaceControlTest.java
+++ b/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/SurfaceControlTest.java
@@ -93,6 +93,12 @@
     }
 
     @Test
+    public void testSurfaceControlFrameRateCategoryHighHint() throws InterruptedException {
+        GraphicsActivity activity = mActivityRule.getActivity();
+        activity.testSurfaceControlFrameRateCategory(Surface.FRAME_RATE_CATEGORY_HIGH_HINT);
+    }
+
+    @Test
     public void testSurfaceControlFrameRateCategoryNormal() throws InterruptedException {
         GraphicsActivity activity = mActivityRule.getActivity();
         activity.testSurfaceControlFrameRateCategory(Surface.FRAME_RATE_CATEGORY_NORMAL);
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingSecondaryActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingSecondaryActivity.java
index 29cbf01..e3988cd 100644
--- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingSecondaryActivity.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingSecondaryActivity.java
@@ -24,8 +24,8 @@
 import android.view.View;
 import android.widget.ToggleButton;
 
+import androidx.window.embedding.ActivityEmbeddingController;
 import androidx.window.embedding.SplitAttributes;
-import androidx.window.embedding.SplitAttributesCalculatorParams;
 import androidx.window.embedding.SplitController;
 
 /**
@@ -66,7 +66,9 @@
                     @Override
                     public void onClick(View v) {
                         // This triggers a recalcuation of splitatributes.
-                        mSplitController.invalidateTopVisibleSplitAttributes();
+                        ActivityEmbeddingController
+                                .getInstance(ActivityEmbeddingSecondaryActivity.this)
+                                .invalidateTopVisibleActivityStacks();
                     }
         });
         findViewById(R.id.secondary_enter_pip_button).setOnClickListener(
diff --git a/tests/FsVerityTest/src/com/android/fsverity/FsVerityHostTest.java b/tests/FsVerityTest/src/com/android/fsverity/FsVerityHostTest.java
index be479f2..1b02792 100644
--- a/tests/FsVerityTest/src/com/android/fsverity/FsVerityHostTest.java
+++ b/tests/FsVerityTest/src/com/android/fsverity/FsVerityHostTest.java
@@ -25,6 +25,7 @@
 import android.security.Flags;
 
 import com.android.blockdevicewriter.BlockDeviceWriter;
+import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
 import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
@@ -52,7 +53,6 @@
     private static final String TARGET_PACKAGE = "com.android.fsverity";
 
     private static final String BASENAME = "test.file";
-    private static final String TARGET_PATH = "/data/data/" + TARGET_PACKAGE + "/files/" + BASENAME;
 
     @Rule
     public final CheckFlagsRule mCheckFlagsRule =
@@ -63,11 +63,11 @@
         prepareTest(10000);
 
         ITestDevice device = getDevice();
-        BlockDeviceWriter.damageFileAgainstBlockDevice(device, TARGET_PATH, 0);
-        BlockDeviceWriter.damageFileAgainstBlockDevice(device, TARGET_PATH, 8192);
+        BlockDeviceWriter.damageFileAgainstBlockDevice(device, getTargetFilePath(), 0);
+        BlockDeviceWriter.damageFileAgainstBlockDevice(device, getTargetFilePath(), 8192);
         BlockDeviceWriter.dropCaches(device);
 
-        verifyRead(TARGET_PATH, "0,2");
+        verifyRead(getTargetFilePath(), "0,2");
     }
 
     @Test
@@ -75,12 +75,17 @@
         prepareTest(128 * 4096 + 1);
 
         ITestDevice device = getDevice();
-        BlockDeviceWriter.damageFileAgainstBlockDevice(device, TARGET_PATH, 4096);
-        BlockDeviceWriter.damageFileAgainstBlockDevice(device, TARGET_PATH, 100 * 4096);
-        BlockDeviceWriter.damageFileAgainstBlockDevice(device, TARGET_PATH, 128 * 4096 + 1);
+        BlockDeviceWriter.damageFileAgainstBlockDevice(device, getTargetFilePath(), 4096);
+        BlockDeviceWriter.damageFileAgainstBlockDevice(device, getTargetFilePath(), 100 * 4096);
+        BlockDeviceWriter.damageFileAgainstBlockDevice(device, getTargetFilePath(), 128 * 4096 + 1);
         BlockDeviceWriter.dropCaches(device);
 
-        verifyRead(TARGET_PATH, "1,100,128");
+        verifyRead(getTargetFilePath(), "1,100,128");
+    }
+
+    private String getTargetFilePath() throws DeviceNotAvailableException {
+        return "/data/user/" + getDevice().getCurrentUser() + "/" + TARGET_PACKAGE + "/files/"
+                + BASENAME;
     }
 
     private void prepareTest(int fileSize) throws Exception {
@@ -97,7 +102,7 @@
         options.setTestClassName(TARGET_PACKAGE + ".Helper");
         options.setTestMethodName("verifyFileRead");
         options.addInstrumentationArg("brokenBlockIndicesCsv", indicesCsv);
-        options.addInstrumentationArg("filePath", TARGET_PATH);
+        options.addInstrumentationArg("filePath", getTargetFilePath());
         assertThat(runDeviceTests(options)).isTrue();
     }
 }
diff --git a/tests/Input/src/android/hardware/input/StickyModifierStateListenerTest.kt b/tests/Input/src/android/hardware/input/StickyModifierStateListenerTest.kt
new file mode 100644
index 0000000..e2b0c36
--- /dev/null
+++ b/tests/Input/src/android/hardware/input/StickyModifierStateListenerTest.kt
@@ -0,0 +1,215 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.input
+
+import android.content.Context
+import android.content.ContextWrapper
+import android.os.Handler
+import android.os.HandlerExecutor
+import android.os.test.TestLooper
+import android.platform.test.annotations.Presubmit
+import android.platform.test.flag.junit.SetFlagsRule
+import android.view.KeyEvent
+import androidx.test.core.app.ApplicationProvider
+import com.android.server.testutils.any
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.Mockito.doAnswer
+import org.mockito.Mockito.`when`
+import org.mockito.junit.MockitoJUnitRunner
+import kotlin.test.assertEquals
+import kotlin.test.assertNotNull
+import kotlin.test.assertNull
+import kotlin.test.assertTrue
+import kotlin.test.fail
+
+/**
+ * Tests for [InputManager.StickyModifierStateListener].
+ *
+ * Build/Install/Run:
+ * atest InputTests:StickyModifierStateListenerTest
+ */
+@Presubmit
+@RunWith(MockitoJUnitRunner::class)
+class StickyModifierStateListenerTest {
+
+    @get:Rule
+    val rule = SetFlagsRule()
+
+    private val testLooper = TestLooper()
+    private val executor = HandlerExecutor(Handler(testLooper.looper))
+    private var registeredListener: IStickyModifierStateListener? = null
+    private lateinit var context: Context
+    private lateinit var inputManager: InputManager
+    private lateinit var inputManagerGlobalSession: InputManagerGlobal.TestSession
+
+    @Mock
+    private lateinit var iInputManagerMock: IInputManager
+
+    @Before
+    fun setUp() {
+        // Enable Sticky keys feature
+        rule.enableFlags(com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_STICKY_KEYS_FLAG)
+        rule.enableFlags(com.android.input.flags.Flags.FLAG_ENABLE_INPUT_FILTER_RUST_IMPL)
+
+        context = Mockito.spy(ContextWrapper(ApplicationProvider.getApplicationContext()))
+        inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManagerMock)
+        inputManager = InputManager(context)
+        `when`(context.getSystemService(Mockito.eq(Context.INPUT_SERVICE)))
+                .thenReturn(inputManager)
+
+        // Handle sticky modifier state listener registration.
+        doAnswer {
+            val listener = it.getArgument(0) as IStickyModifierStateListener
+            if (registeredListener != null &&
+                    registeredListener!!.asBinder() != listener.asBinder()) {
+                // There can only be one registered sticky modifier state listener per process.
+                fail("Trying to register a new listener when one already exists")
+            }
+            registeredListener = listener
+            null
+        }.`when`(iInputManagerMock).registerStickyModifierStateListener(any())
+
+        // Handle sticky modifier state listener being unregistered.
+        doAnswer {
+            val listener = it.getArgument(0) as IStickyModifierStateListener
+            if (registeredListener == null ||
+                    registeredListener!!.asBinder() != listener.asBinder()) {
+                fail("Trying to unregister a listener that is not registered")
+            }
+            registeredListener = null
+            null
+        }.`when`(iInputManagerMock).unregisterStickyModifierStateListener(any())
+    }
+
+    @After
+    fun tearDown() {
+        if (this::inputManagerGlobalSession.isInitialized) {
+            inputManagerGlobalSession.close()
+        }
+    }
+
+    private fun notifyStickyModifierStateChanged(modifierState: Int, lockedModifierState: Int) {
+        registeredListener!!.onStickyModifierStateChanged(modifierState, lockedModifierState)
+    }
+
+    @Test
+    fun testListenerIsNotifiedOnModifierStateChanged() {
+        var callbackCount = 0
+
+        // Add a sticky modifier state listener
+        inputManager.registerStickyModifierStateListener(executor) {
+            callbackCount++
+        }
+
+        // Notifying sticky modifier state change will notify the listener.
+        notifyStickyModifierStateChanged(0, 0)
+        testLooper.dispatchNext()
+        assertEquals(1, callbackCount)
+    }
+
+    @Test
+    fun testListenerHasCorrectModifierStateNotified() {
+        // Add a sticky modifier state listener
+        inputManager.registerStickyModifierStateListener(executor) {
+            state: StickyModifierState ->
+            assertTrue(state.isAltModifierOn)
+            assertTrue(state.isAltModifierLocked)
+            assertTrue(state.isShiftModifierOn)
+            assertTrue(!state.isShiftModifierLocked)
+            assertTrue(!state.isCtrlModifierOn)
+            assertTrue(!state.isCtrlModifierLocked)
+            assertTrue(!state.isMetaModifierOn)
+            assertTrue(!state.isMetaModifierLocked)
+            assertTrue(!state.isAltGrModifierOn)
+            assertTrue(!state.isAltGrModifierLocked)
+        }
+
+        // Notifying sticky modifier state change will notify the listener.
+        notifyStickyModifierStateChanged(
+                KeyEvent.META_ALT_ON or KeyEvent.META_ALT_LEFT_ON or
+                        KeyEvent.META_SHIFT_ON or KeyEvent.META_SHIFT_LEFT_ON,
+                KeyEvent.META_ALT_ON or KeyEvent.META_ALT_LEFT_ON
+        )
+        testLooper.dispatchNext()
+    }
+
+    @Test
+    fun testAddingListenersRegistersInternalCallbackListener() {
+        // Set up two callbacks.
+        val callback1 = InputManager.StickyModifierStateListener {}
+        val callback2 = InputManager.StickyModifierStateListener {}
+
+        assertNull(registeredListener)
+
+        // Adding the listener should register the callback with InputManagerService.
+        inputManager.registerStickyModifierStateListener(executor, callback1)
+        assertNotNull(registeredListener)
+
+        // Adding another listener should not register new internal listener.
+        val currListener = registeredListener
+        inputManager.registerStickyModifierStateListener(executor, callback2)
+        assertEquals(currListener, registeredListener)
+    }
+
+    @Test
+    fun testRemovingListenersUnregistersInternalCallbackListener() {
+        // Set up two callbacks.
+        val callback1 = InputManager.StickyModifierStateListener {}
+        val callback2 = InputManager.StickyModifierStateListener {}
+
+        inputManager.registerStickyModifierStateListener(executor, callback1)
+        inputManager.registerStickyModifierStateListener(executor, callback2)
+
+        // Only removing all listeners should remove the internal callback
+        inputManager.unregisterStickyModifierStateListener(callback1)
+        assertNotNull(registeredListener)
+        inputManager.unregisterStickyModifierStateListener(callback2)
+        assertNull(registeredListener)
+    }
+
+    @Test
+    fun testMultipleListeners() {
+        // Set up two callbacks.
+        var callbackCount1 = 0
+        var callbackCount2 = 0
+        val callback1 = InputManager.StickyModifierStateListener { _ -> callbackCount1++ }
+        val callback2 = InputManager.StickyModifierStateListener { _ -> callbackCount2++ }
+
+        // Add both sticky modifier state listeners
+        inputManager.registerStickyModifierStateListener(executor, callback1)
+        inputManager.registerStickyModifierStateListener(executor, callback2)
+
+        // Notifying sticky modifier state change trigger the both callbacks.
+        notifyStickyModifierStateChanged(0, 0)
+        testLooper.dispatchAll()
+        assertEquals(1, callbackCount1)
+        assertEquals(1, callbackCount2)
+
+        inputManager.unregisterStickyModifierStateListener(callback2)
+        // Notifying sticky modifier state change should still trigger callback1 but not callback2.
+        notifyStickyModifierStateChanged(0, 0)
+        testLooper.dispatchAll()
+        assertEquals(2, callbackCount1)
+        assertEquals(1, callbackCount2)
+    }
+}
diff --git a/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt b/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt
index c1784f3..7b191f8 100644
--- a/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt
+++ b/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt
@@ -22,7 +22,6 @@
 import android.hardware.display.DisplayViewport
 import android.hardware.input.InputManager
 import android.hardware.input.InputManagerGlobal
-import android.os.IInputConstants
 import android.os.test.TestLooper
 import android.platform.test.annotations.Presubmit
 import android.provider.Settings
@@ -295,14 +294,13 @@
 
         localService.setPointerIconVisible(false, 10)
         verify(native).setPointerIconType(eq(PointerIcon.TYPE_NULL))
-        localService.setPointerAcceleration(5f, 10)
-        verify(native).setPointerAcceleration(eq(5f))
+        localService.setMousePointerAccelerationEnabled(false, 10)
+        verify(native).setMousePointerAccelerationEnabled(eq(false))
 
         service.onDisplayRemoved(10)
         verify(native).displayRemoved(eq(10))
         verify(native).setPointerIconType(eq(PointerIcon.TYPE_NOT_SPECIFIED))
-        verify(native).setPointerAcceleration(
-            eq(IInputConstants.DEFAULT_POINTER_ACCELERATION.toFloat()))
+        verify(native).setMousePointerAccelerationEnabled(true)
         verifyNoMoreInteractions(native)
 
         // This call should not block because the virtual mouse pointer override was never removed.
@@ -318,38 +316,38 @@
 
         localService.setPointerIconVisible(false, 10)
         verify(native).setPointerIconType(eq(PointerIcon.TYPE_NULL))
-        localService.setPointerAcceleration(5f, 10)
-        verify(native).setPointerAcceleration(eq(5f))
+        localService.setMousePointerAccelerationEnabled(false, 10)
+        verify(native).setMousePointerAccelerationEnabled(eq(false))
 
         localService.setPointerIconVisible(true, 10)
         verify(native).setPointerIconType(eq(PointerIcon.TYPE_NOT_SPECIFIED))
-        localService.setPointerAcceleration(1f, 10)
-        verify(native).setPointerAcceleration(eq(1f))
+        localService.setMousePointerAccelerationEnabled(true, 10)
+        verify(native).setMousePointerAccelerationEnabled(eq(true))
 
         // Verify that setting properties on a different display is not propagated until the
         // pointer is moved to that display.
         localService.setPointerIconVisible(false, 20)
-        localService.setPointerAcceleration(6f, 20)
+        localService.setMousePointerAccelerationEnabled(false, 20)
         verifyNoMoreInteractions(native)
 
         clearInvocations(native)
         setVirtualMousePointerDisplayIdAndVerify(20)
 
         verify(native).setPointerIconType(eq(PointerIcon.TYPE_NULL))
-        verify(native).setPointerAcceleration(eq(6f))
+        verify(native).setMousePointerAccelerationEnabled(eq(false))
     }
 
     @Test
     fun setAdditionalInputPropertiesBeforeOverride() {
         localService.setPointerIconVisible(false, 10)
-        localService.setPointerAcceleration(5f, 10)
+        localService.setMousePointerAccelerationEnabled(false, 10)
 
         verifyNoMoreInteractions(native)
 
         setVirtualMousePointerDisplayIdAndVerify(10)
 
         verify(native).setPointerIconType(eq(PointerIcon.TYPE_NULL))
-        verify(native).setPointerAcceleration(eq(5f))
+        verify(native).setMousePointerAccelerationEnabled(eq(false))
     }
 
     @Test
diff --git a/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt b/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt
index 9c33576..bbd4567 100644
--- a/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt
+++ b/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt
@@ -36,12 +36,12 @@
 import android.view.InputDevice
 import android.view.inputmethod.InputMethodInfo
 import android.view.inputmethod.InputMethodSubtype
-import androidx.test.core.R
 import androidx.test.core.app.ApplicationProvider
 import com.android.dx.mockito.inline.extended.ExtendedMockito
 import com.android.internal.os.KeyboardConfiguredProto
 import com.android.internal.util.FrameworkStatsLog
 import com.android.modules.utils.testing.ExtendedMockitoRule
+import com.android.test.input.R
 import org.junit.After
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertNotEquals
diff --git a/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java b/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java
index ba9e4a8..f82d9ca 100644
--- a/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java
+++ b/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java
@@ -130,14 +130,13 @@
     private static final Pattern PATTERN_SYSTEM_FONT_FILES =
             Pattern.compile("^/(system|product)/fonts/");
 
-    private String mKeyId;
     private FontManager mFontManager;
     private UiDevice mUiDevice;
 
     @Before
     public void setUp() throws Exception {
         Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
-        mKeyId = insertCert(CERT_PATH);
+        insertCert(CERT_PATH);
         mFontManager = context.getSystemService(FontManager.class);
         expectCommandToSucceed("cmd font clear");
         mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
@@ -147,9 +146,6 @@
     public void tearDown() throws Exception {
         // Ignore errors because this may fail if updatable system font is not enabled.
         runShellCommand("cmd font clear", null);
-        if (mKeyId != null) {
-            expectCommandToSucceed("mini-keyctl unlink " + mKeyId + " .fs-verity");
-        }
     }
 
     @Test
@@ -369,20 +365,11 @@
         assertThat(isFileOpenedBy(fontPath, EMOJI_RENDERING_TEST_APP_ID)).isFalse();
     }
 
-    private static String insertCert(String certPath) throws Exception {
-        Pair<String, String> result;
-        try (InputStream is = new FileInputStream(certPath)) {
-            result = runShellCommand("mini-keyctl padd asymmetric fsv_test .fs-verity", is);
-        }
+    private static void insertCert(String certPath) throws Exception {
         // /data/local/tmp is not readable by system server. Copy a cert file to /data/fonts
         final String copiedCert = "/data/fonts/debug_cert.der";
         runShellCommand("cp " + certPath + " " + copiedCert, null);
         runShellCommand("cmd font install-debug-cert " + copiedCert, null);
-        // Assert that there are no errors.
-        assertThat(result.second).isEmpty();
-        String keyId = result.first.trim();
-        assertThat(keyId).matches("^\\d+$");
-        return keyId;
     }
 
     private int updateFontFile(String fontPath, String signaturePath) throws IOException {
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java
index 692c8a8..49665f7 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java
@@ -27,15 +27,18 @@
 import static android.net.vcn.VcnGatewayConnectionConfig.VCN_GATEWAY_OPTION_ENABLE_DATA_STALL_RECOVERY_WITH_MOBILITY;
 
 import static com.android.server.vcn.VcnGatewayConnection.DUMMY_ADDR;
+import static com.android.server.vcn.VcnGatewayConnection.SAFEMODE_TIMEOUT_SECONDS;
 import static com.android.server.vcn.VcnGatewayConnection.VcnChildSessionConfiguration;
 import static com.android.server.vcn.VcnGatewayConnection.VcnIkeSession;
 import static com.android.server.vcn.VcnGatewayConnection.VcnNetworkAgent;
+import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper;
 
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.CALLS_REAL_METHODS;
@@ -55,6 +58,7 @@
 import android.net.TelephonyNetworkSpecifier;
 import android.net.vcn.VcnGatewayConnectionConfig;
 import android.net.vcn.VcnGatewayConnectionConfigTest;
+import android.net.vcn.VcnManager;
 import android.net.vcn.VcnTransportInfo;
 import android.net.wifi.WifiInfo;
 import android.os.ParcelUuid;
@@ -81,6 +85,7 @@
 import java.util.Set;
 import java.util.UUID;
 import java.util.concurrent.Executor;
+import java.util.concurrent.TimeUnit;
 
 /** Tests for TelephonySubscriptionTracker */
 @RunWith(AndroidJUnit4.class)
@@ -352,4 +357,71 @@
                         any(Executor.class),
                         any(ConnectivityDiagnosticsCallback.class));
     }
+
+    private void verifyGetSafeModeTimeoutMs(
+            boolean isInTestMode,
+            boolean isConfigTimeoutSupported,
+            PersistableBundleWrapper carrierConfig,
+            long expectedTimeoutMs)
+            throws Exception {
+        doReturn(isInTestMode).when(mVcnContext).isInTestMode();
+        doReturn(isConfigTimeoutSupported).when(mVcnContext).isFlagSafeModeTimeoutConfigEnabled();
+
+        final TelephonySubscriptionSnapshot snapshot = mock(TelephonySubscriptionSnapshot.class);
+        doReturn(carrierConfig).when(snapshot).getCarrierConfigForSubGrp(TEST_SUB_GRP);
+
+        final long result =
+                VcnGatewayConnection.getSafeModeTimeoutMs(mVcnContext, snapshot, TEST_SUB_GRP);
+
+        assertEquals(expectedTimeoutMs, result);
+    }
+
+    @Test
+    public void testGetSafeModeTimeoutMs_configTimeoutUnsupported() throws Exception {
+        verifyGetSafeModeTimeoutMs(
+                false /* isInTestMode */,
+                false /* isConfigTimeoutSupported */,
+                null /* carrierConfig */,
+                TimeUnit.SECONDS.toMillis(SAFEMODE_TIMEOUT_SECONDS));
+    }
+
+    @Test
+    public void testGetSafeModeTimeoutMs_configTimeoutSupported() throws Exception {
+        final int carrierConfigTimeoutSeconds = 20;
+        final PersistableBundleWrapper carrierConfig = mock(PersistableBundleWrapper.class);
+        doReturn(carrierConfigTimeoutSeconds)
+                .when(carrierConfig)
+                .getInt(eq(VcnManager.VCN_SAFE_MODE_TIMEOUT_SECONDS_KEY), anyInt());
+
+        verifyGetSafeModeTimeoutMs(
+                false /* isInTestMode */,
+                true /* isConfigTimeoutSupported */,
+                carrierConfig,
+                TimeUnit.SECONDS.toMillis(carrierConfigTimeoutSeconds));
+    }
+
+    @Test
+    public void testGetSafeModeTimeoutMs_configTimeoutSupported_carrierConfigNull()
+            throws Exception {
+        verifyGetSafeModeTimeoutMs(
+                false /* isInTestMode */,
+                true /* isConfigTimeoutSupported */,
+                null /* carrierConfig */,
+                TimeUnit.SECONDS.toMillis(SAFEMODE_TIMEOUT_SECONDS));
+    }
+
+    @Test
+    public void testGetSafeModeTimeoutMs_configTimeoutOverrideTestModeDefault() throws Exception {
+        final int carrierConfigTimeoutSeconds = 20;
+        final PersistableBundleWrapper carrierConfig = mock(PersistableBundleWrapper.class);
+        doReturn(carrierConfigTimeoutSeconds)
+                .when(carrierConfig)
+                .getInt(eq(VcnManager.VCN_SAFE_MODE_TIMEOUT_SECONDS_KEY), anyInt());
+
+        verifyGetSafeModeTimeoutMs(
+                true /* isInTestMode */,
+                true /* isConfigTimeoutSupported */,
+                carrierConfig,
+                TimeUnit.SECONDS.toMillis(carrierConfigTimeoutSeconds));
+    }
 }
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
index edced87..4c7b25a 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
@@ -67,6 +67,8 @@
 import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
 import com.android.server.vcn.Vcn.VcnGatewayStatusCallback;
 import com.android.server.vcn.VcnGatewayConnection.VcnChildSessionCallback;
+import com.android.server.vcn.VcnGatewayConnection.VcnIkeSession;
+import com.android.server.vcn.VcnGatewayConnection.VcnNetworkAgent;
 import com.android.server.vcn.VcnGatewayConnection.VcnWakeLock;
 import com.android.server.vcn.routeselection.UnderlyingNetworkController;
 import com.android.server.vcn.routeselection.UnderlyingNetworkRecord;
@@ -118,13 +120,7 @@
             NetworkCapabilities networkCapabilities,
             LinkProperties linkProperties,
             boolean isBlocked) {
-        return new UnderlyingNetworkRecord(
-                network,
-                networkCapabilities,
-                linkProperties,
-                isBlocked,
-                false /* isSelected */,
-                0 /* priorityClass */);
+        return new UnderlyingNetworkRecord(network, networkCapabilities, linkProperties, isBlocked);
     }
 
     protected static final String TEST_TCP_BUFFER_SIZES_1 = "1,2,3,4";
@@ -226,6 +222,7 @@
         doReturn(mTestLooper.getLooper()).when(mVcnContext).getLooper();
         doReturn(mVcnNetworkProvider).when(mVcnContext).getVcnNetworkProvider();
         doReturn(mFeatureFlags).when(mVcnContext).getFeatureFlags();
+        doReturn(true).when(mVcnContext).isFlagSafeModeTimeoutConfigEnabled();
 
         doReturn(mUnderlyingNetworkController)
                 .when(mDeps)
diff --git a/tests/vcn/java/com/android/server/vcn/routeselection/IpSecPacketLossDetectorTest.java b/tests/vcn/java/com/android/server/vcn/routeselection/IpSecPacketLossDetectorTest.java
new file mode 100644
index 0000000..9daba6a
--- /dev/null
+++ b/tests/vcn/java/com/android/server/vcn/routeselection/IpSecPacketLossDetectorTest.java
@@ -0,0 +1,419 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.vcn.routeselection;
+
+import static android.net.vcn.VcnManager.VCN_NETWORK_SELECTION_IPSEC_PACKET_LOSS_PERCENT_THRESHOLD_KEY;
+import static android.net.vcn.VcnManager.VCN_NETWORK_SELECTION_POLL_IPSEC_STATE_INTERVAL_SECONDS_KEY;
+
+import static com.android.server.vcn.routeselection.IpSecPacketLossDetector.PACKET_LOSS_UNAVALAIBLE;
+import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.BroadcastReceiver;
+import android.content.Intent;
+import android.net.IpSecTransformState;
+import android.os.OutcomeReceiver;
+import android.os.PowerManager;
+
+import com.android.server.vcn.routeselection.IpSecPacketLossDetector.PacketLossCalculator;
+import com.android.server.vcn.routeselection.NetworkMetricMonitor.IpSecTransformWrapper;
+import com.android.server.vcn.routeselection.NetworkMetricMonitor.NetworkMetricMonitorCallback;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.Spy;
+
+import java.util.Arrays;
+import java.util.BitSet;
+import java.util.concurrent.TimeUnit;
+
+public class IpSecPacketLossDetectorTest extends NetworkEvaluationTestBase {
+    private static final String TAG = IpSecPacketLossDetectorTest.class.getSimpleName();
+
+    private static final int REPLAY_BITMAP_LEN_BYTE = 512;
+    private static final int REPLAY_BITMAP_LEN_BIT = REPLAY_BITMAP_LEN_BYTE * 8;
+    private static final int IPSEC_PACKET_LOSS_PERCENT_THRESHOLD = 5;
+    private static final long POLL_IPSEC_STATE_INTERVAL_MS = TimeUnit.SECONDS.toMillis(30L);
+
+    @Mock private IpSecTransformWrapper mIpSecTransform;
+    @Mock private NetworkMetricMonitorCallback mMetricMonitorCallback;
+    @Mock private PersistableBundleWrapper mCarrierConfig;
+    @Mock private IpSecPacketLossDetector.Dependencies mDependencies;
+    @Spy private PacketLossCalculator mPacketLossCalculator = new PacketLossCalculator();
+
+    @Captor private ArgumentCaptor<OutcomeReceiver> mTransformStateReceiverCaptor;
+    @Captor private ArgumentCaptor<BroadcastReceiver> mBroadcastReceiverCaptor;
+
+    private IpSecPacketLossDetector mIpSecPacketLossDetector;
+    private IpSecTransformState mTransformStateInitial;
+
+    @Before
+    public void setUp() throws Exception {
+        super.setUp();
+        mTransformStateInitial = newTransformState(0, 0, newReplayBitmap(0));
+
+        when(mCarrierConfig.getInt(
+                        eq(VCN_NETWORK_SELECTION_POLL_IPSEC_STATE_INTERVAL_SECONDS_KEY), anyInt()))
+                .thenReturn((int) TimeUnit.MILLISECONDS.toSeconds(POLL_IPSEC_STATE_INTERVAL_MS));
+        when(mCarrierConfig.getInt(
+                        eq(VCN_NETWORK_SELECTION_IPSEC_PACKET_LOSS_PERCENT_THRESHOLD_KEY),
+                        anyInt()))
+                .thenReturn(IPSEC_PACKET_LOSS_PERCENT_THRESHOLD);
+
+        when(mDependencies.getPacketLossCalculator()).thenReturn(mPacketLossCalculator);
+
+        mIpSecPacketLossDetector =
+                new IpSecPacketLossDetector(
+                        mVcnContext,
+                        mNetwork,
+                        mCarrierConfig,
+                        mMetricMonitorCallback,
+                        mDependencies);
+    }
+
+    private static IpSecTransformState newTransformState(
+            long rxSeqNo, long packtCount, byte[] replayBitmap) {
+        return new IpSecTransformState.Builder()
+                .setRxHighestSequenceNumber(rxSeqNo)
+                .setPacketCount(packtCount)
+                .setReplayBitmap(replayBitmap)
+                .build();
+    }
+
+    private static byte[] newReplayBitmap(int receivedPktCnt) {
+        final BitSet bitSet = new BitSet(REPLAY_BITMAP_LEN_BIT);
+        for (int i = 0; i < receivedPktCnt; i++) {
+            bitSet.set(i);
+        }
+        return Arrays.copyOf(bitSet.toByteArray(), REPLAY_BITMAP_LEN_BYTE);
+    }
+
+    private void verifyStopped() {
+        assertFalse(mIpSecPacketLossDetector.isStarted());
+        assertFalse(mIpSecPacketLossDetector.isValidationFailed());
+        assertNull(mIpSecPacketLossDetector.getLastTransformState());
+
+        // No event scheduled
+        mTestLooper.moveTimeForward(POLL_IPSEC_STATE_INTERVAL_MS);
+        assertNull(mTestLooper.nextMessage());
+    }
+
+    @Test
+    public void testInitialization() throws Exception {
+        assertFalse(mIpSecPacketLossDetector.isSelectedUnderlyingNetwork());
+        verifyStopped();
+    }
+
+    private OutcomeReceiver<IpSecTransformState, RuntimeException>
+            startMonitorAndCaptureStateReceiver() {
+        mIpSecPacketLossDetector.setIsSelectedUnderlyingNetwork(true /* setIsSelected */);
+        mIpSecPacketLossDetector.setInboundTransformInternal(mIpSecTransform);
+
+        // Trigger the runnable
+        mTestLooper.dispatchAll();
+
+        verify(mIpSecTransform)
+                .getIpSecTransformState(any(), mTransformStateReceiverCaptor.capture());
+        return mTransformStateReceiverCaptor.getValue();
+    }
+
+    @Test
+    public void testStartMonitor() throws Exception {
+        final OutcomeReceiver<IpSecTransformState, RuntimeException> xfrmStateReceiver =
+                startMonitorAndCaptureStateReceiver();
+
+        assertTrue(mIpSecPacketLossDetector.isStarted());
+        assertFalse(mIpSecPacketLossDetector.isValidationFailed());
+        assertTrue(mIpSecPacketLossDetector.isSelectedUnderlyingNetwork());
+        assertEquals(mIpSecTransform, mIpSecPacketLossDetector.getInboundTransformInternal());
+
+        // Mock receiving a state
+        xfrmStateReceiver.onResult(mTransformStateInitial);
+
+        // Verify the first polled state is stored
+        assertEquals(mTransformStateInitial, mIpSecPacketLossDetector.getLastTransformState());
+        verify(mPacketLossCalculator, never())
+                .getPacketLossRatePercentage(any(), any(), anyString());
+
+        // Verify next poll is scheduled
+        assertNull(mTestLooper.nextMessage());
+        mTestLooper.moveTimeForward(POLL_IPSEC_STATE_INTERVAL_MS);
+        assertNotNull(mTestLooper.nextMessage());
+    }
+
+    @Test
+    public void testStartedMonitor_enterDozeMoze() throws Exception {
+        final OutcomeReceiver<IpSecTransformState, RuntimeException> xfrmStateReceiver =
+                startMonitorAndCaptureStateReceiver();
+
+        // Mock receiving a state
+        xfrmStateReceiver.onResult(mTransformStateInitial);
+        assertEquals(mTransformStateInitial, mIpSecPacketLossDetector.getLastTransformState());
+
+        // Mock entering doze mode
+        final Intent intent = mock(Intent.class);
+        when(intent.getAction()).thenReturn(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
+        when(mPowerManagerService.isDeviceIdleMode()).thenReturn(true);
+
+        verify(mContext).registerReceiver(mBroadcastReceiverCaptor.capture(), any(), any(), any());
+        final BroadcastReceiver broadcastReceiver = mBroadcastReceiverCaptor.getValue();
+        broadcastReceiver.onReceive(mContext, intent);
+
+        assertNull(mIpSecPacketLossDetector.getLastTransformState());
+    }
+
+    @Test
+    public void testStartedMonitor_updateInboundTransform() throws Exception {
+        final OutcomeReceiver<IpSecTransformState, RuntimeException> xfrmStateReceiver =
+                startMonitorAndCaptureStateReceiver();
+
+        // Mock receiving a state
+        xfrmStateReceiver.onResult(mTransformStateInitial);
+        assertEquals(mTransformStateInitial, mIpSecPacketLossDetector.getLastTransformState());
+
+        // Update the inbound transform
+        final IpSecTransformWrapper newTransform = mock(IpSecTransformWrapper.class);
+        mIpSecPacketLossDetector.setInboundTransformInternal(newTransform);
+
+        // Verifications
+        assertNull(mIpSecPacketLossDetector.getLastTransformState());
+        mTestLooper.moveTimeForward(POLL_IPSEC_STATE_INTERVAL_MS);
+        mTestLooper.dispatchAll();
+        verify(newTransform).getIpSecTransformState(any(), any());
+    }
+
+    @Test
+    public void testStartedMonitor_updateCarrierConfig() throws Exception {
+        startMonitorAndCaptureStateReceiver();
+
+        final int additionalPollIntervalMs = (int) TimeUnit.SECONDS.toMillis(10L);
+        when(mCarrierConfig.getInt(
+                        eq(VCN_NETWORK_SELECTION_POLL_IPSEC_STATE_INTERVAL_SECONDS_KEY), anyInt()))
+                .thenReturn(
+                        (int)
+                                TimeUnit.MILLISECONDS.toSeconds(
+                                        POLL_IPSEC_STATE_INTERVAL_MS + additionalPollIntervalMs));
+        mIpSecPacketLossDetector.setCarrierConfig(mCarrierConfig);
+        mTestLooper.dispatchAll();
+
+        // The already scheduled event is still fired with the old timeout
+        mTestLooper.moveTimeForward(POLL_IPSEC_STATE_INTERVAL_MS);
+        mTestLooper.dispatchAll();
+
+        // The next scheduled event will take 10 more seconds to fire
+        mTestLooper.moveTimeForward(POLL_IPSEC_STATE_INTERVAL_MS);
+        assertNull(mTestLooper.nextMessage());
+        mTestLooper.moveTimeForward(additionalPollIntervalMs);
+        assertNotNull(mTestLooper.nextMessage());
+    }
+
+    @Test
+    public void testStopMonitor() throws Exception {
+        mIpSecPacketLossDetector.setIsSelectedUnderlyingNetwork(true /* setIsSelected */);
+        mIpSecPacketLossDetector.setInboundTransformInternal(mIpSecTransform);
+
+        assertTrue(mIpSecPacketLossDetector.isStarted());
+        assertNotNull(mTestLooper.nextMessage());
+
+        // Unselect the monitor
+        mIpSecPacketLossDetector.setIsSelectedUnderlyingNetwork(false /* setIsSelected */);
+        verifyStopped();
+    }
+
+    @Test
+    public void testClose() throws Exception {
+        mIpSecPacketLossDetector.setIsSelectedUnderlyingNetwork(true /* setIsSelected */);
+        mIpSecPacketLossDetector.setInboundTransformInternal(mIpSecTransform);
+
+        assertTrue(mIpSecPacketLossDetector.isStarted());
+        assertNotNull(mTestLooper.nextMessage());
+
+        // Stop the monitor
+        mIpSecPacketLossDetector.close();
+        verifyStopped();
+        verify(mIpSecTransform).close();
+    }
+
+    @Test
+    public void testTransformStateReceiverOnResultWhenStopped() throws Exception {
+        final OutcomeReceiver<IpSecTransformState, RuntimeException> xfrmStateReceiver =
+                startMonitorAndCaptureStateReceiver();
+        xfrmStateReceiver.onResult(mTransformStateInitial);
+
+        // Unselect the monitor
+        mIpSecPacketLossDetector.setIsSelectedUnderlyingNetwork(false /* setIsSelected */);
+        verifyStopped();
+
+        xfrmStateReceiver.onResult(newTransformState(1, 1, newReplayBitmap(1)));
+        verify(mPacketLossCalculator, never())
+                .getPacketLossRatePercentage(any(), any(), anyString());
+    }
+
+    @Test
+    public void testTransformStateReceiverOnError() throws Exception {
+        final OutcomeReceiver<IpSecTransformState, RuntimeException> xfrmStateReceiver =
+                startMonitorAndCaptureStateReceiver();
+        xfrmStateReceiver.onResult(mTransformStateInitial);
+
+        xfrmStateReceiver.onError(new RuntimeException("Test"));
+        verify(mPacketLossCalculator, never())
+                .getPacketLossRatePercentage(any(), any(), anyString());
+    }
+
+    private void checkHandleLossRate(
+            int mockPacketLossRate, boolean isLastStateExpectedToUpdate, boolean isCallbackExpected)
+            throws Exception {
+        final OutcomeReceiver<IpSecTransformState, RuntimeException> xfrmStateReceiver =
+                startMonitorAndCaptureStateReceiver();
+        doReturn(mockPacketLossRate)
+                .when(mPacketLossCalculator)
+                .getPacketLossRatePercentage(any(), any(), anyString());
+
+        // Mock receiving two states with mTransformStateInitial and an arbitrary transformNew
+        final IpSecTransformState transformNew = newTransformState(1, 1, newReplayBitmap(1));
+        xfrmStateReceiver.onResult(mTransformStateInitial);
+        xfrmStateReceiver.onResult(transformNew);
+
+        // Verifications
+        verify(mPacketLossCalculator)
+                .getPacketLossRatePercentage(
+                        eq(mTransformStateInitial), eq(transformNew), anyString());
+
+        if (isLastStateExpectedToUpdate) {
+            assertEquals(transformNew, mIpSecPacketLossDetector.getLastTransformState());
+        } else {
+            assertEquals(mTransformStateInitial, mIpSecPacketLossDetector.getLastTransformState());
+        }
+
+        if (isCallbackExpected) {
+            verify(mMetricMonitorCallback).onValidationResultReceived();
+        } else {
+            verify(mMetricMonitorCallback, never()).onValidationResultReceived();
+        }
+    }
+
+    @Test
+    public void testHandleLossRate_validationPass() throws Exception {
+        checkHandleLossRate(
+                2, true /* isLastStateExpectedToUpdate */, true /* isCallbackExpected */);
+    }
+
+    @Test
+    public void testHandleLossRate_validationFail() throws Exception {
+        checkHandleLossRate(
+                22, true /* isLastStateExpectedToUpdate */, true /* isCallbackExpected */);
+    }
+
+    @Test
+    public void testHandleLossRate_resultUnavalaible() throws Exception {
+        checkHandleLossRate(
+                PACKET_LOSS_UNAVALAIBLE,
+                false /* isLastStateExpectedToUpdate */,
+                false /* isCallbackExpected */);
+    }
+
+    private void checkGetPacketLossRate(
+            IpSecTransformState oldState, IpSecTransformState newState, int expectedLossRate)
+            throws Exception {
+        assertEquals(
+                expectedLossRate,
+                mPacketLossCalculator.getPacketLossRatePercentage(oldState, newState, TAG));
+    }
+
+    private void checkGetPacketLossRate(
+            IpSecTransformState oldState,
+            int rxSeqNo,
+            int packetCount,
+            int packetInWin,
+            int expectedDataLossRate)
+            throws Exception {
+        final IpSecTransformState newState =
+                newTransformState(rxSeqNo, packetCount, newReplayBitmap(packetInWin));
+        checkGetPacketLossRate(oldState, newState, expectedDataLossRate);
+    }
+
+    @Test
+    public void testGetPacketLossRate_replayWindowUnchanged() throws Exception {
+        checkGetPacketLossRate(
+                mTransformStateInitial, mTransformStateInitial, PACKET_LOSS_UNAVALAIBLE);
+        checkGetPacketLossRate(mTransformStateInitial, 3000, 2000, 2000, PACKET_LOSS_UNAVALAIBLE);
+    }
+
+    @Test
+    public void testGetPacketLossRate_againstInitialState() throws Exception {
+        checkGetPacketLossRate(mTransformStateInitial, 7000, 7001, 4096, 0);
+        checkGetPacketLossRate(mTransformStateInitial, 7000, 6000, 4096, 15);
+        checkGetPacketLossRate(mTransformStateInitial, 7000, 6000, 4000, 14);
+    }
+
+    @Test
+    public void testGetPktLossRate_oldHiSeqSmallerThanWinSize_overlappedWithNewWin()
+            throws Exception {
+        final IpSecTransformState oldState = newTransformState(2000, 1500, newReplayBitmap(1500));
+
+        checkGetPacketLossRate(oldState, 5000, 5001, 4096, 0);
+        checkGetPacketLossRate(oldState, 5000, 4000, 4096, 29);
+        checkGetPacketLossRate(oldState, 5000, 4000, 4000, 27);
+    }
+
+    @Test
+    public void testGetPktLossRate_oldHiSeqSmallerThanWinSize_notOverlappedWithNewWin()
+            throws Exception {
+        final IpSecTransformState oldState = newTransformState(2000, 1500, newReplayBitmap(1500));
+
+        checkGetPacketLossRate(oldState, 7000, 7001, 4096, 0);
+        checkGetPacketLossRate(oldState, 7000, 5000, 4096, 37);
+        checkGetPacketLossRate(oldState, 7000, 5000, 3000, 21);
+    }
+
+    @Test
+    public void testGetPktLossRate_oldHiSeqLargerThanWinSize_overlappedWithNewWin()
+            throws Exception {
+        final IpSecTransformState oldState = newTransformState(10000, 5000, newReplayBitmap(3000));
+
+        checkGetPacketLossRate(oldState, 12000, 8096, 4096, 0);
+        checkGetPacketLossRate(oldState, 12000, 7000, 4096, 36);
+        checkGetPacketLossRate(oldState, 12000, 7000, 3000, 0);
+    }
+
+    @Test
+    public void testGetPktLossRate_oldHiSeqLargerThanWinSize_notOverlappedWithNewWin()
+            throws Exception {
+        final IpSecTransformState oldState = newTransformState(10000, 5000, newReplayBitmap(3000));
+
+        checkGetPacketLossRate(oldState, 20000, 16096, 4096, 0);
+        checkGetPacketLossRate(oldState, 20000, 14000, 4096, 19);
+        checkGetPacketLossRate(oldState, 20000, 14000, 3000, 10);
+    }
+}
diff --git a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkEvaluationTestBase.java b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkEvaluationTestBase.java
new file mode 100644
index 0000000..355c221
--- /dev/null
+++ b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkEvaluationTestBase.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.vcn.routeselection;
+
+import static com.android.server.vcn.VcnTestUtils.setupSystemService;
+import static com.android.server.vcn.routeselection.UnderlyingNetworkControllerTest.getLinkPropertiesWithName;
+
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.net.LinkProperties;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.TelephonyNetworkSpecifier;
+import android.net.vcn.FeatureFlags;
+import android.os.Handler;
+import android.os.IPowerManager;
+import android.os.IThermalService;
+import android.os.ParcelUuid;
+import android.os.PowerManager;
+import android.os.test.TestLooper;
+import android.telephony.TelephonyManager;
+
+import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
+import com.android.server.vcn.VcnContext;
+import com.android.server.vcn.VcnNetworkProvider;
+
+import org.junit.Before;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Set;
+import java.util.UUID;
+
+public abstract class NetworkEvaluationTestBase {
+    protected static final String SSID = "TestWifi";
+    protected static final String SSID_OTHER = "TestWifiOther";
+    protected static final String PLMN_ID = "123456";
+    protected static final String PLMN_ID_OTHER = "234567";
+
+    protected static final int SUB_ID = 1;
+    protected static final int WIFI_RSSI = -60;
+    protected static final int WIFI_RSSI_HIGH = -50;
+    protected static final int WIFI_RSSI_LOW = -80;
+    protected static final int CARRIER_ID = 1;
+    protected static final int CARRIER_ID_OTHER = 2;
+
+    protected static final int LINK_UPSTREAM_BANDWIDTH_KBPS = 1024;
+    protected static final int LINK_DOWNSTREAM_BANDWIDTH_KBPS = 2048;
+
+    protected static final int TEST_MIN_UPSTREAM_BANDWIDTH_KBPS = 100;
+    protected static final int TEST_MIN_DOWNSTREAM_BANDWIDTH_KBPS = 200;
+
+    protected static final ParcelUuid SUB_GROUP = new ParcelUuid(new UUID(0, 0));
+
+    protected static final NetworkCapabilities WIFI_NETWORK_CAPABILITIES =
+            new NetworkCapabilities.Builder()
+                    .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
+                    .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+                    .setSignalStrength(WIFI_RSSI)
+                    .setSsid(SSID)
+                    .setLinkUpstreamBandwidthKbps(LINK_UPSTREAM_BANDWIDTH_KBPS)
+                    .setLinkDownstreamBandwidthKbps(LINK_DOWNSTREAM_BANDWIDTH_KBPS)
+                    .build();
+
+    protected static final TelephonyNetworkSpecifier TEL_NETWORK_SPECIFIER =
+            new TelephonyNetworkSpecifier.Builder().setSubscriptionId(SUB_ID).build();
+    protected static final NetworkCapabilities CELL_NETWORK_CAPABILITIES =
+            new NetworkCapabilities.Builder()
+                    .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+                    .addCapability(NetworkCapabilities.NET_CAPABILITY_DUN)
+                    .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
+                    .setSubscriptionIds(Set.of(SUB_ID))
+                    .setNetworkSpecifier(TEL_NETWORK_SPECIFIER)
+                    .setLinkUpstreamBandwidthKbps(LINK_UPSTREAM_BANDWIDTH_KBPS)
+                    .setLinkDownstreamBandwidthKbps(LINK_DOWNSTREAM_BANDWIDTH_KBPS)
+                    .build();
+
+    protected static final LinkProperties LINK_PROPERTIES = getLinkPropertiesWithName("test_iface");
+
+    @Mock protected Context mContext;
+    @Mock protected Network mNetwork;
+    @Mock protected FeatureFlags mFeatureFlags;
+    @Mock protected com.android.net.flags.FeatureFlags mCoreNetFeatureFlags;
+    @Mock protected TelephonySubscriptionSnapshot mSubscriptionSnapshot;
+    @Mock protected TelephonyManager mTelephonyManager;
+    @Mock protected IPowerManager mPowerManagerService;
+
+    protected TestLooper mTestLooper;
+    protected VcnContext mVcnContext;
+    protected PowerManager mPowerManager;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+
+        when(mNetwork.getNetId()).thenReturn(-1);
+
+        mTestLooper = new TestLooper();
+        mVcnContext =
+                spy(
+                        new VcnContext(
+                                mContext,
+                                mTestLooper.getLooper(),
+                                mock(VcnNetworkProvider.class),
+                                false /* isInTestMode */));
+        doNothing().when(mVcnContext).ensureRunningOnLooperThread();
+
+        doReturn(true).when(mVcnContext).isFlagNetworkMetricMonitorEnabled();
+        doReturn(true).when(mVcnContext).isFlagIpSecTransformStateEnabled();
+
+        setupSystemService(
+                mContext, mTelephonyManager, Context.TELEPHONY_SERVICE, TelephonyManager.class);
+        when(mTelephonyManager.createForSubscriptionId(SUB_ID)).thenReturn(mTelephonyManager);
+        when(mTelephonyManager.getNetworkOperator()).thenReturn(PLMN_ID);
+        when(mTelephonyManager.getSimSpecificCarrierId()).thenReturn(CARRIER_ID);
+
+        mPowerManager =
+                new PowerManager(
+                        mContext,
+                        mPowerManagerService,
+                        mock(IThermalService.class),
+                        mock(Handler.class));
+        setupSystemService(mContext, mPowerManager, Context.POWER_SERVICE, PowerManager.class);
+    }
+}
diff --git a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java
index 2266041..d85c515 100644
--- a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java
+++ b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java
@@ -24,152 +24,48 @@
 import static android.net.vcn.VcnUnderlyingNetworkTemplateTestBase.TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS;
 import static android.net.vcn.VcnUnderlyingNetworkTemplateTestBase.TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS;
 
-import static com.android.server.vcn.VcnTestUtils.setupSystemService;
 import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.PRIORITY_FALLBACK;
 import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.PRIORITY_INVALID;
 import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.checkMatchesCellPriorityRule;
 import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.checkMatchesPriorityRule;
 import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.checkMatchesWifiPriorityRule;
-import static com.android.server.vcn.routeselection.UnderlyingNetworkControllerTest.getLinkPropertiesWithName;
 import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.when;
 
-import android.content.Context;
-import android.net.LinkProperties;
-import android.net.Network;
 import android.net.NetworkCapabilities;
-import android.net.TelephonyNetworkSpecifier;
 import android.net.vcn.VcnCellUnderlyingNetworkTemplate;
 import android.net.vcn.VcnGatewayConnectionConfig;
 import android.net.vcn.VcnManager;
 import android.net.vcn.VcnUnderlyingNetworkTemplate;
 import android.net.vcn.VcnWifiUnderlyingNetworkTemplate;
-import android.os.ParcelUuid;
 import android.os.PersistableBundle;
-import android.os.test.TestLooper;
-import android.telephony.TelephonyManager;
 import android.util.ArraySet;
 
-import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
-import com.android.server.vcn.VcnContext;
-import com.android.server.vcn.VcnNetworkProvider;
-
 import org.junit.Before;
 import org.junit.Test;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
 
 import java.util.Collections;
 import java.util.List;
 import java.util.Set;
-import java.util.UUID;
 
-public class NetworkPriorityClassifierTest {
-    private static final String SSID = "TestWifi";
-    private static final String SSID_OTHER = "TestWifiOther";
-    private static final String PLMN_ID = "123456";
-    private static final String PLMN_ID_OTHER = "234567";
-
-    private static final int SUB_ID = 1;
-    private static final int WIFI_RSSI = -60;
-    private static final int WIFI_RSSI_HIGH = -50;
-    private static final int WIFI_RSSI_LOW = -80;
-    private static final int CARRIER_ID = 1;
-    private static final int CARRIER_ID_OTHER = 2;
-
-    private static final int LINK_UPSTREAM_BANDWIDTH_KBPS = 1024;
-    private static final int LINK_DOWNSTREAM_BANDWIDTH_KBPS = 2048;
-
-    private static final int TEST_MIN_UPSTREAM_BANDWIDTH_KBPS = 100;
-    private static final int TEST_MIN_DOWNSTREAM_BANDWIDTH_KBPS = 200;
-
-    private static final ParcelUuid SUB_GROUP = new ParcelUuid(new UUID(0, 0));
-
-    private static final NetworkCapabilities WIFI_NETWORK_CAPABILITIES =
-            new NetworkCapabilities.Builder()
-                    .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
-                    .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
-                    .setSignalStrength(WIFI_RSSI)
-                    .setSsid(SSID)
-                    .setLinkUpstreamBandwidthKbps(LINK_UPSTREAM_BANDWIDTH_KBPS)
-                    .setLinkDownstreamBandwidthKbps(LINK_DOWNSTREAM_BANDWIDTH_KBPS)
-                    .build();
-
-    private static final TelephonyNetworkSpecifier TEL_NETWORK_SPECIFIER =
-            new TelephonyNetworkSpecifier.Builder().setSubscriptionId(SUB_ID).build();
-    private static final NetworkCapabilities CELL_NETWORK_CAPABILITIES =
-            new NetworkCapabilities.Builder()
-                    .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
-                    .addCapability(NetworkCapabilities.NET_CAPABILITY_DUN)
-                    .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
-                    .setSubscriptionIds(Set.of(SUB_ID))
-                    .setNetworkSpecifier(TEL_NETWORK_SPECIFIER)
-                    .setLinkUpstreamBandwidthKbps(LINK_UPSTREAM_BANDWIDTH_KBPS)
-                    .setLinkDownstreamBandwidthKbps(LINK_DOWNSTREAM_BANDWIDTH_KBPS)
-                    .build();
-
-    private static final LinkProperties LINK_PROPERTIES = getLinkPropertiesWithName("test_iface");
-
-    @Mock private Network mNetwork;
-    @Mock private TelephonySubscriptionSnapshot mSubscriptionSnapshot;
-    @Mock private TelephonyManager mTelephonyManager;
-
-    private TestLooper mTestLooper;
-    private VcnContext mVcnContext;
+public class NetworkPriorityClassifierTest extends NetworkEvaluationTestBase {
     private UnderlyingNetworkRecord mWifiNetworkRecord;
     private UnderlyingNetworkRecord mCellNetworkRecord;
 
     @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
+    public void setUp() throws Exception {
+        super.setUp();
 
-        final Context mockContext = mock(Context.class);
-        mTestLooper = new TestLooper();
-        mVcnContext =
-                spy(
-                        new VcnContext(
-                                mockContext,
-                                mTestLooper.getLooper(),
-                                mock(VcnNetworkProvider.class),
-                                false /* isInTestMode */));
-        doNothing().when(mVcnContext).ensureRunningOnLooperThread();
-
-        setupSystemService(
-                mockContext, mTelephonyManager, Context.TELEPHONY_SERVICE, TelephonyManager.class);
-        when(mTelephonyManager.createForSubscriptionId(SUB_ID)).thenReturn(mTelephonyManager);
-        when(mTelephonyManager.getNetworkOperator()).thenReturn(PLMN_ID);
-        when(mTelephonyManager.getSimSpecificCarrierId()).thenReturn(CARRIER_ID);
-
-        mWifiNetworkRecord =
-                getTestNetworkRecord(
-                        WIFI_NETWORK_CAPABILITIES,
-                        VcnGatewayConnectionConfig.DEFAULT_UNDERLYING_NETWORK_TEMPLATES);
-        mCellNetworkRecord =
-                getTestNetworkRecord(
-                        CELL_NETWORK_CAPABILITIES,
-                        VcnGatewayConnectionConfig.DEFAULT_UNDERLYING_NETWORK_TEMPLATES);
+        mWifiNetworkRecord = getTestNetworkRecord(WIFI_NETWORK_CAPABILITIES);
+        mCellNetworkRecord = getTestNetworkRecord(CELL_NETWORK_CAPABILITIES);
     }
 
-    private UnderlyingNetworkRecord getTestNetworkRecord(
-            NetworkCapabilities nc, List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates) {
-        return new UnderlyingNetworkRecord(
-                mNetwork,
-                nc,
-                LINK_PROPERTIES,
-                false /* isBlocked */,
-                mVcnContext,
-                underlyingNetworkTemplates,
-                SUB_GROUP,
-                mSubscriptionSnapshot,
-                null /* currentlySelected */,
-                null /* carrierConfig */);
+    private UnderlyingNetworkRecord getTestNetworkRecord(NetworkCapabilities nc) {
+        return new UnderlyingNetworkRecord(mNetwork, nc, LINK_PROPERTIES, false /* isBlocked */);
     }
 
     @Test
@@ -186,14 +82,14 @@
                         mWifiNetworkRecord,
                         SUB_GROUP,
                         mSubscriptionSnapshot,
-                        null /* currentlySelecetd */,
+                        false /* isSelected */,
                         null /* carrierConfig */));
     }
 
     private void verifyMatchesPriorityRuleForUpstreamBandwidth(
             int entryUpstreamBandwidth,
             int exitUpstreamBandwidth,
-            UnderlyingNetworkRecord currentlySelected,
+            boolean isSelected,
             boolean expectMatch) {
         final VcnWifiUnderlyingNetworkTemplate wifiNetworkPriority =
                 new VcnWifiUnderlyingNetworkTemplate.Builder()
@@ -208,14 +104,14 @@
                         mWifiNetworkRecord,
                         SUB_GROUP,
                         mSubscriptionSnapshot,
-                        currentlySelected,
+                        isSelected,
                         null /* carrierConfig */));
     }
 
     private void verifyMatchesPriorityRuleForDownstreamBandwidth(
             int entryDownstreamBandwidth,
             int exitDownstreamBandwidth,
-            UnderlyingNetworkRecord currentlySelected,
+            boolean isSelected,
             boolean expectMatch) {
         final VcnWifiUnderlyingNetworkTemplate wifiNetworkPriority =
                 new VcnWifiUnderlyingNetworkTemplate.Builder()
@@ -231,7 +127,7 @@
                         mWifiNetworkRecord,
                         SUB_GROUP,
                         mSubscriptionSnapshot,
-                        currentlySelected,
+                        isSelected,
                         null /* carrierConfig */));
     }
 
@@ -240,7 +136,7 @@
         verifyMatchesPriorityRuleForUpstreamBandwidth(
                 TEST_MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS,
                 TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS,
-                null /* currentlySelected */,
+                false /* isSelected */,
                 true);
     }
 
@@ -249,7 +145,7 @@
         verifyMatchesPriorityRuleForUpstreamBandwidth(
                 LINK_UPSTREAM_BANDWIDTH_KBPS + 1,
                 LINK_UPSTREAM_BANDWIDTH_KBPS + 1,
-                null /* currentlySelected */,
+                false /* isSelected */,
                 false);
     }
 
@@ -258,7 +154,7 @@
         verifyMatchesPriorityRuleForDownstreamBandwidth(
                 TEST_MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS,
                 TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS,
-                null /* currentlySelected */,
+                false /* isSelected */,
                 true);
     }
 
@@ -267,7 +163,7 @@
         verifyMatchesPriorityRuleForDownstreamBandwidth(
                 LINK_DOWNSTREAM_BANDWIDTH_KBPS + 1,
                 LINK_DOWNSTREAM_BANDWIDTH_KBPS + 1,
-                null /* currentlySelected */,
+                false /* isSelected */,
                 false);
     }
 
@@ -276,7 +172,7 @@
         verifyMatchesPriorityRuleForUpstreamBandwidth(
                 TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS,
                 TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS,
-                mWifiNetworkRecord,
+                true /* isSelected */,
                 true);
     }
 
@@ -285,7 +181,7 @@
         verifyMatchesPriorityRuleForUpstreamBandwidth(
                 LINK_UPSTREAM_BANDWIDTH_KBPS + 1,
                 LINK_UPSTREAM_BANDWIDTH_KBPS + 1,
-                mWifiNetworkRecord,
+                true /* isSelected */,
                 false);
     }
 
@@ -294,7 +190,7 @@
         verifyMatchesPriorityRuleForDownstreamBandwidth(
                 TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS,
                 TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS,
-                mWifiNetworkRecord,
+                true /* isSelected */,
                 true);
     }
 
@@ -303,7 +199,7 @@
         verifyMatchesPriorityRuleForDownstreamBandwidth(
                 LINK_DOWNSTREAM_BANDWIDTH_KBPS + 1,
                 LINK_DOWNSTREAM_BANDWIDTH_KBPS + 1,
-                mWifiNetworkRecord,
+                true /* isSelected */,
                 false);
     }
 
@@ -318,14 +214,12 @@
                                 TEST_MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS,
                                 TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS)
                         .build();
-        final UnderlyingNetworkRecord selectedNetworkRecord =
-                isSelectedNetwork ? mWifiNetworkRecord : null;
         assertEquals(
                 expectMatch,
                 checkMatchesWifiPriorityRule(
                         wifiNetworkPriority,
                         mWifiNetworkRecord,
-                        selectedNetworkRecord,
+                        isSelectedNetwork,
                         carrierConfig == null
                                 ? null
                                 : new PersistableBundleWrapper(carrierConfig)));
@@ -381,7 +275,7 @@
                 checkMatchesWifiPriorityRule(
                         wifiNetworkPriority,
                         mWifiNetworkRecord,
-                        null /* currentlySelecetd */,
+                        false /* isSelected */,
                         null /* carrierConfig */));
     }
 
@@ -516,7 +410,7 @@
                         mCellNetworkRecord,
                         SUB_GROUP,
                         mSubscriptionSnapshot,
-                        null /* currentlySelected */,
+                        false /* isSelected */,
                         null /* carrierConfig */));
     }
 
@@ -543,7 +437,16 @@
 
     @Test
     public void testCalculatePriorityClass() throws Exception {
-        assertEquals(2, mCellNetworkRecord.priorityClass);
+        final int priorityClass =
+                NetworkPriorityClassifier.calculatePriorityClass(
+                        mVcnContext,
+                        mCellNetworkRecord,
+                        VcnGatewayConnectionConfig.DEFAULT_UNDERLYING_NETWORK_TEMPLATES,
+                        SUB_GROUP,
+                        mSubscriptionSnapshot,
+                        false /* isSelected */,
+                        null /* carrierConfig */);
+        assertEquals(2, priorityClass);
     }
 
     private void checkCalculatePriorityClassFailToMatchAny(
@@ -561,10 +464,19 @@
             ncBuilder.addCapability(NET_CAPABILITY_INTERNET);
         }
 
-        final UnderlyingNetworkRecord nonDunNetworkRecord =
-                getTestNetworkRecord(ncBuilder.build(), templatesRequireDun);
+        final UnderlyingNetworkRecord nonDunNetworkRecord = getTestNetworkRecord(ncBuilder.build());
 
-        assertEquals(expectedPriorityClass, nonDunNetworkRecord.priorityClass);
+        final int priorityClass =
+                NetworkPriorityClassifier.calculatePriorityClass(
+                        mVcnContext,
+                        nonDunNetworkRecord,
+                        templatesRequireDun,
+                        SUB_GROUP,
+                        mSubscriptionSnapshot,
+                        false /* isSelected */,
+                        null /* carrierConfig */);
+
+        assertEquals(expectedPriorityClass, priorityClass);
     }
 
     @Test
diff --git a/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkControllerTest.java b/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkControllerTest.java
index 2941fde..992f102 100644
--- a/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkControllerTest.java
+++ b/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkControllerTest.java
@@ -29,13 +29,12 @@
 import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.WIFI_EXIT_RSSI_THRESHOLD_DEFAULT;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.any;
 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;
@@ -67,6 +66,7 @@
 import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
 import com.android.server.vcn.VcnContext;
 import com.android.server.vcn.VcnNetworkProvider;
+import com.android.server.vcn.routeselection.UnderlyingNetworkController.Dependencies;
 import com.android.server.vcn.routeselection.UnderlyingNetworkController.NetworkBringupCallback;
 import com.android.server.vcn.routeselection.UnderlyingNetworkController.UnderlyingNetworkControllerCallback;
 import com.android.server.vcn.routeselection.UnderlyingNetworkController.UnderlyingNetworkListener;
@@ -77,6 +77,7 @@
 import org.mockito.Captor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.mockito.Spy;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -154,10 +155,13 @@
     @Mock private UnderlyingNetworkControllerCallback mNetworkControllerCb;
     @Mock private Network mNetwork;
 
+    @Spy private Dependencies mDependencies = new Dependencies();
+
     @Captor private ArgumentCaptor<UnderlyingNetworkListener> mUnderlyingNetworkListenerCaptor;
 
     private TestLooper mTestLooper;
     private VcnContext mVcnContext;
+    private UnderlyingNetworkEvaluator mNetworkEvaluator;
     private UnderlyingNetworkController mUnderlyingNetworkController;
 
     @Before
@@ -189,13 +193,28 @@
 
         when(mSubscriptionSnapshot.getAllSubIdsInGroup(eq(SUB_GROUP))).thenReturn(INITIAL_SUB_IDS);
 
+        mNetworkEvaluator =
+                spy(
+                        new UnderlyingNetworkEvaluator(
+                                mVcnContext,
+                                mNetwork,
+                                VcnGatewayConnectionConfigTest.buildTestConfig()
+                                        .getVcnUnderlyingNetworkPriorities(),
+                                SUB_GROUP,
+                                mSubscriptionSnapshot,
+                                null));
+        doReturn(mNetworkEvaluator)
+                .when(mDependencies)
+                .newUnderlyingNetworkEvaluator(any(), any(), any(), any(), any(), any());
+
         mUnderlyingNetworkController =
                 new UnderlyingNetworkController(
                         mVcnContext,
                         VcnGatewayConnectionConfigTest.buildTestConfig(),
                         SUB_GROUP,
                         mSubscriptionSnapshot,
-                        mNetworkControllerCb);
+                        mNetworkControllerCb,
+                        mDependencies);
     }
 
     private void resetVcnContext() {
@@ -489,13 +508,7 @@
             NetworkCapabilities networkCapabilities,
             LinkProperties linkProperties,
             boolean isBlocked) {
-        return new UnderlyingNetworkRecord(
-                network,
-                networkCapabilities,
-                linkProperties,
-                isBlocked,
-                false /* isSelected */,
-                0 /* priorityClass */);
+        return new UnderlyingNetworkRecord(network, networkCapabilities, linkProperties, isBlocked);
     }
 
     @Test
@@ -515,24 +528,12 @@
         UnderlyingNetworkRecord recordC =
                 new UnderlyingNetworkRecord(
                         mNetwork,
-                        INITIAL_NETWORK_CAPABILITIES,
-                        INITIAL_LINK_PROPERTIES,
-                        false /* isBlocked */,
-                        true /* isSelected */,
-                        -1 /* priorityClass */);
-        UnderlyingNetworkRecord recordD =
-                getTestNetworkRecord(
-                        mNetwork,
                         UPDATED_NETWORK_CAPABILITIES,
                         UPDATED_LINK_PROPERTIES,
                         false /* isBlocked */);
 
         assertEquals(recordA, recordB);
-        assertEquals(recordA, recordC);
-        assertNotEquals(recordA, recordD);
-
-        assertTrue(UnderlyingNetworkRecord.isEqualIncludingPriorities(recordA, recordB));
-        assertFalse(UnderlyingNetworkRecord.isEqualIncludingPriorities(recordA, recordC));
+        assertNotEquals(recordA, recordC);
     }
 
     @Test
@@ -540,6 +541,19 @@
         verifyRegistrationOnAvailableAndGetCallback();
     }
 
+    @Test
+    public void testUpdateSubscriptionSnapshotAndCarrierConfig() {
+        verifyRegistrationOnAvailableAndGetCallback();
+
+        TelephonySubscriptionSnapshot subscriptionUpdate =
+                mock(TelephonySubscriptionSnapshot.class);
+        when(subscriptionUpdate.getAllSubIdsInGroup(eq(SUB_GROUP))).thenReturn(UPDATED_SUB_IDS);
+
+        mUnderlyingNetworkController.updateSubscriptionSnapshot(subscriptionUpdate);
+
+        verify(mNetworkEvaluator).reevaluate(any(), any(), any(), any());
+    }
+
     private UnderlyingNetworkListener verifyRegistrationOnAvailableAndGetCallback() {
         return verifyRegistrationOnAvailableAndGetCallback(INITIAL_NETWORK_CAPABILITIES);
     }
@@ -583,6 +597,7 @@
                         INITIAL_LINK_PROPERTIES,
                         false /* isBlocked */);
         verifyOnSelectedUnderlyingNetworkChanged(expectedRecord);
+        verify(mNetworkEvaluator).setIsSelected(eq(true), any(), any(), any(), any());
         return cb;
     }
 
@@ -713,7 +728,8 @@
                 VcnGatewayConnectionConfigTest.buildTestConfig(networkTemplates),
                 SUB_GROUP,
                 mSubscriptionSnapshot,
-                mNetworkControllerCb);
+                mNetworkControllerCb,
+                mDependencies);
 
         verify(cm)
                 .registerNetworkCallback(
@@ -724,30 +740,43 @@
         return mUnderlyingNetworkListenerCaptor.getValue();
     }
 
-    private UnderlyingNetworkRecord bringupNetworkAndGetRecord(
+    private UnderlyingNetworkEvaluator bringupNetworkAndGetEvaluator(
             UnderlyingNetworkListener cb,
             NetworkCapabilities requestNetworkCaps,
-            List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates,
-            UnderlyingNetworkRecord currentlySelected) {
+            List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates) {
         final Network network = mock(Network.class);
         final NetworkCapabilities responseNetworkCaps =
                 buildResponseNwCaps(requestNetworkCaps, INITIAL_SUB_IDS);
+        final UnderlyingNetworkEvaluator evaluator =
+                spy(
+                        new UnderlyingNetworkEvaluator(
+                                mVcnContext,
+                                network,
+                                underlyingNetworkTemplates,
+                                SUB_GROUP,
+                                mSubscriptionSnapshot,
+                                null));
+        doReturn(evaluator)
+                .when(mDependencies)
+                .newUnderlyingNetworkEvaluator(any(), any(), any(), any(), any(), any());
 
         cb.onAvailable(network);
         cb.onCapabilitiesChanged(network, responseNetworkCaps);
         cb.onLinkPropertiesChanged(network, INITIAL_LINK_PROPERTIES);
         cb.onBlockedStatusChanged(network, false /* isFalse */);
-        return new UnderlyingNetworkRecord(
-                network,
-                responseNetworkCaps,
-                INITIAL_LINK_PROPERTIES,
-                false /* isBlocked */,
-                mVcnContext,
-                underlyingNetworkTemplates,
-                SUB_GROUP,
-                mSubscriptionSnapshot,
-                currentlySelected,
-                null /* carrierConfig */);
+
+        return evaluator;
+    }
+
+    private void verifySelectNetwork(UnderlyingNetworkEvaluator expectedEvaluator) {
+        verifyOnSelectedUnderlyingNetworkChanged(expectedEvaluator.getNetworkRecord());
+        verify(expectedEvaluator).setIsSelected(eq(true), any(), any(), any(), any());
+    }
+
+    private void verifyNeverSelectNetwork(UnderlyingNetworkEvaluator expectedEvaluator) {
+        verify(mNetworkControllerCb, never())
+                .onSelectedUnderlyingNetworkChanged(eq(expectedEvaluator.getNetworkRecord()));
+        verify(expectedEvaluator, never()).setIsSelected(eq(true), any(), any(), any(), any());
     }
 
     @Test
@@ -759,19 +788,15 @@
         UnderlyingNetworkListener cb = setupControllerAndGetNetworkListener(networkTemplates);
 
         // Bring up CBS network
-        final UnderlyingNetworkRecord cbsNetworkRecord =
-                bringupNetworkAndGetRecord(
-                        cb,
-                        CBS_NETWORK_CAPABILITIES,
-                        networkTemplates,
-                        null /* currentlySelected */);
-        verify(mNetworkControllerCb).onSelectedUnderlyingNetworkChanged(eq(cbsNetworkRecord));
+        final UnderlyingNetworkEvaluator cbsNetworkEvaluator =
+                bringupNetworkAndGetEvaluator(cb, CBS_NETWORK_CAPABILITIES, networkTemplates);
+        verifySelectNetwork(cbsNetworkEvaluator);
 
         // Bring up DUN network
-        final UnderlyingNetworkRecord dunNetworkRecord =
-                bringupNetworkAndGetRecord(
-                        cb, DUN_NETWORK_CAPABILITIES, networkTemplates, cbsNetworkRecord);
-        verify(mNetworkControllerCb).onSelectedUnderlyingNetworkChanged(eq(dunNetworkRecord));
+        final UnderlyingNetworkEvaluator dunNetworkEvaluator =
+                bringupNetworkAndGetEvaluator(cb, DUN_NETWORK_CAPABILITIES, networkTemplates);
+        verifySelectNetwork(dunNetworkEvaluator);
+        verify(cbsNetworkEvaluator).setIsSelected(eq(false), any(), any(), any(), any());
     }
 
     @Test
@@ -783,20 +808,14 @@
         UnderlyingNetworkListener cb = setupControllerAndGetNetworkListener(networkTemplates);
 
         // Bring up DUN network
-        final UnderlyingNetworkRecord dunNetworkRecord =
-                bringupNetworkAndGetRecord(
-                        cb,
-                        DUN_NETWORK_CAPABILITIES,
-                        networkTemplates,
-                        null /* currentlySelected */);
-        verify(mNetworkControllerCb).onSelectedUnderlyingNetworkChanged(eq(dunNetworkRecord));
+        final UnderlyingNetworkEvaluator dunNetworkEvaluator =
+                bringupNetworkAndGetEvaluator(cb, DUN_NETWORK_CAPABILITIES, networkTemplates);
+        verifySelectNetwork(dunNetworkEvaluator);
 
         // Bring up CBS network
-        final UnderlyingNetworkRecord cbsNetworkRecord =
-                bringupNetworkAndGetRecord(
-                        cb, CBS_NETWORK_CAPABILITIES, networkTemplates, dunNetworkRecord);
-        verify(mNetworkControllerCb, never())
-                .onSelectedUnderlyingNetworkChanged(eq(cbsNetworkRecord));
+        final UnderlyingNetworkEvaluator cbsNetworkEvaluator =
+                bringupNetworkAndGetEvaluator(cb, CBS_NETWORK_CAPABILITIES, networkTemplates);
+        verifyNeverSelectNetwork(cbsNetworkEvaluator);
     }
 
     @Test
@@ -808,13 +827,9 @@
         UnderlyingNetworkListener cb = setupControllerAndGetNetworkListener(networkTemplates);
 
         // Bring up an Internet network without DUN capability
-        final UnderlyingNetworkRecord networkRecord =
-                bringupNetworkAndGetRecord(
-                        cb,
-                        INITIAL_NETWORK_CAPABILITIES,
-                        networkTemplates,
-                        null /* currentlySelected */);
-        verify(mNetworkControllerCb).onSelectedUnderlyingNetworkChanged(eq(networkRecord));
+        final UnderlyingNetworkEvaluator evaluator =
+                bringupNetworkAndGetEvaluator(cb, INITIAL_NETWORK_CAPABILITIES, networkTemplates);
+        verifySelectNetwork(evaluator);
     }
 
     @Test
@@ -825,10 +840,8 @@
                 new VcnCellUnderlyingNetworkTemplate.Builder().setDun(MATCH_REQUIRED).build());
         UnderlyingNetworkListener cb = setupControllerAndGetNetworkListener(networkTemplates);
 
-        bringupNetworkAndGetRecord(
-                cb, CBS_NETWORK_CAPABILITIES, networkTemplates, null /* currentlySelected */);
-
-        verify(mNetworkControllerCb, never())
-                .onSelectedUnderlyingNetworkChanged(any(UnderlyingNetworkRecord.class));
+        final UnderlyingNetworkEvaluator evaluator =
+                bringupNetworkAndGetEvaluator(cb, CBS_NETWORK_CAPABILITIES, networkTemplates);
+        verifyNeverSelectNetwork(evaluator);
     }
 }
diff --git a/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluatorTest.java b/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluatorTest.java
new file mode 100644
index 0000000..985e70c
--- /dev/null
+++ b/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluatorTest.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.vcn.routeselection;
+
+import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.PRIORITY_INVALID;
+import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.net.vcn.VcnGatewayConnectionConfig;
+import android.os.PersistableBundle;
+
+import org.junit.Before;
+import org.junit.Test;
+
+public class UnderlyingNetworkEvaluatorTest extends NetworkEvaluationTestBase {
+    private PersistableBundleWrapper mCarrierConfig;
+
+    @Before
+    public void setUp() throws Exception {
+        super.setUp();
+        mCarrierConfig = new PersistableBundleWrapper(new PersistableBundle());
+    }
+
+    private UnderlyingNetworkEvaluator newUnderlyingNetworkEvaluator() {
+        return new UnderlyingNetworkEvaluator(
+                mVcnContext,
+                mNetwork,
+                VcnGatewayConnectionConfig.DEFAULT_UNDERLYING_NETWORK_TEMPLATES,
+                SUB_GROUP,
+                mSubscriptionSnapshot,
+                mCarrierConfig);
+    }
+
+    @Test
+    public void testInitializedEvaluator() throws Exception {
+        final UnderlyingNetworkEvaluator evaluator = newUnderlyingNetworkEvaluator();
+
+        assertFalse(evaluator.isValid());
+        assertEquals(mNetwork, evaluator.getNetwork());
+        assertEquals(PRIORITY_INVALID, evaluator.getPriorityClass());
+
+        try {
+            evaluator.getNetworkRecord();
+            fail("Expected to fail because evaluator is not valid");
+        } catch (Exception expected) {
+        }
+    }
+
+    @Test
+    public void testValidEvaluator() {
+        final UnderlyingNetworkEvaluator evaluator = newUnderlyingNetworkEvaluator();
+        evaluator.setNetworkCapabilities(
+                CELL_NETWORK_CAPABILITIES,
+                VcnGatewayConnectionConfig.DEFAULT_UNDERLYING_NETWORK_TEMPLATES,
+                SUB_GROUP,
+                mSubscriptionSnapshot,
+                mCarrierConfig);
+        evaluator.setLinkProperties(
+                LINK_PROPERTIES,
+                VcnGatewayConnectionConfig.DEFAULT_UNDERLYING_NETWORK_TEMPLATES,
+                SUB_GROUP,
+                mSubscriptionSnapshot,
+                mCarrierConfig);
+        evaluator.setIsBlocked(
+                false /* isBlocked */,
+                VcnGatewayConnectionConfig.DEFAULT_UNDERLYING_NETWORK_TEMPLATES,
+                SUB_GROUP,
+                mSubscriptionSnapshot,
+                mCarrierConfig);
+
+        final UnderlyingNetworkRecord expectedRecord =
+                new UnderlyingNetworkRecord(
+                        mNetwork,
+                        CELL_NETWORK_CAPABILITIES,
+                        LINK_PROPERTIES,
+                        false /* isBlocked */);
+
+        assertTrue(evaluator.isValid());
+        assertEquals(mNetwork, evaluator.getNetwork());
+        assertEquals(2, evaluator.getPriorityClass());
+        assertEquals(expectedRecord, evaluator.getNetworkRecord());
+    }
+}
diff --git a/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionFix.kt b/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionFix.kt
index 25d208d..5cbb1aa 100644
--- a/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionFix.kt
+++ b/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionFix.kt
@@ -77,7 +77,9 @@
             .autoFix()
             .build()
 
-        return LintFix.create().composite(annotateFix, *replaceOrRemoveFixes.toTypedArray())
+        return LintFix.create()
+            .name(annotateFix.getDisplayName())
+            .composite(annotateFix, *replaceOrRemoveFixes.toTypedArray())
     }
 
     private val annotation: String